【小家Spring】探索Spring AOP中aopalliance的Joinpoint、MethodInvocation、Interceptor、MethodInterceptor...(上)

简介: 【小家Spring】探索Spring AOP中aopalliance的Joinpoint、MethodInvocation、Interceptor、MethodInterceptor...(上)

前言



在这篇博文:【小家Spring】详解Spring AOP中底层代理模式之JdkDynamicAopProxy和CglibAopProxy(ObjenesisCglibAopProxy)的源码分析

我们已经能够知道了,代理对象创建好后,其实最终的拦截工作都是交给了MethodInvocation,JDK交给:ReflectiveMethodInvocation,CGLIB交给CglibMethodInvocation


备注:此处所说的MethodInvocation是AOP联盟包里的,也就是org.aopalliance.intercept.MethodInvocation。

AOP联盟包里和cglib包里都有的叫:MethodInterceptor,不要弄混了。

image.png


org.aopalliance.intercept.Joinpoint


首先需要注意的是,一般我们会接触到两个Joinpoint


1.org.aspectj.lang.JoinPoint:该对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,可以很方便的获得更多信息。(一般用于@Aspect标注的切面的方法入参里),它的API很多,常用的有下面几个:

1. Signature getSignature(); :封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息

2. Object[] getArgs();:传入目标方法的参数们

3. Object getTarget();:被代理的对象(目标对象)

4. Object getThis();:该代理对象

备注:ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中


2.org.aopalliance.intercept.Joinpoint是本文的重点,下面主要看看它的解释和相关方法:

// 此接口表示运行时的连接点(AOP术语)  (和aspectj里的连接点意思有点像)
public interface Joinpoint {
  // 执行此拦截点,并进入到下一个连接点
  Object proceed() throws Throwable;
  // 返回保存当前连接点静态部分【的对象】。  这里一般指的target
  Object getThis();
  // 返回此静态连接点  一般就为当前的Method(至少目前的唯一实现是MethodInvocation,所以连接点得静态部分肯定就是本方法喽)
  AccessibleObject getStaticPart();
}


org.aopalliance.intercept.Invocation


它的中文意思:祈祷; 乞求,它继承自Joinpoint

这个类没有同名的,只有的aopalliance里有。


// 此接口表示程序中的调用~
// 该调用是一个可以被拦截器拦截的连接点
public interface Invocation extends Joinpoint {
  // 获得参数们。比如方法的入参们
  Object[] getArguments();
}

org.aopalliance.intercept.MethodInvocation


接口到了这一层,就比较具象了。它表示方法的执行器,显然就是和Method方法有关喽


// 方法调用时,对这部分进行描述
public interface MethodInvocation extends Invocation {
  // 返回正在被调用得方法~~~  返回的是当前Method对象。
  // 此时,效果同父类的AccessibleObject getStaticPart() 这个方法
  Method getMethod();
}


MethodInvocation作为aopalliance里提供的最底层接口了。Spring提供了相关的实现,如下图:


image.png


Spring自己也定义了一个接口,来进行扩展和统一管理:ProxyMethodInvocation


org.springframework.aop.ProxyMethodInvocation



这个接口是Spring提供的对aopalliance里MethodInvocation的继承扩展接口


// 这是Spring提供的对MethodInvocation 的一个扩展。
// 它允许访问  方法被调用的代理对象以及其它相关信息
public interface ProxyMethodInvocation extends MethodInvocation {
  // 返回代理对象
  Object getProxy();
  // 克隆一个,使用的Object得clone方法
  MethodInvocation invocableClone();
  MethodInvocation invocableClone(Object... arguments);
  // 设置参数  增强器、通知们执行的时候可能会用到
  void setArguments(Object... arguments);
  // 添加一些属性kv。这些kv并不会用于AOP框架内,而是保存下来给特殊的一些拦截器实用
  void setUserAttribute(String key, @Nullable Object value);
  @Nullable
  Object getUserAttribute(String key);
}


下面我们就是主菜了,Spring给我们提供的唯一(其实算唯二吧)的实现类,它执行着拦截的核心逻辑。会让所有的通知器都执行~~


ReflectiveMethodInvocation


Reflective中文意思:可被反射的


显然它作为实现类,需要实现包括父接口在内的所有的方法们。

