AOP原理解析

穿精又带淫゛_ 提交于 2020-08-11 04:54:50

MainConfigOfAOP.class


/**
 * AOP:
 * 指在程序运行期间动态的将某段代码切入到指定方法指定位置运行的编程方式
 * 1. 导入AOP模块:Spring aop,(Spring-aspects)
 * 2. 定义一个业务逻辑类(MathCalculator),在业务逻辑运行时将日志进行打印(方法之前、方法运行、方法之后、方法异常)
 * 3. 定义一个日志切面(LogAspect),切面里的方法要动态感知MathCalculator.div运行到哪
 * 通知方法:
 * 前置通知(@Before):logStart:在目标方法(div)运行之前运行
 * 后置通知(@After):logEnd:在目标方法(div)运行之后运行(正常结束和异常结束)
 * 返回通知(@Returning):logEnd:在目标方法(div)返回之后运行
 * 异常通知(@AfterThrowing):logEnd:在目标方法(div)异常之后运行
 * 环绕通知(@Around):动态代理,手动控制目标方法运行(joinPoint.proceed())
 * 4. 给切面的方法标注何时何地运行
 * 5. 将切面类和业务逻辑类都加入到容器中
 * 6. 必须告诉Spring容器哪个类是切面类,在切面类加@Aspect注解
 * 7. 使用@EnableAspectJAutoProxy注解开启
 *
 * 主要是:
 * 1. 将业务逻辑组件和切面类都加入到容器中,告诉Spring哪个是切面类(@Aspect)
 * 2. 在切面类的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
 * 3. 开启基于注解的aop模式,@EnableAspectJAutoProxy
 *
 * AOP原理:
 * 1.  @EnableAspectJAutoProxy是什么?
 * 1.1 @Import(AspectJAutoProxyRegistrar.class)给容器中导入AspectJAutoProxy
 * 1.2 利用AspectJAutoProxyRegistrar自定义给容器中注入bean,
 * 1.3 internalAutoProxyCreator = AnnotationAwareAspectJAutoProxyCreator
 * 1.4 给容器中注册一个AnnotationAwareAspectJAutoProxyCreator
 *
 * 2. AnnotationAwareAspectJAutoProxyCreator
 *      ->AspectJAwareAdvisorAutoProxyCreator
 *          ->AbstractAdvisorAutoProxyCreator
 *              ->AbstractAutoProxyCreator
 *                  -> SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
 *                  关注后置处理器的工作,自动装配BeanFactory
 *
 * AbstractAutoProxyCreator.setBeanFactory
 * AbstractAutoProxyCreator.postProcessBeforeInstantiation
 *
 * AbstractAdvisorAutoProxyCreator.setBeanFactory->initBeanFactory
 *
 * AspectJAwareAdvisorAutoProxyCreator.initBeanFactory
 *
 * 流程:
 *      1. 传入配置类,创建IOC容器
 *      2. 注册配置类,调用refresh()刷新容器
 *      3. registerBeanPostProcessors(beanFactory);注册bean的后置处理器,拦截bean的创建
 *         3.1  先获取ioc容器已经定义了的需要创建的后置处理器;beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
 *         3.2 给容器中加别的BeanPostProcessor
 *         3.3 优先注册实现PriorityOrdered接口的BeanPostProcessor
 *         3.4 再注册实现Ordered接口的BeanPostProcessor
 *         3.5 注册没有实现优先级接口的BeanPostProcessor
 *         3.6 注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象保存到IOC容器中
 *         创建internalAutoProxyCreator的BeanPostProcessor[AnnotationAwareAspectJAutoProxyCreator]
 *              1. 创建bean实例
 *              2. populateBean(beanName,mbd,instanceWrapper)给Bean属性赋值
 *              3. initializeBean,初始化bean。
 *                  1. invokeAwareMethods(),处理Aware接口的方法
 *                  2. applyBeanPostProcessorsBeforeInitialization,应用后置处理器的BeforeInitialization方法
 *                  3. invokeInitMethods(beanName, wrappedBean, mbd),执行自定义初始化方法
 *                  4. applyBeanPostProcessorsAfterInitialization,应用后置处理器的AfterInitialization
 *              4.BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功,->aspectJAdvisorFactory
 *          3.7 把BeanPostProcessor注册到FactoryBean中,
 *          BeanFactory.addBeanPostProcessor(postProcessor)
 *          ==========以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程=========
 *      4.finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作,创建剩下的单实例
 *          1. 遍历获取容器所有的bean依次创建对象getBean(beanName)
 *              getBean->doGetBean->getSingleton
 *          2. 创建Bean
 *          【AnnotationAwareAspectJAutoProxyCreator是InstantiationAwareBeanPostProcessor,所以会在所有的bean创建之前先尝试返回bean的实例】
 *              1. 从缓存中获取当前bean,如果能获取到说明bean之前是被创建过的,直接使用,否则创建
 *                  只要被创建好的bean都会被缓存起来
 *              2. createBean();创建Bean;AnnotationAwareAspectJAutoProxyCreator会在所有的bean创建之前先尝试返回bean的实例
 *                  【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】
 *                  【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象】
 *
 *                  1. resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation
 *                      希望后置处理器能返回一个代理对象;如果能返回代理对象就返回,否则继续
 *                      1. applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
 *                          拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor,
 *                          则执行postProcessBeforeInstantiation(beanClass, beanName);
 *                  2. doCreateBean(beanName, mbdToUse, args);真正的去创建一个Bean和3.6过程一样
 *
 * AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】作用
 * 1. 会在所有的bean创建之前调用postProcessBeforeInstantiation
 *      关心MathCalculator和LogAspect的创建
 *      1. 判断当前bean是否在advisedBeans中(保存了所有需要增加的bean)
 *      2. 判断当前bean是否是基础类型的 AdviceAdvisorPointcutAopInfrastructureBean
 *          是否是切面(@Aspect)
 *      3. 判断是否需要跳过
 *          1. 获取候选的增强器(切面中的通知方法)List<Advisor> candidateAdvisors
 *              每一个封装的通知方法的增强器是InstantiationModelAwarePointcutAdvisor;
 *              判断每一个增强器是AspectJPointcutAdvisor类型的;返回true
 *          2. 永远返回false
 * 2. 创建对象
 *      postProcessorBeforeInitialization
 *          return wrapIfNecessary(bean,beanName,cacheKey);包装如果需要的情况下
 *          1. 获取当前bean的所有增强器(通知方法)Object[] specificInterceptors
 *              1. 找到能应用到当前bean的增强器(找到哪些通知方法是需要切入当前bean方法的)
 *              2. 获取能在bean使用的增强器
 *              3. 给增强器排序
 *          2. 保存当前bean在advisedBeans
 *          3. 如果当前bean需要的增强器,创建当前bean的代理对象
 *              1. 获取所有增强器
 *              2. 保存在proxyFactory代理工厂中
 *              3. 创建代理对象,Spring自动决定
 *                  JdkDynamicAopProxy(config);jdk动态代理
 *                  ObjenesisCglibAopProxy;cglib的动态代理
 *          4. 给容器中返回当前组件使用cglib增强了的代理对象
 *          5. 以后容器中获取的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程
 *
 * 3. 目标方法的执行
 *      容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象等)
 *      1. CglibAopProxy.intercept();拦截目标方法的执行
 *      2. 根据proxyFactory获取目标方法将要执行的拦截器链;
 *      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
 *          1.List<Object> interceptorList保存所有拦截器 5个
 *              一个默认的ExposeInvocationInterceptor 和4个自定义的增强器
 *          2. 遍历所有的增强器,将其转为interceptor,
 *              registry.getInterceptors(advisor)
 *          3. 将增强器转为List<MethodInterceptor>
 *              如果是MethodInterceptor,直接加入到集合中
 *              如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor
 *              转换完成为MethodInterceptor数组
 *
 *      3. 如果没有拦截器链,直接执行目标方法
 *          拦截器链(每一个通知方法被包装成方法拦截器,利用MethodInterceptor机制)
 *      4. 如果有拦截器链,把需要执行的目标对象,目标方法,拦截器链等信息传入一个CglibMethodInvocation对象
 *          并调用Object retVal = mi.proceed()
 *      5. 拦截器链的触发过程
 *          1. 如果没有拦截器直接执行目标方法,或者拦截器的索引与拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法
 *          2. 链式获得每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行
 *              拦截器链的机制,保证通知方法与目标方法的执行顺序
 *
 * 总结:
 *      1. 利用@EnableAspectJAutoProxy开启AOP功能
 *      2. @EnableAspectJAutoProxy会给容器中注册一个组件AnnotationAwareAspectJAutoProxyCreator
 *      3. AnnotationAwareAspectJAutoProxyCreator是一个后置处理器
 *      4. 容器的创建流程
 *          1. registerBeanPostProcessors()注册后置处理器,创建AnnotationAwareAspectJAutoProxyCreator对象
 *          2. finishBeanFactoryInitialization(beanFactory)初始化剩下的单实例bean
 *              1. 创建业务逻辑组件和切面组件
 *              2. AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
 *              3. 组件创建完成之后判断组件是否需要增强
 *                  是,切面的通知方法,包装成增强器(advisor);给业务逻辑组件创建一个代理对象
 *      5. 执行目标方法
 *          1. 代理对象执行目标方法
 *          2. cglibAopProxy.interceptor()
 *              1. 得到目标方法的拦截器链,(增强器包装成methodInterceptor)
 *              2. 利用拦截器的链式机制,依次进入每一个拦截器进行执行
 *              3. 效果:
 *                  正常执行:前置通知->目标方法->后置通知->返回通知
 *                  异常执行:前置通知->目标方法->后置通知->异常通知
 *
 */
