Spring AOP源码:拦截器责任链处理过程

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: Spring AOP源码:拦截器责任链处理过程

文章导航

Spring AOP:基本概述

Spring AOP源码:配置文件解析过程

Spring AOP源码:配置文件解析过程

Spring AOP源码:拦截器责任链处理过程

目录

文章导航

前言

正文

方法1:getInterceptorsAndDynamicInterceptionAdvice

方法2:getInterceptorsAndDynamicInterceptionAdvice

方法3:getInterceptors

方法4:proceed

AOP的几种增强方法

切面配置方式

实现MethodInterceptor接口

实现AdvisorAdapter子接口

总结

前言

前面的章节讲解了AOP的配置文件解析过程,及其代理文件的生成过程,AOP生成代理的方式有Cglib和JDK动态代理两种方法,根据配置及其接口实现情况,挑选出代理方法。生成代理类后,调用其具体方法时,会调用其所配置的切面方法,其使用的是责任链设计模式,本篇文章会讲解其解析过程。

正文

    public static void main(String[] args) {
        //指定代理文件生成位置
        //JDK
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:\\Code\\SourceCode\\LeanCode\\Spring\\spring-framework-zh\\com");
        //CGLIB
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
        ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("application-aop.xml");
        MyAccount myAccount = (MyAccount) applicationContext.getBean("myAccount");
        myAccount.transfer();
    }


我们从调用代理类的方法作为入口,这里以Cglib动态代理方式为例。在调用myAccount.transfer()方法时,会调用其回调方法。

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
      Object oldProxy = null;
      boolean setProxyContext = false;
      Object target = null;
      //获取目标类类型,TargetSource 是其封装类
      TargetSource targetSource = this.advised.getTargetSource();
      try {
        if (this.advised.exposeProxy) {
          // Make invocation available if necessary.
          oldProxy = AopContext.setCurrentProxy(proxy);
          setProxyContext = true;
        }
        // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
        target = targetSource.getTarget();
        //获取被代理类类型
        Class<?> targetClass = (target != null ? target.getClass() : null);
        // 从advised中获取配置好的AOP通知
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        Object retVal;
        // Check whether we only have one InvokerInterceptor: that is,
        // no real advice, but just reflective invocation of the target.
        // 如果没有aop通知配置,那么直接调用target对象的调用方法
        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
          // 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 = methodProxy.invoke(target, argsToUse);
        }
        else {
          // We need to create a method invocation...
          // 通过cglibMethodInvocation来启动advice通知
          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);
        }
      }
    }

this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass),见方法1详解

new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(),见方法4详解

方法1:getInterceptorsAndDynamicInterceptionAdvice

  public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
    // 创建一个method的缓存对象,在MethodCacheKey中实现了equals和hashcode方法同时还实现了compareTo方法
    MethodCacheKey cacheKey = new MethodCacheKey(method);
    List<Object> cached = this.methodCache.get(cacheKey);
    // 先从缓存中获取,如果缓存中获取不到,则再调用方法获取,获取之后放入到缓存中
    if (cached == null) {
      // 调用的是advisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice方法
      cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
          this, method, targetClass);
      this.methodCache.put(cacheKey, cached);
    }
    return cached;
  }

this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass),见方法2详解

