Spring注解(三)——————SpringAOP原理

关注微信公众号【行走在代码行的寻路人】获取Java相关资料,分享项目经验及知识干货。

以下测试的源码地址:https://github.com/877148107/Spring-Annotation

  • SpringAOP

AOP面向切面编程【动态代理】指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式

AOP案例如下:

在业务逻辑除法运行的时候打印相关日志信息

通知方法:
 *             前置通知(@Before):logStart:在目标方法(div)运行之前运行
 *             后置通知(@After):logEnd:在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束)
 *             返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
 *             异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行
 *             环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())

AOP实现的三步:

*      1)、将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect)
 *     2)、在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
 *     3)、开启基于注解的aop模式;@EnableAspectJAutoProxy

/**
 * @ClassName: LogAspects
 * =================================================
 * @Description: AOP切面类
 *  @Aspect告诉Spring这是一个切面类
 * =================================================
 * CreateInfo:
 * @Author: William.Wangmy
 * @CreateDate: 2019/11/24 22:20
 * @Version: V1.0
 */
@Aspect
public class LogAspects {

    /**
     * 抽取公共的切入点
     * 如果在本类引入的话直接写pointCut()即可
     */
    @Pointcut("execution(public int com.spring.annotation.aop.MyCalculate.*(..))")
    public void pointCut(){

    }

    /**
     * MyCalculate.division运行之前的监控
     * 前置通知
     */
    @Before("pointCut()")
    public void startLog(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        Object[] args = joinPoint.getArgs();
        System.out.println(signature.getName()+"开始。。。。。。参数:"+ Arrays.asList(args));
    }

    /**
     * MyCalculate.division运行结束的监控
     * 后置通知
     */
    @After("pointCut()")
    public void endLog(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        System.out.println(signature.getName()+"结束。。。。。。");
    }

    /**
     * MyCalculate.division运行返回监控
     * 返回通知
     */
    @AfterReturning(value = "pointCut()",returning = "reslut")
    public void returnLog(JoinPoint joinPoint,Object reslut){
        Signature signature = joinPoint.getSignature();
        System.out.println(signature.getName()+"MyCalculate.division返回结果。。。。。。"+reslut);
    }

    /**
     * 运行异常的监控
     * 异常通知
     */
    @AfterThrowing(value = "pointCut()",throwing = "exception")
    public void exceptionLog(JoinPoint joinPoint,Exception exception){
        Signature signature = joinPoint.getSignature();
        System.out.println(signature.getName()+"异常。。。。。。"+exception);
    }
}

/**
 * @ClassName: MyAopConfiguration
 * =================================================
 * @Description: AOP的一个配置类
 * AOP面向切面编程【动态代理】
 *              指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式
 *  AOP的通知方法:前置通知startLog(注解方式:@Before)
 *               后置通知endLog(注解方式:@After)
 *               返回通知returnLog(注解方式:@AfterReturning)
 *               异常通知exceptionLog(注解方式:@AfterThrowing)
 *               环绕通知:动态代理,手动推进目标方法运行(joinPoint.procced())(注解方式:@Around)
 * @Aspect:告诉Spring这是一个切面类,而不是普通的类
 * @EnableAspectJAutoProxy:开启基于注解的APO模式,在Spring中还有许多@Enablexxxx开启某一项功能
 *
 *
 * =================================================
 * CreateInfo:
 * @Author: William.Wangmy
 * @CreateDate: 2019/11/22 23:22
 * @Version: V1.0
 */
@EnableAspectJAutoProxy
@Configuration
public class MyAopConfiguration {

    @Bean
    public MyCalculate myCalculate(){
        return new MyCalculate();
    }

    @Bean
    public LogAspects logAspects(){
        return new LogAspects();
    }
}
/**
 * @ClassName: MyCalculate
 * =================================================
 * @Description: AOP业务逻辑测试类
 * =================================================
 * CreateInfo:
 * @Author: William.Wangmy
 * @CreateDate: 2019/11/24 22:18
 * @Version: V1.0
 */
public class MyCalculate {

    public int division(int i,int j){
        return i/j;
    }
}
  • SpringAOP的原理

主要是看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么?

