spring-aop源码(三)调用aop代理方法过程

    科技2022-07-11  82

    系列文章目录

    【spring-aop源码(一)创建AnnotationAwareAspectJAutoProxyCreator过程】

    【spring-aop源码(二)创建代理对象过程】

    【spring-aop源码(三)调用aop代理方法过程】

    SpringBoot源码地址:https://github.com/spring-projects

    SpringBoot版本:2.4.0-SNAPSHOT

    SpringBoot分支:master


     

    文章目录

    准备工作

    一、获取目标方法拦截器链

    二、调用拦截器方法和目标方法

    三、目标方法的返回值

    总结

     


    准备工作

    服务类:smoketest.aop.service.HelloWorldService package smoketest.aop.service; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class HelloWorldService { @Value("${name:World}") private String name; public String getHelloMessage() { System.out.println("method:HelloWorldService|getHelloMessage()|" + name); int i = 3 / 0; return "hello"; } }

     

    切面配置类: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()方法,该方法代码如下,其中位置①②③是需要注意的点。

     

    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; Object target = null; TargetSource targetSource = this.advised.getTargetSource(); try { if (this.advised.exposeProxy) { // 标记为可调用 oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // 如果目标来自线程池,最小化拥有目标的时间 target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null); // ① 根据ProxyFactory对象获取将要执行的目标方法拦截器链 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; // 检查我们是否只有一个InvokerInterceptor:如果只有一个,也就是该对象没有真正的advice,而只是对目标的反射调用。 if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { // 可以跳过创建MethodInvocation的操作:只是目标直接使用反射调用操作 Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = methodProxy.invoke(target, argsToUse); } else { // ② 需要创建一个方法调用 retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); } //③ 目标方法的返回值 retVal = processReturnType(proxy, target, method, retVal); return retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }

    一、获取目标方法拦截器链

    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源码、调用代理方法过程


    Processed: 0.008, SQL: 8