spring動態註冊bean會使AOP失效?

linyb極客之路發表於2022-04-12

前言

本文的素材來自讀者的一個問題,他看過我之前寫的一篇博文聊聊如何把第三方服務註冊到我們專案的spring容器中。剛好他專案中也有類似這樣的一個需求,他就採用我文中介紹的第三種方法

呼叫beanFactory.registerSingleton()

一開始專案執行得還可以,後面他在這個第三方服務中使用AOP,發現AOP始終沒有生效。於是他就給我留言了。今天就來聊一下這個話題,為什麼使用registerSingleton()註冊的bean,無法使AOP生效

問題根源

registerSingleton()這個方法直接將bean存放到單例池裡面了。

如果對bean的生命週期有了解的朋友,應該會知道,bean可能會經過一系列的後置處理器後,再存放到單例池裡面。因此這個bean可能是會被增強的,其中當然包括經過AOP增強

而使用registerSingleton()相當於是直接走捷徑,不經過後置處理器,一步到位直接存放到單例池中。如果第三方服務是直接通過new出來的,就是一個普通的物件,因此注入到IOC容器後,也只是一個普通的bean,並沒有任何增強

問題修復

方案一:不使用registerSingleton(),而是使用BeanDefinition註冊方式

這種方式本質是讓這個物件完整經歷了bean的生命週期

示例:

@Configuration
public class HelloServiceConfiguration implements BeanFactoryPostProcessor {


    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
        AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
        beanDefinition.setBeanClass(HelloService.class);
        HelloServiceProperties properties = new HelloServiceProperties();
        properties.setBeanName("helloService");
        beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,properties);
        defaultListableBeanFactory.registerBeanDefinition(properties.getBeanName(),beanDefinition);

    }
}
方案二、使用registerSingleton(),但注入的物件不是用new出來的,而是直接注入AOP代理物件

主要利用AOP的代理api:AnnotationAwareAspectJAutoProxyCreator

示例

@Configuration
public class HelloServiceWithProxyConfiguration implements BeanFactoryAware, InitializingBean {

    private BeanFactory beanFactory;

    @Autowired
    private AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator;


    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void afterPropertiesSet() throws Exception {

        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory)beanFactory;
        HelloServiceProperties properties = new HelloServiceProperties();
        properties.setBeanName("helloService");
        HelloService helloServicePrxoy = (HelloService) annotationAwareAspectJAutoProxyCreator.postProcessAfterInitialization(new HelloService(properties), "helloService$$Prxoy");
        defaultListableBeanFactory.registerSingleton(properties.getBeanName(),helloServicePrxoy);

    }
}

總結

以上兩種方案,建議使用方案一。因為方案一完整經歷過bean的生命週期,這就意味著可以獲取spring提供的各種增強功能。方案二反而更像是硬編碼進去,如果後面要使用spring的其他增強的功能,就還必須呼叫其他API。不過如果可以確定業務不會使用spring提供的各種擴充套件功能。方案二也是可以的

demo連結

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-registerSingleton-aop-invalid

相關文章