它也是JdkDynamicAopProxy最终执行时候new出来的执行对象,话不多说,下面看看具体的逻辑吧~~


public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
  protected final Object proxy; // 代理对象
  @Nullable
  protected final Object target; // 目标对象
  protected final Method method; // 被拦截的方法
  protected Object[] arguments = new Object[0];
  @Nullable
  private final Class<?> targetClass;
  @Nullable
  private Map<String, Object> userAttributes;
  protected final List<?> interceptorsAndDynamicMethodMatchers;
  // currentInterceptorIndex初始值为 -1 
  private int currentInterceptorIndex = -1;
  // 唯一的构造函数。注意是protected  相当于只能本包内、以及子类可以调用。外部是不能直接初始化的此对象的(显然就是Spring内部使用的类了嘛)
  //invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
  // proxy:代理对象
  // target:目标对象
  // method:被代理的方法
  // args:方法的参数们
  // targetClass:目标方法的Class (target != null ? target.getClass() : null)
  // interceptorsAndDynamicMethodMatchers:拦截链。  this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)这个方法找出来的
  protected ReflectiveMethodInvocation(
      Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
      @Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
    this.proxy = proxy;
    this.target = target;
    this.targetClass = targetClass;
    // 找到桥接方法,作为最后执行的方法。至于什么是桥接方法,自行百度关键字:bridge method
    // 桥接方法是 JDK 1.5 引入泛型后,为了使Java的泛型方法生成的字节码和 1.5 版本前的字节码相兼容,由编译器自动生成的方法(子类实现父类的泛型方法时会生成桥接方法)
    this.method = BridgeMethodResolver.findBridgedMethod(method);
    // 对参数进行适配
    this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
    this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
  }
  @Override
  public final Object getProxy() {
    return this.proxy;
  }
  @Override
  @Nullable
  public final Object getThis() {
    return this.target;
  }
  // 此处:getStaticPart返回的就是当前得method
  @Override
  public final AccessibleObject getStaticPart() {
    return this.method;
  }
  // 注意:这里返回的可能是桥接方法哦
  @Override
  public final Method getMethod() {
    return this.method;
  }
  @Override
  public final Object[] getArguments() {
    return this.arguments;
  }
  @Override
  public void setArguments(Object... arguments) {
    this.arguments = arguments;
  }
  // 这里就是核心了,要执行方法、执行通知、都是在此处搞定的
  // 这里面运用 递归调用 的方式,非常具有技巧性
  @Override
  @Nullable
  public Object proceed() throws Throwable {
    //  currentInterceptorIndex初始值为 -1  如果执行到链条的末尾 则直接调用连接点方法 即 直接调用目标方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      // 这个方法相当于调用了目标方法~~~下面会分析
      return invokeJoinpoint();
    }
    // 获取集合中的 MethodInterceptor(并且currentInterceptorIndex + 1了哦)
    Object interceptorOrInterceptionAdvice =
        this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    //InterceptorAndDynamicMethodMatcher它是Spring内部使用的一个类。很简单,就是把MethodInterceptor实例和MethodMatcher放在了一起。看看在advisor chain里面是否能够匹配上
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
      InterceptorAndDynamicMethodMatcher dm =
          (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
      // 去匹配这个拦截器是否适用于这个目标方法  试用就执行拦截器得invoke方法
      if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
        return dm.interceptor.invoke(this);
      }
      else {
        // 如果不匹配。就跳过此拦截器,而继续执行下一个拦截器
        // 注意:这里是递归调用  并不是循环调用
        return proceed();
      }
    }
    else {
      // 直接执行此拦截器。说明之前已经匹配好了,只有匹配上的方法才会被拦截进来的
      // 这里传入this就是传入了ReflectiveMethodInvocation,从而形成了一个链条了
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
  }
  // 其实就是简单的一个:method.invoke(target, args);
  // 子类可以复写此方法,去执行。比如它的唯一子类CglibAopProxy内部类  CglibMethodInvocation就复写了这个方法  它对public的方法做了一个处理(public方法调用MethodProxy.invoke)
  @Nullable
  protected Object invokeJoinpoint() throws Throwable {
    // 此处传入的是target,而不能是proxy,否则进入死循环
    return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
  }
  @Override
  public MethodInvocation invocableClone() {
    Object[] cloneArguments = this.arguments;
    if (this.arguments.length > 0) {
      // Build an independent copy of the arguments array.
      cloneArguments = new Object[this.arguments.length];
      System.arraycopy(this.arguments, 0, cloneArguments, 0, this.arguments.length);
    }
    return invocableClone(cloneArguments);
  }
  @Override
  public MethodInvocation invocableClone(Object... arguments) {
    if (this.userAttributes == null) {
      this.userAttributes = new HashMap<>();
    }
    try {
      ReflectiveMethodInvocation clone = (ReflectiveMethodInvocation) clone();
      clone.arguments = arguments;
      return clone;
    } catch (CloneNotSupportedException ex) {
      throw new IllegalStateException(
          "Should be able to clone object of type [" + getClass() + "]: " + ex);
    }
  }
  @Override
  public void setUserAttribute(String key, @Nullable Object value) {
    if (value != null) {
      if (this.userAttributes == null) {
        this.userAttributes = new HashMap<>();
      }
      this.userAttributes.put(key, value);
    }
    else {
      if (this.userAttributes != null) {
        this.userAttributes.remove(key);
      }
    }
  }
  @Override
  @Nullable
  public Object getUserAttribute(String key) {
    return (this.userAttributes != null ? this.userAttributes.get(key) : null);
  }
  public Map<String, Object> getUserAttributes() {
    if (this.userAttributes == null) {
      this.userAttributes = new HashMap<>();
    }
    return this.userAttributes;
  }
  @Override
  public String toString() {
    // Don't do toString on target, it may be proxied.
    StringBuilder sb = new StringBuilder("ReflectiveMethodInvocation: ");
    sb.append(this.method).append("; ");
    if (this.target == null) {
      sb.append("target is null");
    }
    else {
      sb.append("target is of class [").append(this.target.getClass().getName()).append(']');
    }
    return sb.toString();
  }
}




