likes
comments
collection
share

Spring AOP 引入——AOP 源码解析(六)

作者站长头像
站长
· 阅读数 28

一、概述

到此为止,AOP的具体实现已经分析的差不多了,下面关注最后一个问题,动态代理是如何启用的,其实无非两种配置方式。

  1. 注解方式配置,使用@EnableAspectJAutoProxy启动AOP;
  2. xml方式配置,使用aop:aspectj-autoproxy/标签引入。

本文主要以注解配置的方式为例,分析一下,Spring如何启用AOP。其实也就是分析,@EnableAspectJAutoProxy是如何把AnnotationAwareAspectJAutoProxyCreator这个PostProcesser注册到BeanFactory中的。

二、@EnableAspectJAutoProxy

打开EnableAspectJAutoProxy可以看到一个Import注解,引入了AspectJAutoProxyRegistrar,这就是注册AOP所需要组件的注册类,实现了ImportBeanDefinitionRegistrar接口,用于灵活的注册组件。实现也跟简单,就是注册了AnnotationAwareAspectJAutoProxyCreator这个BeanPostProcessor。

分析路径 @EnableAspectJAutoProxy -> @Import(AspectJAutoProxyRegistrar.class) -> implements ImportBeanDefinitionRegistrar

@Override
public void registerBeanDefinitions(
        AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    // 在这里注册AnnotationAwareAspectJAutoProxyCreator,是一个BeanPostProcessor,用来增强Bean
    AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    // 这里处理了proxyTargetClass和exposeProxy属性
    AnnotationAttributes enableAspectJAutoProxy =
            AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
    if (enableAspectJAutoProxy != null) {
        // 是否强制使用CGLIB代理
        if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }
        // 是否要把代理对象暴露出来
        if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
        }
    }
}
源码出自:org.springframework.context.annotation.AspectJAutoProxyRegistrar.registerBeanDefinitions

2.1 注册AnnotationAwareAspectJAutoProxyCreator

注册AnnotationAwareAspectJAutoProxyCreator的过程还是比较简单的,调用了AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,最终调用了

@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
      Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

   Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

   if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
      //已经存在,判断下优先级,如果参数cls类的优先级更高,则替换class
      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;
   }

   //没有冲突,则注册到BeanDefinitionRegistry中
   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;
}

从名字就能看出来,是注册或者增强(Escalate)AutoProxyCreator

  • 如果不存在BeanName是‘org.springframework.aop.config.internalAutoProxyCreator’的beanDefinition,就注册这个bean
  • 如果存在,获取这个beanDefinition下的class,与参数中的cls比较优先级,参数中的优先级更高,就替换这个beanDefinition的class;

优先级是写死的,AnnotationAwareAspectJAutoProxyCreator的优先级最高:

private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<>(3);
static {
    // Set up the escalation list...
    APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
    APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
    APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
}

此处cls的参数就是AnnotationAwareAspectJAutoProxyCreator.class,优先级是最高的。

还有个比较有趣的细节是,这个BeanPostProcessor的优先级是最高的,注册的时候,设置了order是最高优先级,Integer.MIN_VALUE,也就是说,这个BeanPostProcessor中最先执行。 beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);

2.2 proxyTargetClass & exposeProxy

回顾下proxyTargetClass & exposeProxy这两个属性,这些之前都提到过,这里简单介绍一下。

  • proxyTargetClass - AOP有两种实现,默认Spring自动选择,如果实现了接口,就使用JDK,否则是CGLIB,也可以强制指定使用CGLIB,可以在EnableAspectJAutoProxy注解中,设置proxyTargetClass=true。
  • exposeProxy - 是否把当前代理对象,作为线程变量,暴露出来。就是解决被代理对象,内部调用的问题。暴露出来后,代理后的对象,会放置在AopContext的线程变量中,可以通过((XXXService) AopContext.currentProxy()).another();方式调用,解决内部调用无法代理的问题。

三、xml的方式引入

xml引入的方式与注解代码差不多,只是入口不同,xml的方式,需要引入aop:aspectj-autoproxy标签,通过查找AOP对应包下的spring.handlers,就可以逐步找到对应代码,最后也是调用AopNamespaceUtils的注册代码。

