Spring源码解析AOP(二)AOP后置处理器的工作

前言

上一篇【Spring源码解析AOP(一)@EnableAspectJAutoProxy的使命】说到@EnableAspectJAutoProxy注解开启开启后,就在Spring容器启动时动态添加了一个后置处理器类,这个类就会被Spring加入到BeanPostProcessor的列表里进行初始化,并且用来处理AOP代理。那么这篇博客会跟着Bean的初始化脚步,去探究这个后置处理器做了什么动作完成了AOP动态代理的。更多Spring内容进入【Spring解读系列目录】

AOP过程图示

Spring AOP的过程从代码层面来看十分的复杂,因此笔者先捋出来一个图示,跟着这个图示可能就比较好理解一些了。
在这里插入图片描述

在说源码之前要先说明下,本篇主要讲的是AOP后置处理器是怎么工作的,捎带会有一些Bean初始化的内容。因为Bean的初始化流程很长,一篇博客都不可能全部讲完其源码,因此这里将会跳过Bean的一些前置过程,直接到实例化的部分。但是由于主要讲的是AOP代理的过程,因此Bean的实例化的部分也会一笔带过,本篇并不会深入探究Bean是如何实例化的,只要能讲清楚AOP后置处理器是怎么工作的,这篇博客就算是完成了任务了。至于Proxy是怎么做出来的限于篇幅的原因,可能要另写一个博客来详细说了。

AOP Bean的实例化

所以直接到ConfigurableListableBeanFactory#preInstantiateSingletons()方法里面去。这是一个接口方法,因此其实现是由DefaultListableBeanFactory#preInstantiateSingletons()方法做的,从这里开始要准备开始实例化了。

public void preInstantiateSingletons() throws BeansException {
   /**不相关,略去**/
   // 拿到所有要初始化的类,也就是所有bean的名字
   List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
   // 触发所有非延迟加载单例bean的初始化,主要的步骤为调用getBean
   for (String beanName : beanNames) {
      //根据名字拿到一个BeanDefinition,同样也是合并父类的BeanDefinition
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      //判断:BD不是抽象的,是单例的,且不是懒加载的,进入
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
         //判断是不是一个FactoryBean
         if (isFactoryBean(beanName)) {
            /**如果是FactoryBean则会加上"&"符号,因此会有一个转换,这里的逻辑和主题无关,可以忽略**/
              }
         else {//不是直接到这里来,进入getBean
            getBean(beanName);
         }
      }
   }
/**不相关,略去**/
}

方法进入以后首先从beanDefinitionNames这个list中把所有需要初始化的bean的名字都取出来,这一步是在之前扫描出来的。紧接着是一个for循环用来遍历所有bean名字。在循环里面第一步就是根据bean名字拿出一个BeanDefinition,这里的动作不仅仅是取出,而且包含了把之前所有生成的BeanDefinition合并的动作,姑且认为取出了一个指定的BeanDefinition。紧接着的if语句判断这个bean需要满足不是抽象的,是单例的,且不是懒加载的才可以进一步往下。当然AOP这个后置处理器也同样不是一个FactoryBean,因此直接跳到getBean(beanName);方法。有关FactoryBean知识点,参考【区别BeanFactory和FactoryBean】

public Object getBean(String name) throws BeansException {
   return doGetBean(name, null, null, false);
}

这是一个空壳方法,直接进入doGetBean(),这个方法非常的长也非常复杂,所以有选择性贴出来重点。

protected <T> T doGetBean(
      String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
      throws BeansException {
   //转换beanName,这里是防止传入的name是一个代理,那么这个名字就会是&name,这里就是把&去掉的
   String beanName = transformedBeanName(name);
   Object bean;
   //首先看下能不能拿到这个实例,如果拿不到就说明是null,从这里把bean实例从容器中拿出来
   Object sharedInstance = getSingleton(beanName);
   //这里为什么要判断是null呢,因为我们自己的bean在第一次运行的时候一定为null,因为没有东西给初始化,
   if (sharedInstance != null && args == null) {
      /**如果不是null,就打印到log里,然后直接拿出来,略去**/
   }
   else {        //但是可以说,如果是自建的bean,99%的都应该是null,会走这里的逻辑
      /**此处是父工厂逻辑,略**/
      //添加到alreadyCreated set集合当中,标识这个bean已经被创建过了,防止重复创建
      if (!typeCheckOnly) {
         markBeanAsCreated(beanName);
      }
      try {
         //根据bean名字拿一个BD出来
         RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         checkMergedBeanDefinition(mbd, beanName, args);
         //判断是不是有依赖
         String[] dependsOn = mbd.getDependsOn();
         if (dependsOn != null) {
            /**AOP相关没有依赖,略去**/
         }
         // Create bean instance. 这里的官方注释,此时开始创建Bean Instance
         if (mbd.isSingleton()) {
            // 这里使用java8的表达式,new了一个匿名类
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  //进入createBean方法,看看里面是如何创建类的
                  return createBean(beanName, mbd, args);
               }
               catch (BeansException ex) {
                  destroySingleton(beanName);
                  throw ex;
               }
            });
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }
         else if (mbd.isPrototype()) {
            /**无关,略**/
         }
         else {
            /**无关,略**/
         }
      }
      catch (BeansException ex) {
         cleanupAfterBeanCreationFailure(beanName); throw ex;
      }
   }
   /**无关,略**/
   return (T) bean;
}

