• spring注解开发-AOP(含原理)


    一:AOP基本使用

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

    步骤一:导入aop模块;Spring AOP:(spring-aspects)

            <dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-context</artifactId>
    			<version>4.3.12.RELEASE</version>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-aspects</artifactId>
    			<version>4.3.12.RELEASE</version>
    		</dependency>
    

    步骤二:定义一个业务逻辑类;在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常,环绕)

    public class MathCalculator {
    	public int div(int i,int j){
    		System.out.println("MathCalculator...method...");
    		return i/j;	
    	}
    }
    

    步骤三:定义一个日志切面类:切面类里面的方法需要动态感知业务逻辑类中需要切入的方法运行到哪里然后执行;

    通知方法:
      	前置通知(@Before):logStart:在目标方法运行之前运行
      	后置通知(@After):logEnd:在目标方法运行结束之后运行(无论方法正常结束还是异常结束)
     	返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
      	异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行
      	环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())
    
    @Aspect //步骤六
    public class LogAspects {
    	
    	//抽取公共的切入点表达式
    	//1、本类引用
    	//2、其他的切面引用
    	@Pointcut("execution(public int com.atguigu.aop.MathCalculator.*(..))")
    	public void pointCut(){};
    	
    	//步骤4:@Before @After @AfterReturning @AfterThrowing
    	
    	//@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
    	@Before("pointCut()")
    	public void logStart(JoinPoint joinPoint){
    		Object[] args = joinPoint.getArgs();
    		System.out.println(""+joinPoint.getSignature().getName()+"运行。。。@Before:参数列表是:{"+Arrays.asList(args)+"}");
    	}
    	
    	@After("com.atguigu.aop.LogAspects.pointCut()")
    	public void logEnd(JoinPoint joinPoint){
    		System.out.println(""+joinPoint.getSignature().getName()+"结束。。。@After");
    	}
    	
    	//JoinPoint一定要出现在参数表的第一位
    	@AfterReturning(value="pointCut()",returning="result")
    	public void logReturn(JoinPoint joinPoint,Object result){
    		System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}");
    	}
    	
    	@AfterThrowing(value="pointCut()",throwing="exception")
    	public void logException(JoinPoint joinPoint,Exception exception){
    		System.out.println(""+joinPoint.getSignature().getName()+"异常。。。异常信息:{"+exception+"}");
    	}
    
    }
    

    步骤四:给切面类的目标方法标注何时何地运行(通知注解);

    步骤五:将切面类和业务逻辑类(目标方法所在类)都加入到容器中;

    @EnableAspectJAutoProxy  //步骤七
    @Configuration
    public class MainConfigOfAOP {
    	 
    	//业务逻辑类加入容器中
    	@Bean
    	public MathCalculator calculator(){
    		return new MathCalculator();
    	}
    
    	//切面类加入到容器中
    	@Bean
    	public LogAspects logAspects(){
    		return new LogAspects();
    	}
    }
    

    步骤六:必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect)

    步骤七:给配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop模式】==>等价于

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    

    二:通过注解解析AOP原理

    1. @EnableAspectJAutoProxy是什么?

    该注解通过@Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar;利用AspectJAutoProxyRegistrar自定义给容器中注册bean;返回BeanDefinetion

    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
            return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
        }
    
    registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
    

    因此internalAutoProxyCreator即为AnnotationAwareAspectJAutoProxyCreator;所以给容器中注册一个AnnotationAwareAspectJAutoProxyCreator;

    2. AnnotationAwareAspectJAutoProxyCreator的UML示例:
    AnnotationAwareAspectJAutoProxyCreator
      		->AspectJAwareAdvisorAutoProxyCreator
     		->AbstractAdvisorAutoProxyCreator
      		->AbstractAutoProxyCreator implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
    关注后置处理器(在bean初始化完成前后做事情)、自动装配BeanFactory
    

    logical RDD 转换成 physical stage

    3. AOP的处理流程

    1)传入配置类,创建ioc容器

    2)注册配置类,调用refresh()刷新容器;

    public AnnotationConfigApplicationContext(Class... annotatedClasses) {
            this();
            this.register(annotatedClasses);
            this.refresh();
        }
    

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

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

    3.2)给容器中加别的BeanPostProcessor

    3.3)优先注册实现了PriorityOrdered接口的BeanPostProcessor;

    3.4)再给容器中注册实现了Ordered接口的BeanPostProcessor;

    3.5)注册没实现优先级接口的BeanPostProcessor;

    3.6)注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;
    创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】

    3.6.1)创建Bean的实例

    3.6.2)populateBean;给bean的各种属性赋值

    3.6.3)initializeBean:初始化bean;

    3.6.3.1)invokeAwareMethods():处理Aware接口的方法回调;

    3.6.3.2)applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization()

    3.6.3.3)invokeInitMethods();执行自定义的初始化方法

    3.6.3.4)applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessAfterInitialization();

    3.6.4)BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;-->aspectJAdvisorsBuilder

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

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

    4)finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;创建剩下的单实例bean

    4.1)遍历获取容器中所有的Bean,依次创建对象getBean(beanName);
    getBean->doGetBean()->getSingleton()->

    4.2)创建bean
    【AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()】

    4.2.1)先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建; 只要创建好的Bean都会被缓存起来

    4.2.2)createBean();创建bean; AnnotationAwareAspectJAutoProxyCreator 会在任何bean创建之前先尝试返回bean的实例

    【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】 ;

    【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】

    4.2.2.1)resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续

    4.2.2.1.1)、后置处理器先尝试返回对象;

    bean = applyBeanPostProcessorsBeforeInstantiation():
    

    拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor;就执行postProcessBeforeInstantiation--->下面的作用部分

       	if (bean != null) {
     		bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
     	}
    

    4.2.2.2)doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例;和3.6流程一样;

    3.1 AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】的作用:

    1)每一个bean创建之前,调用postProcessBeforeInstantiation();关心MathCalculator和LogAspect的创建

    1.1)判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
    
    1.2)判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,
    		或者是否是切面(@Aspect)
    		
    1.3)是否需要跳过
    
    	1.3.1)获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】
    		  每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;
    		  判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true
    			
    	1.3.2)永远返回false
    

    2)创建对象

    postProcessAfterInitialization;
      		return wrapIfNecessary(bean, beanName, cacheKey);//包装如果需要的情况下
    
    2.1)获取当前bean的所有增强器(通知方法)  Object[]  specificInterceptors
    	
    	2.1.1)找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
    		
    	2.1.2)获取到能在当前bean使用的增强器。
    		
    	2.1.3)给增强器排序
    	
    2.2)保存当前bean在advisedBeans中;
    
    *2.3)如果当前bean需要增强,创建当前bean的代理对象;
    
    	2.3.1)获取所有增强器(通知方法)
    	
    	2.3.2)保存到proxyFactory
    	
    	*2.3.3)创建代理对象:Spring自动决定
    		JdkDynamicAopProxy(config);jdk动态代理;
    		ObjenesisCglibAopProxy(config);cglib的动态代理;
    		
    2.4)给容器中返回当前组件使用cglib增强了的代理对象;
    
    2.5)以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;
    

    3)目标方法执行;容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx);

    3.1)CglibAopProxy.intercept();拦截目标方法的执行
    	
    3.2)根据ProxyFactory对象获取将要执行的目标方法拦截器链;
    		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    		
    	3.2.1)List<Object> interceptorList保存所有拦截器 ,5个:
    		一个默认的ExposeInvocationInterceptor 和 4个增强器;
    			
    	3.2.2)遍历所有的增强器,将其转为Interceptor;
    		registry.getInterceptors(advisor);
    		
    	3.2.3)将增强器转为List<MethodInterceptor>;
    		如果是MethodInterceptor,直接加入到集合中
    		如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;
    		转换完成返回MethodInterceptor数组;
    
    3.3)如果没有拦截器链,直接执行目标方法;
         拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)
    		
    3.4)如果有拦截器链,把需要执行的目标对象,目标方法,
    	 拦截器链等信息传入创建一个 CglibMethodInvocation 对象,并调用 Object retVal =  mi.proceed();
    		
    3.5)拦截器链的触发过程;
    
    		3.5.1)如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;
    		
    		3.5.2)链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;拦截器链的机制,保证通知方法与目标方法的执行顺序;
    

    链接器链调用流程:
    logical RDD 转换成 physical stage

    总结:

    1) @EnableAspectJAutoProxy 开启AOP功能

    2) @EnableAspectJAutoProxy会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator

    3)AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;

    4)、容器的创建流程:

    4.1)registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象
    4.2)finishBeanFactoryInitialization()初始化剩下的单实例bean
    	4.2.1)创建业务逻辑组件和切面组件
    	4.2.2)AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
    	4.2.3)组件创建完之后,判断组件是否需要增强
    		是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib);
    

    5)执行目标方法:

    5.1)代理对象执行目标方法
    5.2)CglibAopProxy.intercept();
    	5.2.1)得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
    	5.2.2)利用拦截器的链式机制,依次进入每一个拦截器进行执行;
    	5.2.3)效果:
    		正常执行:前置通知-》目标方法-》后置通知-》返回通知
    		出现异常:前置通知-》目标方法-》后置通知-》异常通知
  • 相关阅读:
    Nginx的安装与部署
    什么是Nginx?为什么使用Nginx?
    spark 中的RDD编程 -以下基于Java api
    java反射
    POST-GET请求
    Spring Boot 专栏
    【Shiro】Apache Shiro架构之身份认证(Authentication)
    maven项目如何启动运行---发布到tomcat中
    一位资深程序员大牛给予Java初学者的学习路线建议
    SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)
  • 原文地址:https://www.cnblogs.com/ntbww93/p/10100680.html
Copyright © 2020-2023  润新知