乐趣区

关于spring:SpringAop源码分析四Aop源码解析

家喻户晓,Aop 各种切面必定是通过创立代理(Aop 的各种基本概念各位听都应该听会了,这里就不多赘述了)。然而问题随之产生了,咱们曾经剖析了一般 bean 的解析及创立,aop 是在哪边创立代理对象的呢,怎么匹配切点的呢。这篇也是围绕这两个问题进行剖析。动静代理的剖析上一篇曾经剖析完了,感兴趣的能够看一下。传送门

本篇钻研的问题

  • 代理对象的创立
  • 匹配切点

    测试代码

    @Aspect
    class AdviceUsingThisJoinPoint {
    
      private String lastEntry = "";
    
      public String getLastMethodEntered() {return this.lastEntry;}
    
      @Pointcut("execution(int *.getAge())")
      public void methodExecution() {}
    
      @Before("methodExecution()")
      public void entryTrace(JoinPoint jp) {this.lastEntry = jp.toString();
          System.out.println(this.lastEntry);
      }
    }
    
    public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOther, Comparable<Object> {
    // 多余局部删除了
      private String name;
    
      private int age;
    
      public TestBean() {}
    
      public TestBean(String name) {this.name = name;}
      
      @Override
      public String getName() {return name;}
    
      @Override
      public void setName(String name) {this.name = name;}
    
      @Override
      public int getAge() {return age;}
    
      @Override
      public void setAge(int age) {this.age = age;}
    
      /**
       * @see org.springframework.beans.testfixture.beans.ITestBean#exceptional(Throwable)
       */
      @Override
      public void exceptional(Throwable t) throws Throwable {if (t != null) {throw t;}
      }
    
      @Override
      public void unreliableFileOperation() throws IOException {throw new IOException();
      }
      /**
       * @see org.springframework.beans.testfixture.beans.ITestBean#returnsThis()
       */
      @Override
      public Object returnsThis() {return this;}
    
      /**
       * @see org.springframework.beans.testfixture.beans.IOther#absquatulate()
       */
      @Override
      public void absquatulate() {}
    
      @Override
      public int haveBirthday() {return age++;}
    
      @Override
      public boolean equals(Object other) {if (this == other) {return true;}
          if (other == null || !(other instanceof TestBean)) {return false;}
          TestBean tb2 = (TestBean) other;
          return (ObjectUtils.nullSafeEquals(this.name, tb2.name) && this.age == tb2.age);
      }
    
      @Override
      public int hashCode() {return this.age;}
    
      @Override
      public int compareTo(Object other) {if (this.name != null && other instanceof TestBean) {return this.name.compareTo(((TestBean) other).getName());
          }
          else {return 1;}
      }
    
      @Override
      public String toString() {return this.name;}
    
    }
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "https://www.springframework.org/dtd/spring-beans-2.0.dtd">
    
    <beans>
    
      <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>
    
      <bean id="aspect" class="org.springframework.aop.aspectj.autoproxy.AdviceUsingThisJoinPoint"/>
    
      <bean id="adrian" class="org.springframework.beans.testfixture.beans.TestBean" scope="prototype">
          <property name="name" value="adrian"/>
          <property name="age" value="34"/>
      </bean>
    
    </beans>
      @Test
      public void testAdviceUsingJoinPoint() {ClassPathXmlApplicationContext bf = newContext("usesJoinPointAspect.xml");
    
          ITestBean adrian1 = (ITestBean) bf.getBean("adrian");
          adrian1.getAge();
          adrian1.getDoctor();
          AdviceUsingThisJoinPoint aspectInstance = (AdviceUsingThisJoinPoint) bf.getBean("aspect");
          assertThat(aspectInstance.getLastMethodEntered().indexOf("TestBean.getAge())") != 0).isTrue();}

    源码剖析

    创立代理对象的入口为AbstractAutoProxyCreator#postProcessBeforeInstantiation,能够看出外围办法为getAdvicesAndAdvisorsForBean,前面就是创立代理对象了

    @Override
      public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {Object cacheKey = getCacheKey(beanClass, beanName);
    
          if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {if (this.advisedBeans.containsKey(cacheKey)) {return null;}
              if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);
                  return null;
              }
          }
    
          // Create proxy here if we have a custom TargetSource.
          // Suppresses unnecessary default instantiation of the target bean:
          // The TargetSource will handle target instances in a custom fashion.
          TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
          if (targetSource != null) {if (StringUtils.hasLength(beanName)) {this.targetSourcedBeans.add(beanName);
              }
              Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
              Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
              this.proxyTypes.put(cacheKey, proxy.getClass());
              return proxy;
          }
    
          return null;
      }

    此办法是获取对应的 Advisor

      protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {List<Advisor> candidateAdvisors = findCandidateAdvisors();// 获取候选 Advisor
          List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);// 获取实用于 bean 的 Advisor: 例如 Pointcut 匹配
          extendAdvisors(eligibleAdvisors);// 非凡解决
          if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);// 排序
          }
          return eligibleAdvisors;
      }

    findCandidateAdvisors最终会调用 buildAspectJAdvisors 获取对应的 Advisor
    第一次进入会找到 @Aspect 定义过的办法,生成对应的Advisor(封装了 Advice),后续就会从缓存中取

    public List<Advisor> buildAspectJAdvisors() {
          List<String> aspectNames = this.aspectBeanNames;
    
          if (aspectNames == null) {synchronized (this) {
                  aspectNames = this.aspectBeanNames;// 并发问题
                  if (aspectNames == null) {List<Advisor> advisors = new ArrayList<>();
                      aspectNames = new ArrayList<>();
                      String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(//
                              this.beanFactory, Object.class, true, false);
                      for (String beanName : beanNames) {if (!isEligibleBean(beanName)) {continue;}
                          // We must be careful not to instantiate beans eagerly as in this case they
                          // would be cached by the Spring container but would not have been weaved.
                          Class<?> beanType = this.beanFactory.getType(beanName, false);
                          if (beanType == null) {continue;}
                          if (this.advisorFactory.isAspect(beanType)) {// 是否为 @Aspect 润饰的 bean
                              aspectNames.add(beanName);
                              AspectMetadata amd = new AspectMetadata(beanType, beanName);
                              if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                                  MetadataAwareAspectInstanceFactory factory =
                                          new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                                  List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);// 生成 Adivsor
                                  if (this.beanFactory.isSingleton(beanName)) {this.advisorsCache.put(beanName, classAdvisors);
                                  }
                                  else {this.aspectFactoryCache.put(beanName, factory);
                                  }
                                  advisors.addAll(classAdvisors);
                              }
                              else {
                                  // Per target or per this.
                                  if (this.beanFactory.isSingleton(beanName)) {
                                      throw new IllegalArgumentException("Bean with name'" + beanName +
                                              "'is a singleton, but aspect instantiation model is not singleton");
                                  }
                                  MetadataAwareAspectInstanceFactory factory =
                                          new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                                  this.aspectFactoryCache.put(beanName, factory);
                                  advisors.addAll(this.advisorFactory.getAdvisors(factory));
                              }
                          }
                      }
                      this.aspectBeanNames = aspectNames;
                      return advisors;
                  }
              }
          }
    
          if (aspectNames.isEmpty()) {return Collections.emptyList();
          }
          List<Advisor> advisors = new ArrayList<>();
          for (String aspectName : aspectNames) {List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
              if (cachedAdvisors != null) {advisors.addAll(cachedAdvisors);
              }
              else {MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
                  advisors.addAll(this.advisorFactory.getAdvisors(factory));
              }
          }
          return advisors;
      }

    下面获取到了所有的 Advisor 汇合接下来就是获取到匹配的 Advisor

      public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {if (candidateAdvisors.isEmpty()) {return candidateAdvisors;}
          List<Advisor> eligibleAdvisors = new ArrayList<>();
          for (Advisor candidate : candidateAdvisors) {if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {eligibleAdvisors.add(candidate);
              }
          }
          boolean hasIntroductions = !eligibleAdvisors.isEmpty();
          for (Advisor candidate : candidateAdvisors) {// 遍历 Advisor 找到匹配的
              if (candidate instanceof IntroductionAdvisor) {
                  // already processed
                  continue;
              }
              if (canApply(candidate, clazz, hasIntroductions)) {eligibleAdvisors.add(candidate);
              }
          }
          return eligibleAdvisors;
      }

    具体匹配是在 org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>, boolean) 办法中判断,先判断类是否匹配再判断办法是否匹配

      public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {Assert.notNull(pc, "Pointcut must not be null");
          if (!pc.getClassFilter().matches(targetClass)) {// 匹配 class
              return false;
          }
    
          MethodMatcher methodMatcher = pc.getMethodMatcher();
          if (methodMatcher == MethodMatcher.TRUE) {
              // No need to iterate the methods if we're matching any method anyway...
              return true;
          }
    
          IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
          if (methodMatcher instanceof IntroductionAwareMethodMatcher) {introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
          }
    
          Set<Class<?>> classes = new LinkedHashSet<>();
          if (!Proxy.isProxyClass(targetClass)) {classes.add(ClassUtils.getUserClass(targetClass));
          }
          classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    
          for (Class<?> clazz : classes) {Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
              for (Method method : methods) {
                  if (introductionAwareMethodMatcher != null ?
                          introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                          methodMatcher.matches(method, targetClass)) {return true;}
              }
          }
    
          return false;
      }

    前面就会创立代理对象,依据有无接口为次要条件判断是 JDK 代理还是 Cglib 动静代理, 这两个上篇曾经讲过了,这里就不过多赘述了

      @Override
      public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();
              if (targetClass == null) {
                  throw new AopConfigException("TargetSource cannot determine target class:" +
                          "Either an interface or a target is required for proxy creation.");
              }
              if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);
              }
              return new ObjenesisCglibAopProxy(config);
          }
          else {return new JdkDynamicAopProxy(config);
          }
      }

    总结

    先扫描所有 @Aspect 注解的对象,封装成 Advisor 对象,缓存起来,创建对象的时候循环判断是否匹配。

轻易说两句

至此 SpringIoC 和 Aop 的局部曾经全副剖析完了。
后面几篇也来个传送门
「Spring-IoC」源码剖析一获取 bean 信息
「Spring-IoC」源码剖析二依赖注入 & 依赖循环
「Spring-Aop」源码剖析三:JDK 动静代理 &Cglib

退出移动版