Spring AOP拦截器调用的实现

简介: Spring AOP拦截器调用的实现

在Spring AOP通过jdk的proxy方式或者CGLIB方式生成代理对象的时候,相关的拦截器已经配置到代理对象中去了,拦截器在代理对象中起作用是通过对这些方法的回调来完成的。


如果使用jdk的proxy来生成代理对象,那么需要通过InvocationHandler来设置拦截器回调。而如果使用CGLIB来生成代理对象,就需要根据CGLIB的使用要求,提供过DynamicAdvisedInterceptor来完成回调。


【1】JdkDynamicAopProxy的invoke拦截

JdkDynamicAopProxy 实现了AopProxy, InvocationHandler, Serializable接口,内部维护了AdvisedSupport advised成员。


那么也就是说当Proxy对象的代理方法被调用时,JdkDynamicAopProxy的invoke方法作为Proxy对象的回调函数被触发,从而通过invoke的具体实现,来完成对目标对象方法调用的拦截或者说功能增强的工作。


其invoke方法如下所示,对Proxy对象的代理设置是在invoke方法中完成的,这些设置包括获取目标对象、拦截器链,同时把这些对象作为输入创建了ReflectiveMethodInvocation对象,通过这个对象来完成对AOP功能实现的封装。

@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  Object oldProxy = null;
  boolean setProxyContext = false;
  TargetSource targetSource = this.advised.targetSource;
  Object target = null;
  try {
    // 如果目标对象没有实际object类的基本方法:equals
    if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
      // The target does not implement the equals(Object) method itself.
      return equals(args[0]);
    }
    // 如果目标对象没有实际object类的基本方法:hashCode
    else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
      // The target does not implement the hashCode() method itself.
      return hashCode();
    }
    else if (method.getDeclaringClass() == DecoratingProxy.class) {
      // There is only getDecoratedClass() declared -> dispatch to proxy config.
      return AopProxyUtils.ultimateTargetClass(this.advised);
    }
    //根据代理对象的配置来调用服务
    else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
        method.getDeclaringClass().isAssignableFrom(Advised.class)) {
      // Service invocations on ProxyConfig with the proxy config...
      return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
    }
    Object retVal;
    //将代理对象暴露给AopContext
    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);
    // Get the interception chain for this method.
    // 这个很重要,获取拦截器链
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    // Check whether we have any advice. If we don't, we can fallback on direct
    // reflective invocation of the target, and avoid creating a MethodInvocation.
    if (chain.isEmpty()) {
      // 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.
      // 如果没有设定拦截器则直接调用target的对应方法
      Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
      retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
    }
    else {
    // 如果有拦截器的设定,那么需要调用拦截器之后才调用目标对象的相应方法
    // 通过构造一个ReflectiveMethodInvocation赖世雄
      // We need to create a method invocation...
      MethodInvocation invocation =
          new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
      // Proceed to the joinpoint through the interceptor chain.
      // 沿着拦截器链继续前进
      retVal = invocation.proceed();
    }
    // Massage return value if necessary.
    Class<?> returnType = method.getReturnType();
    if (retVal != null && retVal == target &&
        returnType != Object.class && returnType.isInstance(proxy) &&
        !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
      // Special case: it returned "this" and the return type of the method
      // is type-compatible. Note that we can't help if the target sets
      // a reference to itself in another returned object.
      retVal = proxy;
    }
    else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
      throw new AopInvocationException(
          "Null return value from advice does not match primitive return type for: " + method);
    }
    return retVal;
  }
  finally {
    if (target != null && !targetSource.isStatic()) {
      // Must have come from TargetSource.
      targetSource.releaseTarget(target);
    }
    if (setProxyContext) {
      // Restore old proxy.
      AopContext.setCurrentProxy(oldProxy);
    }
  }
}

在这个invoke方法中,包含了一个完成的拦截器链对目标对象的拦截过程,比如获得拦截器链并对拦截器链中的拦截器进行配置,逐个运行拦截器链里的拦截增强,直到最后对目标对象方法的运行等。

【2】CglibAopProxy的intercept拦截

在分析CglibAopProxy的代理对象生成的时候,我们了解到对于AOP的拦截调用其回调是在DynamicAdvisedInterceptor对象中实现的,这个回调的实现在intercept方法中。


CglibAopProxy的intercept回调方法的实现和JdkDynamicAopProxy的回调实现是非常类似的,只是在DynamicAdvisedInterceptor的intercept方法中构造的是CglibMethodInvocation对象来完成拦截器链的调用,而在JdkDynamicAopProxy中是通过构造ReflectiveMethodInvocation对象来完成这个功能的。

需要注意点的是,CglibMethodInvocation是CglibAopProxy的静态内部类,继承于ReflectiveMethodInvocation重写了proceed方法。