主要入口@EnableAspectJAutoProxy,引入@Import(AspectJAutoProxyRegistrar.class) 利用AspectJAutoProxyRegistrar自定义给容器中注册Bean:beanDefinition,bean名称:internalAutoProxyCreator,bean类:org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator:注解切面的自动代理创建器

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

	/**
	 * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
	 * to standard Java interface-based proxies. The default is {@code false}.
	 */
	boolean proxyTargetClass() default false;

	/**
	 * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
	 * for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
	 * Off by default, i.e. no guarantees that {@code AopContext} access will work.
	 * @since 4.3.1
	 */
	boolean exposeProxy() default false;

}

  • AnnotationAwareAspectJAutoProxyCreator的功能分析

*          父类=>AnnotationAwareAspectJAutoProxyCreator
 *              父类=>AspectJAwareAdvisorAutoProxyCreator
 *                  父类=>AbstractAdvisorAutoProxyCreator
 *                      父类=>AbstractAutoProxyCreator 实现了implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
 *                          父类=>ProxyProcessorSupport 实现了implements Ordered, BeanClassLoaderAware, AopInfrastructureBean
 *          这个类主要实现了一个bean的后置处理器和一个BeanFactoryAware
 *          SmartInstantiationAwareBeanPostProcessor:bean的后置处理器
 *          BeanFactoryAware:可以将bean工厂传进来,自动装备beanfactory

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
		implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
        .......
}
  •  分析AbstractAutoProxyCreator关于后置处理器 装配bean前后的方法,和自动装备beanfactory的方法

 * AbstractAutoProxyCreator:
 *      AbstractAutoProxyCreator=>setBeanFactory()
 *      AbstractAutoProxyCreator=>postProcessBeforeInstantiation()
 *      AbstractAutoProxyCreator=>postProcessAfterInitialization()
 * AbstractAdvisorAutoProxyCreator:
 *      AbstractAdvisorAutoProxyCreator=>setBeanFactory()重写了父类的方法,并且调用了initBeanFactory()
 * AnnotationAwareAspectJAutoProxyCreator:
 *      AnnotationAwareAspectJAutoProxyCreator=>initBeanFactory()重写了父类的方法

  • AOP-BeanPostProcessors流程

 *      1.传入配置类创建IOC容器
 *      2.注册配置类,然后调用刷新方法刷新容器


 *      3.registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建;


 *          1).先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor


 *          2).给容器中加别的BeanPostProcessor


 *          3).优先注册实现了PriorityOrdered接口的BeanPostProcessor,


 *          4).再给容器中注册实现了Ordered接口的BeanPostProcessor,AbstractAutoProxyCreator就实现Ordered接口
 *              org.springframework.aop.config.internalAutoProxyCreator


 *          5).注册没实现优先级接口的BeanPostProcessor;


 *          6).注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中
 *             1.创建internalAutoProxyCreator的Processor(AnnotationAwareAspectJAutoProxyCreator)


 *             2.populateBean;给bean的各种属性赋值
 *             3.initializeBean:初始化bean,跟前面beanPostProcessor初始化一样


 *                 invokeAwareMethods():处理Aware接口的方法回调
 *                 applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization()
 *                 invokeInitMethods();执行自定义的初始化方法
 *                 applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessAfterInitialization();


 *             4.调用了AnnotationAwareAspectJAutoProxyCreator.initBeanFactory将BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功
 *                并且得到了aspectJAdvisorFactory通知工厂


 *          7).把BeanPostProcessor注册到BeanFactory中;beanFactory.addBeanPostProcessor(postProcessor)

***********************上面是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程************
*AnnotationAwareAspectJAutoProxyCreator是InstantiationAwareBeanPostProcessor这种类型的后置处理器


4.finishBeanFactoryInitialization:完成BeanFactory初始化工作,创建剩下的单实例bean
*          1).遍历获取容器中所有的Bean,依次创建对象getBean(beanName)


*              getBean(beanName)->doGetBean()->getSingleton()


2).创建bean:AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截
*                因为继承了这个后置处理器InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()
*              【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】
*              【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】

*              1.先从缓存中获取当前bean,如果能获取到说明bean在之前被创建过,然后就直接使用,否则再创建。创建好的bean都会放入到缓存中
*                  后置处理器先去尝试返回一个对象:
*                  bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
*                  拿到所有的后置处理器,如果是InstantiationAwareBeanPostProcessor,就执行postProcessBeforeInstantiation
*                 if (bean != null) {
*                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
*                   }

