implosivesilence
implosivesilence

Reputation: 546

Unable to read custom annotation in spring boot

I have some custom annotation to distinguish the Service classes as

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) 
public @interface DataService {
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) 
public @interface FooData {
    String value() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) 
public @interface BarData {
    String value() default "";
}

And My Service layer looks something like :

public interface IService {
    String getData();
}

@DataService
@FooData("Foo Service")
@Service
public class FooService implements IService {
    @Override
    public String getData() {
        return "Data from FooService";
    }
}

@DataService
@BarData("Bar Service")
@Service
public class BarService implements IService {
    @Override
    public String getData() {
        return "Data from BarService";
    }
}

I am creating Map based on this custom annotations at application startup using logic written as:

@Component
public class AnnotationProcessor {

    @Autowired
    private ApplicationContext applicationContext;

    // Map to store services with annotation values as keys
    private Map<String, IService> serviceMap = new HashMap<>();

    @PostConstruct
    public void init() {
        // Get all beans annotated with @DataService
        Map<String, Object> dataServiceBeans = applicationContext.getBeansWithAnnotation(DataService.class);
        System.out.println("Services annotated with @DataService:");

        // Iterate through the beans and collect their names, classes, and annotation values
        for (Map.Entry<String, Object> entry : dataServiceBeans.entrySet()) {
            String beanName = entry.getKey();
            IService bean = (IService) entry.getValue(); // Cast to IService
            System.out.println("Bean Name: " + beanName + ", Class: " + bean.getClass().getSimpleName());

            String key = null;

            // Check for FooData annotation
            // if condition failing here
            if (bean.getClass().isAnnotationPresent(FooData.class)) {
                FooData fooData = bean.getClass().getAnnotation(FooData.class);
                key = fooData.value(); // getting NPE here
                serviceMap.put(key, bean);
            }

            // Check for BarData annotation
            if (bean.getClass().isAnnotationPresent(BarData.class)) {
                BarData barData = bean.getClass().getAnnotation(BarData.class);
               
                    key = barData.value(); 
                    serviceMap.put(key, bean);
                
            }

            
        }

    }
}

And My Main class:

@SpringBootApplication
@EnableCaching
public class MySpringBootApplication {

    public static void main(String[] args) {
        SpringApplication.run(MySpringBootApplication.class, args);
    }
}

Earlier this code was working just fine, but once I have added spring-boot-starter-cache dependency and added annotation @EnableCaching on the main class, I am not able to read the custom annotation's value. My isAnnotationPresent check is failing for some of the classes and if I try to read the annotation's value without any check then I am getting NullPointerException. I tried printing class's name, before adding @EnableCaching annotation it was printing something like:

FooService
BarService

but after adding @EnableCaching on the main class, It is printing like:

FooService$$SpringCGLIB$$0
BarService$$SpringCGLIB$$0

I know there is some kind of proxy mechanism happening here cause of this caching but I am not sure what is happening in the background and how to fix the problem. How to to get the annotation value? I am using Spring Boot 3.2.2 and java version 17. Any help would be appreciated.

Upvotes: 1

Views: 63

Answers (1)

Olufemi Thompson
Olufemi Thompson

Reputation: 29

Could you share a copy of your pom.xml? I tried running your code with the same setup, including @EnableCaching and spring-boot-starter-cache, and it worked as expected. Here’s the output I see:

Services annotated with @DataService:
Bean Name: barService, Class: BarService
Bean Name: fooService, Class: FooService

One thing you might want to try is using AopProxyUtils.ultimateTargetClass(bean) in your AnnotationProcessor.

This approach helps because AopProxyUtils.ultimateTargetClass(bean) retrieves the original class behind any proxies that Spring might create when caching is enabled.

When you use this method, you’ll get the actual class name, which ensures that any annotations on the original class are accessible, even when proxies are involved.

Here’s an example of how you might update the code:

IService bean = (IService) entry.getValue();
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
System.out.println("Bean Name: " + beanName + ", Class: " + targetClass.getSimpleName());

Using targetClass like this should prevent NullPointerException issues caused by accessing annotations on a proxy class. Let me know if this helps!

Upvotes: 1

Related Questions