@Override
@Nullable
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 {
    // 暴露proxy给AopContext
    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
    target = targetSource.getTarget();
    Class<?> targetClass = (target != null ? target.getClass() : null);
    // 核心方法,获取拦截器链
    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.
    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);
      // 这里通过methodProxy实现
      retVal = methodProxy.invoke(target, argsToUse);
    }
    else {
      // We need to create a method invocation...
      // 封装CglibMethodInvocation 调用proceed,执行拦截器链
      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);
    }
  }
}

【3】目标对象方法的调用

如果没有设置拦截器,那么会对目标对象的方法直接进行调用。对于JdkDynamicAopProxy代理对象,这个对目标对象的方法调用是通过AopUtils使用反射机制在 AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse)的方法中实现的。


代码如下所示,首先得到调用方法的反射对象,然后使用invoke启动对方法反射对象的调用。

public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
    throws Throwable {
  // Use reflection to invoke the method.
  try {
  // 设置访问权限
    ReflectionUtils.makeAccessible(method);
    // 方法反射调用
    return method.invoke(target, args);
  }
  catch (InvocationTargetException ex) {
    // Invoked method threw a checked exception.
    // We must rethrow it. The client won't see the interceptor.
    throw ex.getTargetException();
  }
  catch (IllegalArgumentException ex) {
    throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
        method + "] on target [" + target + "]", ex);
  }
  catch (IllegalAccessException ex) {
    throw new AopInvocationException("Could not access method [" + method + "]", ex);
  }
}

对于使用CglibAopProxy代理对象,它对目标对象的调用是通过CGLIB的MethodProxy对象来直接完成的,这个对象的使用是由CGLIB的设计决定的。具体的调用在DynamicAdvisedInterceptor的intercept方法可以看到,使用的是CGLIB封装好的功能。相对JdkDynamicAopProxy的实现来说,形式上看起来较为简单,但它们的功能却是一样的,都是完成对目标对象方法的调用。

retVal = methodProxy.invoke(target, argsToUse);
// org.springframework.cglib.proxy.MethodProxy#invoke
public Object invoke(Object obj, Object[] args) throws Throwable {
  try {
    init();
    FastClassInfo fci = fastClassInfo;
    return fci.f1.invoke(fci.i1, obj, args);
  }
  catch (InvocationTargetException ex) {
    throw ex.getTargetException();
  }
  catch (IllegalArgumentException ex) {
    if (fastClassInfo.i1 < 0)
      throw new IllegalArgumentException("Protected method: " + sig1);
    throw ex;
  }
}

【4】AOP拦截器链的调用

AOP对目标对象增强是通过将实现封装在AOP拦截器链中,由一个个具体的拦截器完成的。对于jdk和CGLIB两种方式来言,虽然使用了不同的AopProxy代理对象,但最终对AOP拦截的处理可谓殊途同归:它们对拦截器链的调用都是在ReflectiveMethodInvocation的proceed方法实现的。在proceed方法中,会逐个运行拦截器的拦截方法。


在运行拦截器的拦截方法之前,需要对代理方法完成一个匹配判断,通过这个匹配判断来决定拦截器是否满足切面增强的要求。即我们熟知的Pointcut切点中需要进行matches的匹配过程,即matches调用对方法进行匹配判断,来决定是否需要实行通知增强。


如下代码所示,在proceed方法中,先进行判断如果现在已经运行到拦截器链的末尾,俺么就会直接调用目标对象的实现方法。否则,沿着拦截器链继续进行,得到下一个拦截器。通过这个拦截器进行matches判断,判断是否适用于横切增强的场合。如果是,从拦截器得到通知器,并启动通知器的invoke方法进行切面增强。在这个过程结束以后,会迭代调用proceed凡那个发,直到拦截器链当中的拦截器都完成以上的拦截过程为止。

public Object proceed() throws Throwable {
  // We start with an index of -1 and increment early.
  // 从索引为-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.
      // 如果不匹配则递归调用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.
    // 如果是一个Interceptor,直接调用这个Interceptor对应的方法
    return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
  }
}


目录
相关文章
|
4月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
19天前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
2月前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
40 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
24天前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
30 1
|
20天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
31 0
|
3月前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
2月前
|
Java API Spring
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中拦截器的入门教程和实战项目场景实现的详细指南。
28 0
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
|
2月前
|
Java 编译器 Spring
Spring AOP 和 AspectJ 的区别
Spring AOP和AspectJ AOP都是面向切面编程(AOP)的实现,但它们在实现方式、灵活性、依赖性、性能和使用场景等方面存在显著区别。‌
85 2
|
2月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
134 9
|
2月前
|
XML Java 数据格式
Spring的IOC和AOP
Spring的IOC和AOP
48 0