美文网首页Spring
[Spring]@EnableAspectJAutoProxy原

[Spring]@EnableAspectJAutoProxy原

作者: AbstractCulture | 来源:发表于2021-03-13 22:55 被阅读0次

    @EnableAspectJAutoProxy

    @EnableAspectJAutoProxy注解是Spring AOP开启的标志,在启动类标记此注解,即可加载对应的切面类逻辑.此注解的ElementTypeTYPE,表示标记在类上。同时用@Retention(RetentionPolicy.RUNTIME)声明了注解在运行时得到保留。此外最重要的是使用了@Import(AspectJAutoProxyRegistrar.class)来注册AOP的

    proxyTargetClass

    @EnableAspectJAutoProxy支持处理标有AspectJ的@Aspect批注的组件,用户可以主动声明proxyTargetClass来指定Spring AOP使用哪种动态代理方式来创建代理类(默认使用基于实现接口的JDK动态代理方式).

    • 使用CGLIB动态代理来创建代理类
       @Configuration
       @EnableAspectJAutoProxy(proxyTargetClass=true)
       @ComponentScan("com.foo")
       public class AppConfig {
           // ...
       }
    

    exposeProxy

    为了解决一些由于代理引发的切面失效问题,Spring AOP在Spring 4.3.1后引入了AopContext类来将代理类的引用存储在ThreadLocal中,通过AopContext可以快速获取当前类的代理类.
    默认为不支持,如果声明为true,即可使用AopContext获取代理类.

    同时,为了使用AspectJ,需要确保当前jar仓库存在aspectjweaver.

    通过@Import注册AspectJAutoProxyRegistrar

    通常情况下,我们的启动类本身也是一个Bean,Spring支持使用@Import来导入一个没有标记任何Spring 注解的类来将该Java类注册成Spring的Bean.
    @EnableAspectJAutoProxy注解正是通过@Import的方式来将AspectJAutoProxyRegistrar类注册成Spring的Bean,以便在容器解析切面类时派上用场.
    那么AspectJAutoProxyRegistrar类的作用是什么?
    我们从JavaDoc中可以看到这样一句话:

    Registers an AnnotationAwareAspectJAutoProxyCreator against the current BeanDefinitionRegistry as appropriate based on a given @EnableAspectJAutoProxy annotation.
    根据当前BeanDefinitionRegistry在适当的位置注册AnnotationAwareAspectJAutoProxyCreator。

    UML

    UML
    • ImportBeanDefinitionRegistrar: 用来导入一些特殊的BeanDefinition,Spring在处理@Configuration时,会去扫描是否有通过@Import标签导入的类,对ImportBeanDefinitionRegistrar这类接口,还会执行其中的registerBeanDefinitions方法.
    • AspectJAutoProxyRegistrar: 实现了ImportBeanDefinitionRegistrar接口,用来注册AspectJAnnotationAutoProxyCreator,也就是支持注解驱动(同时兼容XML)解析的AspectJ自动代理创建器.

    AspectJAutoProxyRegistrar

    class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    
        @Override
        public void registerBeanDefinitions(
                AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            // 向容器注册AspectJAnnotationAutoProxyCreator
            AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    
            AnnotationAttributes enableAspectJAutoProxy =
                    AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
            // 如果@EnableAspectJAutoProxy上存在标签内容
            if (enableAspectJAutoProxy != null) {
                // 如果proxyTargetClass为true,则强制指定AutoProxyCreator使用CGLIB进行代理
                if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                }
                // 是否开启exposeProxy特性
                if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                    AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
                }
            }
        }
    
    }
    

    这里关键的地方就是通过AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);向容器注册AspectJAnnotationAutoProxyCreator.
    随后解析@EnableAspectJAutoProxy注解上的元数据来决定是否开启上述我们讲到的proxyTargetClassexposeProxy特性.
    为了了解registerBeanDefinitions方法的执行链路和调用时机,我们使用IDE的debug来查看调用栈分析执行流程.

    • refresh中激活后置处理器ConfigurationClassPostProcessor加载@Configuration上的元数据
    refresh
    1. 首先容器会加载refresh方法.
    2. 执行invokeBeanFactoryPostProcessors(beanFactory);激活工厂级别的后置处理器.
    3. 由于启动类都是被@Configuration标记的,Spring会使用ConfigurationClassPostProcessor来解析被@Configuration的类.
    4. 使用ConfigurationClassBeanDefinitionReader来加载配置类解析成BeanDefinition.
    5. org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass中,会执行loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());这行代码,从代码的语义上我们可以大致可以猜出来,这是解析当前配置类上是否存在通过@Import导入的实现了ImportBeanDefinitionRegistrar的类,
    AspectJAutoProxyRegistrar
    1. 回调registrar的registerBeanDefinitions方法.
    2. 执行AspectJAutoProxyRegistrar#registerBeanDefinitions方法.
    • AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary

    使用IDE跳进AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);中,向下跳2层,来到AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary方法中.

    • org.springframework.aop.config.AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)
    @Nullable
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
            BeanDefinitionRegistry registry, @Nullable Object source) {
    
        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }
    

    这里我们发现了一个新的角色-AnnotationAwareAspectJAutoProxyCreator,该类就是Spring用来处理应用上下文中被@AspectJ注解标记的类的.继续进入registerOrEscalateApcAsRequired方法中看看注册流程.

    • org.springframework.aop.config.AopConfigUtils#registerOrEscalateApcAsRequired
    private static BeanDefinition registerOrEscalateApcAsRequired(
            Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        // 当前容器是否包含 org.springframework.aop.config.internalAutoProxyCreator
        if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
            BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
            if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                int requiredPriority = findPriorityForClass(cls);
                if (currentPriority < requiredPriority) {
                    apcDefinition.setBeanClassName(cls.getName());
                }
            }
            return null;
        }
        // 将传入的class包装成BeanDefinition,然后注册到容器中,并讲其order的执行顺序调整为最优  
        // 在aop中,这里会注册AnnotationAwareAspectJAutoProxyCreator.class这个类
        RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
        beanDefinition.setSource(source);
        beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
        return beanDefinition;
    }
    

    这里的逻辑较为清晰.
    首先查看当前容器中是否包含org.springframework.aop.config.internalAutoProxyCreator的BeanDefiniton.
    如果没有,将传入的class(在此处传入了AnnotationAwareAspectJAutoProxyCreator.class)包装成RootBeanDefinition,然后注册到容器中.

    • 设置proxyTargetClass与exposeProxy

    这个也是比较清晰的,这里简单讲一下.我们看一下如何设置proxyTargetClass即可,大体上设置proxyTargetClass与exposeProxy的逻辑都是相通的.

    // 如果@EnableAspectJAutoProxy上存在标签内容
    if (enableAspectJAutoProxy != null) {
        // 如果proxyTargetClass为true,则强制指定AutoProxyCreator使用CGLIB进行代理
        if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }
        // 是否开启exposeProxy特性
        if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
        }
    }
    

    进入AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);

    • AopConfigUtils#forceAutoProxyCreatorToUseClassProxying
    public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {  
        // 如果容器中包含 org.springframework.aop.config.internalAutoProxyCreator
        if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {  
            // 取出 org.springframework.aop.config.internalAutoProxyCreator的BeanDefinition
            BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);  
            // 设置proxyTargetClass为true
            definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
        }
    }
    

    如果容器中包含名为org.springframework.aop.config.internalAutoProxyCreator的BeanDefinition,那么取出该BeanDefinition,设置proxyTargetClasstrue.
    这里随便提及一下,Spring为了兼容不同的BeanDefinition持有不同的属性值,将它们都抽象成了MutablePropertyValues,所以definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);就跟我们平时JavaBean中的set方法是一样的.

    简单理解@Import和ImportBeanDefinitionRegistrar

    下面我们通过两个用例来理解@ImportImportBeanDefinitionRegistrar

    通过@Import导入类让Spring进行管理

    public class NeedImportBean {
    
        public void doSomething(){
            Logger.getGlobal().info("Through @Import registry to a bean ");
        }
    
    }
    
    • 在启动类中将NeedImportBean导入
    /**
     * @author jaymin
     * 2020/11/30 20:13
     */
    @Configuration
    @ComponentScan(value = "com.xjm")
    @Import(NeedImportBean.class)
    @EnableAspectJAutoProxy
    public class ApplicationConfig {
        public static AnnotationConfigApplicationContext getApplicationContext() {
            return new AnnotationConfigApplicationContext(ApplicationConfig.class);
        }
    }
    
    • 对NeedImportBean做getBean
    public class BeanFactoryDemo {
        public static void main(String[] args) throws Exception {
            AnnotationConfigApplicationContext applicationContext = ApplicationConfig.getApplicationContext();
            NeedImportBean needImportBean = applicationContext.getBean(NeedImportBean.class);
        }
    }
    
    • 输出结果
    result

    可以看到,通过@Import可以将没有被Spring注解标记的类进行注册,注册后即可当成普通的Bean来使用。

    实现ImportBeanDefinitionRegistrar

    现在我们换一种方式,让NeedImportBean实现ImportBeanDefinitionRegistrar接口,然后验证两个点:

    1. 是否会回调registerBeanDefinitions方法。
    2. 通过getBean是否能获取NeedImportBean
    public class NeedImportBean implements ImportBeanDefinitionRegistrar{
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            Logger.getGlobal().info("Through implements ImportBeanDefinitionRegistrar and @Import to callback me.");
        }
    
        public void doSomething(){
            Logger.getGlobal().info("Through @Import registry to a bean ");
        }
    
    }
    
    • Result

    测试的方法于上面一致.这里不重复贴了.

    1 2

    这里,我们验证了:

    1. 实现了ImportBeanDefinitionRegistrar接口后会执行回调
    2. 实现了ImportBeanDefinitionRegistrar接口后,通过@Import并不会注册成Bean.

    扩展阅读

    在搜寻资料的过程中,发现此文章对ImportBeanDefinitionRegistrar解析的比较透彻.有兴趣可以阅读一下:
    点我前往

    总结

    总结

    相关文章

      网友评论

        本文标题:[Spring]@EnableAspectJAutoProxy原

        本文链接:https://www.haomeiwen.com/subject/wkefcltx.html