文章目录
- 前言
- 一、wrapIfNecessary
- 1.1、getAdvicesAndAdvisorsForBean
- 1.1.1、findCandidateAdvisors
- 1.1.2、findAdvisorsThatCanApply
- 1.2、createProxy
- 二、invoke
- 2.1、getInterceptorsAndDynamicInterceptionAdvice
- 2.1.1、getInterceptors
- 2.2、proceed
- 2.2.1、invoke
- 三、@AspectJ模式下注解的解析
- 总结
前言
在Spring中,AOP通常是通过动态代理实现的,通过运行时增强的机制,以实现在目标代码执行前后的统一的逻辑。而Spring将两大动态代理,封装成为了ProxyFactory
,在ProxyFactory
中,会自动进行动态代理方式的选择:
@Component
public class UserService {public void originalMethod() {System.out.println("UserService originalMethod");}
}
同时在ProxyFactory
中,允许在目标对象上添加切面(advisors 或 advices),自定义切面的逻辑:
/*** 方法执行前后执行*/
public class AroundAdvice implements MethodInterceptor {/*** Implement this method to perform extra treatments before and* after the invocation. Polite implementations would certainly* like to invoke {@link Joinpoint#proceed()}.** @param invocation the method invocation joinpoint* @return the result of the call to {@link Joinpoint#proceed()};* might be intercepted by the interceptor* @throws Throwable if the interceptors or the target object* throws an exception*/@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("方法执行前");invocation.proceed();System.out.println("方法执行后");return null;}
}
其中ThrowsAdvice 较为特殊,接口中没有具体的方法,但是子类在定义具体的实现时,方法的名称,签名必须要遵循特定的格式:
/*** 抛出异常后执行*/
public class AfterThrowingAdvice implements ThrowsAdvice {public void afterThrowing(Method method, Object[] args, Object target, Exception ex){System.out.println("AfterThrowingAdvice");}
}
在ProxyFactory
中添加切面,其中Advisor是Advice + Pointcut组合,等于切入点+切面
,指定在哪些方法上应用 Advice:
public class ProxyFactoryDemo {public static void main(String[] args) {UserService userService = new UserService();ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTarget(userService);proxyFactory.addAdvice(new AroundAdvice());proxyFactory.addAdvisor(new PointcutAdvisor() {@Overridepublic Pointcut getPointcut() {return new StaticMethodMatcherPointcut() {@Overridepublic boolean matches(Method method, Class<?> targetClass) {return method.getName().equals("originalMethod");}};}@Overridepublic Advice getAdvice() {return new BeforeAdvice();}@Overridepublic boolean isPerInstance() {return false;}});UserService proxy = (UserService) proxyFactory.getProxy();proxy.originalMethod();}
}
而在Spring的底层,如果通过AOP配置类的方式进行:
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("org.ragdollcat.aop.aspect")
public class AppConfig {
}
@Aspect
@Component
public class MyAspect {@Before("execution(public void org.ragdollcat.aop.aspect.UserService.originalMethod())")public void before(JoinPoint joinPoint) {System.out.println("before method");}
}
@Component
public class UserService {public void originalMethod() {System.out.println("UserService originalMethod");}
}
public class Test {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);UserService bean = (UserService) context.getBean("userService");bean.originalMethod();}
}
会在refresh的invokeBeanFactoryPostProcessors
这一步,解析AppConfig 配置类,扫描到了@EnableAspectJAutoProxy
注解,就会向
beanDefinitionMap
中存放一个AnnotationAwareAspectJAutoProxyCreator
类型的bean后处理器。
最终在bean生命周期的初始化后这一步
执行
AbstractAutoProxyCreator
的postProcessAfterInitialization
方法:
一、wrapIfNecessary
最终通过AbstractAutoProxyCreator
的postProcessAfterInitialization
方法,会进入wrapIfNecessary
方法,在AbstractAutoProxyCreator
中,有两个重要的属性:
- targetSourcedBeans是一个set集合,用于存储不需要代理的 Bean 名称。
- advisedBeans 是一个Map,记录了哪些bean需要代理,哪些不需要代理,value存放了布尔值,表示该bean是否应该被代理。
这两个属性都起到缓存的作用。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { // 如果 beanName 非空且 targetSourcedBeans 集合中包含该 beanName,则直接返回 bean,不进行代理if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean;}// 如果 cacheKey 对应的 bean 在 advisedBeans 中已明确标记为不需要代理,则直接返回 beanif (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean;}// 如果该 bean 是基础设施类(比如加上了@Component注解的自定义advice类)或者应跳过代理(shouldSkip 方法返回 true),// 则将其在 advisedBeans 中标记为不需要代理,并直接返回 beanif (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// 1.1、getAdvicesAndAdvisorsForBean 获取该 bean 适用的增强(Advice)或拦截器(Advisor)Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);// 如果返回的拦截器数组不是 DO_NOT_PROXY(表示需要代理),则创建代理对象if (specificInterceptors != DO_NOT_PROXY) { // 在 advisedBeans 记录该 bean 需要代理this.advisedBeans.put(cacheKey, Boolean.TRUE);// 1.2、createProxy 创建代理对象Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));// 进行缓存,方便后续使用this.proxyTypes.put(cacheKey, proxy.getClass());// 返回代理对象return proxy;}// 如果不需要代理,则记录该 bean 不需要代理,并返回原始 beanthis.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;
}
1.1、getAdvicesAndAdvisorsForBean
getAdvicesAndAdvisorsForBean
最终会进入findEligibleAdvisors
方法,在该方法中,做了四件事,主要是用于判断,当前的bean是否需要aop
- 获取所有候选的 Advisor(增强逻辑),即当前 Spring 容器中所有可用的通知器。
- 从所有候选 Advisor(candidateAdvisors)中筛选出适用于当前 beanClass 的 Advisor。
- 对筛选出的 Advisor 进行扩展处理。
- 如果有可用的 Advisor,则进行排序。
1.1.1、findCandidateAdvisors
在findCandidateAdvisors
中,也做了两件事,分别是调用父类findCandidateAdvisors()
方法,获取 Spring 机制自动发现的 Advisor,以及调用 buildAspectJAdvisors()
解析所有 @Aspect
类。
如果我们像前言中那样手动注册了
Advisor
,则会走父类的逻辑,找到所有类型为Advisor
的自定义切面。
否则使用了
@Aspect
注解,就会走后续的逻辑,先是从容器中获取所有的bean,然后判断哪些bean上加入了@Aspect
注解,在getAdvisors
方法中真正进行解析:
ReflectiveAspectJAdvisorFactory
的getAdvisors
,会找到标注了@Aspect
类中,没有加@PointCut
的方法,然后循环这些方法,解析成切面:
最终包装成
InstantiationModelAwarePointcutAdvisorImpl
对象返回:
InstantiationModelAwarePointcutAdvisorImpl
是Advisor
的子类
1.1.2、findAdvisorsThatCanApply
这一段代码的重点在于canApply
方法,会判断是否有匹配的切点。
// 该方法用于判断一个给定的 Pointcut 是否适用于目标类
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {// 确保 Pointcut 对象不为 null,若为 null 则抛出异常Assert.notNull(pc, "Pointcut must not be null");// 如果目标类不匹配 Pointcut 的类过滤器,则直接返回 falseif (!pc.getClassFilter().matches(targetClass)) {return false;}// 获取与 Pointcut 相关联的方法匹配器MethodMatcher methodMatcher = pc.getMethodMatcher();// 如果方法匹配器是 "TRUE",表示所有方法都可以匹配,直接返回 trueif (methodMatcher == MethodMatcher.TRUE) {return true;}// 如果方法匹配器是 IntroductionAwareMethodMatcher 类型,则强制转换为该类型IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;if (methodMatcher instanceof IntroductionAwareMethodMatcher) {introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;}// 使用 LinkedHashSet 存储目标类和它的接口,避免重复Set<Class<?>> classes = new LinkedHashSet<>();// 如果目标类不是代理类,则添加目标类的用户类(去除代理的类)if (!Proxy.isProxyClass(targetClass)) {classes.add(ClassUtils.getUserClass(targetClass));}// 将目标类的所有接口也加入到类集合中classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));// 遍历类集合中的每一个类,检查该类中每个方法是否匹配 Pointcutfor (Class<?> clazz : classes) {// 获取该类的所有的方法Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);// 遍历方法,检查每个方法是否匹配 Pointcut 的方法匹配器for (Method method : methods) {// 如果使用的是 IntroductionAwareMethodMatcher,则使用其匹配方法,否则使用普通的 methodMatcher 匹配if (introductionAwareMethodMatcher != null ? introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : methodMatcher.matches(method, targetClass)) {// 如果匹配成功,返回 truereturn true;}}}// 如果没有方法匹配,则返回 falsereturn false;
}
1.2、createProxy
createProxy
方法的作用是创建代理对象,底层使用的也是proxyFactory:
最终调用的也是
proxyFactory
的getProxy
方法,在该方法中,主要做了两件事:
- 代理模式的选择
- 根据选择的代理模式,创建代理对象
- 如果当前的jvm是GraalVM,就直接用jdk动态代理。
- 如果当前的jvm不是GraalVM,并且下面三个条件满足其一,就会再次进行判断:启用优化(cglib的性能某些情况要优于jdk动态代理)或
isProxyTargetClass
为true或没有为目标类提供用户自定义的代理接口。- 如果目标类是接口或目标类是代理类或目标类是 Lambda 类,还是会使用jdk动态代理。
- 否则就会走cglib动态代理
我的案例中,使用的是jdk动态代理,在进行了代理的选择后
就会根据jdk和cglib不同的方式去创建代理对象:
以jdk动态代理为例:
二、invoke
invoke
方法是在调用代理类目标方法时执行的逻辑。
在invoke
方法中首先会进行判断,如果当前方法是equals
或者hashcode
,就不会进行代理,进到invoke
是以被代理类中单个方法的维度。
并且
@EnableAspectJAutoProxy
的exposeProxy
属性为true时(默认为false),就会把当前的代理放入ThreadLocal中。(解决事务自调用失效)
然后会获取方法的拦截器链:
最后会进行判断,如果拦截器链为空,说明没有适合该方法的切面,则不需要AOP,直接调用目标方法,否则会按照顺序调用拦截器链:
2.1、getInterceptorsAndDynamicInterceptionAdvice
获取方法的拦截器链,最终会调用DefaultAdvisorChainFactory
的getInterceptorsAndDynamicInterceptionAdvice
方法,这一步会拿到所有的advisor
如果定义的是advice,也会被封装成advisor:
接着会去遍历所有的advisor,关键性的代码:
if (advisor instanceof PointcutAdvisor) {// Add it conditionally.PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;//先匹配类,可以重写Pointcut接口的getClassFilter方法,自定义匹配的逻辑 比如根据类名匹配if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {//再匹配方法,可以重写Pointcut接口的getMethodMatcher方法,自定义匹配的逻辑 MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();boolean match;if (mm instanceof IntroductionAwareMethodMatcher) {if (hasIntroductions == null) {hasIntroductions = hasMatchingIntroductions(advisors, actualClass);}match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);}else {//matches如果没有自定义,默认是truematch = mm.matches(method, actualClass);}if (match) {//2.1.1、getInterceptors 将所有的advisor转换为MethodInterceptor对象MethodInterceptor[] interceptors = registry.getInterceptors(advisor);//如果方法匹配器的.isRuntime属性设置成了true,就会对所有匹配的方法,再进行参数的筛选。if (mm.isRuntime()) {// Creating a new object instance in the getInterceptors() method// isn't a problem as we normally cache created chains.for (MethodInterceptor interceptor : interceptors) {interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));}}else {interceptorList.addAll(Arrays.asList(interceptors));}}}
}
2.1.1、getInterceptors
getInterceptors
是将advisor
封装成MethodInterceptor
的逻辑,在这一段代码中,运用了适配器模式
:
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {List<MethodInterceptor> interceptors = new ArrayList<>(3);Advice advice = advisor.getAdvice();//如果advice 的类型本来就属于 MethodInterceptorif (advice instanceof MethodInterceptor) {//直接添加进集合interceptors.add((MethodInterceptor) advice);}//遍历所有的适配器for (AdvisorAdapter adapter : this.adapters) {//如果某一个适配器和当前的advice类型匹配if (adapter.supportsAdvice(advice)) {//就由该适配器进行类型转换interceptors.add(adapter.getInterceptor(advisor));}}if (interceptors.isEmpty()) {throw new UnknownAdviceTypeException(advisor.getAdvice());}return interceptors.toArray(new MethodInterceptor[0]);
}
在构造DefaultAdvisorAdapterRegistry
时,默认添加了三个适配器:
适配器类
某一个具体子类,实现了适配器类:
2.2、proceed
proceed
是执行拦截器链以及目标方法的逻辑,有两个实现,目前主要看ReflectiveMethodInvocation
的实现:
在该类中有一个重要的属性,初始值是-1,会根据该属性的值去判断是否应该执行目标方法:
proceed
方法中,主要对于InterceptorAndDynamicMethodMatcher
类型的拦截器进行了判断,如果拦截器是该类型,说明需要对于方法的参数也进行匹配。
public Object proceed() throws Throwable { // proceed 方法用于执行拦截器链中的下一个拦截器,或最终执行目标方法// 当拦截器链中的所有拦截器都执行完毕时(即当前索引等于最后一个拦截器的索引),执行目标方法if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {// 通过反射调用目标方法,相当于 method.invoke(target, args);return invokeJoinpoint();}// 从拦截器链中取出下一个拦截器(或拦截器 + 动态方法匹配器)Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);// 判断当前拦截器是否是 InterceptorAndDynamicMethodMatcher 类型//InterceptorAndDynamicMethodMatcher 类型的拦截器,支持对于方法的参数进行匹配if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {// 强制类型转换,将拦截器对象转换为 InterceptorAndDynamicMethodMatcherInterceptorAndDynamicMethodMatcher dm =(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;// 确定目标类,如果 targetClass 为空,则使用 method 所在的类Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());// 使用动态方法匹配器判断当前方法是否符合匹配规则if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {// 如果方法匹配,则调用对应的拦截器return dm.interceptor.invoke(this);} else {// 如果方法不匹配,则跳过该拦截器,继续执行下一个拦截器return proceed();}} else {// 如果拦截器不是 InterceptorAndDynamicMethodMatcher 类型,则直接执行其 invoke 方法return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}
}
在invoke
方法中,实际调用的就是各自切面的逻辑,假设我的切面类如下:
@Aspect
@Component
public class MyAspect {@Before("execution(public void org.ragdollcat.aop.aspect.UserService.originalMethod())")public void before(JoinPoint joinPoint) {System.out.println("before method");}@Around("execution(public void org.ragdollcat.aop.aspect.UserService.originalMethod())")public Object around(ProceedingJoinPoint pjp) throws Throwable {System.out.println("around - before method");Object proceed = pjp.proceed();System.out.println("around - after method");return proceed;}@After("execution(public void org.ragdollcat.aop.aspect.UserService.originalMethod())")public void after() {System.out.println("after method");}
}
那么执行切面的顺序是:
2.2.1、invoke
这里的invoke
最终调用的是各个切面的invoke
方法
基于基于 Spring AOP代理方式的拦截器:
- AfterReturningAdviceInterceptor:在目标方法正常返回后执行的拦截器(不包括异常情况),需要实现AfterReturningAdvice接口。相当于注解模式下的@AfterReturning 和@After注解
- MethodBeforeAdviceInterceptor:在目标方法执行前执行的拦截器,需要实现MethodBeforeAdvice接口。相当于注解模式下的@Before注解
- ThrowsAdviceInterceptor:在目标方法抛出异常后执行的拦截器。需要实现ThrowsAdvice接口。相当于注解模式下@AfterThrowing注解
- MethodInterceptor:目标方法执行前、执行后,相当于注解模式下的@Around注解
是基于 AspectJ 注解方式的拦截器:
- AspectJAfterAdvice:在目标方法执行后触发,不管方法是否抛出异常,对应@After注解。
- AspectJMethodBeforeAdvice:在目标方法执行前触发,对应@Before 注解
- AspectJAroundAdvice:在目标方法执行前、执行后、异常时都可以进行拦截,相当于 @Before + @AfterReturning + @AfterThrowing 组合,对应@Around注解。
- AspectJAfterThrowingAdvice:异常通知拦截器,在目标方法抛出异常后触发。对应@AfterThrowing注解。
- AspectJAfterReturningAdvice:在目标方法正常返回后执行的拦截器(不包括异常情况),对应@AfterReturning注解。
可以简单的看一下其中的invoke
逻辑:
前置增强,先执行前置增强逻辑,然后继续调用链
后置增强,先执行调用链,最终再执行后置增强逻辑
三、@AspectJ模式下注解的解析
在@AspectJ
模式下,对于注解的解析,最终会调用到ReflectiveAspectJAdvisorFactory
的getAdvice
方法,其中有对于每个注解的解析逻辑:
最终也是将其包装成不同的切面,然后会统一再次封装成
InstantiationModelAwarePointcutAdvisorImpl
对象,作为该对象的instantiatedAdvice
属性。
并且在
2.1.1、getInterceptors
中,会对其进行二次适配,目的是将不同类型的 Advice 转换为 MethodInterceptor
Advice 类型 | 作用 | 适配的 AdvisorAdapter | 转换后的 MethodInterceptor |
---|---|---|---|
@Before | 方法执行前 | MethodBeforeAdviceAdapter | MethodBeforeAdviceInterceptor |
@AfterReturning | 方法正常返回后 | AfterReturningAdviceAdapter | AfterReturningAdviceInterceptor |
@AfterThrowing | 方法抛出异常后 | ThrowsAdviceAdapter | ThrowsAdviceInterceptor |
@After | 方法执行完成(无论是否异常) | AspectJAfterAdvice | 直接实现了 MethodInterceptor |
@Around | 方法执行前后 | AspectJAroundAdvice | 直接实现了 MethodInterceptor |
总结
Spring的AOP,分为了两部分:
第一部分是@EnableAspectJAutoProxy
向Spring容器中添加的AnnotationAwareAspectJAutoProxyCreator
后置处理器的调用(在bean生命周期的初始化后调用),主要完成了:
- 获取并解析切面
- 获取所有候选的 Advisor(增强逻辑),即当前 Spring 容器中所有可用的通知器。
- 对于Spring AOP API注册的切面的适配。
- 对于AspectJ 注解方式的解析。
- 从所有候选 Advisor(candidateAdvisors)中筛选出适用于当前 beanClass 的 Advisor。
- 对筛选出的 Advisor 进行扩展处理。
- 如果有可用的 Advisor,则进行排序。
- 获取所有候选的 Advisor(增强逻辑),即当前 Spring 容器中所有可用的通知器。
- 构建代理
- 选择代理模式
- 创建代理对象
第二部分是执行目标方法,代理类的invoke
方法的调用:
- 获取到与方法匹配的拦截器链,这里会再次进行切面匹配:
- 匹配类
- 匹配方法
- 特殊情况匹配方法的参数
- 目标方法的调用(递归)