美文网首页
Spring AOP 实现源码分析

Spring AOP 实现源码分析

作者: 白袜子先生 | 来源:发表于2018-08-22 19:02 被阅读127次

    1. AOP 概念

    AOP(Aspect Oriented Programming),即面向切面编程。

    • 连接点(JoinPoint)
      程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特定点,这些点中的特定点就称为“连接点”。Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。
    • 切点(Pointcut)
      每个类具有多个连接点,如果一个类拥有15个方法,那么这些方法都是连接点,连接点相当于数据库中的记录,而切点相当于查询条件。切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。Spring AOP的规则解析引擎负责切点所设定的查询条件,找到对应的连接点。其实确切地说,不能称之为查询连接点,因为连接点是方法执行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息。
    • 增强(Advice)
      增强是织入到目标类连接点上的一段程序代码,在Spring中,增强除用于描述一段程序代码外,还拥有另一个和连接点相关的信息,这便是执行点的方位。结合执行点方位信息切点信息,我们就可以找到特定的连接点。
    • 通知器(Advisor)
      当我们完成切面增强设计(Advice)和切入点的设计(Pointcut),需要一个对象把他们结合起来,Advisor 就是起到这个作用,通过Advisor ,可以确定在哪个Pointcut 使用哪个Advice。所以一个Advisor包含一个Advice 和 一个Pointcut 信息。

    2. 一些疑问

    1. Spring AOP 增强的代理类 在什么时候创建 ?
    2. Spring AOP 怎样为一个类 和 方法 匹配 增强的 ?
    3. Spring AOP 是如何 协调 前置通知 后置通知 异常通知 返回通知的?
    4. 切面类 可以被 AOP增强么?

    3. 注册 AnnotationAwareAspectJAutoProxyCreator

    AnnotationAwareAspectJAutoProxyCreator是用来处理 当前项目当中 所有 AspectJ 注解的切面类。以及所有的 Spring Advisor ,同时 也是一个 BeanPostProcessor 。所以 想了解Spring AOP 如何实现,就要先分析 AnnotationAwareAspectJAutoProxyCreator,那么他从何而来,是如何注册到容器内的。
    注册AnnotationAwareAspectJAutoProxyCreator方式:

    1. 使用 EnableAspectJAutoProxy注解,改注解 又被 @Import 注解注释,向容器中注册了AspectJAutoProxyRegistrar,最后由 AspectJAutoProxyRegistrar 向容器中注册 AnnotationAwareAspectJAutoProxyCreator。
    class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
        @Override
        public void registerBeanDefinitions(
                AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
                //向容器注册 AnnotationAwareAspectJAutoProxyCreator
            AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
            AnnotationAttributes enableAspectJAutoProxy =
                    AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
            //对proxyTargetClass 属性的支持,切换JDK代理 和 CGLIB 代理
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            //是否对 AopContext.currentPoxy支持。
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }
    
    1. 使用 @EnableAutoConfiguration注解。改注解会向容器注册Spring.factories文件中声明的类,其中AopAutoConfiguration是对Spring AOP自动配置的支持。
    @Configuration
    @ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class })
    @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
    public class AopAutoConfiguration {
        @Configuration
        最终还是通过 EnableAspectJAutoProxy 注册AnnotationAwareAspectJAutoProxyCreator
        @EnableAspectJAutoProxy(proxyTargetClass = false)
        判断条件 ,可以在 application.properties 中 配置。
        @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = true)
        public static class JdkDynamicAutoProxyConfiguration {
        }
        @Configuration
        @EnableAspectJAutoProxy(proxyTargetClass = true)
        @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = false)
        public static class CglibAutoProxyConfiguration {
        }
    }
    
    1. xml 标签
      Spring 采用 自定义标签 <aop:aspectj-autoproxy /> 注册AnnotationAwareAspectJAutoProxyCreator

    4. AnnotationAwareAspectJAutoProxyCreator的初始化

    AnnotationAwareAspectJAutoProxyCreator初始化 主要初始一些 组件对象。

    组件包括:

    • AspectJAdvisorFactory
      根据AspectJ 注释的切面类 创建 Spring AOP Advisors 的工厂接口。Advisor增强器的创建 都是由AspectJAdvisorFactory完成。
    • BeanFactoryAspectJAdvisorsBuilder
      从项目中 查找所有的切面类,然后使用 AspecJAdvisorFactory 创建 Advisors。
    • advisorRetrievalHelper
      对Xml 配置Advisor 的支持。从容器中检索 所有的 Advisor 类型。

    5 . 创建代理类过程

    由于 AnnotationAwareAspectJAutoProxyCreator 是一个 BeanPostProcessor 实现类,Spring 会在对一个对象的初始化前后执行 BeanPostProcssor 的接口方法。Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;就是 创建增强代理对象的方法。该方法被AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator实现。

    • postProcessAfterInitialization
    @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //bean 就是需要 被增强的类。
            if (bean != null) {
                Object cacheKey = getCacheKey(bean.getClass(), beanName);
                //防止 bean 多次被增强。
                if (!this.earlyProxyReferences.contains(cacheKey)) {
                //如果有必要 就进行封装,有没有必要主要取决于 bean 是否需要被增强。
                    return wrapIfNecessary(bean, beanName, cacheKey);
                }
            }
            return bean;
        }
    
    • wrapIfNecessary
      对给定bean 就行封装。
    /**
         *如果有必要 封装 bean
         */
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    //如果已经处理过
            if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
                return bean;
            }
    //如果不需要增强 就直接返回
            if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
                return bean;
            }
            //如果是一个 组件类,组件类包括Advice,Pointcut,Advisor,AopInfrastructureBean实现类,和 Aspect注解注释的类。所以 切面类 自己不会被AOP 增强。
            if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return bean;
            }
            // 为bean 获取和 匹配合适的 增强器 
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
            //如果 没有 匹配到增强器 那么就不用创建增强代理类。
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
                //创建 增强代理类
                Object proxy = createProxy(
                        bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            }
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    
    5.1 获取增强器
    • getAdvicesAndAdvisorsForBean(为bean获取和匹配合适的增强器)
      getAdvicesAndAdvisorsForBean ---> findEligibleAdvisors:

      为bean获取和匹配增强器分为两步:

    5.1.1 获取到所有的增强器
    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        
        //获取所有的增强器
            List<Advisor> candidateAdvisors = findCandidateAdvisors();
        //匹配合适的增强器
            List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
            extendAdvisors(eligibleAdvisors);
            if (!eligibleAdvisors.isEmpty()) {
                eligibleAdvisors = sortAdvisors(eligibleAdvisors);
            }
            return eligibleAdvisors;
        }
    
    • findCandidateAdvisors 获取到所有的增强器
      调用AnnotationAwareAspectjAutoProxyCreator 的 findCandidateAdvisors
      @Override
      protected List<Advisor> findCandidateAdvisors() {
         // 对xml 配置 Advisor 的支持.
         List<Advisor> advisors = super.findCandidateAdvisors();
         // 搜索容器中所有 @Aspecj 注解申明的 切面类,构建增强器
         advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
         return advisors;
      }
      
    • aspectJAdvisorsBuilder.buildAspectJAdvisors()
      代码比较多,就不粘出来了,大致逻辑就是
    1. 获取容器中已经注册的BeanNames
    2. 遍历 所有已经注册的Bean,查找 @AspectJ
    3. 通过AspectJAdvisorFactory获取Advisor。怎么获取的 就不解释了。
    5.1.2 匹配合适的增强器

    匹配合适的增强器,Spring 使用了 AopUtils.canApply。

    匹配逻辑就是:

    遍历 所有增强器,采用增强器的 Poincut 对一个对象的所有方法进行匹配,一旦有方法匹配到,就返回true.

    对方法的匹配需要遍历 所有方法 一一就行匹配。

    5.2 创建 代理类

    通过 ProxyFactory 创建动态代理类,创建之前 先配置ProxyFactory,设置目标对象,设置是否对目标对象代理(会影响采用JDK代理或者Cglib代理),设置增强器集合,等。

    配置ProxyFactory完成之后就是获取代理类了,

    调用ProxyFactory.getProxy:

    public Object getProxy(ClassLoader classLoader) {
        //先获取 AopProxy,AopProxy有JDK实现和 Cglib实现两种。
            return createAopProxy().getProxy(classLoader);
    }
    

    判断采取JDK代理 或Cglib代理。
    判断条件:

    1. Optimize: 是否采用了 激进的优化策略,该优化仅支持 Cglib代理
    2. ProxyTargetClass: 代理目标类,代理目标类 就是采用 子类继承的方式创建代理,所以也是Cglib代理,可以通过。
    3. hasNoUserSuppliedProxyInterfaces:判断是否是实现了接口,如果没有必须采用Cglib代理。
      所以如果我们想在项目中 采用Cglib代理的话 application.properties中配置:
      spring.aop.proxy-target-class=false,或者使用注解配置proxyTargetClass = true .
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        //判断采用什么代理类型。
        //Optimize 是否采用了 激进的优化策略,该优化仅支持 Cglib代理
        //ProxyTargetClass 代理目标类,代理目标类 就是采用 子类继承的方式创建代理,所以也是Cglib代理,可以通过
        // 判断是否是实现了接口,如果没有必须采用Cglib代理。
            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.");
                }
                如果目标对象是接口 采用 JDK代理。
                if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                    return new JdkDynamicAopProxy(config);
                }
               
                return new ObjenesisCglibAopProxy(config);
            }
            else {
                return new JdkDynamicAopProxy(config);
            }
        }
    
    
    • 拿JdkDynamicAopProxy来说。

    获取代理类:

    JDK动态代理的关键是 InvocationHandler,JdkDynamicAopProxy实现了InvocationHandler接口。

    @Override
        public Object getProxy(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);
            //获取代理类,其中InvocationHanler 是 this,就是JdkDynamicAopProxy。
            return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
        }
    

    6.增强方法的执行

    增强方法的执行 是AOP的核心所在,理解Spring Aop 是如何 协调 各种通知 的执行,是理解的关键。

    通知 分为前置 后置 异常 返回 环绕通知,在一个方法中每种通知的执行时机不同,协调他们之间执行顺序很重要。

    但是Spring AOP 采用了很聪明的方法让各种各样的通知准确有序的工作。

    • JdkDynamicAopProxy.invoker

    由于invoker 方法很大,我删除了大部分的代码,保留几处关键代码:

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //省略了 对 hashCode equals 等方法的处理....
        //exposeProxy属性的支持。
                if (this.advised.exposeProxy) {
                    // Make invocation available if necessary.
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }
                // 获取目标对象类
                target = targetSource.getTarget();
                if (target != null) {
                    targetClass = target.getClass();
                }
          // 获取拦截器链 这里this.advised 就是AnnotationAwareAspectJAutoProxyCreator
          List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
         //如果 没有拦截器链 则直接执行。
          if (chain.isEmpty()) {
             Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
             retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
          }
          else {    
              //开始执行拦截器链
             invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
             retVal = invocation.proceed();
          }
        //返回执行结果。
          return retVal;
      
    }
    
    6.1 创建拦截器链

    拦截器链InterceptorAndDynamicMethodMatcher 类 和 MethodInterceptor 类型的集合。

    InterceptorAndDynamicMethodMatcher 封装了 MethodInterceptor 和 MethodMather ,作用就是在执行时进行再次匹配。

    创建拦截器的过程就是 对所有 适合 目标类的 Advisor进行再一次筛选。对匹配的Advisor封装成 MethodInterceptor。通过MethodInterceptor 统一 增强方法的调用。

    6.2 执行拦截器链

    使用ReflectiveMethodInvocation 对连接器链进行封装。通过process 方法触发拦截器开始执行。

    public Object proceed() throws Throwable {
            //  判断拦截器链 是否执行结束,如果执行结束执行目标方法。
            if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
                return invokeJoinpoint();
            }
            //获取下一个 需要执行的 拦截器
            Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
            //如果需要执行时 进行匹配
            if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
                InterceptorAndDynamicMethodMatcher dm =
                        (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
                //如果匹配,并不清楚 为什么还要在这里 进行再次匹配。
                if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) 
                //注意这里 将ReflectiveMethodInvocation 自己当参数 ,传入调用。
                    return dm.interceptor.invoke(this);
                }
                else {
                //递归的调用
                    return proceed();
                }
            }
            else {
                //如果 MethodInterceptor 
                return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
            }
        }
    
    

    查看代码并没有发现任何的关于协调前置,后置各种通知的代码。其实所有的协调工作都是由MethodInterceptor 自己维护。

    • 前置MethodInterceptor(MethodBeforeAdviceInterceptor)的invoke
        @Override
      public Object invoke(MethodInvocation mi) throws Throwable {
            //激活 前置增强方法
          this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
            //继续调用下一个 拦截器。
          return mi.proceed();
      }
    
    
    • 后置MethodInterceptor(AspectJAfterAdvice)

      @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            try {
                  //继续调用一下拦截器。
                return mi.proceed();
            }
            finally {
                  //在finally 里面激活 后置增强方法
                invokeAdviceMethod(getJoinPointMatch(), null, null);
            }
        }
      

      通过对比前置 和 后置 拦截器,可以发现 具体协调各种通知 的 逻辑 实际是利用了 递归的思想。
      后置拦截器为例: 先执行 递归方法 proceed()。最后从递归方法返回的时候 最后调用 后置方法。

    7. 模拟拦截器调用

    为了更好理解 拦截器调用,自己实现了一个简单的拦截器链调用过程。

    7.1 准备工作
    • 接口

      //方法调用   
      interface MethodInvocation{
               Object process() throws Throwable;
          }
      //方法拦截器
          interface MethodInterceptor{
              Object invoke(MethodInvocation mi) throws Throwable;
          }
      
    • 实现

      //后置增强方法的 拦截器
      
      class AfterMethodInterceptor implements MethodInterceptor{
              String identification;
              public AfterMethodInterceptor(String identification){
                  this.identification = identification;
              }
              @Override
              public Object invoke(MethodInvocation mi) throws Throwable {
                  try {
                      return mi.process();
                  }finally {
                      System.out.println("执行后置通知"+identification);
                  }
              }
          }
      //前置 的 方法拦截器 
          class BeforMethodInterceptor implements MethodInterceptor{
              String identification;
              public BeforMethodInterceptor(String identification){
                  this.identification = identification;
              }
              @Override
              public Object invoke(MethodInvocation mi) throws Throwable {
                  System.out.println("执行前置通知"+identification);
                  return mi.process();
              }
          }
      // 默认的 方法调用实现
       class DefaultMethodInvacation implements MethodInvocation {
              List<MethodInterceptor> chian;
              Object target; //目标对象
              Method method; //目标方法
              Object[] args; //方法参数
              int currentChianIndex; //记录拦截器链当前执行位置
              public  DefaultMethodInvacation(List<MethodInterceptor> chian,Object target,Method method,Object[] args){
                  this.chian = chian;
                  this.method = method;
                  this.target = target;
                  this.args = args;
              }
              @Override
              public Object process() throws Throwable{
                  Object value ;
                  //如果 拦截器 执行完毕 执行 目标方法
                  if(currentChianIndex == chian.size()){
                      value = method.invoke(target, args);
                      return value;
                  }
                  //获取下一个 拦截器
                  MethodInterceptor methodInterceptor = chian.get(currentChianIndex++);
                  return methodInterceptor.invoke(this);
              }
          }
      //目标对象
      class TargetObj{
          //目标方法
            public void targetMethod(){
                 System.out.println("-----目标方法执行----");
             }
          }
      
    7.2 测试代码
    @Test
        public void aopchain() throws Throwable {
            List<MethodInterceptor> chain = Lists.newArrayList();
            //拦截器链,穿插这 放入 前置 和 后置 拦截器 。
            chain.add(new AfterMethodInterceptor("1"));
            chain.add(new BeforMethodInterceptor("1"));
            chain.add(new AfterMethodInterceptor("2"));
            chain.add(new BeforMethodInterceptor("2"));
            chain.add(new AfterMethodInterceptor("3"));
            chain.add(new BeforMethodInterceptor("3"));
            //目标对象
            TargetObj targetObj = new TargetObj();
            //目标方法
            Method method = TargetObj.class.getMethod("targetMethod");
            DefaultMethodInvacation defaultMethodInvacation = new DefaultMethodInvacation(chain, targetObj, method, null);
            //执行拦截器链
            defaultMethodInvacation.process();
        }
    
    7.3 执行结果

    虽然 前置 和 后置 都是 无序的 存放在 拦截器链中,但是 前置 和 后置 都在各自的位置执行。

    执行前置通知1
    执行前置通知2
    执行前置通知3
    -----目标方法执行----
    执行后置通知3
    执行后置通知2
    执行后置通知1
    

    相关文章

      网友评论

          本文标题:Spring AOP 实现源码分析

          本文链接:https://www.haomeiwen.com/subject/jceeiftx.html