Spring

[Spring]@EnableAspectJAutoProxy原

2021-03-13  本文已影响0人  AbstractCulture

@EnableAspectJAutoProxy

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

proxyTargetClass

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

   @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

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
  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方法.

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

@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        BeanDefinitionRegistry registry, @Nullable Object source) {

    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

这里我们发现了一个新的角色-AnnotationAwareAspectJAutoProxyCreator,该类就是Spring用来处理应用上下文中被@AspectJ注解标记的类的.继续进入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即可,大体上设置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);

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

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

}

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

1 2

这里,我们验证了:

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

扩展阅读

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

总结

总结
上一篇 下一篇

猜你喜欢

热点阅读