Spring源码分析:AOP源码梳理

/ Spring源码分析 / 0 条评论 / 256人围观

基于注解方式的AOP代理

Spring的两大核心是IOC和AOP,而AOP是基于IOC的,AOP翻译过来意思是面向切面编程,说白了就是在方法前后处理点事情,Spring为我们提供了强大易用的AOP功能,最方便的就是注解方式的,下面来简单实现下:

常用注解

@Aspect,@Before,@After,@AfterReturning,@AfterThrowing,@Around,@PointCut以及@EnableAspectJAutoProxy

普通业务类

/**
 * description:模拟业务类
 * @author 70KG
 */
public class CalculateService {

    public int calculate(int i, int j) {
        int result = i / j;
        System.out.println("---->CalculateService-业务方法执行。。。。。。");
        return result;
    }

}

切面类

切面类必须标注它为切面类(@Aspect),关于切入点表达式,可以参考官方文档,写法很多种,还有关于JoinPoint这个参数必须放在第一位,否则报错。

/**
 * description:日志切面类,JoinPoint必须在参数的第一位
 * @author 70KG
 */
@Aspect // ---> 声明此类是一个切面类
public class LogAdvice {

    @Pointcut("execution(* com.nmys.story.springCore.springaop.sample01.CalculateService.*(..))")
    public void pointCut() {
    }

    /**
     * 目标方法执行前执行
     */
    @Before("pointCut()")
    public void logBefore(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        Object[] args = joinPoint.getArgs();
        System.out.println("---->" + signature + "方法执行前,传入的参数是:" + Arrays.asList(args));
    }

    /**
     * 目标方法执行后执行
     */
    @After("pointCut()")
    public void logAfter(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        Object[] args = joinPoint.getArgs();
        System.out.println("---->" + signature + "方法执行后,传入的参数是:" + Arrays.asList(args));

    }

    /**
     * 方法发生异常时执行
     */
    @AfterThrowing(value = "pointCut()", throwing = "ex")
    public void logException(JoinPoint joinPoint, Exception ex) {
        Signature signature = joinPoint.getSignature();
        System.out.println("---->" + signature + "方法执行后,抛出了异常信息:" + ex.getMessage());
    }


    /**
     * 正常返回时执行
     */
    @AfterReturning(value = "pointCut()", returning = "result")
    public void logReturn(JoinPoint joinPoint, Object result) {
        Signature signature = joinPoint.getSignature();
        System.out.println("---->" + signature + "方法执行后,返回的结果是:" + result);
    }

}

配置类

主要是向IOC容器中注册业务类和切面。

/**
 * description
 * @author 70KG
 */
@Configuration
@EnableAspectJAutoProxy // ---> 开启切面的自动代理
public class MyConfig {

    @Bean
    public CalculateService calculateService() {
        return new CalculateService();
    }

    // 将切面类也交给容器管理
    @Bean
    public LogAdvice logAdvice() {
        return new LogAdvice();
    }

}

测试类

/**
 * description
 * @author 70KG
 */
public class Test01 {

    public static void main(String[] args) {

        // 加载配置文件
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
        CalculateService calc = (CalculateService) ac.getBean("calculateService");
        calc.calculate(4,2);
    }

}

结果

---->int com.nmys.story.springCore.springaop.sample01.CalculateService.calculate(int,int)方法执行前,传入的参数是:[4, 2]
---->CalculateService-业务方法执行。。。。。。
---->int com.nmys.story.springCore.springaop.sample01.CalculateService.calculate(int,int)方法执行后,传入的参数是:[4, 2]
---->int com.nmys.story.springCore.springaop.sample01.CalculateService.calculate(int,int)方法执行后,返回的结果是:2

AOP入口代码分析

通过注解的方式来实现AOP
1. @EnableAspectJAutoProxy通过@Import注解向容器中注入了AspectJAutoProxyRegistrar这个类,而它在容器中的名字是org.springframework.aop.config.internalAutoProxyCreator。
2. AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,所以可以向容器中注册Bean的定义信息。
3. 通过跟踪AspectJAutoProxyRegistrar,我们发现它向容器中注册了AnnotationAwareAspectJAutoProxyCreator的定义信息。
4. 观察AnnotationAwareAspectJAutoProxyCreator的继承结构图,发现,它既是一个后置处理器,又是一个BeanFactoryAware的实现类。
所以我们可以分析:
一、 AnnotationAwareAspectJAutoProxyCreator作为后置处理器做了什么?
二、 作为Aware做了什么?

