前言
上一篇【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实例的方式:
- 通过工厂方法创建bean实例。
- 通过构造方法自动注入(autowire by constructor)的方式创建bean实例。
- 通过无参构造方法创建
bean
实例。
注:如果bean的配置信息中配置了loopup-method
和replace-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的应用】。