[Spring AOP] 代理bean对象是如何创建的?主谈spring aop中被代理bean的创建流程

2,276 阅读5分钟

文章中所有内容不保证绝对正确,如果在阅读过程中发现任何问题欢迎在评论中指正或讨论

本篇文章基于Spring Framework 5.3.0-SNAPSHOT

本文章主要讨论spring aop创建代理对象这一块的大体流程是什么样的,不会讨论什么是AOP,java proxy和cglib的区别等概念。

为什么我要写这篇文章?我在学习spring aop时,想了解我在XML中的配置是什么时候被加载的,是什么时候被“实装”的,而不是了解如何被加载的,如何被“实装”的。并没有搜到对整体流程整的比较好的文章,更多地文章还是对细节的一些讨论。

XML的加载

这部分内容需要对IOC容器的创建流程有一定的了解,这里只做简单解释

spring会创建一个BeanFactory来完成bean的创建,在BeanFactory初始化时,会读取我们写的.xml文件,解析其中的节点,将节点中的信息保存在每个bean自己的BeanDefinition.

当解析到.xml中的<aop:config>xxxxx<\aop:confg>节点或<aop:aspectj-autoproxy>节点时,会调用aop的XML解析器

如何确定的解析器,代码见org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions()

//如果节点为 http://www.springframework.org/schema/beans 中的节点则使用默认解析器,否则使用自定义解析器
// 自定义解析器其实也是根据你XML中上边写的URI地址生成的
// 默认解析器解析的节点:import alias bean beans
if (delegate.isDefaultNamespace(ele)) {
	parseDefaultElement(ele, delegate);
}
else {
	elegate.parseCustomElement(ele);
}

至于是什么时候确定的解析器,从refresh()方法中跟进,在创建BeanFactory时会有一个loadBeanDefinition(),如果对细节感兴趣可以自己了解

解析器详细代码见 org.springframework.aop.config.AopNamespaceHandler#init()

// In 2.0 XSD as well as in 2.1 XSD.
// 显示声明的命名空间 <aop:config>
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());

// AspectJ注解的命名空间<aop:aspectj-autoproxy>
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());

在解析的时候都是通过调用解析器的parse()方法进行解析的

在<aop:config>节点被解析完成后,会生成对应的BeanDefinition去保存节点信息,至于到底保存了什么信息,是用什么数据结构保存的,保存KEY-VALUE分别是什么,不在此进行讨论,其实是因为我也没了解特别清楚,太多了。如果感兴趣,可以写demo然后通过断点debug的方式查看内存里的变量结构。

至于使用注释的解析器,我没有看的特别详细,怕误导别人就不在此乱说了,我的了解就是主要就是通过扫描+反射获得的对应的信息,最后同样是保存在BeanDefinition中

到此,AOP配置信息加载结束。

被代理对象的生成

这里要先说一下Bean的生命周期:实例化 -> 属性赋值 -> 初始化 -> 销毁

spring中还有一种机制:BeanPostProccesor, 此处如果不了解,简单理解为处理器,有各种各样的实现,用来对数据进行处理

Bean的相关信息都存在BeanDefinition中,所以处理Bean的相关信息就是操作BeanDefinition

因此Bean的生命流程大概可以是这样的:

BeanPostProccesor -> 实例化 -> BeanPostProccesor -> 属性赋值 -> BeanPostProccesor -> 初始化 -> BeanPostProccesor -> 使用 -> 随着BeanFactory销毁

这其中的BeanPostProccesor是不一样的,而且每步执行的数量也不一样,可能一个都不执行,可能执行很多个,

带删除线内容来源于网络,我没有在源代码中验证过,不保证正确。如果你知道这部分是否正确,欢迎留言告诉我,万分感谢

初始化前的BeanPostProccesor会根据BeanDefinition中的信息来判断该类是否需要被代理

如果需要就添加到缓存中(一个MAP),在创建代理对象时读取Map中的内容就能知道哪些对象是要被代理的

在初始化完成后,会执行一个用来创建代理对象的BeanPostProccesor,关于这个BeanPostProccesor可以看看

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization()

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

(这部分看确实初始化之前将需要代理的对象在放到了一个earlyProxyReferences中,但是具体发生在哪部我没有仔细看过)

接下来就是一些参数的设置,并且把之前存的那些advice(after before等等)还有pointcut都读出来,然后看看哪个是这个bean要用的,筛选出来

接下来判断使用的代理方式以及创建代理对象,详见 org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy()

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	//Optimize 或 ProxyTargetClass 或hasNoUserSuppliedProxyInterfaces(config) 为true则使用 cglib代理
	if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
		Class<?> targetClass = config.getTargetClass();
		//此处有一个空的判断异常,删掉了,我们假设他不空,不影响我们看顺序
		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
			return new JdkDynamicAopProxy(config);
		}
		return new ObjenesisCglibAopProxy(config);
	}
	else {
		return new JdkDynamicAopProxy(config);
	}
}

这里可以看到,即使你将Optimize或ProxyTargetClass设置为true(默认为false,如果不是接口实现,ProxyTargetClass会被修改为true),也存在一种特殊情况会使用jdk代理,而不是cglib代理。这targetClass目前我还没看明白具体指的是什么,看明白后我会修改这部分内容,说明这个情况是什么。

关于JDK动态代理和cglib动态代理这里就不讲了,网上一查一大堆,而且都比我讲得好~~~~~

主要就是在我们方法执行前后,按照你的配置顺序织入指定的方法,当生成完成代理对象后,我们在getBean的时候get到的就不是原来的bean了,而是被代理的bean了。

到此,spring aop生成代理对象结束。


下一篇文章可能会是关于Bean的生命周期、IOC容器创建过程、如何解决的循环依赖以及看这部分源代码时你可能会有的疑惑、mysql索引相关、线程并发锁相关,说了和没说一样,毕竟写文章,方向就是要随心所欲~

如果你在文章中发现任何问题,欢迎及时纠正我,一起讨论。

转载请标明作者:少年人W 以及 文章链接。