Spring AOP 原理分析

1、AOP 使用

/**
 * FileName:MainConfigOfAOP
 * @author Sam
 * @date 2021-06-13-23:20
 * Description: AOP 测试
 *
 *  1、导入aop 模块: Spring AOP ;(spring-aspects)
 *  2、定义一个业务逻辑类:在业务逻辑前、后、运行时打印
 *  3、定义一个日志切面类(LogAspects) : 切面类里面的方法需要动态感知 业务逻辑类 放发运行到的存在
 *      通知方法:
 *          前置通知(@Before):在目标方法运行之前运行
 *          后置通知(@After):在目标方法运行结束之后运行,无论方法是正常返回还是异常返回。
 *          返回通知(@AfterReturning):在目标方法正常返回之后运行
 *          异常通知(@AfterThrowing):在目标方法出现异常以后运行
 *          环绕通知(@Around):动态代理,手动推进目标方法运行
 *  4、给切面类的目标方法标注何时何地运行(通知注解)
 *  5、将切面类和业务逻辑类都加入到容器中
 *  6、告诉Spring那个类是切面类(给切面类上加一个注解:@Aspect)
 *  7、给配置类中加  @EnableAspectJAutoProxy 开启基于注解的AOP模式
 */
@Configuration
@EnableAspectJAutoProxy
public class MainConfigOfAOP {

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


/**
 * FileName:MathCalculator
 *
 * @author Sam
 * @date 2021-06-13-23:28
 * Description: AOP业务逻辑类
 */
public class MathCalculator {

    public void div(int i, int j) {
        int a = i/j;
        System.out.println("MathCalculator 。。div = " + a);
    }
}



/**
 * FileName:LogAspects
 *
 * @author Sam
 * @date 2021-06-13-23:30
 * Description: 切面类
 */
@Aspect
public class LogAspects {

    /**
     * 抽取公共的切入点表达式
     *
     */
    @Pointcut("execution(public void com.hupun.aop.MathCalculator.*(..))")
    public void pointCut(){};

    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        System.out.println(joinPoint.getSignature() + "除法开始。。。。@Before" + " 参数" + Arrays.toString(args));
    }
    @After("pointCut()")
    public void logEnd(JoinPoint joinPoint) {

        System.out.println("方法名"+ joinPoint.getSignature().getName()+"除法结束。。。。@After");
    }

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

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

// 使用的时候 必须要从 IOC 容器中获取,不然切面是不生效的,因为用的是spring的注解,所以不用spring管理的对象,是不可以的
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        MathCalculator mathCalculator = (MathCalculator) SpringApplication.run(DemoApplication.class, args).getBean("mathCalculator");
        mathCalculator.div(1, 0);
    }
}

在这里插入图片描述

2、AOP原理

AOP 原理:【看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么?】
    @EnableAspectJAutoProxy
1@EnableAspectJAutoProxy 是什么?
    @ImportAspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar.class
        利用AspectJAutoProxyRegistrar自定义给容器中注册bean。
        internalAutoProxyCreator = AnnotationAwareAspectAutoProxyCreate

图后面补

继承关系
2AnnotationAwareAspectJAutoProxyCreator
        -AspectJAwareAdvisorAutoProxyCreator
        	-》abstractAdvisorAutoProxyCreator
        		-》abstractAutoProxyCreator
        		   implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware 
        		关注后置处理器(在bean初始化完成前后做事情)、自动装配BeanFactory

在这里插入图片描述
准备调试,以下类打上端点:

每个实现类中主要用到的方法都打上端点:        
AbstractAutoProxyCreator.setBeanFactory()  
AbstractAutoProxyCreator.postProcessBeforeInstantiation()
AbstractAutoProxyCreator.postProcessAfterInitialization()
        
AbstractAdvisorAutoProxyCreator.setBeanFactory()  -> initBeanFactory()   
      -》其中initBeanFactory()方法使用了模板模式
AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()   

流程:(每一步的图后面补)

1)、传入配置类,创建一个新的IOC(ApplicationContext)容器。SpringApplication.run()
    2)、注册配置类,刷新容器 refresh()
    3)、registerBeanPostProcessors(beanFactory):注册bean的后置处理器来方便拦截bean
        1、先从IOC容器中获取到已经定义了的需要创建对象的所有BeanPostProcessor
        2、给容器中加入别的BeanPostProcessor
        3、优先注册实现Ordered接口的BeanPostProcessor
        4、再给容器中注册实现了Ordered接口的BeanPostProcessor
        5、注册没实现接口优先级接口的BeanPostProcessor
        6、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中。
        	创建internalAutoProxyCreator 的 BeanPostProcessorAnnotationAwareAspectJAutoProxyCreator1)、创建Bean的实例
            2)、populateBean(beanName, mbd, instanceWrapper); 给bean的各种属性赋值
        	3)、initializeBean(beanName, exposedObject, mbd); 初始化bean
        		1)、invokeAwareMethods(beanName, bean);  处理Aware接口的方法回调
          		2)、applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName):应用bean的后置处理器						BeforeInitialization通知。
         		3)、invokeInitMethods(beanName, wrappedBean, mbd);  执行自定义初始化方法
        		4)、applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);应用Bean的后置处理器的						AfterInitialization通知。
        	4)、BeanPostProcessorAnnotationAwareAspectJAutoProxyCreator)创建成功
        7、把BeanPostProcessor注册到BeanFactory中
        	beanFactory.addBeanPostProcessor(postProcessor);