分析路径: spring.handlers -> AopNamespaceHandler -> AspectJAutoProxyBeanDefinitionParser -> AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary

这里不再详细对代码进行解释。

四、TargetSource

之前已经详细讨论过TargetSource这个东西了,在上一篇文章中,还留了个小尾巴,如何把TargetSourceCreator注册到AnnotationAwareAspectJAutoProxyCreator中(确切说是AbstractAutoProxyCreator),实现使用TargetSource扩展功能的目的。

本文我们分析了AnnotationAwareAspectJAutoProxyCreator是如何引入的。那么我们现在思考一下,该如何引入自定义的TargetSourceCreator。如果你对Spring IOC的扩展方式比较了解的话,那么可能会想到多种思路,我在这里抛砖引玉,提出一种使用BeanFactoryPostProcessor的方式,注入自定义的TargetSourceCreator。

BeanFactoryPostProcessor这个接口,有个postProcessBeanFactory方法,可以在beanFactory初始化完成后执行,我们可以利用这个扩展,修改bean的定义。我们就在这里,修改代表AnnotationAwareAspectJAutoProxyCreator的bean的定义。

4.1 LazyInitTargetSourceCreator

首先我们要有一个TargetSourceCreator,我们也不自定义了,直接使用spring已经给我们提供的一个LazyInitTargetSourceCreator,它解决什么问题呢?

假设你有两个bean,分别是AopService和AopDao,前者依赖后者。现在我们希望AopDao懒加载,那么可以使用@Lazy注解实现这个能力。但是这样操作后,你就会发现,行不通。因为AopService并不是懒加载的,它依赖AopDao,在AopService初始化时,也会把他的依赖,也就是AopDao实例化。

@Service
public class AopService {

    @Resource
    private AopDao aopDao;

    public void doSomething(){
        aopDao.selectAll();
        aopDao.update("xxx");
    }
}

@Repository
@Lazy
public class AopDaoImpl implements AopDao {

    @Override
    public List<String> selectAll() {
        return Arrays.asList("hello", "world");
    }

    @Override
    public int update(String s) {
        System.out.println("update " + s);
        return 1;
    }
}

怎么办呢,就可以使用LazyInitTargetSourceCreator。他会把懒加载的对象,包装为TargetSource,只有在真正调用的时候,才会实例化。

protected AbstractBeanFactoryBasedTargetSource createBeanFactoryBasedTargetSource(
      Class<?> beanClass, String beanName) {
   //如果是懒加载,则包装为TargetSource,那么这个bean,直到调用TargetSource的getTarget方法时,才会实例化
   if (getBeanFactory() instanceof ConfigurableListableBeanFactory) {
      BeanDefinition definition =
            ((ConfigurableListableBeanFactory) getBeanFactory()).getBeanDefinition(beanName);
      if (definition.isLazyInit()) {
         return new LazyInitTargetSource();
      }
   }
   return null;
}
//代码出自:org.springframework.aop.framework.autoproxy.target.LazyInitTargetSourceCreator

LazyInitTargetSource的懒加载实现,也很简单,就是初次调用才初始化。

@Override
@Nullable
public synchronized Object getTarget() throws BeansException {
   if (this.target == null) {
      this.target = getBeanFactory().getBean(getTargetBeanName());
      postProcessTargetObject(this.target);
   }
   return this.target;
}

4.2 自定义BeanFactoryPostProcessor

有了TargetSourceCreator,下面我们按照思路,定义一个BeanFactoryPostProcessor,修改AnnotationAwareAspectJAutoProxyCreator的bean的定义。

@Component
public class AddCustomerTargetSourceBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
        LazyInitTargetSourceCreator lazyInitTargetSourceCreator = new LazyInitTargetSourceCreator();
        lazyInitTargetSourceCreator.setBeanFactory(beanFactory);
        beanDefinition.getPropertyValues().add("customTargetSourceCreators", new TargetSourceCreator[]{lazyInitTargetSourceCreator});
    }
}

好嘞,大功告成,有兴趣,你可以撸下代码,看下是否按照预期那样,AopDaoImpl在第一次执行方法时,才会被调用。

系列所有文章

转载自:https://juejin.cn/post/7368302351213445159
评论
请登录