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");
}
}
}
}
}
}