Reputation: 546
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
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