2.创建bean:createBean()AnnotationAwareAspectJAutoProxyCreator这个会在任何bean创建之前返回bean实例
*                  Object bean = resolveBeforeInstantiation(beanName, mbdToUse)解析BeforeInstantiation,后置处理器在此能够返回一个代理对象;
*                  如果能返回就是使用,如果不能返回就调用doCreateBean(beanName, mbdToUse, args)去创建一个bean实例和上面创建internalAutoProxyCreator的Processor的流程一样。


AnnotationAwareAspectJAutoProxyCreator是InstantiationAwareBeanPostProcessor这种类型的后置处理器,其作用:


* 关注:MyCalculate和LogAspects的bean创建
*        1.在每个bean创建之前,调用postProcessBeforeInstantiation()


2.判断当前bean是否在advisedBeans中(保存了所有需要增强bean:就是myCalculate的业务逻辑,需要切面进行切的。不能按照之前简单的bean需要增强)


3.判断当前bean是不是基础类型的或者是不是切面
*            基础类型:Advice、Pointcut、Advisor、AopInfrastructureBean
*            切面:是不是@Aspect注解


*        4.是否需要跳过
*            获取候选的增强器(切面里面的通知方法)
*            每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;
*            判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true,
*        5.永远返回false


*  MyCalculate创建完后:会调用postProcessAfterInitialization()
*        return wrapIfNecessary(bean, beanName, cacheKey);如果需要的情况下进行包装


1.获取当前bean的所有增强器(通知方法)
*          1).找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
*          2).根据List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);找到能在当前bean使用并返回能用的增强器


3).给增强器排序


2.获取到所有的增强器Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(),并将当前bean保存到缓存中advisedBeans.put(cacheKey, Boolean.TRUE)


1).创建代理对象


 2).保存到代理工厂proxyFactory中


 3).创建代理对象:Spring自动决定
*              JdkDynamicAopProxy(config);jdk动态代理;
*              ObjenesisCglibAopProxy(config);cglib的动态代理;


4)、给容器中返回当前组件使用cglib增强了的代理对象;
5)、以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;

如果要执行MyCalculate的业务逻辑,就是使用cglib增强了的代理对象,调用提交保存的那些通知方法

  • 目标方法MyCalculate.division的执行:

容器中保存了组件的代理对象:CGLIB增强后的对象,这个对象里面包含了增强器、目标对象等详细信息
 *      1.CglibAopProxy.intercept();拦截目标方法的执行


 *      2.根据ProxyFactory对象获取将要执行目标方法的拦截器链
 *          拦截器链:把每一个通知方法包装成拦截器,利用MethodInterceptor机制
 *          List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);


 *          1).List<Object> interceptorList保存所有的拦截器,list长度为5:一个默认的和四个增强器


 *          2).遍历所有的增强器并转换成Interceptor
 *              registry.getInterceptors(advisor)


 *          3).将增强器转换成List<MethodInterceptor>
 *                  如果是MethodInterceptor,直接加入到集合中
 *                  如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;
 *                  转换完成返回MethodInterceptor数组;


 *                  AspectJAfterThrowingAdvice和AspectJAfterAdvice是经过转换成为了拦截器,其他三个本身就是拦截器
 *      3.如果没有拦截链直接执行目标方法
 *      4.如果有拦截器链,把需要执行的目标对象,目标方法,拦截器链等详细传入创建一个CglibMethodInvocation对象并调用。
 *      retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();


 *      5.拦截器链触发的过程:
 *          1).如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;


 *          2)、链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;
 *                  拦截器链的机制,保证通知方法与目标方法的执行顺序;
 *              MethodBeforeAdviceInterceptor:


 *              AspectJAfterAdvice:


 *              AspectJAfterThrowingAdvice:


 *              AfterReturningAdviceInterceptor:

  • AOP通知方法执行流程

  • AOP总结