进入方法首先是一个转换名字的地方,接着就是getSingleton(beanName);企图直接拿到一个单例对象。但是这里其实有一个矛盾点,很明显初始化的时候不可能拿到任何数据的,为什么Spring要在最开始就判断能不能get到一个对象呢?

因为getSingleton()这个方法在初始化的时候会调用,在getBean()的时候也会调用,为什么要这么做去拿到实例? 就是说Spring在初始化的时候先获取这个对象,判断这个对象是不是被初始化好了(一般情况下绝对为null)。如果实例化好了就直接从Spring的bean容器中获取一个实例使用就可以了。由于Spring中bean容器是一个单例Map(一般称作单例对象池),所以可以理解为getSingleton(beanName) 等同于 beanMap.get(beanName)。由于方法会在Spring环境初始化的时候调用,也就是对象被创建的时候调用一次。因此如果有同学想在这里断点调试,笔者建议指定beanName进行断点,这样能够确保我们是在获取特定目标bean的时候被调用的。

需要说明下:初始化的时候普通类一般都是null,因为没有东西进行初始化。 但是有一种情况不为null,那就是当这个类为lazy的时候。因为lazy的类只会在使用的时候才被初始化。因此第一次调用的时候,lazy的类有可能会被初始化,但是不使用,比如它被依赖了。使用的时候还会再走一次初始化流程,那个时候就会直接拿出来使用了。

跟着注释一路走到sharedInstance = getSingleton(beanName, () -> {…},这是实例化了一个匿名类,但是要注意这个getSingleton()和上面的不是一个方法。所以我们得暂时停在这里进入这个匿名类的getSingleton()方法。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(beanName, "Bean name must not be null");
   synchronized (this.singletonObjects) {
      //从容器中拿出来bean,如果是第一次进来,就应该是null
      Object singletonObject = this.singletonObjects.get(beanName);
      //如果是null,进入逻辑
      if (singletonObject == null) {
         /**无关,略**/
         //将beanName添加到singletonsCurrentlyInCreation的一个set集合中
         beforeSingletonCreation(beanName);
         boolean newSingleton = false;
         boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
         if (recordSuppressedExceptions) {
            this.suppressedExceptions = new LinkedHashSet<>();
         }
         try {
            //试图拿到创建的对象,断点到这里可以发现拿到的是代理对象
       singletonObject = singletonFactory.getObject();
            newSingleton = true;
         }
         catch (**Exception ex) {
             /**Exception,略**/
         }
         finally {
            if (recordSuppressedExceptions) {
               this.suppressedExceptions = null;
            }
            //实例对象创建完成后,把这个对象从singletonsCurrentlyInCreation里面移除出去
            afterSingletonCreation(beanName);
         }
         if (newSingleton) {
            addSingleton(beanName, singletonObject);
         }
      }
      return singletonObject;
   }
}

进入方法后,会发现通篇都没有创建的地方,倒是一大堆get方法去各种获取。值得说的就是beforeSingletonCreation(beanName);这个方法。调用这个方法将会使得beanName被存入一个叫做singletonsCurrentlyInCreation的set里,这个有什么作用以后再说。但是存进去以后就表示beanName对应的bean正在创建中,后面还有个afterSingletonCreation(beanName);方法,用来创建完毕以后,从singletonsCurrentlyInCreation里面移除。

想要验证这里面拿到了什么,可以在singletonObject = singletonFactory.getObject();断点调试。其实到这一步,bean已经被创建出来。但是此时打开断点就会发现,singletonObject这里不是原生对象,而是已经成为一个代理对象。也就意味着,此时原生的对象在这里被后置处理器做了手脚,出去看return createBean(beanName, mbd, args);是不是做了什么事情。