@Configuration
@EnableAspectJAutoProxy
public class MainConfigOfAOP {

    @Bean
    public MathCalculator mathCalculator() {
        return new MathCalculator();
    }

    @Bean
    public LogAspect logAspect() {
        return new LogAspect();
    }

}

MathCalculator.class

public class MathCalculator {

    public int div(int num1, int num2) {
        System.out.println("MathCalculator........div......");
        return num1 / num2;
    }

}

LogAspect.class

@Aspect
public class LogAspect {

    @Pointcut("execution(public int com.lun.aop.MathCalculator.*(..))")
    public void pointCut() {

    }

    /**
     * joinPoint一定要出现在参数的第一位
     *
     */
    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        System.out.println(joinPoint.getSignature().getName() + "除法运行......,参数列表:" + args);
    }

    @After("pointCut()")
    public void logEnd() {
        System.out.println("除法结束......");
    }

    @AfterReturning(value = "pointCut()", returning = "result")
    public void logReturn(Object result) {
        System.out.println("除法返回......运行结果{}" + result);
    }

    @AfterThrowing(value = "pointCut()", throwing = "ex")
    public void logException(Exception ex) {
        System.out.println("除法运行异常......异常信息{}" + ex);
    }
}

cglibAOPProxy拦截方法执行过程

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!