方法2:getInterceptorsAndDynamicInterceptionAdvice

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
      Advised config, Method method, @Nullable Class<?> targetClass) {
    // This is somewhat tricky... We have to process introductions first,
    // but we need to preserve order in the ultimate list.
    // 这里用了一个单例模式 获取DefaultAdvisorAdapterRegistry实例
    // 在Spring中把每一个功能都分的很细,每个功能都会有相应的类去处理 符合单一职责原则的地方很多 这也是值得我们借鉴的一个地方
    // AdvisorAdapterRegistry这个类的主要作用是将Advice适配为Advisor 将Advisor适配为对应的MethodInterceptor
    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
    Advisor[] advisors = config.getAdvisors();
    // 创建一个初始大小为 之前获取到的 通知个数的集合
    List<Object> interceptorList = new ArrayList<>(advisors.length);
    // 如果目标类为null的话,则从方法签名中获取目标类
    Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
    // 判断目标类是否存在引介增强,通常为false
    Boolean hasIntroductions = null;
    // 循环目标方法匹配的通知
    for (Advisor advisor : advisors) {
      // 如果是PointcutAdvisor类型的实例
      if (advisor instanceof PointcutAdvisor) {
        // Add it conditionally.
        PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
        // 如果提前进行过切点的匹配了或者当前的Advisor适用于目标类
        if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
          MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
          boolean match;
          //检测Advisor是否适用于此目标方法
          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) {
            // 拦截器链是通过AdvisorAdapterRegistry来加入的,这个AdvisorAdapterRegistry对advice织入具备很大的作用
            MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
            // 使用MethodMatchers的matches方法进行匹配判断
            if (mm.isRuntime()) {
              // Creating a new object instance in the getInterceptors() method
              // isn't a problem as we normally cache created chains.
              // 动态切入点则会创建一个InterceptorAndDynamicMethodMatcher对象
              // 这个对象包含MethodInterceptor和MethodMatcher的实例
              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)) {
          // 将Advisor转换为Interceptor
          Interceptor[] interceptors = registry.getInterceptors(advisor);
          interceptorList.addAll(Arrays.asList(interceptors));
        }
      }
      // 以上两种都不是
      else {
        // 将Advisor转换为Interceptor
        Interceptor[] interceptors = registry.getInterceptors(advisor);
        interceptorList.addAll(Arrays.asList(interceptors));
      }
    }
    return interceptorList;
  }

这个方法会将IOC容器中的所有的Advisor对象进行匹配,看是否符合目标对象及其方法,并筛选或封装成有实现MethodInterceptor类的对象。

registry.getInterceptors(advisor),见方法3详解

方法3:getInterceptors

  public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    List<MethodInterceptor> interceptors = new ArrayList<>(3);
    // 从Advisor中获取 Advice
    Advice advice = advisor.getAdvice();
    //如果有实现了MethodInterceptor接口,则添加到集合中
    if (advice instanceof MethodInterceptor) {
      interceptors.add((MethodInterceptor) advice);
    }
    for (AdvisorAdapter adapter : this.adapters) {
      if (adapter.supportsAdvice(advice)) {
        // 转换为对应的 MethodInterceptor类型
        // AfterReturningAdviceInterceptor MethodBeforeAdviceInterceptor  ThrowsAdviceInterceptor
        interceptors.add(adapter.getInterceptor(advisor));
      }
    }
    if (interceptors.isEmpty()) {
      throw new UnknownAdviceTypeException(advisor.getAdvice());
    }
    return interceptors.toArray(new MethodInterceptor[0]);
  }

这个方法判断传递进来的通知类是否有实现MethodInterceptor接口,有则直接添加到集合中。如果没有则判断是否为AfterReturningAdvice、MethodBeforeAdviceAdapter、ThrowsAdviceAdapter的实现类,如果是的话,通过adapter.getInterceptor(advisor)方法,将advisor封装成MethodInterceptor的实现子类。

方法4:proceed

    public Object proceed() throws Throwable {
      try {
        return super.proceed();
      }
      catch (RuntimeException ex) {
        throw ex;
      }
      catch (Exception ex) {
        if (ReflectionUtils.declaresException(getMethod(), ex.getClass())) {
          throw ex;
        }
        else {
          throw new UndeclaredThrowableException(ex);
        }
      }
    }
public Object proceed() throws Throwable {
    // We start with an index of -1 and increment early.
    // 从索引为-1的拦截器开始调用,并按序递增,如果拦截器链中的拦截器迭代调用完毕,开始调用被代理类的方法,这个方法是通过反射机制完成的
    // 具体实现在AopUtils.invokeJoinpointUsingReflection方法中
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
    }
    // 获取下一个要执行的拦截器,沿着定义好的interceptorOrInterceptionAdvice链进行处理
    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.
      // 这里对拦截器进行动态匹配的判断,这里是对pointcut触发进行匹配的地方,如果和定义的pointcut匹配,那么这个advice将会得到执行
      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.
        // 如果不匹配,那么proceed会被递归调用,知道所有的拦截器都被运行过位置
        return proceed();
      }
    }
    else {
      // It's an interceptor, so we just invoke it: The pointcut will have
      // been evaluated statically before this object was constructed.
      // 普通拦截器,直接调用拦截器,将this作为参数传递以保证当前实例中调用链的执行
      //这个方法会通过递归的方法一直调用proceed方法,直到所有的拦截器执行完毕后,执行被代理类的方法后直接返回。
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
  }

