Spring获取某注解下的所有类(bean),前提是该类在是spring容器进行了注册。Spring获取bean的注解为null
代码重现
注解类NettyHandler
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NettyHandler {
String name();
int order();
}
无法获取bean的注解NettyHandler
监听Application,在项目准备就绪时获取被@NettyHandler注释的beans,再通过bean获取@NettyHandler的信息
import xxx.annotation.NettyHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import java.util.Map;
@Slf4j
@Component
public class AnnotationListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
System.out.println("=============" + event.getApplicationContext());
Map<String, Object> beansWithAnnotation = event.getApplicationContext().getBeansWithAnnotation(NettyHandler.class);
for (Object value : beansWithAnnotation.values()) {
NettyHandler nettyHandler = value.getClass().getAnnotation(NettyHandler.class);
if (nettyHandler == null) {
log.error("NettyHandler is Null");
} else {
log.info("NettyHandler is not Null");
}
}
}
}
通过日志可知,该listener会被执行两次:
第一次在event.getApplicationContext()为对象org.springframework.context.annotation.AnnotationConfigApplicationContext。
第二次在event.getApplicationContext()为对象org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext。
只有第二次event.getApplicationContext().getBeansWithAnnotation(NettyHandler.class)才获取到数据,打印信息如下:
2020-04-24 08:45:39,623 | INFO | TraceId: | NettyHandler is not Null
2020-04-24 08:45:39,623 | INFO | TraceId: | NettyHandler is not Null
2020-04-24 08:45:39,630 | ERROR | TraceId: | NettyHandler is Null
2020-04-24 08:45:39,636 | ERROR | TraceId: | NettyHandler is Null
2020-04-24 08:45:39,642 | ERROR | TraceId: | NettyHandler is Null
问题分析
经对比,能获取到注解的bean和没有获取的注解的bean的信息,发现两个bean存在差异,没有获取到注解信息的bean中有如下信息:
由此可见,该bean是Spring通过CGLIB生成的代理对象,不是原始对象,因此无法通过代理对象的Class信息获取注解信息。检查对象的java代码,发现是使用了注解@Async,去掉@Async注解后,bean对象可以获取到注解信息了。
但,如果一定要在bean对象中放入类似于@Async的注解,或其它代表切面的信息,该怎么办呢?可以按照如下代码处理。
解决方案
import xxx.annotation.NettyHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import java.util.Map;
@Slf4j
@Component
public class AnnotationListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
System.out.println("=============" + event.getApplicationContext());
Map<String, Object> beansWithAnnotation = event.getApplicationContext().getBeansWithAnnotation(NettyHandler.class);
//方法一:可以获取非代理类的注解,但无法获取代理类的注解
for (Object value : beansWithAnnotation.values()) {
NettyHandler nettyHandler = AnnotationUtils.getAnnotation(value.getClass(), NettyHandler.class);
if (nettyHandler == null) {
log.error("NettyHandler is Null");
} else {
log.info("NettyHandler is not Nul");
}
}
//方法二:可以获取代理类和非代理类的注解
for (String beanName : beansWithAnnotation.keySet()) {
ConfigurableListableBeanFactory clbf = event.getApplicationContext().getBeanFactory();
Object value = clbf.getSingleton(beanName);
if (value != null) {
NettyHandler nettyHandler = AnnotationUtils.findAnnotation(value.getClass(), NettyHandler.class);
if (nettyHandler == null) {
log.error("beanFactroy: NettyHandler is Null");
} else {
log.info("beanFactroy: NettyHandler is not Nul");
}
}
}
//方法三:可以获取代理类的注解,但无法获取非代理类的注解
try {
for (Object value : beansWithAnnotation.values()) {
//仅适用于代理类
NettyHandler nettyHandler = Class.forName(value.getClass().getGenericSuperclass().getTypeName()).getAnnotation(NettyHandler.class);
if (nettyHandler == null) {
log.error("Class.forName: NettyHandler is Null");
} else {
log.info("Class.forName: NettyHandler is not Nul");
}
}
}catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
日志信息
2020-04-24 10:49:35,158 | INFO | TraceId: | NettyHandler is not Nul
2020-04-24 10:49:35,159 | INFO | TraceId: | NettyHandler is not Nul
2020-04-24 10:49:35,159 | ERROR | TraceId: | NettyHandler is Null
2020-04-24 10:49:35,160 | ERROR | TraceId: | NettyHandler is Null
2020-04-24 10:49:35,160 | ERROR | TraceId: | NettyHandler is Null
2020-04-24 10:49:35,161 | INFO | TraceId: | beanFactroy: NettyHandler is not Nul
2020-04-24 10:49:35,161 | INFO | TraceId: | beanFactroy: NettyHandler is not Nul
2020-04-24 10:49:35,161 | INFO | TraceId: | beanFactroy: NettyHandler is not Nul
2020-04-24 10:49:35,161 | INFO | TraceId: | beanFactroy: NettyHandler is not Nul
2020-04-24 10:49:35,161 | INFO | TraceId: | beanFactroy: NettyHandler is not Nul
2020-04-24 10:49:35,162 | ERROR | TraceId: | Class.forName: NettyHandler is Null
2020-04-24 10:49:35,162 | ERROR | TraceId: | Class.forName: NettyHandler is Null
2020-04-24 10:49:35,163 | INFO | TraceId: | Class.forName: NettyHandler is not Nul
2020-04-24 10:49:35,163 | INFO | TraceId: | Class.forName: NettyHandler is not Nul
2020-04-24 10:49:35,163 | INFO | TraceId: | Class.forName: NettyHandler is not Nul
由日志可见,使用方法二更加保险。方法一只适用于非代理类,方法三只适用于代理类。
调整后代码
import xxx.annotation.NettyHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.web.context.ConfigurableWebServerApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import java.util.Map;
@Slf4j
@Component
public class AnnotationListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
//避免不必要的重复执行
if (event.getApplicationContext() instanceof ConfigurableWebServerApplicationContext) {
System.out.println("=============" + event.getApplicationContext());
Map<String, Object> beansWithAnnotation = event.getApplicationContext().getBeansWithAnnotation(NettyHandler.class);
//方法二:可以获取代理类和非代理类的注解
for (String beanName : beansWithAnnotation.keySet()) {
ConfigurableListableBeanFactory clbf = event.getApplicationContext().getBeanFactory();
Object value = clbf.getSingleton(beanName);
if (value != null) {
NettyHandler nettyHandler = AnnotationUtils.findAnnotation(value.getClass(), NettyHandler.class);
if (nettyHandler == null) {
log.error("beanFactroy: NettyHandler is Null");
} else {
log.info("beanFactroy: NettyHandler is not Nul");
}
}
}
}
}
}