- spring-AOP源码浅析
- 入口
- 自动代理创建器
- 类图
- 功能实现
- 切面获取流程
- ProxyFactory
- 通知
- 切面
- 执行过程
spring aop功能可以使用@EnableAspectJAutoProxy注解开启,这个注解中,通过@Import(AspectJAutoProxyRegistrar.class)导入了AspectJAutoProxyRegistrar这个注册类,该类实现了ImportBeanDefinitionRegistrar接口,这个接口会在ConfigurationClassPostProcessor后置处理器中被调用的(具体位置在:postProcessBeanDefinitionRegistry=> processConfigBeanDefinitions=> reader.loadBeanDefinitions=> loadBeanDefinitionsForConfigurationClass=> loadBeanDefinitionsFromRegistrars,也就是说它也是在beanFactory后置处理器中执行的,可以用于注册bean定义),这里注册了AnnotationAwareAspectJAutoProxyCreator这个类;
自动代理创建器即AnnotationAwareAspectJAutoProxyCreator
类图可以看到这个类实现了SmartInstantiationAwareBeanPostProcessor后置处理器接口和Aware注入接口
功能实现重写postProcessBeforeInstantiation方法:
- 根据advisedBeans判断是否需要增强,不需要直接跳过,否则继续;
- 判断是否是aop基础类,是的话标记到advisedBeans,跳出执行,否则继续;判断是否aop有两个方法:
- isInfrastructureClass:判断是否通知类(Advice)、切入点类(Pointcut)、切面类(Advisor)、aop基础类(AopInfrastructureBean)
- shouldSkip:调用findCandidateAdvisors方法获取所有候选切面,判断bean名称是否与候选切面相同;
- 判断是否存在TargetSourceCreator,如果有则尝试调用getTargetSource方法获取TargetSource;
- 获取成功则提前创建代理类,并添加到targetSourcedBeans,否则什么也不做;
重写postProcessAfterInitialization方法:
- 根据earlyProxyReferences判断是否已经处理(当存在循环依赖时,会在getEarlyBeanReference方法中提前完成增强),是则直接返回,否则调用wrapIfNecessary方法增强对象并返回;
重写getEarlyBeanReference方法:当有循环依赖时,会在实例化完成之前调用这个方法:
- 将bean缓存到earlyProxyReferences;
- 调用wrapIfNecessary方法提前增强目标对象;
wrapIfNecessary方法执行流程:
- 判断targetSourcedBeans,即是否已经代理,没有则继续;
- 根据advisedBeans判断是否需要增强,需要则继续;
- 判断是否aop基础类,是则标记到advisedBeans,否则继续;
- 调用getAdvicesAndAdvisorsForBean方法获取通知方法(这个方法又是调用findEligibleAdvisors方法实现的)
- 查找所有候选切面;
- 从候选切面中获取满足条件的切面;
- 如果切面列表不为空,则排序;
- 如果获取到的通知方法为空,则标记到advisedBeans为false,否则标记为true,然后继续;
- 调用createProxy方法,先创建ProxyFactory,设置通知方法,然后创建并返回代理对象;
了解了代理工厂的用法,这里再简单分析下,查找候选切面、查找满足条件的切面以及创建proxyFactory的过程;
查找所有候选切面(org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors):这个方法只会在首次调用时查找和创建切面并缓存到map中,后续调用就直接在map中取,这里主要看第一次调用:
- 从容器中获取所有bean,遍历所有bean,做后续的处理:
- 判断:有Aspect注解,并且没有被ajc编译;
- 将切面类封装到AspectMetadata;
- 判断代理类型是否单例,这里只考虑单例;
- 创建BeanFactoryAspectInstanceFactory;
- 调用this.advisorFactory.getAdvisors方法创建切面;
- 获取类型、名称;校验Aspect注解、ajc、切面类型;
- 将BeanFactoryAspectInstanceFactory包装到LazySingletonAspectInstanceFactoryDecorator,使得每个切面实例只实例化一次;
- 获取所有方法(排除掉Pointcut注解修饰的方法,然后按Around、Before、After、AfterReturning、AfterThrowing注解顺序排序)
- 根据方法注解创建AspectJExpressionPointcut切入点;
- 创建InstantiationModelAwarePointcutAdvisorImpl切面,这是PointcutAdvisor的一个子接口,添加了懒加载的功能;
- 处理DeclareParents注解修饰方法的切面;
- 将创建的切面缓存到map中;
查找满足条件的切面(org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply):
- 通过ThreadLocal保存当前正在操作的Bean;
- 调用AopUtils工具类的findAdvisorsThatCanApply方法判断是否满足增强条件:
- 先判断IntroductionAdvisor类型的切面,调用matches方法,满足条件的引介切面;
- 再判断PointcutAdvisor:获取ClassFilter,调用matches方法判断是否满足条件,如果满足则继续判断;
- 获取MethodMatcher,遍历所有class和实现了的接口,调用matches方法判断是否满足条件;
- 最后移除ThreadLocal中保存的变量;
如果找到的满足条件的切面不为空,spring默认会在切面列表的第一个位置插入一个增强为ExposeInvocationInterceptor的DefaultPointcutAdvisor切面,
ProxyFactory在上面的分析中,可以知道AOP最终是要获取通知,然后通过ProxyFactory这个来创建增强对象的(这个类最终还是调用的jdk或者cglib的方法来创建的代理类,两个实现类分别对应JdkDynamicAopProxy和
CglibAopProxy,需要的时候可以借鉴下里面的代码,这里不细讨论),这里先了解下代理工厂结合通知、切面的简单用法;
Advice接口标记的接口,它有下面这些子接口:
- MethodBeforeAdvice:前置通知;
- ThrowsAdvice:异常通知;
- AfterReturningAdvice:返回通知;
- MethodInterceptor:可以简单理解为环绕通知;
用法示例:
package com.demo.core.spring;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.util.ClassUtils;
import java.lang.reflect.Method;
class AdviceTest {
@Test
void test() {
ProxyFactory proxyFactory = new ProxyFactory(new A());
// 前置通知
proxyFactory.addAdvice(new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("======> before ....");
}
});
// 异常通知
proxyFactory.addAdvice(new CustomThrowAdvice());
// 返回通知
proxyFactory.addAdvice(new AfterReturningAdvice() {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("======> after returning ...");
}
});
// 可以理解为环绕通知
proxyFactory.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("======> method interceptor ...");
return invocation.proceed();
}
});
A proxy = (A) proxyFactory.getProxy();
proxy.doSth("参数....");
System.out.println("n==================下面这个方法会抛异常================n");
try {
proxy.doSth(null);
} catch (Exception e) {
Assertions.assertTrue(ClassUtils.isAssignable(RuntimeException.class, e.getClass()));
System.out.println(e.getMessage());
}
}
public static class CustomThrowAdvice implements ThrowsAdvice {
public void afterThrowing(Method method, Object[] args, Object target, Exception ex) {
System.out.println("======> after throw ... ");
}
}
public static class A {
public String doSth(String param) {
System.out.println("===> do sth: " + param);
if (param == null) {
throw new RuntimeException("随便抛个异常");
}
return "返回值...";
}
}
}
切面
Advisor接口标记的接口,由切入点加通知组成,它有下面这些子接口:
- IntroductionAdvisor:引介切面,通过matches方法判断是否需要增强类,可以做到直接代理接口,不需要实现类;
- PointcutAdvisor:切入点切面,根据指定规则判断是否需要增强类,具体的规则一般有:
- NameMatchMethodPointcut:通过方法名精确匹配;
- ControlFlowPointcut:根据当前线程的堆栈信息确定是否切入(效率低);
- ComposablePointcut:组合模式;
- JdkRegexpMethodPointcut:正则匹配;
- AspectJExpressionPointcut:切点表达式匹配;
用法示例:
package com.demo.core.spring;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.jupiter.api.Test;
import org.springframework.aop.*;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.stream.Collectors;
class AdvisorTest {
@Test
void introduction() {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addAdvisor(new CustomIntroductionAdvisor(new IntroductionInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("======> introduction interceptor ...... ");
if (invocation.getThis() == null) {
Method method = invocation.getMethod();
return String.format("%s#%s %s(%s)", method.getDeclaringClass().getSimpleName(),
method.getReturnType().getSimpleName(), method.getName(),
StringUtils.collectionToDelimitedString(
Arrays.stream(invocation.getArguments()).collect(Collectors.toList()),
",", """, """));
}
return invocation.proceed();
}
@Override
public boolean implementsInterface(Class> intf) {
return false;
}
}));
I1 i1 = (I1) proxyFactory.getProxy();
System.out.println("=========> " + i1.doSth("i1 param ...."));
I2 i2 = (I2) proxyFactory.getProxy();
// 随手一写发现个问题,这里的代理配置相当于:创建代理类同时实现I1、I2两个接口,这两个接口刚好有相同签名的方法;
// 那么这里默认只实现第一个接口的同签名方法,所以调用i2.doSth方法时,打印的是i1的信息;
// 如果这两个方法只有返回值不一样,则会报错,这一点和Java语法是一致的;
System.out.println("=========> " + i2.doSth("i2 param ...."));
System.out.println("=========> " + i2.doSth("i2 param1 ....", "i2 param2 ...."));
}
@Test
void pointcut() {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addAdvisor(new CustomPointcutAdvisor(new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("======> pointcut advisor before ....");
}
}));
proxyFactory.setTarget(new A());
A a = (A) proxyFactory.getProxy();
System.out.println("=========> " + a.doSth("param a..."));
System.out.println("===> b");
proxyFactory.setTarget(new B());
B b = (B) proxyFactory.getProxy();
System.out.println("=========> " + b.doSth("param b..."));
}
public static class CustomIntroductionAdvisor implements IntroductionAdvisor, ClassFilter {
private final Advice advice;
public CustomIntroductionAdvisor(Advice advice) {
this.advice = advice;
}
@Override
public boolean matches(Class> clazz) {
// 这个方法返回false,则不增强这个class
return ClassUtils.isAssignable(I1.class, clazz) || ClassUtils.isAssignable(I2.class, clazz);
}
@Override
public ClassFilter getClassFilter() {
return this;
}
@Override
public void validateInterfaces() throws IllegalArgumentException {
}
@Override
public Advice getAdvice() {
return advice;
}
@Override
public boolean isPerInstance() {
return true;
}
@Override
public Class>[] getInterfaces() {
return new Class[]{I1.class, I2.class};
}
}
public static class CustomPointcutAdvisor implements PointcutAdvisor {
private final Advice advice;
public CustomPointcutAdvisor(Advice advice) {
this.advice = advice;
}
@Override
public Pointcut getPointcut() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(public * com.demo.core.spring.AdvisorTest$B.*(..))");
return pointcut;
}
@Override
public Advice getAdvice() {
return this.advice;
}
@Override
public boolean isPerInstance() {
return false;
}
}
public interface I1 {
String doSth(String param);
}
public interface I2 {
String doSth(String param);
String doSth(String param1, String param2);
}
public static class A {
public String doSth(String param) {
System.out.println("=========> do sth: " + param);
return "a...";
}
}
public static class B {
public String doSth(String param) {
System.out.println("=========> do sth: " + param);
return "b...";
}
}
}
执行过程
ProxyFactory的执行过程要分情况,当frozen为true时,走FixedChainStaticTargetInterceptor,为false时,走DynamicAdvisedInterceptor,这两个类都实现了org.springframework.cglib.proxy.MethodInterceptor接口,这里以false情况为例做简单分析;
DynamicAdvisedInterceptor:是CglibAopProxy的私有静态内部类,其AdvisedSupport成员变量保存切面相关信息;还实现了MethodInterceptor接口的intercept方法,这个方法是执行的入口,其执行大致流程如下:
- 获取目标对象和class类型;
- 获取执行链(就是排序好的包装了通知方法的和目标方法的MethodInterceptor的子接口的list集合);
- 创建CglibMethodInvocation(这里会判断:是非Object派生方法的共有方法),然后执行proceed方法;
- proceed方法会被递归调用,直到执行链执行结束(这里并不是按执行链依次调用,而是在执行每个执行链节点方法中的适当位置调用proceed方法,这个方法中会根据currentInterceptorIndex的值从执行链list中去对应的执行节点后自增,直到超过list大小,调用invokeJoinpoint方法,具体调用顺序为:before -> throws -> after return -> method interceptor -> invokeJoinpoint -> 目标方法);