if (mbd.isSingleton()) {
   // 这里使用java8的表达式,new了一个匿名类
   sharedInstance = getSingleton(beanName, () -> {
   try {
      //进入createBean方法,看看里面是如何创建类的
      return createBean(beanName, mbd, args);
   }
   catch (BeansException ex) { }
});

我们又回到了getSingleton()方法的上一层去看看createBean(beanName, mbd, args);做了什么操作。这也是一个接口,其唯一实现方法在AbstractAutowireCapableBeanFactory#createBean()这里,主线干扰代码很多,挑主要的看。

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {
    //拿到BeanDefinition
    RootBeanDefinition mbdToUse = mbd;
   /**用于Spring内部赋值,略去**/
   try {
      //doCreateBean从名字看,这里就是创建bean的地方,那么代理应该也是在这里被创建的
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      /**logger,略**/
      return beanInstance;
   }
   catch (Exception ex) {
      /**Exception,略**/
   }
}

进来后先把传入进来的BeanDefinition构造一个局部变量接住,然后经过各种赋值,最后传递给了doCreateBean(),Spring的方法名字写的真的非常实诚。但是断点到这个方法会发现出来的还是代理对象$Proxy这样的对象,就是说doCreateBean()做什么事情了也需要看一下。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {
   // BeanWrapper对真实bean类的一个包装
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      //创建bean实例,并将实例包裹在BeanWrapper实现类对象中返回。
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   //拿到原始对象的,也就是说bean就是原始对象了,那么下面就应该是创建代理了
   Object bean = instanceWrapper.getWrappedInstance();
   Class<?> beanType = instanceWrapper.getWrappedClass();
   /**无关,略**/
   // 传递原生对象给exposedObject
   Object exposedObject = bean;
   try {
      //赋值属性的,如果有依赖,就是在这里处理的
      populateBean(beanName, mbd, instanceWrapper);
      // 也是执行后置处理器的地方,AOP就是在这里完成的处理
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   catch (Throwable ex) {
      /**Exception,略**/
   }
   /**无关,略**/
   return exposedObject;
}

进入这个方法,有一个比较重要的类BeanWrapper。这个接口就是用于封装真实bean的,把真实bean的方法,属性等等一切信息包装成为一个BeanWrapper。其有一个方法getWrappedInstance()可以通过这个方法去获取到真的bean。既然说到是个接口,其真实实现的类叫做BeanWrapperImpl,这里大家先记住这个接口的作用就好了。

接着碰到一个if (instanceWrapper == null)条件语句:如果sharedInstance==null,就创建一个出来createBeanInstance(beanName, mbd, args);。这个方法的主要作用是创建bean实例,并将实例包裹在BeanWrapper实现类对象中返回。createBeanInstance()方法中包含三种创建bean实例的方式:

  1. 通过工厂方法创建bean实例。
  2. 通过构造方法自动注入(autowire by constructor)的方式创建bean实例。
  3. 通过无参构造方法创建bean实例。

注:如果bean的配置信息中配置了loopup-methodreplace-method则会代理增强bean实例。

可见整个bean创建的过程就是在这个方法里做的,这个方法过于复杂,笔者会单开一个帖子去说详细解释,这里大家只要知道过了这句代码,bean的原始实例就被创建出来了,到此一个bean的实例化就结束了。要注意的一点是:这里只是bean实例化出来了,但是其中的属性没有赋值,因此bean实例化的过程并没有结束,这点要分清楚。

创建出来以后紧接着就是bean = instanceWrapper.getWrappedInstance();,此时bean就是被创建出来的,那么下面就应该是创建代理了。略过一些非主线的内容,看到一个很重要的方法populateBean(beanName, mbd, instanceWrapper);这个方法是Spring给属性赋值用的。就是说项目重被Spring自动注入的bean中如果有依赖,就是在这个方法里做的,这里也是仅仅介绍一下。因为重点的方法就在下面exposedObject = initializeBean(beanName, exposedObject, mbd);,这个方法就是后置处理器操作原始bean成为一个代理对象的地方,进入看下。

AOP 后置处理的使用

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
   /**用于Spring内部安全校验,略去**/
   Object wrappedBean = bean;
   if (mbd == null || !mbd.isSynthetic()) {
      //从名字看这里是执行BeanPostProcessorsBeforeInitialization方法,做了一些准备
      // 就是BeanPostProcessors接口的两个方法之一:before
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }
   try {
      //执行bean的生命周期回调中的init方法(@PostConstructor或者实现InitializingBean里面的afterPropertiesSet()方法)不是AOP相关的,只是解释下
      invokeInitMethods(beanName, wrappedBean, mbd);
   }
   catch (Throwable ex) {
      /**Exception,略**/
   }
   if (mbd == null || !mbd.isSynthetic()) {
      //从名字看这里是执行BeanPostProcessorsBeforeInitialization方法,
      // 就是BeanPostProcessors接口的两个方法之一:after。这里运行以后,就变成代理了
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }
   return wrappedBean;
}

进入以后,要提醒一点mbd这个对象是存在的,但是后面!mbd.isSynthetic()的判断是true。这个字段是用来判断这个对象是不是一个系统提供的类,如果这个类被标识为synthetic就不需要处理器去做个代理了。由于这里判断不是系统类,应该是false,又被加了一个非符号,因此会去执行 applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);方法给wrappedBean对象赋值。但是这个方法什么也没有做,只做了一个返回,传进去什么返回什么。

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
      throws BeansException {
   Object result = existingBean;  //result就是原生对象
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
      //什么也没有做直接返回result出来给current
      Object current = processor.postProcessBeforeInitialization(result, beanName);
      if (current == null) {
         return result;
      }
      result = current;
   }
   return result;
}

