Spring源码系列(四):AOP流程源码分析
一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情。
前置知识:
- Spring源码系列(一):<context:component-scan /> juejin.cn/post/708130…
- Spring源码系列(三):通知(advice)的5种类型及转换 juejin.cn/post/708749…
- Spring源码系列(五):JDK、CGLIB动态代理源码解析
AOP执行过程流程图
AOP执行过程大概
- 解析xml文件中配置的aop:xxx标签,通过BeanDefinitionParser解析成BeanDefinition。产生代理对象创建器,五种通知类型对象等等
- 当Spring容器实例化Bean对象完成后,且初始化开始,AOP的执行时机就是Bean实例化之后(AfterInitialization)。
- 为目标类选择合适的代理方式(JDK or CGLIB),生成代理类。
- 执行代理类中的invoke方法,完成增强。
测试类
-
spring-aop.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="userService" class="com.ming.aop.UserServiceImpl"/> <!--切面对象--> <bean id="myAspect" class="com.ming.aop.MyAspect"/> <!--配置织入:告诉spring框架,哪些方法(切点)需要进行增强(前置、后置...--> <aop:config> <!--抽取切点表达式--> <aop:pointcut id="myPointcut" expression="execution(* com.ming.aop.*.*(..))"/> <!--声明切面--> <aop:aspect ref="myAspect"> <!--抽取切点表达式--> <!--<aop:pointcut id="myPointcut" expression="execution(* com.ming.aop.*.*(..))"/>--> <!--切面:切点+通知--> <aop:before method="before" pointcut="execution( * com.ming.aop.*.*(..))"/> <aop:after-returning method="afterReturning" pointcut="execution( * com.ming.aop.*.*(..))"/> <aop:around method="around" pointcut="execution(* com.ming.aop.*.*(..))"/> <aop:after-throwing method="afterThrowing" pointcut="execution(* com.ming.aop.*.*(..))"/> <aop:after method="after" pointcut="execution(* com.ming.aop.*.*(..))"/> <aop:around method="around" pointcut-ref="myPointcut"/> <aop:after method="after" pointcut-ref="myPointcut"/> </aop:aspect> </aop:config> </beans> 复制代码
-
MyAspect.java 切面对象
public class MyAspect { public void before() { System.out.println("前置增强......."); } public void afterReturning() { System.out.println("后置增强......."); } // ProceedJoinPoint:正在执行的连接点 == 切点 public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("环绕前增强......."); Object proceed = pjp.proceed(); // 切点方法 System.out.println("环绕后增强......."); return proceed; } public void afterThrowing() { System.out.println("异常抛出增强......."); } public void after() { System.out.println("最终增强......."); } } 复制代码
-
UserServiceImpl 被代理类,实现了UserService接口
public class UserServiceImpl implements UserService{ @Override public void saveUser() { System.out.println("save user....."); } } 复制代码
执⾏BeanDefinitionParser流程分析
我们知道在spring配置文件中,除了import、alias、bean、beans
标签外别都是属于自定义标签,spring会在执行refresh中的第二个方法obtainFreshBeanFactory()
时被解析到spring工厂中去,例如当我们在配置文件中配置了<context:component-scan />标签,spring就会找到处理这个标签的BeanDefinitionParser
来进行解析。
如果不明白这一部分可以看我的另外一篇文章:juejin.cn/post/708130…
AOP的执行流程也是如此,我们只不过是在配置文件中配置了<aop:xxx/>
,所以解析这个标签的时候也会找到一个BeanDefinitionParser也解析,这个类就是ConfigBeanDefinitionParser
。我们找到这个类的parse方法。
configureAutoProxyCreator
会获取产生代理对象的创造器,我们指定AOP过程中的目标类后,就会需要代理类,而这个过程就是这个方法来完成的。点进这个方法内部会发现new了一个AspectJAwareAdvisorAutoProxyCreator
类。- 这里的for循环主要来处理aop:config下面的aop:xxx标签,aop标签冒号后面的xxx,默认有三种。
- <aop:pointcut />
-
根据parserContext.getDelegate().getLocalName(elt);方法获取到localName,就是aop:xxx/,就是xxx的name,然后通过if判断,这个localName归属于哪个,就执行相应的逻辑。 <aop:pointcut />:切点,这是必要的。我们在配置aop相关的参数的时候,配置pointcut的时机主要有三种。也就是说无论怎么配置,
if (POINTCUT.equals(localName))
至少为真执行逻辑一次。<!--第一种,和aop:aspect平级--> <aop:config> <aop:pointcut id="" expression=""/> <aop:aspect/> </aop:config> <!--第二种,在aop:aspect里配置平级--> <aop:config> <aop:aspect> <aop:pointcut id="" expression=""/> </aop:aspect> </aop:config> <!--第三种,在aop:advisor里配置--> <aop:config> <aop:advisor advice-ref="" pointcut="" pointcut-ref=""> </aop:advisor> </aop:config> 复制代码
-
- aop:asepectj-aspect/
- 我们主要使用的配置方式(AspectJ),配置一个具体的切面(切点+通知)。同时会产生9个对象(见下方代码)
- <aop:scoped-advisor />
-
spring自带的aop方式,会产生两个对象
-
Advisor是Ponitcut和Advice的组合对象。一个目标对象可以被多个Advisor进行功能增强。Adivisor是一种特殊的Aspect,Advisor代表spring中的Aspect。区别:advisor只持有一个Pointcut和一个advice,而aspect可以多个pointcut和多个advice
- <aop:pointcut />
通过解析AOP所有配置的标签的过程(注解配置同理),我们得到了产生代理对象的代理对象创建器,得到配置的多个Advisor(Advice + Pointcut)。下一步我们就要产生具体的代理对象,并执行。
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 获取产生代理对象的代理对象创建器
// AspectJAwareAdvisorAutoProxyCreator
configureAutoProxyCreator(parserContext, element);
for (Element elt: childElts) {
// 获取的是aop标签冒号后面的标签名称(aop:pointcut,aop:advisor,aop:aspect)
String localName = parserContext.getDelegate().getLocalName(elt);
if (POINTCUT.equals(localName)) {
// 产生AspectJExpressionPointcut对象
parsePointcut(elt, parserContext);
}
else if (ADVISOR.equals(localName)) {
// spring自带的aop功能
// DefaultBeanFactoryPointcutAdvisor
// AspectJExpressionPointcut
parseAdvisor(elt, parserContext);
}
else if (ASPECT.equals(localName)) {
// AspectJExpressionPointcut 切点表达式
// MethodLocationFactoryBean
// SimpleBeanFactoryAwareAspectInstanceFactory
// AspectJPointcutAdvisor // 具体的Advisor,一个Advisor对应一个Advice
// 五种通知类型
// AspectJAfterReturningAdvice
// AspectJMethodBeforeAdvice
// AspectJAfterAdvice
// AspectJAroundAdvice
// AspectJAfterThrowingAdvice
parseAspect(elt, parserContext);
}
}
}
复制代码
产生AOP代理流程分析
上一步生成了AspectJAwareAdvisorAutoProxyCreator
类,通过这个类来产生具体的代理对象
先来看这个类的继承关系
-
BeanPostProcessor是针对于Bean的后置处理器(增强器),里面有两个方法:Bean对象初始化前增强,初始化后增强。
public interface BeanPostProcessor { // 初始化前调用 default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } // 初始化后调用 default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } } 复制代码
-
InstantiationAwareBeanPostProcessor类继承了BeanPostProcessor,里面有3个方法。相比于BeanPostProcessor增加了两个实例化前后调用的方法,以及处理属性值的方法。
postProcessBeforeInstantiation
实例化前调用postProcessAfterInstantiation
实例化后调用postProcessProperties
后置处理属性值
-
SmartInstantiationAwareBeanPostProcessor继承了InstantiationAwareBeanPostProcessor里面主要处理对构造函数的解析和推断、以及为了解决循环依赖问题,提前暴露对象等等。有兴趣的可以点进去看看。
-
AbstractAutoProxyCreator(关键类),这个类里有个非常重要的方法postProcessAfterInitialization,方法的注释非常重要。这也就是AOP功能的入口,也就知道了AOP功能的调用时机是在**初始化之后。**里面的主要处理逻辑就是wrapIfNecessary方法。方法的入参有bean:目标对象,所以我们就知道了目标类(Target)。然后根据目标类产生代理类。
/**
* 如果 bean 被子类标识为要代理的一个,则使用配置的拦截器创建一个代理。
*
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
// cacheKey是beanName或beanClass
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 使用动态代理技术,产生代理对象
// bean:目标对象,beanName:目标对象名称,cacheKey:beanName或beanClass
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
复制代码
wrapIfNecessary
先看注释,Create proxy if we have advice.创建代理对象,如果我们有通知(advice)的话。
// Create proxy if we have advice.
// 查找对代理相关的advisor对象集合,此处与point-cut表达式有关
// execution(* *..*.method(args))
// 第一步:查找候选Advisor(增强器)
// 第二步:针对目标对象获取合适的Advisor(增强器)
// 创建一个代理对象,获取当前的bean的增强器有哪些
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 对相应的advisor不为空,则采用代理
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;
}
复制代码
- getAdvicesAndAdvisorsForBean方法,我们点进入看看,发现此方法是个接口,点接口实现,选择
AbstractAdvisorAutoProxyCreator
类,就来到了最上面继承关系图的第五个类。然后看它的getAdvicesAndAdvisorsForBean方法。里面有个findEligibleAdvisors
方法,寻找合格的Advisor。-
findCandidateAdvisors方法先找到候选的所有Advisors,然后再通过ClassFilter和MethodMatch,来选中合适的Advisors。这就对应了流程图左边的部分,我们已经寻找到了多个Advisor,然后需要根据PointCut(切入点表达式)来获取合适的Advisors。而PointCut是根据ClassFilter(类过滤器)和MethodMatcher(方法匹配器)来判断哪个类的哪个方法需要被增强。(具体的匹配过程可以看
AopUtils
里的canApply
方法)
-
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 获取到候选的Advisors
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 获取到合适的Advisors,通过ClassFilter和MethodMatch来判断是否是否合格
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
复制代码
-
获取到适合于目标类的所有增强功能,下面开始创建代理工厂对象。点击
createProxy
方法,先创建一个代理工厂对象,然后将Advice和Advisor适配成Advisor,方便后面处理(之前我们获取到的合适的Advisors,specificInterceptors 对象是个object对象数组,所以现在需要转换一下,方便处理),然后将Advisor对象数组加到proxyFactory中,这样代理工厂对象就能将这些增强的功能加上将要生成的代理对象上。继续点击proxyFactory.getProxy
方法。protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) { // 创建代理工厂对象 ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); // 将Advice和Advisors都适配成Advisor,方便后面统一处理 Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); // 把增强器(通知方法)保存到proxyFactory中 proxyFactory.addAdvisors(advisors); proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } // 获取使用JDK动态代理或者CGLIB动态代理产生的对象 // 重点方法:用proxyFactory.getProxy代理工厂为我们创建代理对象是如何创建代理对象呢?跟进getProxy方法 return proxyFactory.getProxy(getProxyClassLoader()); } 复制代码
-
进入proxyFactory.getProxy方法,点击creatAopProxy,然后继续点击createAopProxy,进入到DefaultAopProxyFactory类中的
createAopProxy
方法。这个方法很重要,它用来判断我们是使用JDK还是CGLIB动态代理。但是默认使用JDK动态代理。所以下面我们就JdkDynamicAopProxy
这个方法来具体说说。
```java
// 类名:ProxyFactory
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
// 类名:ProxyCreatorSupport
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
// 类名:DefaultAopProxyFactory
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
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.");
}
// 如果目标类是接口或者目标类是Proxy的子类,则使用动态代理方式
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 使用CGLIB动态代理
return new ObjenesisCglibAopProxy(config);
}
else {
// 默认使用JDK动态代理
return new JdkDynamicAopProxy(config);
}
}
```
复制代码
JdkDynamicAopProxy
在执行ProxyFactory中的getProxy
方法时,代码如下:
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
复制代码
我们已经完成createAopProxy这一步,创建了AOP代理类,也就是JDKDynamicAopProxy
,下一步开始调用该类里的getProxy方法。
JDK动态代理是代理类实现目标类的接口,然后产生一个代理类,代理类和目标类是兄弟关系。所有在执行获取代理对象的过程中,要先获取目标类完整的接口。然后调用Proxy.newProxyInstance
方法,开始产生一个代理实例。点击这个方法。继续跟入。
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if(logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
//获取完整的代理接口
Class<?>[]proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
//调用JDK动态代理方法
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
复制代码
发现进入了Proxy类中的newProxyInstance
,而这个类是JDK包里的。通过反射机制来创建代理对象。如果不够得JDK和CGLIB动态代理的源码执行流程,可以看我的另一篇文章。Spring源码系列(五):JDK、CGLIB动态代理源码解析。
invoke
进入JdkDynamicAopProxy里的invoke方法,这里的proxy就是我们生成的代理对象,我们来查看它的属性。这里的InvocationHandler是代理对象的执行处理器。
我们在上一步已经生成代理对象,InvocationHandler负责组织增强代码和目标代码的结合。
因此proxy.h属性里有advisor集合,每个advisor里又有一个advice(增强方法/通知)。
这一块在流程中对应产生代理之后的绿色方块InvocationHandler。
this.advised.getInterceptorsAndDynamicInterceptionAdvice会产生一个调用链chain(就是MethodInterceptor的集合),每一个Advice会转换成MethodInterceptor来执行,且通知类型有5种,如图所示。但是MethodInterceptor的实现类只有三个,另外的两种通知类型就需要通过适配器来转换。
不清楚这一块的可以看我的另一篇文章juejin.cn/post/708749…。
new ReflectiveMethodInvocation方法产生MethodInvocation 对象,我们执行这个对象的proceed方法开始AOP的拦截过程。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
TargetSource targetSource = this.advised.targetSource;
// Get the interception chain for this method.
// 获取针对该目标对象的所有增强器(advisor),这些advisor都是有顺序的,会按照顺序进行链式调用
// 会将通知类型中不直接属于MethodInterceptor的通知类(advice)通过适配器模式转换为MethodInterceptor的直接子类
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
// 通过反射调用目标对象的方法,此时没有进行功能增强
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
// 我们需要创建一个方法调用
// proxy:生成的动态代理对象
// target:目标对象
// method:目标方法
// args:目标方法参数
// targetClass:目标类对象
// chain:AOP拦截器执行链,是一个MethodInterceptor的集合,
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
// 通过拦截器链进入连接点
// 开始执行AOP的拦截过程
retVal = invocation.proceed();
}
}
复制代码
进入proceed方法,我们省去一些不重要的逻辑,直接点击invoke方法。
public Object proceed() throws Throwable {
....
....
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
复制代码
我在配置文件里配置了前置增强,所以这里会进入AbstractAspectJAdvice里的``invokeAdviceMethodWithGivenArgs方法
,然后利用反射,执行增强方法。
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
// 利用反射,执行增强方法
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
}
复制代码
总结
AOP的执行过程,从标签解析到寻找BeanDefinitionParser,再到目标类实例化之后。开始初始化被BeanPostProcessor前后增强,AOP正是在AfterInitialization中方法执行的。
理解AOP的源码,主要在于SpringIOC源码的理解,也就是对Bean的生命周期理解。如果不理解Bean的生命周期,看AOP源码也是一头雾水。