Spring獲取某註解下的所有類(bean),前提是該類在是spring容器進行了註冊。Spring獲取註解爲null

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");
                    }
                }
            }
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章