文章目录
Spring深度学习:AOP创建过程源码解析
一、序言
大家经常戏称Java工程师为Spring工程师,毫无疑问,这句话体现出Spring框架在Java开发中的重要性和普及度。
本文小豪将带大家深度学习Spring AOP相关知识,包括AOP标签解析及创建代理的底层源码,经过Spring AOP底层源码的解读,以此掌握更多的编程实践方案,同时进一步提升我们的架构能力。
文章最后附有流程图,进一步帮我们梳理业务逻辑
二、前置准备
AOP也就是面向切面编程,它主要将一些非核心的业务逻辑抽离,比如权限认证、日志、事务处理等,从而实现核心业务和非核心业务的解耦,也提高了系统的可维护性,在Spring中AOP典型的应用就是事务机制,在后续深入学习Spring事务之前,我们有必要先行学习Spring AOP。
在进行Spring AOP源码分析之前,我们先提前了解一下AOP代理方式及准备环境搭建:
1、代理方式
- 静态代理
- 编译时织入
- 类加载时织入
- 动态代理
- JDK动态代理
- CGlib动态代理
AOP代理主要分为静态代理和动态代理,静态代理是指代码运行前,代理类就已经生成,比如在编译阶段和类加载阶段进行生成。
动态代理是指在代码运行过程中产生代理类,Spring AOP就是基于动态代理实现的。
各代理方式如何实践在本文不做深入研究,本文着重分析Spring如何进行动态代理。
那就有同学会有疑问了,Spring既然动态代理,是因为动态代理效率比静态代理高吗?
显然不是,动态代理在运行时动态生成的代理类,是通过反射获取目标类信息,这势必会进行查找计算,消耗一定的资源,效率比静态代理低
而Spring选择动态代理,更多的是其更为灵活,扩展性能力更强
2、环境搭建
业务类:User
public class User {
public void hello() {
System.out.println("hello,Spring AOP");
}
}
日志切面类:LogUtils
public class LogUtils {
// 前置通知
private void before(JoinPoint joinPoint) {
System.out.println("start execute before..");
String targetMethod = joinPoint.getSignature().getName();
System.out.println("增强的目标方法:" + joinPoint.getSignature().getName());
System.out.println("----------------------");
}
// 后置通知
private void after(JoinPoint joinPoint) {
System.out.println("----------------------");
System.out.println("start execute after...");
String targetMethod = joinPoint.getSignature().getName();
System.out.println("增强的目标方法:" + targetMethod);
}
}
XML配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="user" class="com.xiaohao.User"/>
<bean id="logUtils" class="com.xiaohao.LogUtils"/>
<aop:config>
<!--定义切面-->
<aop:aspect id="logAspect" ref="logUtils">
<!--定义切点(需要织入的地方)-->
<aop:pointcut id="logPointCut" expression="execution(* com.xiaohao.User.*(..))"/>
<!--前置通知:目标方法执行前被调用-->
<aop:before method="before" pointcut-ref="logPointCut"/>
<!--后置通知:目标方法执行后被调用-->
<aop:after method="after" pointcut-ref="logPointCut"/>
</aop:aspect>
</aop:config>
</beans>
测试类:SpringDemo
public class SpringDemo {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-config.xml");
User user = (User) applicationContext.getBean("user");
user.hello();
}
}
控制台输出结果:
start execute before..
增强的目标方法:hello
----------------------
hello,Spring AOP
----------------------
start execute after...
增强的目标方法:hello
三、源码分析
阶段一:解析标签并注册Advisor
接下来正式进入AOP的源码分析:
我们先思考一个问题,我们在xml配置文件中定义的<AOP>
标签,Spring是在哪解析的呢?
相信大家阅读过之前【Spring深度学习:IOC容器及源码解析】一文,能够迅速反应过来,没错,是通过obtainFreshBeanFactory()
方法扫描xml文件解析标签,注册对应的BeanDefinition
这里我们直接跳到DefaultBeanDefinitionDocumentReader
类的parseBeanDefinitions()
方法:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 默认标签解析
parseDefaultElement(ele, delegate);
}
else {
// 自定义标签解析(进入这里)
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
由于我们在xml配置文件中存在AOP的命名空间url(xmlns:aop),因此进入自定义标签解析逻辑parseCustomElement()
:
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 第一步:获取命名空间URL
String namespaceUri = getNamespaceURI(ele);
// http://www.springframework.org/schema/aop
if (namespaceUri == null) {
return null;
}
// 第二步:根据命名空间获取对应的命名空间解析类
// AOP -> AopNamespaceHandler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// 第三步:调用命名空间解析类的parse方法进行解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
观察源码发现,parseCustomElement()
方法主要分为三步:
- 获取命名空间URL:获取xml文件中配置的命名空间URL
- 获取命名空间解析类:根据命名空间URL获取对应的命名空间解析类
handler
,这里具体拿到的是AopNamespaceHandler
对象 - 调用parse方法进行解析:调用命名空间解析类
handler
的parse()
方法解析对应的标签
这里我们着重关注第二、三步获取命名空间解析类和调用parse方法进行标签解析
(1)获取命名空间解析类handler
首先我们进入第二步,resolve()
方法,获取命名空间解析类AopNamespaceHandler
对象:
public NamespaceHandler resolve(String namespaceUri) {
// 加载META-INFO/spring.handlers配置文件,封装键值存入Map
Map<String, Object> handlerMappings = getHandlerMappings();
// 获取命名空间URL对应的命名空间解析类
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
String className = (String) handlerOrClassName;
try {
// 通过反射机制,创建对应的命名空间解析类对象
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
// 创建handler对象实例,调用init()方法
namespaceHandler.init();
// 实例化完成后添加进缓存
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
"] for namespace [" + namespaceUri + "]", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
className + "] for namespace [" + namespaceUri + "]", err);
}
}
}
查看源码发现resolve()
方法的逻辑也比较清晰:
- 封装spring.handlers文件内容:将spring.handlers配置文件中的封装键值内容,存入Map集合
- 获取命名空间URL对应的handler:根据
namespaceUri
,查询Map集合获取对应的handler
- 实例化handler并注册:通过反射机制获取
handler
类,调用init()
方法实例化handler
对象
这里的getHandlerMappings()
方法会加载spring-aop源码包下META-INFO/spring.handlers配置文件,spring.handlers文件中定义的就是命名空间对应的全限定类名:
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
将配置的key(namespaceUri)和value(类名)封装为Map集合,然后根据传入的命名空间URL获取对应的命名空间解析类AopNamespaceHandler,通过反射创建AopNamespaceHandler
对象
这里创建AopNamespaceHandler
对象实例时调用其init()
方法,将会注册AOP相关标签的处理类ConfigBeanDefinitionParser解析器:
public class AopNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// 注册AOP相关的标签解析器 <aop:config>
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
(2)解析对应的AOP标签
接着我们返回parseCustomElement()
方法,进入第三步parse()
标签解析方法:
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 获取到之前注册的命名空间解析类进行标签解析
BeanDefinitionParser parser = findParserForElement(element, parserContext);
// 具体解析过程
return (parser != null ? parser.parse(element, parserContext) : null);
}
上文我们在实例化AopNamespaceHandler
命名空间解析类时,调用其init()
方法注册了一个标签解析器ConfigBeanDefinitionParser,这里的parse()
方法实际上就是调用了ConfigBeanDefinitionParser类的parse()
方法:
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
// 注册自动代理创建器 AspectJAwareAdvisorAutoProxyCreator
// AspectJAwareAdvisorAutoProxyCreator是AOP的核心类,后续切面对象的过滤、排序、添加拦截器均由它实现
configureAutoProxyCreator(parserContext, element);
// 解析配置文件,封装BeanDefinition
List<Element> childElts = DomUtils.getChildElements(element);
for (Element elt: childElts) {
String localName = parserContext.getDelegate().getLocalName(elt);
// 解析对应的标签
if (POINTCUT.equals(localName)) {
parsePointcut(elt, parserContext);
}
else if (ADVISOR.equals(localName)) {
parseAdvisor(elt, parserContext);
}
// 进入我们配置的<aop:aspect>标签
else if (ASPECT.equals(localName)) {
parseAspect(elt, parserContext);
}
}
parserContext.popAndRegisterContainingComponent();
return null;
}
观察源码发现,在ConfigBeanDefinitionParser
的parse()
方法中,主要做了以下工作:
- 注册自动代理创建器:注册
AspectJAwareAdvisorAutoProxyCreator
的BeanDefinition
- 解析配置文件:解析并注册AOP相关的标签对象的
BeanDefinition
这里的注册自动代理创建器,用来处理AspectJ切面,我们后续切面对象的过滤、排序、添加拦截器均由它实现,具体的实现内容我们先放一放,继续往下走
之后开始遍历Element
,解析对应的标签,紧接着进入aspect
标签匹配的parseAspect()
方法:
private void parseAspect(Element aspectElement, ParserContext parserContext) {
// 获取定义的id
String aspectId = aspectElement.getAttribute(ID);
// 获取定义的ref切面
String aspectName = aspectElement.getAttribute(REF);
try {
this.parseState.push(new AspectEntry(aspectId, aspectName));
List<BeanDefinition> beanDefinitions = new ArrayList<>();
List<BeanReference> beanReferences = new ArrayList<>();
List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
Element declareParentsElement = declareParents.get(i);
beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
}
// 解析aspect标签下的所有元素
NodeList nodeList = aspectElement.getChildNodes();
boolean adviceFoundAlready = false;
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
// 解析before,after等通知标签
// isAdviceNode()判断是否属于通知类型
if (isAdviceNode(node, parserContext)) {
if (!adviceFoundAlready) {
adviceFoundAlready = true;
if (!StringUtils.hasText(aspectName)) {
parserContext.getReaderContext().error(
"<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
aspectElement, this.parseState.snapshot());
return;
}
beanReferences.add(new RuntimeBeanReference(aspectName));
}
// 封装advice通知标签为BeanDefinition
// 最终包装advice为advisor对象
AbstractBeanDefinition advisorDefinition = parseAdvice(
aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
beanDefinitions.add(advisorDefinition);
}
}
AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
parserContext.pushContainingComponent(aspectComponentDefinition);
// 解析pointcut切点标签
List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
for (Element pointcutElement : pointcuts) {
parsePointcut(pointcutElement, parserContext);
}
parserContext.popAndRegisterContainingComponent();
}
finally {
this.parseState.pop();
}
}
仔细看这段源码,其实主要就做了三件事:
- 解析before,after等通知标签
- 注册最终的advisor增强器
- 注册pointcut切点标签
我们着重看一下第二步parseAdvice()
方法,注册最终的advisor增强器,将Advice
对象包装为Advisor
增强器对象
(3)注册AspectJPointcutAdvisor
进入parseAdvice()
方法,具体源码如下:
private AbstractBeanDefinition parseAdvice(
String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
try {
this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));
// 创建method的BeanDefinition,包装before,after等通知标签
RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
methodDefinition.setSynthetic(true);
// 创建aspectFactory的BeanDefinition
RootBeanDefinition aspectFactoryDef =
new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
aspectFactoryDef.setSynthetic(true);
// 将上面两个BeanDefinition封装为AdviceDefinition
AbstractBeanDefinition adviceDef = createAdviceDefinition(
adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
beanDefinitions, beanReferences);
// 最后统一封装为AspectJPointcutAdvisor对象
RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
advisorDefinition.setSource(parserContext.extractSource(adviceElement));
advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
advisorDefinition.getPropertyValues().add(
ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
}
// 注册AspectJPointcutAdvisor到BeanDefinitionMap
parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);
return advisorDefinition;
}
finally {
this.parseState.pop();
}
}
在parseAdvice()
方法中,首先创建了两个BeanDefinition
,分别是methodDefinition和aspectFactoryDef,之后将它们合并为AdviceDefinition,最后统一包装为AspectJPointcutAdvisor类型的BeanDefinition
,并注册到BeanDefinitionMap
中
methodDefinition用来包装before,after这些通知标签,aspectFactoryDef可以简单理解它是一个aspect工厂
这里我们继续详细查看如何将两个BeanDefinition
包装合并为AdviceDefinition
,进入createAdviceDefinition()
方法:
private AbstractBeanDefinition createAdviceDefinition(
Element adviceElement, ParserContext parserContext, String aspectName, int order,
RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
//根据不同的通知标签创建不同的BeanDefinition
RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
adviceDefinition.setSource(parserContext.extractSource(adviceElement));
adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);
if (adviceElement.hasAttribute(RETURNING)) {
adviceDefinition.getPropertyValues().add(
RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
}
if (adviceElement.hasAttribute(THROWING)) {
adviceDefinition.getPropertyValues().add(
THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
}
if (adviceElement.hasAttribute(ARG_NAMES)) {
adviceDefinition.getPropertyValues().add(
ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
}
// 获取adviceDefinition的构造方法
ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
// 设置构造参数
// 参数一:method 方法
cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);
// 参数二:pointcut 切点
Object pointcut = parsePointcutProperty(adviceElement, parserContext);
if (pointcut instanceof BeanDefinition) {
cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
beanDefinitions.add((BeanDefinition) pointcut);
}
else if (pointcut instanceof String) {
RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
beanReferences.add(pointcutRef);
}
// 参数三:aspectFactory 工厂
cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);
return adviceDefinition;
}
观察源码发现,这里首先声明了一个AdviceDefinition
,然后获取AdviceDefinition
的构造方法,设置其对应的三个构造参数:
- 参数一:method方法(传入的
methodDefinition
) - 参数二:pointcut切点
- 参数三:aspectFactory工厂(传入的
aspectFactoryDef
)
在这里我们结合method
方法、pointcut
切点和aspectFactory
工厂,封装为统一的Advice对象
在声明AdviceDefinition
时,会调用getAdviceClass()
方法,顾名思义获取Advice
的Class
类型,进入getAdviceClass()
方法,详细查看具体返回了什么Class
:
private Class<?> getAdviceClass(Element adviceElement, ParserContext parserContext) {
String elementName = parserContext.getDelegate().getLocalName(adviceElement);
// Before -> 返回 AspectJMethodBeforeAdvice
if (BEFORE.equals(elementName)) {
return AspectJMethodBeforeAdvice.class;
}
// After -> 返回 AspectJAfterAdvice
else if (AFTER.equals(elementName)) {
return AspectJAfterAdvice.class;
}
else if (AFTER_RETURNING_ELEMENT.equals(elementName)) {
return AspectJAfterReturningAdvice.class;
}
else if (AFTER_THROWING_ELEMENT.equals(elementName)) {
return AspectJAfterThrowingAdvice.class;
}
else if (AROUND.equals(elementName)) {
return AspectJAroundAdvice.class;
}
else {
throw new IllegalArgumentException("Unknown advice kind [" + elementName + "].");
}
}
这里其实就是依据不同的通知标签匹配对应的Class
,第一次进来before
标签返回对应的AspectJMethodBeforeAdvice
类
许多同学看到这里就绕晕了,这里我们画个图简单梳理一下这些组件之间的关系:
后续我们使用的将会是经过包装的AspectJPointcutAdvisor对象
根据我们demo中xml配置文件里定义的<aop:before>
和<aop:after>
标签,最终将会生成两个AspectJPointcutAdvisor
的BeanDefinition
并注册到BeanDefinitionMap
,分别是:
- AspectJPointcutAdvisor -> AspectJMethodBeforeAdvice(对应前置通知)
- AspectJPointcutAdvisor -> AspectJAfterAdvice(对应后置通知)
小结
到这里我们初步梳理下本阶段的脉络:
- Spring扫描xml配置文件识别到AOP的命名空间URL
- 开始加载spring-aop源码包下
spring.handlers
配置文件,获取到AOP的命名空间解析类handler
并实例化,同时注册AOP的标签解析器parser
- 调用标签解析器
parser
解析AOP相关的标签- 首先会注册一个自动代理创建器到BeanDefinitionMap
- 接着会开始解析aspect标签下的所有元素,将method方法、pointcut切点、aspectFactory工厂三者封装为advice对象,最后将advice对象包装为advisor增强器对象,注册到BeanDefinitionMap,后续统一对外提供
阶段二:创建代理对象
上个阶段已经封装Advisor
增强器对象并完成注册,接下来该进行创建代理对象了。
在【Spring深度学习:Bean生命周期及源码解析】中我们了解到,Bean在进行初始化后会触发Bean的后置处理器,调用applyBeanPostProcessorsAfterInitialization()
方法,大多数的AOP代理对象均在此阶段产生,本文先不考虑循环依赖导致的提前AOP,想要了解循环依赖相关知识的小伙伴,可以查看【Spring深度学习:循环依赖及源码解析】一文。
(1)初始化原始Bean对象
这里我们先简略回顾一下Bean
的初始化阶段,进入initializeBean()
初始化方法,具体源码如下:
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
// 如果实现了AwareMethods接口则进行回调
// 调用顺序依次为BeanNameAware、BeanClassLoaderAware、BeanFactoryAware
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
// 调用BeanPostProcessor初始化前的方法
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// 调用初始化方法
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
// (看这里)调用BeanPostProcessor初始化后的方法(可能会产生AOP代理)
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
(2)调用Bean初始化后的后置处理器
直接进入产生AOP代理对象的applyBeanPostProcessorsAfterInitialization()
方法:
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;
}
这里回调AbstractAutoProxyCreator
类的postProcessAfterInitialization()
方法:
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
// 根据bean的class和名称生成key
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// (核心)是否需要进行代理
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
继续进入核心的wrapIfNecessary()
方法:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 是否之前已处理过
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return 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;
}
// 获取所有可拦截当前Bean的advisor增强器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 若存在则创建代理
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建具体的代理对象
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;
}
观察源码发现,wrapIfNecessary()
方法大致包括以下工作:
- 检查是否已处理过:首先判断当前传入的
Bean
对象是否已经处理过 - 判断是否需要代理:
- 如果
Bean
已经创建过代理对象,则直接返回 - 使用
isInfrastructureClass()
方法判断当前Bean
是否为Spring内部的基础设施类,如Advice
、Pointcut
、Advisor
、AopInfrastructureBean
等,不需要代理 - 使用
shouldSkip()
方法判断当前Bean
是否应该被跳过
- 如果
- 获取所有的增强器:调用
getAdvicesAndAdvisorsForBean()
方法获取当前Bean
的Advices
,主要用于判断当前Bean
是否能被增强 - 创建代理对象:如果存在
Advices
,则进入创建代理对象的逻辑,调用createProxy()
方法创建代理对象 - 缓存代理对象并返回:创建完成后,缓存生成的代理对象并返回
总体来说,wrapIfNecessary()
方法主要是通过一系列的判断确保只有真正需要代理的Bean
才会创建代理对象,这里我们需要着重关注两个方法:
// 方法一:获取所有可拦截当前Bean的advisor增强器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 方法二:创建具体的代理对象
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
(3)获取所有符合条件的Advisor增强器
首先我们进入方法一,查看Spring是如何拿到所有符合资格的Advisor
,进入getAdvicesAndAdvisorsForBean()
方法内部,具体源码如下:
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
// 获取符合条件的Advisors
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
继续进入findEligibleAdvisors()
方法:
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 步骤一:获取所有的Advisor对象(这里会获取到我们定义的before和after)
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 步骤二:过滤部分不符合规则的Advisor对象,大致逻辑就是获取要代理的目标类的所有方法,判断切点是否匹配该目标类的方法
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
// 步骤三:添加特殊的拦截器(内部切面) ExposeInvocationInterceptor
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
// 步骤四:排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
// 步骤五:返回
return eligibleAdvisors;
}
查看源码发现,findEligibleAdvisors()
方法大致进行了如下工作:
- 查找候选的Advisor:通过
findCandidateAdvisors()
方法,从容器中查找所有的Advisor
- 过滤不符合的Advisor:从候选的
Advisor
中过滤出符合当前Bean
的Advisor
,主要是匹配Advisor
的切入点表达式与当前Bean
的方法 - 扩展Advisor:添加特殊的拦截器(内部切面)
ExposeInvocationInterceptor
- 排序Advisor:
Advisor
如果不为空,调用sortAdvisors()
方法进行排序 - 返回Advisor列表:最终返回包含所有适配当前
Bean
的Advisor
列表
由此可知,findEligibleAdvisors()
方法主要是用来筛选出与当前Bean
匹配的Advisor
,这里我们着重关注一下步骤一findCandidateAdvisors()
方法和步骤三extendAdvisors()
方法:
首先我们进入步骤一findCandidateAdvisors()
方法内部:
// 获取所有的Advisor对象
protected List<Advisor> findCandidateAdvisors() {
Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
return this.advisorRetrievalHelper.findAdvisorBeans();
}
继续往下走:
public List<Advisor> findAdvisorBeans() {
// 获取所有的advisorBeanNames
String[] advisorNames = this.cachedAdvisorBeanNames;
if (advisorNames == null) {
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
}
if (advisorNames.length == 0) {
return new ArrayList<>();
}
List<Advisor> advisors = new ArrayList<>();
// 遍历advisorNames
for (String name : advisorNames) {
if (isEligibleBean(name)) {
if (this.beanFactory.isCurrentlyInCreation(name)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping currently created advisor '" + name + "'");
}
}
else {
try {
// 创建Advisor,使用beanFactory工厂生成实例化
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
catch (BeanCreationException ex) {
Throwable rootCause = ex.getMostSpecificCause();
if (rootCause instanceof BeanCurrentlyInCreationException) {
BeanCreationException bce = (BeanCreationException) rootCause;
String bceBeanName = bce.getBeanName();
if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping advisor '" + name +
"' with dependency on currently created bean: " + ex.getMessage());
}
continue;
}
}
throw ex;
}
}
}
}
return advisors;
}
看到这里,findAdvisorBeans()
整体逻辑也很清晰了,在第一个阶段解析标签并注册Advisor时,Spring会将所有AspectJPointcutAdvisor
类型的BeanDefinition
进行注册,在findAdvisorBeans()
方法中,我们会拿到所有的advisorNames
,调用getBean()
方法生成对应的Advisor
实例。
紧接着我们返回findEligibleAdvisors()
方法,进入步骤三extendAdvisors()
方法:
这里实际完成调用的是第一个阶段解析对应的AOP标签时注册的自动代理创建器AspectJAwareAdvisorAutoProxyCreator,包括extendAdvisors()方法执行完毕后调用的sortAdvisors()排序方法同样也是使用此自动代理创建器
// 添加Spring内置拦截器 ExposeInvocationInterceptor
protected void extendAdvisors(List<Advisor> candidateAdvisors) {
AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
}
继续往下走,进入makeAdvisorChainAspectJCapableIfNecessary()
方法:
public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
// 非空判断
if (!advisors.isEmpty()) {
boolean foundAspectJAdvice = false;
for (Advisor advisor : advisors) {
if (isAspectJAdvice(advisor)) {
foundAspectJAdvice = true;
}
}
// 在advisors列表头位置添加ExposeInvocationInterceptor
if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
return true;
}
}
return false;
}
具体的逻辑也比较简单,就是在Advisors
列表下标为0的位置添加了一个ExposeInvocationInterceptor
类型的Advisor
这里是一个重点!ExposeInvocationInterceptor是做什么的,为什么要添加它?
我们先留有这个疑问,继续往下走,后面我们再来分析它的具体作用
至此,我们已经获取到所有符合要求的Advisor
增强器,回到初始调用getAdvicesAndAdvisorsForBean()
方法的代码中
// 获取所有可拦截当前Bean的advisor增强器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
那我们一共获取到几个Advisor
,没错,一共获取到三个:
- ExposeInvocationInterceptor(Spring内置)
- AspectJPointcutAdvisor(AspectJMethodBeforeAdvice)
- AspectJPointcutAdvisor(AspectJAfterAdvice)
小豪起初大家看到这里时,产生了疑惑,在上面环境搭建中我们控制台打印的逻辑里,通知顺序是:Before前置通知 -> 目标方法 -> After后置通知
那这里既然已经调用了sortAdvisors()方法,对Advisor增强器进行了排序,那为何After会排在Before之前呢?想必后续一定会有其它处理的逻辑,我们继续往下看
(4)创建具体的代理对象
现在我们已经拿到当前Bean
的三个Advisor
,回到wrapIfNecessary()
方法,此时就会调用createProxy()
方法,创建代理对象:
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
// 创建代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 构建增强器,强转Object为Advisor
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
// 设置advisors增强器
proxyFactory.addAdvisors(advisors);
// 设置要进行代理的目标类
proxyFactory.setTargetSource(targetSource);
// 扩展点
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// (核心)创建代理对象
return proxyFactory.getProxy(getProxyClassLoader());
}
观察源码发现,createProxy()
方法主要做了如下工作:
- 创建代理工厂:Spring创建一个
ProxyFactory
实例,后续为该工厂配置代理的各种属性。 - 构建通知增强器:将
Object
类型的增强器统一强转为Advisor
类型 - 设置代理工厂属性:为
ProxyFactory
代理工厂设置属性,添加Advisors
增强器,设置代理的目标类,定制化拓展 - 生成代理对象并返回:最后
ProxyFactory
会生成具体的代理对象并返回,替换原始的Bean
实例。
由此可知,createProxy()
方法主要是创建了名为ProxyFactory
的代理工厂,并设置一系列相关属性,包括添加getAdvicesAndAdvisorsForBean()
方法返回的三个Advisor
增强器,最后调用代理工厂的getProxy()
方法创建具体的代理对象
我们继续往下走,进入getProxy()
方法:
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
这里我们看到,ProxyFactory
类的getProxy()
方法主要分为两步:
- createAopProxy():创建
AopProxy
接口的实现类 - getProxy(classLoader):调用
AopProxy
接口的实现类的getProxy()
拿到具体的代理类
由于篇幅过长,本文先介绍Spring如何选择两种不同的AopProxy接口的实现类(Jdk和Cglib),不具体展开其getProxy()方法创建代理过程,会另开下一篇详细介绍Jdk和Cglib两种动态代理区别以及后续拦截链的执行过程
进入createAopProxy()
方法:
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
// 这里传入的this就是刚才创建的ProxyFactory
return getAopProxyFactory().createAopProxy(this);
}
继续进入DefaultAopProxyFactory
实现类的createAopProxy()
方法:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 根据配置选择生成Jdk或者Cglib动态代理类
// 这里不论选择Jdk还是Cglib动态代理,都会继续传入之前创建的ProxyFactory
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 由于我们的User类没有实现接口也没有进行特殊配置,会选择创建Cglib动态代理
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
看到这里,想必大家都清晰了
createAopProxy()
方法最终会根据相关条件判断是生成Jdk动态代理或者Cglib动态代理,同时会将我们传递进来的ProxyFactory
代理工厂继续作为参数传入
如果目标类没有实现接口,就会采用Cglib动态代理(也可以通过设置proxyTargetClass = true指定使用Cglib动态代理)
如果目标类有实现了接口,就会采用JDK动态代理
小结
同样我们梳理下本阶段创建代理对象的基本脉络:
- 初始化
Bean
对象时,调用Bean
的后置处理器BeanPostProcessors
- 判断
Bean
是否已经创建过代理对象,以及是否需要创建代理对象 - 获取所有匹配的
Advisor
增强器,并在增强器列表头部添加一个Spring内置的ExposeInvocationInterceptor类型的增强器 Advisor
增强器不为空则生成代理对象并返回- 创建
ProxyFactory
代理工厂并设置相关属性及Advisor
增强器 - 根据条件选择生成Jdk动态代理 or Cglib动态代理
- 创建
四、流程图
五、后记
本文从AOP代理方式及环境准备开始介绍,引申到AOP标签解析及创建代理的源码分析,小豪带大家认识Spring从识别、解析AOP到创建AOP代理的整个过程。
本篇文章内容相对来说较为简单,无非是通过解析XML文件下的AOP标签,通过一些列操作封装为Advisor
增强器,最后创建Bean
实例时判断是否需要进行AOP代理。但是,碍于篇幅,我们核心的知识:AOP两种动态代理方式以及回调过程中拦截链的执行逻辑我们暂时还没有介绍,同时在本文中也遗留了两个疑问,在下一篇中,小豪会继续深入探索这些迷雾重重的问题,最终理解Spring AOP动态代理,是怎样在不改变原有代码结构的前提下,赋予了程序新的灵魂,大家耐心等等,相信阅读完下一篇,我们会得到进一步升华!
如果觉得内容还不错,大家可以先点点关注,后续小豪也会更新Spring底层源码其它系列文章哦~