类的继承结构分析

AnnotationAwareAspectJAutoProxyCreator的继承结构图,如下:

AnnotationAwareAspectJAutoProxyCreator
    -> extends AspectJAwareAdvisorAutoProxyCreator
        -> extends AbstractAdvisorAutoProxyCreator
            -> extends AbstractAutoProxyCreator
                -> implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
                   【可见AnnotationAwareAspectJAutoProxyCreator是一个后置处理器,也是一个Aware】

AbstractAutoProxyCreator作为一个抽象类,实现了SmartInstantiationAwareBeanPostProcessor和BeanFactoryAware
故,我们分别在他们的实现方法上加断点,比如:postProcessBeforeInstantiation()和setBeanFactory()

如何注册AnnotationAwareAspectJAutoProxyCreator

由AnnotationAwareAspectJAutoProxyCreator的继承结构分析,AbstractAutoProxyCreator直接实现BeanPostProcessor和Aware接口,所以断点应当打在这两个接口的方法上,分别是setBeanFactory()和postProcessBeforeInstantiation()上,ok,debug运行上面的测试用例,首先来到AbstractAutoProxyCreator的setBeanFactory(),调用栈如下图:

请输入图片描述

1. 传入配置类,创建IOC容器
2. 注册配置类,调用refresh()刷新容器
3. registerBeanPostProcessors(beanFactory)注册Bean的后置处理器,来拦截Bean的创建。
    1) 先获取IOC容器已经定义了的需要创建对象的所有BeanPostProcessor
    2) 给容器中添加另外其他的BeanPostProcessor
    3) 优先注册实现了PriorityOrdered接口的BeanPostProcessor
    4) 再注册实现Order接口的BeanPostProcessor
    5) 最后注册没实现任何接口的BeanPostProcessor
    6) 注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,并保存在容器中
       如何创建名字叫internalAutoProxyCreator【AnnotationAwareAspectJAutoProxyCreator】的后置处理器?
       1) 先来创建Bean的实例
       2) populateBean()给Bean的属性赋值
       3) initializeBean()初始化Bean
          1) invokeAwareMethods(beanName, bean)处理Aware接口的方法回调
          2) applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)
             拿到所有的BeanPostProcessor并执行postProcessBeforeInitialization
          3) invokeInitMethods(beanName, wrappedBean, mbd)执行自定义的初始化方法,比如init()和destroy()
          4) applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)
             拿到所有的BeanPostProcessor并执行postProcessAfterInitialization
       4) BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】创建成功
    7) 把BeanPostProcessor注册并添加到BeanFactory中

==========以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程==========

AnnotationAwareAspectJAutoProxyCreator如何工作

前面断点停在了setBeanFactory()上,放过它,断点来到AbstractAutoProxyCreator的postProcessBeforeInstantiation()上,查看调用栈,如图:

请输入图片描述

来到finishBeanFactoryInitialization(beanFactory)完成BeanFactory的初始化工作,创建剩下的单实例Bean
    1)遍历获取所有的Bean,依次创建对象getBean(beanName)
       getBean -> doGetBean() -> getSingleton()
    2)如何创建Bean?
       【首先说明:上面注册过的后置处理器AnnotationAwareAspectJAutoProxyCreator会在所有Bean创建之前进行拦截调用,因为
         AnnotationAwareAspectJAutoProxyCreator实现了InstantiationAwareBeanPostProcessor接口】
       1)先从缓存中获取当前Bean,如果能获取到,说明Bean是之前被创建过了的,直接使用,否则再进行创建
          只要被创建过的Bean都会被缓存起来
       2)createBean()创建Bean的实例,过程如下:
          1)resolveBeforeInstantiation(beanName, mbdToUse)这一步是尝试返回Bean的代理对象
             希望后置处理器在此能返回一个代理对象,如果不能就调用doCreateBean()创建对象
             如何返回代理对象呢?
             if (targetType != null) {
                bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                if (bean != null) {
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
             }
             【注意:这里的applyBeanPostProcessorsBeforeInstantiation不同于BeanPostProcessor的postProcessBeforeInitialization】
             【applyBeanPostProcessorsBeforeInstantiation和applyBeanPostProcessorsAfterInitialization属于InstantiationAwareBeanPostProcessor的方法
                而InstantiationAwareBeanPostProcessor是在创建Bean对象实例之前进行调用,目的是在创建Bean对象之前首先尝试返回代理对象,
                但BeanPostProcessor的两个方法则是在Bean对象创建完成,进行初始化的前后才进行调用,他们两个执行的时机不一样BeanPostProcessor的执行稍微晚一点】
          2)doCreateBean(beanName, mbdToUse, args)此方法才是真正的创建一个Bean实例,创建实例的流程和上面一样