相关文章
|
7天前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
20天前
|
Java 数据库连接 数据库
Spring基础3——AOP,事务管理
AOP简介、入门案例、工作流程、切入点表达式、环绕通知、通知获取参数或返回值或异常、事务管理
Spring基础3——AOP,事务管理
|
2月前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
23 0
Spring高手之路22——AOP切面类的封装与解析
|
2月前
|
Java Spring XML
掌握面向切面编程的秘密武器:Spring AOP 让你的代码优雅转身,横切关注点再也不是难题!
【8月更文挑战第31天】面向切面编程(AOP)通过切面封装横切关注点,如日志记录、事务管理等,使业务逻辑更清晰。Spring AOP提供强大工具,无需在业务代码中硬编码这些功能。本文将深入探讨Spring AOP的概念、工作原理及实际应用,展示如何通过基于注解的配置创建切面,优化代码结构并提高可维护性。通过示例说明如何定义切面类、通知方法及其应用时机,实现方法调用前后的日志记录,展示AOP在分离关注点和添加新功能方面的优势。
38 0
|
2月前
|
缓存 安全 Java
Spring AOP 中两种代理类型的限制
【8月更文挑战第22天】
16 0
|
7天前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
2月前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
|
3月前
|
Java 测试技术 数据库
Spring Boot中的项目属性配置
本节课主要讲解了 Spring Boot 中如何在业务代码中读取相关配置,包括单一配置和多个配置项,在微服务中,这种情况非常常见,往往会有很多其他微服务需要调用,所以封装一个配置类来接收这些配置是个很好的处理方式。除此之外,例如数据库相关的连接参数等等,也可以放到一个配置类中,其他遇到类似的场景,都可以这么处理。最后介绍了开发环境和生产环境配置的快速切换方式,省去了项目部署时,诸多配置信息的修改。
|
3月前
|
Java 应用服务中间件 开发者
Java面试题:解释Spring Boot的优势及其自动配置原理
Java面试题:解释Spring Boot的优势及其自动配置原理
101 0
|
7天前
|
XML Java 关系型数据库
springboot 集成 mybatis-plus 代码生成器
本文介绍了如何在Spring Boot项目中集成MyBatis-Plus代码生成器,包括导入相关依赖坐标、配置快速代码生成器以及自定义代码生成器模板的步骤和代码示例,旨在提高开发效率,快速生成Entity、Mapper、Mapper XML、Service、Controller等代码。
springboot 集成 mybatis-plus 代码生成器
下一篇
无影云桌面