zoukankan      html  css  js  c++  java
  • 从动态代理到Spring AOP(中)

    一.前言

      上一章节主要介绍了JDK动态代理和CGLIB动态代理:https://www.cnblogs.com/GrimMjx/p/11194283.html

      这一章主要结合我们之前学习的动态代理的基础来学习Sring AOP,本章学习需要Spring IOC的基础。首先会有一个Spring AOP的例子,后面逐渐深入会把一些关键的源码贴出来供大家学习。

    二.一个栗子

    2.1 创建Spring配置文件

      本例子使用xml的方式来配置Spring,如果你用Springboot可以用@EnableAspectJAutoProxy来开启AOP功能。xml配置Spring是否使用注解AOP的方式是<aop:aspectj-autoproxy/>,配置文件中有这句话Spring就会支持注解的AOP。

    2.2 创建要被代理的bean

      这个bean的某个方法可能封装着核心逻辑,如果我们想对这个方法的前后加入日志或者其他逻辑进行增强,直接修改这个bean不符合面向对象的设计,还好Spring AOP帮我们做到这一点。那我们先创建一个简单的bean:

    2.3 创建Advisor

      Spring2.0可以采用@AspectJ注解对POJO进行标记,从而定义一个包含切点信息和增强横切逻辑的切面然后织入匹配的目标Bean中

      在AspectJConfig中,我们简单的对test方法前后记录日志。记住光用@AspectJ注解是不够的。要么再添加@Component注解要么在xml添加这个bean,官方解释如下:

      You may register aspect classes as regular beans in your Spring XML configuration, or autodetect them through classpath scanning - just like any other Spring-managed bean. However, note that the @Aspect annotation is not sufficient for autodetection in the classpath: For that purpose, you need to add a separate @Component annotation (or alternatively a custom stereotype annotation that qualifies, as per the rules of Spring’s component scanner).

      再来看一下代码:

    2.4 测试

      我们可以写一个简单的测试类,通过容器拿到TestBean这个bean,然后调用test方法,看一下结果:

    三.源码赏析

      最好带着问题去看源码,要不然自己也是跟着走一遍,源码是无尽,开发思想是有尽的。比如今天看了ConcurrentHashMap的size方法对cas的优化,再看看LongAdder是咋玩的,学到了分散思想。

      -谁来创建AOP的?

      -谁来解析@Aspect注解的类?

      -JDK动态代理怎么创建的?

      -CGlib动态代理怎么创建的?

      -有什么好的设计方式吗?

      -等等

    3.1 谁来创建?

      是AnnotationAwareAspectJAutoProxyCreator。(名字就很通俗易懂)

      Spring扫到<aop:aspectj-autoproxy/>后,AspectJAutoProxyBeanDefinitionParser会注册这位创建者。对于Spring AOP的实现,AnnotationAwareAspectJAutoProxyCreator是负责代理的创建者,也是我们赏析的开始。先来看下这个创建者的类图:

      当Spring加载每个Bean的时候会在实例化前调用postProcessorAfterInitialization方法,对于AOP的逻辑也由此开始:

    3.2 获取所有增强器

      还记得之前的栗子吗?有个类是有@AspectJ注解的AspectJConfig类,这个类里面有@PointCut注解,这个注解的意思是对哪些方法进行增强,这里@Pointcut("execution(* *.test(..))")表示要对所有test方法进行增强。通过不同的切点表达函数可以实现对某些你想要类或者方法进行增强。

      那么,什么叫增强器?Spring AOP在JoinPoint“周围”维护一系列的拦截器。有哪些Advice呢?

    • @Before - 在JoinPoint方法之前执行
    • @AfterReturning - 在JoinPoint方法正常执行后执行
    • @AfterThrowing - 在JoinPoint方法抛出异常退出并执行
    • @After - 无论JoinPoint方法正常返回还是异常返回都会执行
    • @Around - 在JoinPoint方法前后执行

      例子中,有2个增强器。那我们看下源码是如何拿到所有的增强器的,看这个方法:org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors

     1 /**
     2  * Look for AspectJ-annotated aspect beans in the current bean factory,
     3  * and return to a list of Spring AOP Advisors representing them.
     4  * <p>Creates a Spring Advisor for each AspectJ advice method.
     5  * @return the list of {@link org.springframework.aop.Advisor} beans
     6  * @see #isEligibleBean
     7  */
     8 public List<Advisor> buildAspectJAdvisors() {
     9     List<String> aspectNames = null;
    10     synchronized (this) {
    11         aspectNames = this.aspectBeanNames;
    12         if (aspectNames == null) {
    13             List<Advisor> advisors = new LinkedList<Advisor>();
    14             aspectNames = new LinkedList<String>();
    15             // 获取容器内所有的beanName
    16             String[] beanNames =
    17                     BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
    18             // for循环,找出增强方法        
    19             for (String beanName : beanNames) {
    20                 // 过滤不合法的bean,子类实现
    21                 if (!isEligibleBean(beanName)) {
    22                     continue;
    23                 }
    24                 // We must be careful not to instantiate beans eagerly as in this
    25                 // case they would be cached by the Spring container but would not
    26                 // have been weaved
    27                 // 获取到bean类型
    28                 Class beanType = this.beanFactory.getType(beanName);
    29                 if (beanType == null) {
    30                     continue;
    31                 }
    32                 // 如果存在@AspectJ注解
    33                 if (this.advisorFactory.isAspect(beanType)) {
    34                     // 加入list
    35                     aspectNames.add(beanName);
    36                     AspectMetadata amd = new AspectMetadata(beanType, beanName);
    37                     if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
    38                         MetadataAwareAspectInstanceFactory factory =
    39                                 new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
    40 
    41                         // 解析获取增强方法
    42                         List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
    43                         if (this.beanFactory.isSingleton(beanName)) {
    44                             // 放入缓存
    45                             this.advisorsCache.put(beanName, classAdvisors);
    46                         }
    47                         else {
    48                             this.aspectFactoryCache.put(beanName, factory);
    49                         }
    50                         advisors.addAll(classAdvisors);
    51                     }
    52                     else {
    53                         // Per target or per this.
    54                         if (this.beanFactory.isSingleton(beanName)) {
    55                             throw new IllegalArgumentException("Bean with name '" + beanName +
    56                                     "' is a singleton, but aspect instantiation model is not singleton");
    57                         }
    58                         MetadataAwareAspectInstanceFactory factory =
    59                                 new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
    60                         this.aspectFactoryCache.put(beanName, factory);
    61                         advisors.addAll(this.advisorFactory.getAdvisors(factory));
    62                     }
    63                 }
    64             }
    65             this.aspectBeanNames = aspectNames;
    66             return advisors;
    67         }
    68     }
    69     if (aspectNames.isEmpty()) {
    70         return Collections.EMPTY_LIST;
    71     }
    72     List<Advisor> advisors = new LinkedList<Advisor>();
    73     for (String aspectName : aspectNames) {
    74         // 从缓冲拿出增强器
    75         List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
    76         if (cachedAdvisors != null) {
    77             advisors.addAll(cachedAdvisors);
    78         }
    79         else {
    80             MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
    81             advisors.addAll(this.advisorFactory.getAdvisors(factory));
    82         }
    83     }
    84     return advisors;
    85 }

      到此我们找到了所有声明@AspectJ注解的类,接下来是不是该找用@Before,@After等等那些注解的方法了?

     

      等等,我们要的是Advisor,所有增强由Advisor的实现类InstantiationModelAwarePointcutAdvisorImpl封装,不同的注解会封装成不同的增强器。

     1 public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp,
     2         MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) {
     3     /** 检查开始 */
     4     Class<?> candidateAspectClass = aif.getAspectMetadata().getAspectClass();
     5     validate(candidateAspectClass);
     6     // 之前讲过,获取到方法上的注解信息
     7     AspectJAnnotation<?> aspectJAnnotation =
     8             AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
     9     if (aspectJAnnotation == null) {
    10         return null;
    11     }
    12     // If we get here, we know we have an AspectJ method. 
    13     // Check that it's an AspectJ-annotated class
    14     if (!isAspect(candidateAspectClass)) {
    15         throw new AopConfigException("Advice must be declared inside an aspect type: " +
    16                 "Offending method '" + candidateAdviceMethod + "' in class [" +
    17                 candidateAspectClass.getName() + "]");
    18     }
    19     if (logger.isDebugEnabled()) {
    20         logger.debug("Found AspectJ method: " + candidateAdviceMethod);
    21     }
    22     /** 检查结束 */
    23 
    24     // 根据不同的注解生成不同的增强器
    25     AbstractAspectJAdvice springAdvice;
    26     switch (aspectJAnnotation.getAnnotationType()) {
    27         case AtBefore:
    28             springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
    29             break;
    30         case AtAfter:
    31             springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
    32             break;
    33         case AtAfterReturning:
    34             springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
    35             AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
    36             if (StringUtils.hasText(afterReturningAnnotation.returning())) {
    37                 springAdvice.setReturningName(afterReturningAnnotation.returning());
    38             }
    39             break;
    40         case AtAfterThrowing:
    41             springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
    42             AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
    43             if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
    44                 springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
    45             }
    46             break;
    47         case AtAround:
    48             springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
    49             break;
    50         case AtPointcut:
    51             if (logger.isDebugEnabled()) {
    52                 logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
    53             }
    54             return null;
    55         default:
    56             throw new UnsupportedOperationException(
    57                     "Unsupported advice type on method " + candidateAdviceMethod);
    58     }
    59     // Now to configure the advice...
    60     springAdvice.setAspectName(aspectName);
    61     springAdvice.setDeclarationOrder(declarationOrderInAspect);
    62     String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
    63     if (argNames != null) {
    64         springAdvice.setArgumentNamesFromStringArray(argNames);
    65     }
    66     springAdvice.calculateArgumentBindings();
    67     return springAdvice;
    68 }

    3.3 获取匹配的增强器

      前面讲了获取所有的增强器,不一定都适用于现在的bean,我们要选出合适的增强器,也就是满足配置的增强器,具体方法在:org.springframework.aop.support.AopUtils#findAdvisorsThatCanApply:

     

    3.4 创建代理

      首先看这个方法:org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy

      那么接下来就是代理的创建与获取了:

      我们先看下如何创建代理的,注释如图:

      所以我们可以得出以下结论:

    • 如果目标类实现了接口,默认使用JDK动态代理实现AOP,但是也可以强制使用CGLIB实现AOP
    • 如果目标类没有实现接口,那么必须采用CGLIB库

    3.5 调用过程  

      最后就是方法调用的关键方法了,可以说最核心的方法就是这个了,org.springframework.aop.framework.ReflectiveMethodInvocation#proceed:

      Spring aop的精华都在于此,核心就是递归思想,调用完了拦截链(所有增强器)中的所有拦截器方法后,再调用目标对象的方法

      到底invoke方法是啥呢?见下图,我们可以看到是一个接口的方法,不同增强器有不同的实现,我们这里就看Before和After的:

      最后画一张图吧,这样比较好理解一点:

    四.总结

      过程就是先拿出所有适用的Adivsors,然后构造拦截链(chain),最后进行串行调用(递归)。

      最后还是希望多写几个demo来实践一下,打打断点。还是那句话,源码是看不完的,最重要的是思想。这是我第一家公司技术总监老羊说的。我觉得还是挺收益的。本文还有很多不足之处,如果有写的不对的地方还请指点一下,感谢。

  • 相关阅读:
    Search Insert Position
    Substring with Concatenation of All Words
    Swap Nodes in Pairs
    Remove Element
    Remove Duplicates from Sorted Array
    Letter Combinations of a Phone Number
    Remove Nth Node From End of List
    Valid Parentheses
    Merge k Sorted Lists
    Using an Interface as a Type
  • 原文地址:https://www.cnblogs.com/GrimMjx/p/11267836.html
Copyright © 2011-2022 走看看