1.@EnableAspectJAutoProxy开启AOP功能
2.@EnableAspectJAutoProxy中导入组件@Import(AspectJAutoProxyRegistrar.class)注册Bean,AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
3.bean名称:org.springframework.aop.config.internalAutoProxyCreator,bean类:class org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
4.AnnotationAwareAspectJAutoProxyCreator是一个后置处理器。将这个后置处理器注册到容器中
5.注册后置处理器到bean工厂BeanFactory,实际上就是创建一个BeanProcessor对象放在容器中
    5.1 获取所有的后置处理,判断是否实现了相应的接口分别装载到不同的list里面
    5.2 AnnotationAwareAspectJAutoProxyCreator首先从BeanFactory中获取,如果能获取到就加入对应的list中,如果不能获取到就去实例化这个bean
        5.2.1 populateBean(beanName, mbd, instanceWrapper);给bean赋值
        5.2.2 initializeBean(beanName, exposedObject, mbd);初始化bean
        5.2.3 invokeAwareMethods(beanName, bean);处理Aware接口的方法回调
        5.2.4 applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);调用后置处理器初始化之前的方法
        5.2.5 invokeInitMethods(beanName, wrappedBean, mbd);调用后置处理器初始化的方法
        5.2.6 applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);调用后置处理器初始化之后的方法
        5.2.7 返回初始化的后置处理器BeanProcessor
    5.3 beanFactory.addBeanPostProcessor(postProcessor);把BeanPostProcessor注册到BeanFactory中
6.完成BeanFactory初始化工作,创建剩下的单实例bean。关注业务处理bean和切面类的bean
    6.1 Object sharedInstance = getSingleton(beanName);检查缓存中是否存在myCalculate实例bean,存在就直接获取,不存在就实例化这个bean,跟5.2逻辑一样,
    6.2 MyCalculate创建完后:会调用postProcessAfterInitialization()
        6.2.1 Object cacheKey = getCacheKey(beanClass, beanName);先从缓存中获取bean
        6.2.2 this.advisedBeans.containsKey(cacheKey);判断当前bean是否在advisedBeans中(保存了所有需要增强bean:就是myCalculate的业务逻辑,需要切面进行切的。不能按照之前简单的bean需要增强)
        6.2.3 isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName);判断当前bean是不是基础类型的或者是不是切面,基础类型:Advice、Pointcut、Advisor、AopInfrastructureBean,切面:是不是@Aspect注解
        6.2.4 获取所有的增强器
            1). 找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
            2). 根据List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);找到能在当前bean使用并返回能用的增强器
            3). 给增强器(通知方法)排序,后面执行增强器是会有顺序执行
            4). 获取到所有的增强器Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(),并将当前bean保存到缓存中advisedBeans.put(cacheKey, Boolean.TRUE)
            5). 创建一个代理对象,保存到代理工厂proxyFactory中:代理对象Spring自动决定;JdkDynamicAopProxy(config);jdk动态代理;ObjenesisCglibAopProxy(config);cglib的动态代理;
        
    6.3 logAspects创建
        6.7.1 跟上面6.2创建一样,但是这个类是标记了@Aspect注解是一个切面类,将这个切面类放入缓存中
        6.7.1 调用postProcessAfterInitialization()是首先从缓存中获取,上面将这个切面类放入了缓存中所以这里获取到了直接放回
7.以上步骤利用AnnotationAwareAspectJAutoProxyCreator后置处理将myCalculate和logAspects单实例bean创建完成并且闯将了代理对象放入缓存中
8.目标业务方法执行:
    8.1 CglibAopProxy.intercept();拦截目标方法的执行
    8.2 根据ProxyFactory对象获取将要执行目标方法的拦截器链.拦截器链:把每一个通知方法包装成拦截器,利用MethodInterceptor机制
           List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        8.2.1 获取五个增强器。遍历所有的增强器并转换成Interceptor。registry.getInterceptors(advisor)
        8.2.2 这里 AspectJAfterThrowingAdvice和AspectJAfterAdvice是经过转换成为了拦截器,其他三个本身就是拦截器
        8.2.3 如果有拦截器链,把需要执行的目标对象,目标方法,拦截器链等详细传入创建一个CglibMethodInvocation对象并调用。
                   retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
        8.2.4 拦截器链触发的过程:
 *          1).如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;
            2).这里6.2.4中第三点的时候给增强器进行了排序这里触发会按照上面排序进行触发
            MethodBeforeAdviceInterceptor:分别调用切面类对应注解的方法startLog
            AspectJAfterAdvice:分别调用切面类对应注解的方法endLog
            AspectJAfterThrowingAdvice:分别调用切面类对应注解的方法exceptionLog(
            AfterReturningAdviceInterceptor:分别调用切面类对应注解的方法returnLog
    8.3 详细执行流程见如上图AOP通知方法执行流程

    

发布了101 篇原创文章 · 获赞 10 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/WMY1230/article/details/103230681
今日推荐