Spring 源码学习笔记10——Spring AOP

参考书籍《Spring技术内幕》Spring AOP的实现章节
书有点老,但是里面一些概念还是总结比较到位
源码基于Spring-aop 5.3.22 可能和旧版本有所差异但是大体逻辑一致

一丶AOP概述

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。 是一种新的模块化机制,用来描述分散在对象,类,或函数中的横切关注点,分离关注点使解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑代码中不在含义针对特定领域的代码调用,业务逻辑同特定领域问题的关系通过切面封装,维护,这样原本分散在整个应用程序中的变动可以很好地管理起来。

用人话说就是,通过切面完成特定逻辑(事务,入参出参日志等)
可以和业务逻辑(crud)抽离开,便于维护

1. Advice 通知

定义在连接点做什么,为切面增强提供植入接口。描述Spring AOP围绕方法调而注入的切面行为

2.Pointcut切入点

切点决定Advice通知应该作用在哪个连接点,也就是通过Poincut来定义需要增强的方法集合,这些集合可以按照一定规则来完成,这种情况下,Pointcut意味着标识方法(比如事务切面定义了事务注解方法上生效)切入点是一些列织入逻辑代码的连接点集合

3.Advisor通知器

整合Advice 和 Pointcut,定义应该在哪个关注点使用什么通知进行增强。

二丶aop 重要接口和编程体验

我们先抛弃Spring框架,利用Spring aop中存在的工具实现aop增强。

1.基于Advice

Spring 源码学习笔记10——Spring AOP

Advice接口的实现有 AfterAdvice后置通知Beforeadvice前置通知MethodInterceptor方法拦截器可以看做是环绕通知。

/**
 * 服务类
 */
public static class Service{
    public void doSomething(){
        System.out.println("service doSomething");
    }
}

/***
 * 自定义的advice 环绕通知
 */
public static class MyAdvice implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("my advice before");
        //service 方法执行
        Object res = invocation.proceed();
        System.out.println("my advice after");
        return res;
    }
}

public static void main(String[] args) {
    //代理工程 用于生成代理对象
    ProxyFactory proxyFactory = new ProxyFactory();
    //目标对象
    Service service = new Service();

    //设置需要代理的对象
    proxyFactory.setTarget(service);
    proxyFactory.addAdvice(new MyAdvice());

    //生成代理对象
    Service proxyService = (Service) proxyFactory.getProxy();
    //代理对象执行
    proxyService.doSomething();
}

上述代码执行结果

Spring 源码学习笔记10——Spring AOP

基于 Advice, ProxyFactory成功实现了Aop代理增强

2.基于Advisor

Spring 源码学习笔记10——Spring AOP
public static void advisorBased(){
    //代理工程 用于生成代理对象
    ProxyFactory proxyFactory = new ProxyFactory();
    //目标对象
    Service service = new Service();

    //advisor
    DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
    //根据名称匹配方法的pointcut
    NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
    //指定只有doSomething 才增强
    pointcut.setMappedName("doSomething");
    advisor.setPointcut(pointcut);
    advisor.setAdvice(new MyAdvice());

    //设置需要代理的对象
    proxyFactory.setTarget(service);
    proxyFactory.addAdvisor(advisor);
    //生成代理对象
    Service proxyService = (Service) proxyFactory.getProxy();
    //代理对象执行
    proxyService.doSomething();
    System.out.println();
    proxyService.sayHello();
}

Spring 源码学习笔记10——Spring AOP

最后我们发现只有名称匹配的方法才生效。

Advisor接口具备方法 Advice getAdvice()来获取通知。 PointcutAdvisor实现了 Advisor并且新增方法 Pointcut getPointcut()来获取切入点的定义。 Pointcut接口定义了两个方法 ClassFilter getClassFilter(), MethodMatcher getMethodMatcher()分别是对类和方法的筛选,来决定Advise是不是应该作用于当前类。

三丶ProxyFactory 和 ProxyFactoryBean

1.ProxyFactory

1.1类图

Spring 源码学习笔记10——Spring AOP

TargetSource 用于获取 AOP 调用的当前“目标”

Spring 源码学习笔记10——Spring AOP

getTargetClass可以获取被代理对象的类型, getTarget可以获取被代理对象, HotSwappableTargetSource中的swap方法可以替换掉代理对象,Spring aop常用的是 SingletonTargetSource它持有了原始的被代理对象。

1.2 proxyFactory是如何创建代理对象的。

public Object getProxy() {
   return createAopProxy().getProxy();
}

Spring 源码学习笔记10——Spring AOP
1.2.1 DefaultAopProxyFactory # createAopProxy

Spring 源码学习笔记10——Spring AOP

