【小家Spring】Sping AOP中使用到的那些工具类盘点:AopUtils、AopConfigUtils、AspectJAopUtils 、AopProxyUtils、AopContext(下)

简介: 【小家Spring】Sping AOP中使用到的那些工具类盘点:AopUtils、AopConfigUtils、AspectJAopUtils 、AopProxyUtils、AopContext(下)

AopProxyUtils

我理解成它是对org.springframework.aop.support.AopUtils的一个补充。


    public static void main(String[] args) {
        HelloService helloService = getProxy(new HelloServiceImpl());
        //===============演示AopUtils==================
        // 注意:此处的入参必须是一个Advised:也就是被代理过的对象,否则返回null
        // 里面的TargetSource必须是SingletonTargetSource才会有所返回
        //@since 4.3.8
        System.out.println(AopProxyUtils.getSingletonTarget(helloService)); //com.fsx.service.HelloServiceImpl@17d677df
        // 获取一个代理对象的最终对象类型
        System.out.println(AopProxyUtils.ultimateTargetClass(helloService)); //class com.fsx.service.HelloServiceImpl
    }


接下俩的方法,才是这个工具类真正厉害的方法:


public abstract class AopProxyUtils {
  ...
  //很牛逼的方法来了,判断一个advised真正需要代理的目标接口列表 
  // 这个方法在getProxy()上都有应用,非常的重要。核心实现事下面的这个私有方法
  public static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised) {
    return completeProxiedInterfaces(advised, false);
  }
  // 很显然发现,最终代理出来的对象,除了实现了自己的接口外,还实现了额外的接口,如:
  // SpringProxy、Advised、DecoratingProxy等三个接口(备注:CGLIB代理未实现DecoratingProxy接口)
  static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {
    Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
    if (specifiedInterfaces.length == 0) {
      // No user-specified interfaces: check whether target class is an interface.
      Class<?> targetClass = advised.getTargetClass();
      if (targetClass != null) {
        if (targetClass.isInterface()) {
          advised.setInterfaces(targetClass);
        }
        else if (Proxy.isProxyClass(targetClass)) {
          advised.setInterfaces(targetClass.getInterfaces());
        }
        specifiedInterfaces = advised.getProxiedInterfaces();
      }
    }
    boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
    boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
    boolean addDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class));
    int nonUserIfcCount = 0;
    if (addSpringProxy) {
      nonUserIfcCount++;
    }
    if (addAdvised) {
      nonUserIfcCount++;
    }
    if (addDecoratingProxy) {
      nonUserIfcCount++;
    }
    Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length + nonUserIfcCount];
    System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);
    int index = specifiedInterfaces.length;
    if (addSpringProxy) {
      proxiedInterfaces[index] = SpringProxy.class;
      index++;
    }
    if (addAdvised) {
      proxiedInterfaces[index] = Advised.class;
      index++;
    }
    if (addDecoratingProxy) {
      proxiedInterfaces[index] = DecoratingProxy.class;
    }
    return proxiedInterfaces;
  }
  //该方法用于获取一个代理对象中的用户定义的接口,即非(Advised接口体系)之外的其他接口
  public static Class<?>[] proxiedUserInterfaces(Object proxy) {
    Class<?>[] proxyInterfaces = proxy.getClass().getInterfaces();
    int nonUserIfcCount = 0;
    if (proxy instanceof SpringProxy) {
      nonUserIfcCount++;
    }
    if (proxy instanceof Advised) {
      nonUserIfcCount++;
    }
    if (proxy instanceof DecoratingProxy) {
      nonUserIfcCount++;
    }
    Class<?>[] userInterfaces = new Class<?>[proxyInterfaces.length - nonUserIfcCount];
    System.arraycopy(proxyInterfaces, 0, userInterfaces, 0, userInterfaces.length);
    Assert.notEmpty(userInterfaces, "JDK proxy must implement one or more interfaces");
    return userInterfaces;
  }
  //判断两个(即将)代理出来的对象是否相同
  public static boolean equalsInProxy(AdvisedSupport a, AdvisedSupport b) {
    return (a == b ||
        (equalsProxiedInterfaces(a, b) && equalsAdvisors(a, b) && a.getTargetSource().equals(b.getTargetSource())));
  }
  // 判断它哥俩的接口是否相同
  public static boolean equalsProxiedInterfaces(AdvisedSupport a, AdvisedSupport b) {
    return Arrays.equals(a.getProxiedInterfaces(), b.getProxiedInterfaces());
  }
  // 判断它哥俩的增强器(Advisor)是否相同
  public static boolean equalsAdvisors(AdvisedSupport a, AdvisedSupport b) {
    return Arrays.equals(a.getAdvisors(), b.getAdvisors());
  }
}