AOP的几种增强方法

切面配置方式

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class AccountAdvice {
  public void before(){
    System.out.println("before");
  }
  public void after(){
    System.out.println("after");
  }
  public void around(){
    System.out.println("around");
  }
  public void afterReturn(){
    System.out.println("afterReturn");
  }
  public void afterThrow(){
    System.out.println(")");
  }
}
<?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="accountAdvice" class="service.impl.AccountAdvice" ></bean>
    <bean id="myAccount" class="service.impl.MyAccount" ></bean>
    <aop:config >
      <aop:pointcut id="pointCut" expression="execution(* service.impl.*.*())"/>
      <aop:aspect ref="accountAdvice" >
        <aop:after method="after" pointcut-ref="pointCut" ></aop:after>
        <aop:before method="before" pointcut-ref="pointCut"></aop:before>
        <aop:around method="around" pointcut-ref="pointCut"></aop:around>
        <aop:after-returning method="afterReturn" pointcut-ref="pointCut"></aop:after-returning>
        <aop:after-throwing method="afterThrow" pointcut-ref="pointCut"></aop:after-throwing>
      </aop:aspect>
    </aop:config>
</beans>

这种配置方式,在解析过程中会将after 、before 、around 、after-returning、after-throwing 封装成Advisor对象

实现MethodInterceptor接口

从方法3中可以知道,只要实现了MethodInterceptor接口,则会被当做拦截器加入集合中,在后续的责任链调用过程中会进行调用。

public class MyAdvisor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("自定义的方法!!!1");
        return invocation.proceed();
    }
}
<?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="myAccount" class="service.impl.MyAccount" ></bean>
    <bean id="myAdvisor" class="service.aop.MyAdvisor" ></bean>
    <aop:config >
      <aop:pointcut id="pointCut" expression="execution(* service.impl.*.*())"/>
      <aop:advisor advice-ref="myAdvisor" pointcut-ref="pointCut"></aop:advisor>
    </aop:config>
</beans>

实现AdvisorAdapter子接口

从方法3中我们知道,当实现了AfterReturningAdvice、MethodBeforeAdviceAdapter、ThrowsAdviceAdapter接口中任何一个接口时,只要切点符合被代理类及其方法时,会被封装成MethodInterceptor的实现子类,从而来责任链调用过程中会获取调用。

import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class MyAdvisor2 implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("自定义的afterReturning");
    }
}
<?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="myAccount" class="service.impl.MyAccount" ></bean>
    <bean id="myAdvisor2" class="service.aop.MyAdvisor2" ></bean>
    <aop:config >
      <aop:pointcut id="pointCut" expression="execution(* service.impl.*.*())"/>
      <aop:advisor advice-ref="myAdvisor2" pointcut-ref="pointCut"></aop:advisor>
    </aop:config>
</beans>

总结

AOP动态代理过程使用责任链设计模式,将符合条件的MethodInterceptor实现类进行层层调用,但是不一定MethodInterceptor集合中的所有都会被调用,如果某个MethodInterceptor执行过程中,没有调用其下层拦截器,直接返回了,则后面的拦截器不会进行调用。



目录
相关文章
|
16天前
|
XML Java 开发者
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
|
3月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
121 2
|
3月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
129 5
|
24天前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
25天前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
72 8
|
3月前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
2月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
67 2
|
3月前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
103 5
|
3月前
|
监控 Java 数据安全/隐私保护
如何用Spring Boot实现拦截器:从入门到实践
如何用Spring Boot实现拦截器:从入门到实践
72 5
|
3月前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
94 8