这里生成的AopProxy 才是负责生成代理对象的,其中spring内置了两种策略——JDK动态代理和CGLIB动态代理。

只有设置了需要代理目标类,或者说没有指定代理的接口,且代理目标类不是接口,不是lambda,不是已经被JDK动态代理后的类,那么才会使用CGLIB进行动态代理。

1.2.2 AopPorxy

Spring 源码学习笔记10——Spring AOP

其中 JdkDynamicAopProxy,还实现了 InvocationHandler

  • Jdk动态代理
  • 生成代理对象 Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this),这里的this便是自己。
  • invoke
    首先是对 equals, hashCode的处理,目标对象声明了让目标对象执行,反之调用 JdkDynamicAopProxy对应的方法,其次是如果配置中设置了暴露代理对象,那么将其放入到 AopContext中的 ThreadLocal
if (this.advised.exposeProxy) {
   // Make invocation available if necessary.

   oldProxy = AopContext.setCurrentProxy(proxy);
   setProxyContext = true;
}

然后获取当前对象的拦截器链,如果拦截器链为空 那么直接反射调用目标对象的方法。如果存在拦截器链那么new 一个 ReflectiveMethodInvocation利用反射依次执行。Spring只支持方法拦截器 MethodInterceptor进行代理增强,对于Advise都会适配成 MethodInterceptor,Spring采用适配器模式,具体的适配器如下

Spring 源码学习笔记10——Spring AOP
   public interface AdvisorAdapter {
      //支持什么Advise被适配
      boolean supportsAdvice(Advice advice);

      //适配
      MethodInterceptor getInterceptor(Advisor advisor);
   }

自然是Spring遍历每一个Advise责任链模式依次找到 AdvisorAdapter然后调用适配方法得到一个 MethodInterceptor,下面是适配成的 MethodInterceptor

Spring 源码学习笔记10——Spring AOP
  • CGLIB动态代理
    Spring 源码学习笔记10——Spring AOP

2.ProxyFactoryBean

Spring 源码学习笔记10——Spring AOP

ProxyFactoryBean创建代理对象的逻辑和ProxyFactory类似,但是 ProxyFactoryBean是一个FactoryBean,我们可以利用这一点在bean初始化的时候生成一个代理对象

Spring 源码学习笔记10——Spring AOP

Spring 源码学习笔记10——Spring AOP

创建原型对象类似,但是不会判断之前是否创建过,直接无脑创建即可

四丶Spring AOP 和IOC是如何结合起来的

通常我们在使用Spring Aop的时候会在启动类上加一个 @EnableAspectJAutoProxy注解,这个注解 @Import(AspectJAutoProxyRegistrar.class)导入了 AspectJAutoProxyRegistrar,这个类实现了 ImportBeanDefinitionRegistrar,Spring容器会调用其 registerBeanDefinitions方法为我们注入 BeanDefinition,后续会实例化一个 AnnotationAwareAspectJAutoProxyCreator类型的bean,它是Spring IOC和AOP结合的关键

1.AnnotationAwareAspectJAutoProxyCreator类结构图

Spring 源码学习笔记10——Spring AOP

这其中最为关键的必然是 AnnotationAwareAspectJAutoProxyCreator是一个 BeanPostProcessor,从而在Spring 回调 postProcessAfterInitialization对bean进行代理的增强,并且它实现了 SmartInstantiationAwareBeanPostProcessor Spring容器创建bean的时候如果出现了循环依赖那么会调用到 getEarlyBeanReference,在这个方法里面同样也会进行aop的增强

  • AbstractAutoProxyCreator 实现了 SmartInstantiationAwareBeanPostProcessor是一个bean后置处理器,使用 AOP 代理包装每个符合条件的 bean,在调用 bean 本身之前委托给指定的拦截器,AOP代理发生的地方。
  • AbstractAdvisorAutoProxyCreator 为了每一个Bean找到合适的 Advisor并且进行,如果 Advisor标注了 @Order或者说实现了 Ordered接口那么会进行排序。
  • AspectJAwareAdvisorAutoProxyCreator AbstractAdvisorAutoProxyCreator子类,对一个切面中的多个Advisor进行优先级排序
  • AnnotationAwareAspectJAutoProxyCreator AspectJAwareAdvisorAutoProxyCreator的子类,会将容器中标注了 @AspectJ注解的类解析成 Advisor(整合Advice 和 Pointcut,定义应该使用哪个通知器并在哪个关注点使用它)

2. AbstractAutoProxyCreator 是如何进行Aop增强的

Spring 源码学习笔记10——Spring AOP

Spring 源码学习笔记10——Spring AOP

进行AOP增强的地方其实还有 postProcessBeforeInstantiation如果我们自己配置了 TargetSourceCreator并且可以为当前bean生成,那么才可能发生aop,这里一般不会进行任何操作。

