【spring-aop源码(一)创建AnnotationAwareAspectJAutoProxyCreator过程】
【spring-aop源码(二)创建代理对象过程】
【spring-aop源码(三)调用aop代理方法过程】
SpringBoot源码地址:https://github.com/spring-projects
SpringBoot版本:2.4.0-SNAPSHOT
SpringBoot分支:master
准备工作
一、获取目标方法拦截器链
二、调用拦截器方法和目标方法
三、目标方法的返回值
总结
切面配置类:smoketest.aop.monitor.ServiceMonitor (@Around @After @Before @AfterReturning @AfterThrowing) package smoketest.aop.monitor; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.aop.aspectj.AspectJAfterAdvice; import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator; import org.springframework.stereotype.Component; @Aspect @Component public class ServiceMonitor { @Pointcut("execution(* smoketest..*Service.*(..))") public void pointCut() { } @After(value = "pointCut()") public void afterAccess(JoinPoint joinPoint) { System.out.println("after: " + joinPoint); } @Before(value = "pointCut()") public void beforeAccess(JoinPoint joinPoint) { System.out.println("before: " + joinPoint); } @Around("pointCut()") public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("around:start around"); Object obj = pjp.proceed(); System.out.println("around:end around"); return obj; } @AfterReturning(value = " pointCut()") public void returnAccess(JoinPoint joinPoint) { System.out.println("return: " + joinPoint); } @AfterThrowing(throwing = "ex", pointcut = "pointCut()") public void doRecoveryActions(Throwable ex) { System.out.println("Exception thrown in the target method:" + ex); } }
smoketest.aop.SampleAopApplication(题外话,这个类实现了CommandLineRunner) package smoketest.aop; import smoketest.aop.service.HelloWorldService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SampleAopApplication implements CommandLineRunner { // Simple example shows how an application can spy on itself with AOP @Autowired private HelloWorldService helloWorldService; @Override public void run(String... args) { System.out.println("staring......"); helloWorldService.getHelloMessage(); System.out.println("ending......"); } public static void main(String[] args) { SpringApplication.run(SampleAopApplication.class, args); } }
4. Debug位置
Debug的方法为smoketest.aop.service.HelloWorldService.getHelloMessage(),可以看到这时从容器中获取到类型为HelloWorldService的bean是被Spring使用CGLIB增强后的的bean,如下图所示,其中黄色圈为断点位置。
当运行到上图的断点位置,接着Step Into会进入CglibAopProxy的intercept()方法,该方法代码如下,其中位置①②③是需要注意的点。
Step Into位置①进入AdvisedSupport的getInterceptorsAndDynamicInterceptionAdvice()方法,其代码如下,该方法的作用是:根据配置,为给定的方法确定所有需要的MethodInterceptor。
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) { MethodCacheKey cacheKey = new MethodCacheKey(method); List<Object> cached = this.methodCache.get(cacheKey); if (cached == null) { cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( this, method, targetClass); this.methodCache.put(cacheKey, cached); } return cached; }继续Debug,会Step Into到DefaultAdvisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice()方法。在该方法内,真正创建目标方法的拦截器链,其代码如下。
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class<?> targetClass) { //获取全局AdvisorAdapter的注册表。 AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); //从传入的配置中获取所有的Advisor。 Advisor[] advisors = config.getAdvisors(); List<Object> interceptorList = new ArrayList<>(advisors.length); Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass()); Boolean hasIntroductions = null; //遍历所有的Advisor,将Advisor 转换为 for (Advisor advisor : advisors) { if (advisor instanceof PointcutAdvisor) { // 有条件地添加。 PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor; if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { 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 { match = mm.matches(method, actualClass); } if (match) { //根据advisor获取MethodInterceptor MethodInterceptor[] interceptors = registry.getInterceptors(advisor); 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)); } } } } else if (advisor instanceof IntroductionAdvisor) { IntroductionAdvisor ia = (IntroductionAdvisor) advisor; if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) { Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } else { Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } return interceptorList; }该方法的主要逻辑为:
获取全局AdvisorAdapter的注册表,调试发现这里的注册表中有三个adapters,如下图所示。而在spring-aop包的源码中可以发现,每一种adapter都对应了一种interceptor,如下图所示。
从传入的配置中获取所有的Advisor,可以看到这里一共有6个,如下图所示。其中包括1个默认的ExposeInvocationInterceptor和5个增强器
遍历所有的Advisor,将它们转换为MethodInterceptor添加到interceptorList,interceptorList如下图所示从上图可以看出,interceptorList有两种类型,暂且归为interceptor和advice,但是后面我们会知道,实际上最终都会归为advice,和每一个注解对应的advice如下表所示,并且重点在advice中的invokeAdviceMethod()方法。
@Around
AspectJAroundAdvice
@Before
AspectJMethodBeforeAdvice
@After
AspectJAfterAdvice
@AfterReturing
AspectJAfterReturningAdvice
@AfterThrowing
AspectJAfterThrowingAdvice
Step Into位置② 先进入CglibMethodInvocation的构造方法,此构造方法又调用了父类ReflectiveMethodInvocation的构造方法。再进入CglibAopProxy的proceed()方法,此方法调用了父类的proceed()方法。Step Into后进入ReflectiveMethodInvocation的proceed()方法,该方法代码如下
public Object proceed() throws Throwable { // 从索引-1开始并提前增加。 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm =(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; 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 { // Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. return proceed(); } } else { // 它是一个拦截器,所以只需要调用它: 在构造此对象之前,切入点将进行静态评估 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }该方法的逻辑为:
在容器中的ReflectiveMethodInvocation中维护了一个currentInterceptorIndex变量,其默认值为-1,将该值和interceptorsAndDynamicMethodMatchers.size() - 1作比较判断是否执行目标方法(getHelloMessage());从interceptorsAndDynamicMethodMatchers的ExposeInvocationInterceptor开始,currentInterceptorIndex开始自增。判断该interceptorsAndDynamicMethodMatcher是否为InterceptorAndDynamicMethodMatcher实例,然后调用它的invoker()方法;在这里就会调用所有增强器。调用增强器和目标方法的时序图(无异常)
调用增强器和目标方法的时序图(有异常)
Step Into位置③进入CglibAopProxy的processReturnType()方法,该方法获取目标方法的返回类型和返回值,如下图所示
最终控制台的打印如下所示
执行无异常:
staring...... around:start around before: execution(String smoketest.aop.service.HelloWorldService.getHelloMessage()) method:HelloWorldService|getHelloMessage()|Phil return: execution(String smoketest.aop.service.HelloWorldService.getHelloMessage()) after: execution(String smoketest.aop.service.HelloWorldService.getHelloMessage()) around:end around执行有异常:
staring...... around:start around before: execution(String smoketest.aop.service.HelloWorldService.getHelloMessage()) method:HelloWorldService|getHelloMessage()|Phil Exception thrown in the target method:java.lang.ArithmeticException: / by zero after: execution(String smoketest.aop.service.HelloWorldService.getHelloMessage()) Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.关键词:spring-aop源码、调用代理方法过程