分析InstantiationAwareBeanPostProcessor

AnnotationAwareAspectJAutoProxyCreator实现了InstantiationAwareBeanPostProcessor所以每一个Bean在创建之前,都会经过
postProcessBeforeInstantiation()方法。当然也包括被切的实体。

1. 每个Bean在创建之前都会经过postProcessBeforeInstantiation()过程分析:
   1)判断当前Bean是否在advisedBeans(里面保存了需要增强的Bean)中。
      this.advisedBeans.containsKey(cacheKey)
   2)判断当前Bean是否是基础类型(Advice,Pointcut,Advisor,AopInfrastructureBean)。
      isInfrastructureClass(beanClass)
   3)是否需要跳过?(好像永远要跳过^_^)
      shouldSkip(beanClass, beanName)
      这个方法首先获取候选的增强器List<Advisor> candidateAdvisors = findCandidateAdvisors();
      每一个Advisor的类型是InstantiationModelAwarePointcutAdvisor,并不是AspectJPointcutAdvisor

2. 经过postProcessBeforeInstantiation()尝试创建对象出来以后,进入AbstractAutoProxyCreator的postProcessAfterInitialization方法
   postProcessAfterInitialization方法中有一个wrapIfNecessary(bean, beanName, cacheKey)开始进行包装Bean,点击进入:
   1)Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
      找到所有的增强器,并获取到能在该Bean使用的增强器,然后顺便排序(目的是为了有序的切入)
   2)保存当前Bean到advisedBeans中
      this.advisedBeans.put(cacheKey, Boolean.TRUE)
   3)如果当前Bean需要增强,就创建当前Bean的代理对象
      Object proxy = createProxy(...),如何创建?如下:
      1. 获取所有通知方法
      2. 保存到proxyFactory中
      3. 创建代理对象,Spring自动决定
         new JdkDynamicAopProxy(config);实现接口
         ObjenesisCglibAopProxy(config);没有实现接口
   4)通过以上三步,wrapIfNecessary()方法会返回一个代理Bean并且放到容器中,当调用目标方法的时候,实际上是代理类在调用。
==========以上是尝试并返回一个代理对象的过程==========

目标方法执行过程分析

代理对象创建出来了,各种增强器也有了,如何保证增强器的执行顺序跟调用时机?将断点打在测试类中的方法调用上,查看调用栈,如图:

请输入图片描述

进入方法,来到了CglibAopProxy中的intercept()中,流程如下:

   容器中保存了组件的代理对象(cglib增强后的对象),这个对象里保存了详细信息,包括增强器advisor和目标对象等等。
   1)CglibAopProxy.intercept();拦截目标方法的执行
   2)根据PorxyFactory对象获取将要执行的目标方法拦截器链
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
      如何获取拦截器链呢?
       来到DefaultAdvisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice方法
      1)创建一个固定长度的集合List<Object> interceptorList,它的长度就是拦截器的个数config.getAdvisors().length
            List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length);
      2)遍历所有增强器advisor,将其转换为MethodInterceptor(如果Advisor是MethodInterceptor类型直接加到interceptorList中,如果
         不是则使用AdvisorAdapter将Advisor转成MethodInterceptor再加入到interceptorList),并加到上一步创建的interceptorList中
      3)最后拦截器链interceptorList就被返回了,所谓的拦截器链就是每一个Advisor增强器被包装成MethodInterceptor,利用MethodInterceptor
         来执行相关的增强方法。
   3)如果没有拦截器链,直接执行目标方法
        retVal = methodProxy.invoke(target, argsToUse);
   4)如果有,就把需要执行的目标对象目标方法拦截器链等信息传入新创建的CglibMethodInvocation对象中。并调用proceed方法。
        retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
  5)有了拦截器链,它是如何执行的?进入上面的proceed方法
      1)如果没有拦截器,或者currentInterceptorIndex(当前拦截器的索引)和拦截器数组-1大小一样,就执行目标方法
            this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1
      2)链式获取(递归调用proceed方法)每一个拦截器,并执行invoke方法,每一个拦截器需要等待下一个拦截器执行完成返回结果以后再执行,由拦截器链的机制,来保证通知方法与
           目标方法的执行顺序问题。

全文完,Spring的源码真的太庞大了,还需要再多跟几遍。