Spring IOC---AOP代理对象生成的时机

1.概述

Spring AOP可以采用注解或者xml配置的方式实现,那么在spring的生命周期当中,是在什么时候生成的代理对象呢?本文就AOP代理对象生成的时机进行介绍。不清楚spring生命周期的读者可以先阅读另一篇博客《Spring IOC—Bean的生命周期》

2.前置知识

  • BeanPostProcessor接口的作用

简单的讲就是在一个对象初始化的前后做一些事情,里面有两个方法,一个是postProcessBeforeInitialization,实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务。另一个是postProcessAfterInitialization,实例化、依赖注入、初始化完毕时执行

3.Spring AOP代理对象生成的时机

可以分成两类,提前和非提前生成代理对象。

下面分别介绍这两类代理对象生成的时机,以及说明为什么会有这两种情况。

3.1非提前生成代理对象

下面给个例子,通过调试代码来说明代理的时机,读者可以跟着一边调试一边阅读。

//被代理类 A
package hdu.gongsenlin.aoptest.dao;
@Component
public class A {
    
    

    public void f(){
    
    
        System.out.println("AAAAA");
    }
}
//切面类
package hdu.gongsenlin.aoptest.aop;
@Aspect
@Component
public class Aop {
    
    


    @Pointcut("execution(* hdu.gongsenlin.aoptest.dao..*.*(..))")
    private void pointcut(){
    
    }

    @After("pointcut()")
    public void advice(){
    
    
        System.out.println("之后增强------------");
    }
}
//配置类
package hdu.gongsenlin.aoptest;
@Configuration
@ComponentScan("hdu.gongsenlin.aoptest")
@EnableAspectJAutoProxy
public class Appconfig {
    
    
}
//启动类
package hdu.gongsenlin.aoptest;
public class AoptestApplication {
    
    

    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class);
        A a = ac.getBean(A.class);
        a.f();
    }

}

上面的代码AOP会作用在A对象上,也就是生成A的代理对象。

这一过程发生在A的生命周期当中,将代码定位到AbstractAutowireCapableBeanFactory#doCreateBean方法

了解springBean的生命周期的读者都应该清楚,Bean生命周期中有一步是属性填充 population,在属性填充之后会执行initializeBean在这里插入图片描述

initializeBean方法中会执行applyBeanPostProcessorsAfterInitialization方法在这里插入图片描述

applyBeanPostProcessorsAfterInitialization具体的代码如下:

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
      throws BeansException {
    
    

   Object result = existingBean;
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
    
    
      Object current = processor.postProcessAfterInitialization(result, beanName);
      if (current == null) {
    
    
         return result;
      }
      result = current;
   }
   return result;
}

逻辑比较简单,就是遍历所有实现了BeanPostProcessor接口的类,这里一共有这7个,一个一个去执行postProcessAfterInitialization方法。其中AnnotationAwareAspectJAutoProxyCreator就是会实现AOP动态代理,然后返回代理对象。在这里插入图片描述

AnnotationAwareAspectJAutoProxyCreator中的postProcessAfterInitialization代码如下

根据bean类型和名字创建缓存key,判断earlyProxyReferences提前动态代理的集合当中存不存在这个缓存key,若存在则说明已经进行过动态代理了,则不再进行动态代理,而本例子中,很明显是没有执行提前动态代理的,所以会执行wrapIfNecessary方法进行构建动态代理对象,本文仅介绍执行的时机,具体的动态代理的实现过程暂时先不考虑。

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    
    
    if (bean != null) {
    
    
        Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
    
    
            return this.wrapIfNecessary(bean, beanName, cacheKey);
        }
    }

    return bean;
}

所以非提前生成代理对象 是在属性填充populateBean完成之后,执行了initializeBean方法的时候进行的动态代理。

3.2 提前生成代理对象

这种情况比较的复杂,涉及到了循环依赖的问题,对于Bean的循环依赖不了解的读者,可以先阅读《Spring IOC—循环依赖》这篇博客再接着阅读。

同样的以举例的方式说明

//切面类 不变
package hdu.gongsenlin.aoptest.aop;
@Aspect
@Component
public class Aop {
    
    


    @Pointcut("execution(* hdu.gongsenlin.aoptest.dao..*.*(..))")
    private void pointcut(){
    
    }
  
    @After("pointcut()")
    public void advice(){
    
    
        System.out.println("之后增强------------");
    }
}
//启动类 不变
public class AoptestApplication {
    
    

    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class);
        A a = ac.getBean(A.class);
        a.f();
    }
}
//配置类 不变
@Configuration
@ComponentScan("hdu.gongsenlin.aoptest")
@EnableAspectJAutoProxy
public class Appconfig {
    
    
}
//被代理对象 A 依赖了 普通对象B
package hdu.gongsenlin.aoptest.dao;
@Component
public class A {
    
    

    @Autowired
    B b;