JDK代理和CGLIB代理的获取代理对象方法都用使用此方法:


  // JDK代理的获取代理对象~~~
  @Override
  public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isDebugEnabled()) {
      logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
    }
    // 这里就是获取到所有需要被代理的接口
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
  }


AopProxyUtils中的方法不多,但是其中的ultimateTargetClass和completeProxiedInterfaces方法确是Spring AOP中比较重要的方法,也给了我们一个入手观察Spring AOP真正实现过程的一个突破口;我认为这个,才是AopProxyUtils给我们的价值;


AopContext


这个工具类就更简单,它代表AOP的上下文。主要是提供我们访问上下文中当前AOP对象的快速方法。


public abstract class AopContext {
  // 一个ThreadLocal,和当前上下文绑定的AOP对象
  private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");
  // public方法,调用者可以在任何地方获取到当前上下文中的AOP代理对象。
  // 请注意:这个和exposeProxy参数有关,只有为true了才生效,默认都是false的
  public static Object currentProxy() throws IllegalStateException {
    Object proxy = currentProxy.get();
    if (proxy == null) {
      throw new IllegalStateException(
          "Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.");
    }
    return proxy;
  }
  @Nullable
  static Object setCurrentProxy(@Nullable Object proxy) {
    Object old = currentProxy.get();
    if (proxy != null) {
      currentProxy.set(proxy);
    }
    else {
      currentProxy.remove();
    }
    return old;
  }
}


备注:这个工具类主要是和属性exposeProxy相关,让我们能够快捷的获取到AOP代理对象,而不是this对象。这个在事务不生效原因大解读的博文了得到了应用


AutoProxyUtils


为自动代理组件准备的工具类。主要用于框架内部使用(AbstractAutoProxyCreator)

public abstract class AutoProxyUtils {
  // org.springframework.aop.framework.autoproxy.AutoProxyUtils.preserveTargetClass
  // preserve:保护的  保留的
  // determine:查明  测定
  public static final String PRESERVE_TARGET_CLASS_ATTRIBUTE =
      Conventions.getQualifiedAttributeName(AutoProxyUtils.class, "preserveTargetClass");
  // org.springframework.aop.framework.autoproxy.AutoProxyUtils.originalTargetClass
  public static final String ORIGINAL_TARGET_CLASS_ATTRIBUTE =
      Conventions.getQualifiedAttributeName(AutoProxyUtils.class, "originalTargetClass");
  // 判断该beanName是否应该被代理
  // `AbstractAutoProxyCreator`里就有判断是否能够被代理。  如果能够被代理,那就采用CGLIB的代理方式了
  // 往里setAttr,目前只有`ConfigurationClassPostProcessor`对config配置类进行增强的时候
  // 
  public static boolean shouldProxyTargetClass(ConfigurableListableBeanFactory beanFactory, @Nullable String beanName) {
    // 容器内存在这个Bean,并且这个Bean的定义信息里面属性值`PRESERVE_TARGET_CLASS_ATTRIBUTE`必须是true才行  说明才能被代理
    if (beanName != null && beanFactory.containsBeanDefinition(beanName)) {
      BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
      return Boolean.TRUE.equals(bd.getAttribute(PRESERVE_TARGET_CLASS_ATTRIBUTE));
    }
    return false;
  }
  // 看看这个Bean定义的TargetClass
  // 如果Bean定义信息里面有ORIGINAL_TARGET_CLASS_ATTRIBUTE这个字段,那就不用getType()了
  // 以及ScopedProxyUtils创建和Scope有关的代理类的时候,其余地方都不会设置此属性
  @Nullable
  public static Class<?> determineTargetClass(ConfigurableListableBeanFactory beanFactory, @Nullable String beanName) {
    if (beanName == null) {
      return null;
    }
    if (beanFactory.containsBeanDefinition(beanName)) {
      BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
      Class<?> targetClass = (Class<?>) bd.getAttribute(ORIGINAL_TARGET_CLASS_ATTRIBUTE);
      if (targetClass != null) {
        return targetClass;
      }
    }
    return beanFactory.getType(beanName);
  }
  //Expose the given target class for the specified bean, if possible
  // 就是在它的Bean定义信息里面,设置一个ORIGINAL_TARGET_CLASS_ATTRIBUTE属性,然后吧targetClass类型放进去
  // 也是子啊`AbstractAutoProxyCreator`创建代理的时候会这只进去的
  static void exposeTargetClass(ConfigurableListableBeanFactory beanFactory, @Nullable String beanName,
      Class<?> targetClass) {
    if (beanName != null && beanFactory.containsBeanDefinition(beanName)) {
      beanFactory.getMergedBeanDefinition(beanName).setAttribute(ORIGINAL_TARGET_CLASS_ATTRIBUTE, targetClass);
    }
  }
}


AspectJAopUtils

相比于AopUtilsAspectJAopUtils是专门针对于AspectJ advisors的工具类。(当然AspectJ也是当下的主流方式)