2.1 wrapIfNecessary

Spring 源码学习笔记10——Spring AOP

其中 shouldSkipAspectJAwareAdvisorAutoProxyCreator重写,如果 AdvisorAspectJPointcutAdvisor并且切面名称和bean名称相同那么会跳过,这应该是我们标注 @Aspect的时候需要保证这个类会被Spring加入到容器,所有需要加 @Componet所以会过滤掉

2.1.1 如何找到所有合适的advice 和advisor

Spring 源码学习笔记10——Spring AOP
  • findCandidateAdvisors方法会找到容器中所以的 Advisor类型的bean, AnnotationAwareAspectJAutoProxyCreator进行了重写,它还会把所以标注了 @Aspect注解的bean中的增强逻辑封装成 Advisor
  • findAdvisorsThatCanApply这个方法内部逻辑基本上就是调用 PointcutAdvisor获取类过滤器,方法匹配器进行匹配。
  • sortAdvisors 这里默认是通过@Order注解,或者Ordered接口进行排序,但是 AspectJAwareAdvisorAutoProxyCreator进行了重写,因为它需要对同一个标注@Aspect切面里面的前置后置等进行排序

2.1.2 创建代理对象

protected Object createProxy(Class beanClass, @Nullable String beanName,
      @Nullable Object[] specificInterceptors, TargetSource targetSource) {

   if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
      AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
   }

   ProxyFactory proxyFactory = new ProxyFactory();
   proxyFactory.copyFrom(this);
   //这里的ProxyTargetClass 来自上面的copyFrom 取决于EnableAspectJAutoProxy注解的proxyTargetClass
    //proxyTargetClass 表示是否使用基于CGLIB子类的代理
   if (!proxyFactory.isProxyTargetClass()) {
       //shouldProxyTargetClass 方法就是去BeanFactory中看当前bean的BeanDefinition中是否存在AutoProxy.PRESERVE_TARGET_CLASS_ATTRIBUTE=trued的attribute,当我们手动注入bean的时候可以使用这个强制让当前bean使用CGLIB增强
      if (shouldProxyTargetClass(beanClass, beanName)) {
         proxyFactory.setProxyTargetClass(true);
      }
      else {
         //获取当前类中非Spring回调(InitializingBean,DisposableBean,Aware)类型的接口,且如果接口的方法大于0,那么会把接口类型加入到proxyFactory中,否则设置ProxyTargetClass(没有接口那么没办法使用JDK动态代理)
         evaluateProxyInterfaces(beanClass, proxyFactory);
      }
   }

   //主要是把上面找到的advise 适配成Advisor。调用的是advisorAdapterRegistry的wrap方法
   Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
   proxyFactory.addAdvisors(advisors);
   //这里的targetSource是SingletonTargetSource
   proxyFactory.setTargetSource(targetSource);
   //留给子类扩展的方法
   customizeProxyFactory(proxyFactory);

   proxyFactory.setFrozen(this.freezeProxy);
   if (advisorsPreFiltered()) {
      proxyFactory.setPreFiltered(true);
   }
    //生成代理对象
   return proxyFactory.getProxy(getProxyClassLoader());
}

这里其实和我们上面的Aop编程体验中基于 Advisor类似,最后都是AopProxy创建代理对象

2.2 AnnotationAwareAspectJAutoProxyCreator

上面我们讲了其父类 AbstractAutoProxyCreator的大体逻辑, AnnotationAwareAspectJAutoProxyCreator会将@Aspect注解类解析成 Advisor,下面我们重点看下 AnnotationAwareAspectJAutoProxyCreator是怎么将@Aspect注解类解析成 Advisor

Spring 源码学习笔记10——Spring AOP

这里依赖了 BeanFactoryAspectJAdvisorsBuilder,它会遍历所有bean,并调用 isAspect方法

Spring 源码学习笔记10——Spring AOP

然后调用 ReflectiveAspectJAdvisorFactorygetAdvisors方法将其适配成多个 Advisor,会遍历每一个没有标注 @Pointcut的方法,然后获取 @Around, @Before, @After, @AfterReturning, @AfterThrowing(如果没有那么直接返回)然后获取 value中的内容包装成 AspectJExpressionPointcut(AspectJ表达式pointcut),然后包装成 InstantiationModelAwarePointcutAdvisorImpl在这个类中会把对应注解的方法封装成对应的 AbstractAspectJAdvice的子类

Spring 源码学习笔记10——Spring AOP

调用对应方法依旧采用反射,其子类在合适的实际进行调用。

Original: https://www.cnblogs.com/cuzzz/p/16621320.html
Author: Cuzzz
Title: Spring 源码学习笔记10——Spring AOP

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/714263/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球