    public void f(){
    
    
        System.out.println("AAAAA");
    }
}
// 普通对象B 依赖了 A
package hdu.gongsenlin.aoptest.service;
@Component
public class B {
    
    

    @Autowired
    A a;

    public void f(){
    
    
        System.out.println("BBBBBBBB");

    }
}

此时给的例子中,A和B相互依赖,形成了循环依赖,不同的是A需要被动态代理,而B不需要。

继续将代码定位到创建A的时候,还没有执行属性填充populateBean的位置,如下:

在这里插入图片描述

因为涉及到了循环依赖,所以准备三个框框,来代表循环依赖需要用到的三个集合。在这里插入图片描述

至于这三个集合的作用 在循环依赖的博客中已经介绍了,这里就不再赘述。

在上面代码执行之前,会将a的工厂对象放入到singletonFactories,此时三个集合的情况如下:

在这里插入图片描述

之后执行构建A的populateBean进行属性填充,发现A依赖于B,而B又不存在于这三个集合当中,所以会递归的创建B,调用doCreateBean来构建B对象,和A相同,在执行populateBean填充属性之前,会将b的工厂对象放入到singletonFactories当中,此时三个集合的情况如下:

在这里插入图片描述

接着会执行构建B的populateBean进行属性填充,此时B依赖于A,所以会调用doGetBean的去找A对象在这里插入图片描述

然后调用getSingleton,该方法是解决循环依赖的关键,代码如下:

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    
    
   // Quick check for existing instance without full singleton lock
   Object singletonObject = this.singletonObjects.get(beanName);//先从单例池中去获取
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    
    //如果单例池中没有,并且当前的bean正在创建
      singletonObject = this.earlySingletonObjects.get(beanName);// 看看有没有提前暴露的不完整的Bean
      if (singletonObject == null && allowEarlyReference) {
    
    // 如果还没有 且允许提前创建
         synchronized (this.singletonObjects) {
    
    
            // Consistent creation of early reference within full singleton lock
            singletonObject = this.singletonObjects.get(beanName);// 再检查一次 singletonObjects 双重检查
            if (singletonObject == null) {
    
    
               singletonObject = this.earlySingletonObjects.get(beanName);// 再检查一次 earlySingletonObjects 双重检查
               if (singletonObject == null) {
    
    //若都还没有
                  // 则获取beanName对应的单例工厂
                  ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                  if (singletonFactory != null) {
    
    
                     //通过工厂创建一个对象
                     singletonObject = singletonFactory.getObject();
                     //将这个实例化的对象放入到earlySingletonObjects
                     this.earlySingletonObjects.put(beanName, singletonObject);
                     //从单例工厂中移除 这个工厂。
                     this.singletonFactories.remove(beanName);
                  }
               }
            }
         }
      }
   }
   return singletonObject;
}

一级缓存和二级缓存中没有找到A对象,而三级缓存singletonFactories当中有A的工厂对象,所以会调用

singletonFactory.getObject()来获得A对象。

这是之前添加工厂对象到singletonFactories的代码,所以其实执行getObject()也就是执行了getEarlyBeanReference方法

在这里插入图片描述

getEarlyBeanReference的方法代码如下:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    
    
   Object exposedObject = bean;
   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    
    
      for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
    
    
         exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
      }
   }
   return exposedObject;
}

此时又出现了AnnotationAwareAspectJAutoProxyCreator,这里会执行它的getEarlyBeanReference方法

在这里插入图片描述

基于类型和名字 创建缓存key,将其放入到earlyProxyReferences集合当中,用于表示进行了提前的动态代理。调用wrapIfNecessary来构建动态代理对象。

public Object getEarlyBeanReference(Object bean, String beanName) {
    
    
    Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    return this.wrapIfNecessary(bean, beanName, cacheKey);
}

此时获得到了被代理后的A对象,三个集合的结果如下:

在这里插入图片描述

之后doCreateBean 构建B的过程结束了,获得了B对象

在这里插入图片描述

之后会将这个对象,添加到singletonObjects集合当中,三个集合的结果如下:

在这里插入图片描述

B对象构建完成了,那么此时A就可以完成它的填充了,所以走完剩下的逻辑之后,三个集合的结果如下:

在这里插入图片描述

综上提前动态代理,其实是在依赖注入的时候,也就是在populateBean属性填充方法内完成的。

4. 为什么需要两个AOP动态代理的时机

沿用3.2的例子,假设动态代理不提前,那么在构建B对象进行属性填充的时候,填充的A对象是还没有进行动态代理的A。

此时B就完成了它的生命周期到了单例池当中,而后A执行完属性填充之后,再进行动态代理,生成一个被代理的A对象。放入到单例池当中。

此时B中的A和单例池中的被代理的A对象不是同一个对象,这就造成了问题。

猜你喜欢

转载自blog.csdn.net/gongsenlin341/article/details/111240114
今日推荐