public abstract class AspectJAopUtils {
  //拿到AspectJ的优先信息:AspectJPrecedenceInformation 这个就接口保存着
  @Nullable
  public static AspectJPrecedenceInformation getAspectJPrecedenceInformationFor(Advisor anAdvisor) {
    if (anAdvisor instanceof AspectJPrecedenceInformation) {
      return (AspectJPrecedenceInformation) anAdvisor;
    }
    Advice advice = anAdvisor.getAdvice();
    if (advice instanceof AspectJPrecedenceInformation) {
      return (AspectJPrecedenceInformation) advice;
    }
    return null;
  }
  // Advisor 是否是前置通知类型~~~~(Advisor都持有一个advice嘛)
  public static boolean isBeforeAdvice(Advisor anAdvisor) {
    AspectJPrecedenceInformation precedenceInfo = getAspectJPrecedenceInformationFor(anAdvisor);
    if (precedenceInfo != null) {
      return precedenceInfo.isBeforeAdvice();
    }
    return (anAdvisor.getAdvice() instanceof BeforeAdvice);
  }
  // Advisor 是否是后置通知类型~~~~(Advisor都持有一个advice嘛)
  public static boolean isAfterAdvice(Advisor anAdvisor) {
    AspectJPrecedenceInformation precedenceInfo = getAspectJPrecedenceInformationFor(anAdvisor);
    if (precedenceInfo != null) {
      return precedenceInfo.isAfterAdvice();
    }
    return (anAdvisor.getAdvice() instanceof AfterAdvice);
  }
}


AspectJProxyUtils

同样的,它相对于AopProxyUtils,它只是专门处理AspectJ代理对象的工具类。


public abstract class AspectJProxyUtils {
  // 判断,该Advisor是否是AspectJ的的增强器
  private static boolean isAspectJAdvice(Advisor advisor) {
    return (advisor instanceof InstantiationModelAwarePointcutAdvisor ||
        advisor.getAdvice() instanceof AbstractAspectJAdvice ||
        (advisor instanceof PointcutAdvisor &&
             ((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut));
  }
  // 只提供这么一个公共方法,但是这个方法都还是非常重要的。 Capable:有能力的
  // 它在自动代理创建器`AspectJAwareAdvisorAutoProxyCreator#extendAdvisors`方法中有调用(重要~~~)
  // 在AspectJProxyFactory#addAdvisorsFromAspectInstanceFactory方法中也有调用
  // 它的作用:只要发现有AspectJ的Advisor存在,并且advisors还不包含有ExposeInvocationInterceptor.ADVISOR  那就在第一个位置上调添加一个ExposeInvocationInterceptor.ADVISOR
  // 这个`ExposeInvocationInterceptor.ADVISOR`的作用:就是获取到当前的currentInvocation,也是使用的ThreadLocal
  public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
    // Don't add advisors to an empty list; may indicate that proxying is just not required
    if (!advisors.isEmpty()) {
      boolean foundAspectJAdvice = false;
      for (Advisor advisor : advisors) {
        // Be careful not to get the Advice without a guard, as
        // this might eagerly instantiate a non-singleton AspectJ aspect
        if (isAspectJAdvice(advisor)) {
          foundAspectJAdvice = true;
        }
      }
      if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
        advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
        return true;
      }
    }
    return false;
  }
}
相关文章
|
2天前
|
XML 监控 安全
Spring特性之一——AOP面向切面编程
Spring特性之一——AOP面向切面编程
15 1
|
2天前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
2天前
|
JSON 前端开发 Java
【JavaEE】Spring全家桶实现AOP-统一处理
【JavaEE】Spring全家桶实现AOP-统一处理
5 0
|
2天前
|
前端开发 Java 开发者
【JavaEE】面向切面编程AOP是什么-Spring AOP框架的基本使用
【JavaEE】面向切面编程AOP是什么-Spring AOP框架的基本使用
8 0
|
2天前
|
Java Spring 容器
Spring AOP浅谈
Spring AOP浅谈
10 1
|
2天前
|
XML Java 数据格式
Spring高手之路18——从XML配置角度理解Spring AOP
本文是全面解析面向切面编程的实践指南。通过深入讲解切面、连接点、通知等关键概念,以及通过XML配置实现Spring AOP的步骤。
22 6
Spring高手之路18——从XML配置角度理解Spring AOP
|
2天前
|
XML Java 数据格式
Spring使用AOP 的其他方式
Spring使用AOP 的其他方式
16 2
|
2天前
|
XML Java 数据格式
Spring 项目如何使用AOP
Spring 项目如何使用AOP
23 2
|
2天前
|
Java 开发者 Spring
Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
【5月更文挑战第1天】Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
25 5
|
2天前
|
XML Java 数据格式
Spring AOP
【5月更文挑战第1天】Spring AOP
28 5