===========================以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程=========================
  
    AnnotationAwareAspectJAutoProxyCreator =InstantiationAwareBeanPostProcessor
    4)finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作
    	1、遍历获取容器中所有的Bean,依次创建对象getBean(beanName);
			getBean-->doGetBean()-->getSingleton()
        2、创建bean
             【AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,			         						InstantiationAwareBeanPostProcessor, 会调用postProcessorBeforeInstantiation()1)、先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建。把创建好的bean都会缓					存起来。
              2)、createBean();创建bean,AnnotationAwareAspectJAutoProxyCreator会再任何bean创建之前先尝试返回bean					实例。
                	【BeanPostProcessor 是在bean对象创建完初始化前后调用的】
    				【InstantiationAwareBeanPostProcessor 是在创建Bean实例之前先尝试用后置处理器返回对象的】
                	1)、resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation 希望后置处理器在						此返回一个代理对象,如果能返回代理对象就使用,如果不能就继续。
                		1)、后置处理器先尝试返回对象;
                			bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
								拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor就执行												postProcessBeforeInstantiation。
                            if (bean != null) {
                                bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                            }  
						2)、doCreateBean(beanName,mbdTouse.args);真正去创建一个bean
    
                            
AnnotationAwareAspectJAutoProxyCreatorInstantiationAwareBeanPostProcessor1)、每一个bean创建之前,调用postProcessBeforeInstantiation();
        关心MathCalculatorLogAspect的创建
        1)、判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
        2)、判断当前bean是否是基础类型的AdvicePointCutAdvisorAopInfrastructureBeforeXX 或者是否是切面		             (@Aspect3)、是否需要跳出
               1)、获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】
               		每一个封装的通知方法的增强器是InstantiationModelAwarePointCutAdvisor;
                    判断每一个增强器是否是AspectJPointcutAdvisor类型的,返回true2)、永远返回false。 因为我们的是InstantiationModelAwarePointCutAdvisor
2)、创建对象
postProcessAfterInitialization:
	return wrapIfNecessary(bean, beanName, cacheKey); // 包装如果需要的情况下
	1)、获取当前bean的所有增强器(通知方法)Object[] specificInterceptors
        1、找到候选的所有的增强器(找那些通知方法是需要切入当前的bea方法的)
        2、获取到能在bean使用的增强器
        3、给增强器排序
    2)、保存当前bean在advisedBeans中;
    3)、如果当前bean需要增强,创建当前bean的代理对象
        1)、获取所有增强器(通知方法)
        2)、保存到ProxyFactory
        3)、创建代理对象:Spring自行决定
        	JdkDynamicAopProxy(config); jdk 动态代理
        	ObjenesisCglibAopProxy(config);cglib 动态代理
     4)、给容器中返回当前组件使用cglib 增强的代理对象;
     5)、以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程。
        
 
3)、目标方法执行:
	容器中保存了组件的代理对象(cglib增强后的对象),这个对象保存了详细信息(比如增强器,目标对象,XXX);
    1)、CglibAOPProxy.intercept(); 拦截目标方法的执行
    2)、根据ProxyFactory对象获取将要执行的目标方法拦截器链;
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice()
        1)、List<Object> interceptionList保存所有拦截器5
        	一个默认的ExposeInvocationInterceptor4个增强器。
        2)、遍历所有的增强器,将其转为Interceptor
        	registry.genInterceptors(advisor);
		3)、将增强器转为List<MethodInterceptor>;
            如果是MethodInterceptor 将增强器转为MethodInterceptor;
            转换完成返回MethodInterceptor数组。
            
    3)、如果没有拦截器链,直接执行目标方法。
    4)、如果有连接器链,把需要执行的目标对象,目标方法,拦截器链等信息传入创建一个CglibMethodInvocation对象。
         并调用retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
	5)、拦截器链的触发过程;
    	1)、如果没有拦截器执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法。
        2)、链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行,
        	拦截器链的机制,保证通知方法与目标方法的执行顺序。

3、总结

1)、@EnableAspectJAutoProxy 开启AOP功能。
        2)、@EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
        3)、AnnotationAwareAspectJAutoProxyCreator 是一个后置处理器;
        4)、容器的创建流程:
        	1)、registerBeanPostProcessor()注册后置处理器,创建AnnotationAwareAspectJAutoProxyCreator
        	2)、finishBeanFactoryInitialization() 初始化剩下的单实例bean
        		1)、创建业务逻辑组件和切面组件
        		2)、AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
        		3)、组件创建完之后,判断组件是否需要增强
        			是:切面的通知方法,包装成增强器(Advisor),给业务逻辑组件创建一个代理对象
        5)、执行目标方法:
        	1)、代理对象执行目标方法
        	2)、CglibAopProxy.intercept()
        		1)、得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor2)、利用拦截器的链式机制,依次进入每一个拦截器进行执行;
        		3)、效果:
        			正常执行: 前置通知--> 目标方法 --> 后置通知 --> 返回通知
        			出现异常: 前置通知--> 目标方法 --> 后置通知 --> 异常通知

在这里插入图片描述
递归+ 责任链模式:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值