点进去before以后,可以看到里面是用一个for循环来处理所有的后置处理器的before方法,应当记得AOP用的是哪个呢?就是开启AOP功能的AnnotationAwareAspectJAutoProxyCreator这个类的父类做的,如果直接点进去这个类,是看不到任何的before或者after方法的,这个父类就是AbstractAutoProxyCreator,在它的实现方法postProcessBeforeInitialization()里做的逻辑。当然这个不是笔者说谁做的就是谁做的,这是跟踪调试出来的结果,做技术是不能信口雌黄的。

public Object postProcessBeforeInitialization(Object bean, String beanName) {
   return bean;
}

是如上所说,直接把传进来的bean返回了。但是这并不是说这个方法没有用,而是在这个处理器里的before的方法用不到而已。牢记:一个什么都不做的Spring容器在启动的时候也要自己初始化6个后置处理器,所以这里什么都不做只是AbstractAutoProxyCreator这个后置处理器什么都没有做。从上面的for循环处理来看,肯定不止一个处理器,别的处理器用到了before,这里直接返回只是AbstractAutoProxyCreator这个处理器没有做而已。

过了这个before以后就走到了after这里applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
      throws BeansException {
   Object result = existingBean; //result就是原生对象
   //这个for循环就是在执行Spring初始化的那些后置处理器里面的逻辑
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
      //最终是被AbstractAutoProxyCreator.postProcessAfterInitialization方法做的处理,
      // 因为这个是处理AOP的而这个调试是基于AOP的,其实所有的PostProcessor都会使用这个处理
      Object current = processor.postProcessAfterInitialization(result, beanName);
      if (current == null) {
         return result;
      }
      result = current;  //代理对象赋值
   }
//返回出去的时候已经是代理对象了
   return result;  
}

可以看到基本上内容都是一样的,但是这次我们到AbstractAutoProxyCreator#postProcessAfterInitialization()方法里的时候就不一样了。

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
   if (bean != null) {
      //首先从缓存中拿,拿出来名字来
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      //由于还没有代理出来,remove()出来就是null,因此肯定不等于bean,走到逻辑里面
      if (this.earlyProxyReferences.remove(cacheKey) != bean) {
         //进入这个方法
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}

首先从缓存中拿出来信息,因为代理对象此时并没有生成因此earlyProxyReferences这个map里不可能有当前的bean在里面,所以会走到这个逻辑里面,也就是说到wrapIfNecessary(bean, beanName, cacheKey);方法里面。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
   if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }
   //不需要代理直接返回bean,但是我们现在当然需要代理,过去
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }
   // 解析所有的切面,并判断是否符合pointcut所描述的切点位置。specificInterceptors还会传给代理工厂用来创建代理对象
   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   //如果不代理,就不走这里了。但是Spring AOP如果不做特殊方法就是代理,因此走下去。
   // 这里外部可以设置,但是一旦设置了,就再也没有代理了
   if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
      //创建代理,传入相关的参数,beanName,specificInterceptors等等。
      Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }
   this.advisedBeans.put(cacheKey, Boolean.FALSE);
   return bean;
}

进入以后跳过一些验证,用specificInterceptors数组去存储对切面的解析,解析的细节就不说了,无非就是解析@CutPoint那些内容已经验证一个bean是不是在范围里,有兴趣的同学可以研究下。再往下就到了关键点了createProxy()创建代理。不过这点要到下一篇【Spring源码解析AOP(三)AOP代理的生成】再说了。

总结

源码贴到这里,其实就是想告诉大家,所谓的Spring AOP就是这样一个把Bean变成Bean代理的过程,并没有什么神奇的地方。本篇唯一可能迷茫的地方可能就是为什么@Import的类是AnnotationAwareAspectJAutoProxyCreator,为什么执行的时候是其父类AbstractAutoProxyCreator执行的呢?因为AbstractAutoProxyCreator实现了BeanPostProcessor接口,所以就可以实现这两个方法。运行到AnnotationAwareAspectJAutoProxyCreator的时候,直接调用的就是父类的相关方法,其逻辑也是在父类中实现的,因此子类中就没有再重写这两个方法了。BeanPostProcessor相关知识可以参考【Spring后置处理器BeanPostProcessor的应用】

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值