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
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方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;拦截器链的机制,保证通知方法与目标方法的执行顺序;
链接器链调用流程:  			
总结:
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)效果:
		正常执行:前置通知-》目标方法-》后置通知-》返回通知
		出现异常:前置通知-》目标方法-》后置通知-》异常通知spring注解开发-AOP(含原理)的更多相关文章
- 浅尝Spring注解开发_AOP原理及完整过程分析(源码)
		浅尝Spring注解开发_AOP原理及完整过程分析(源码) 浅尝Spring注解开发,基于Spring 4.3.12 分析AOP执行过程及源码,包含AOP注解使用.AOP原理.分析Annotation ... 
- Spring注解开发系列Ⅵ --- AOP&事务
		注解开发 --- AOP AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,横向重复,纵向抽取.详细的AO ... 
- Spring注解开发_Spring容器创建概述
		浅尝Spring注解开发_Spring容器创建概述 浅尝Spring注解开发,基于Spring 4.3.12 概述Spring容器创建的过程,包括12个方法的执行 浅尝Spring注解开发_自定义注册 ... 
- Annotation(三)——Spring注解开发
		Spring框架的核心功能IoC(Inversion of Control),也就是通过Spring容器进行对象的管理,以及对象之间组合关系的映射.通常情况下我们会在xml配置文件中进行action, ... 
- spring注解开发中常用注解以及简单配置
		一.spring注解开发中常用注解以及简单配置 1.为什么要用注解开发:spring的核心是Ioc容器和Aop,对于传统的Ioc编程来说我们需要在spring的配置文件中邪大量的bean来向sprin ... 
- Spring注解开发-全面解析常用注解使用方法之生命周期
		本文github位置:https://github.com/WillVi/Spring-Annotation/ 往期文章:Spring注解开发-全面解析常用注解使用方法之组件注册 bean生命周期  ... 
- Spring注解开发系列专栏
		这个系列主要是讲Spring注解的使用,可以为后面SpringBoot的学习带来一定的帮助.我觉得从Spring直接过度到SpringBoot还是有点快,还是得需要一个演变的过程.从Spring开发, ... 
- 浅尝Spring注解开发_Bean生命周期及执行过程
		Spring注解开发 浅尝Spring注解开发,基于Spring 4.3.12 包含Bean生命周期.自定义初始化方法.Debug BeanPostProcessor执行过程及在Spring底层中的应 ... 
- 浅尝Spring注解开发_Servlet3.0与SpringMVC
		浅尝Spring注解开发_Servlet 3.0 与 SpringMVC 浅尝Spring注解开发,基于Spring 4.3.12 Servlet3.0新增了注解支持.异步处理,可以省去web.xml ... 
随机推荐
- JavaScript面向对象轻松入门之综合
			javascrpit面向对象之综合 这一章是对前几章的一个总结,通过一个案例来综合认识javascript面向对象的基本用法 需求: 几乎所有的web应用都需要保存数据一些到本地,那么我们就来 ... 
- 从mysql高可用架构看高可用架构设计
			高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间. 假设系统一直能够提供服务,我们说系统的可用性是100%.如果 ... 
- 利用ASP .NET Core的静态文件原理实现远程访问Nlog日志内容及解决遇到的坑
			最近项目上试运行发现,很多时候网站出了问题或者某个功能不正常,常常需要运维人员去服务器里面查看一下日志,看看日志里面会产生什么异常,这样导致每次都要去远程服务器很不方便,有时服务器是客户保管的不能让我 ... 
- Java简单高精度合集
			第一个Java的算法程序.记得可以使用Alt+'/'自动补全sysout和main之类的. BigInteger在java.math.BigInteger中. import java.math.Big ... 
- bzoj 2131: 免费的馅饼【dp+树状数组】
			简单粗暴的dp应该是把馅饼按时间排序然后设f[i]为i接到馅饼能获得的最大代价,转移是f[i]=max(f[j])+v[i],t[j]<=t[i],2t[i]-2t[j]>=abs(p[i ... 
- video.py  OpenCv例程阅读
			#!/usr/bin/env python ''' Video capture sample. Sample shows how VideoCapture class can be used to a ... 
- Access 中case when then else end不支持使用switch代替
			Access 中case when then else end不支持使用switch代替 这里主要是实现一个表中多个字段,多个字段之间作比较然后取得最大值或者最小值用来处理 case when the ... 
- 16G 手机清理
			1.16G 手机清理 清理top 5 的应用的缓存即可 2,hw wife 连接模块 低于 app wifi 的连接模块. 在同样的电脑热点面前,hw 连补上电脑热点,apple 可以连上电脑热点. ... 
- 151 Reverse Words in a String 翻转字符串里的单词
			给定一个字符串,翻转字符串中的每个单词.例如,给定 s = "the sky is blue",返回 "blue is sky the".对于C程序员:请尝试用 ... 
- UGUI_屏幕适配
			引用:http://www.xuanyusong.com/archives/3278#comments 1.可以选择的有三种: 1.Screen Space – overlay 此模式不需要UI摄像 ... 
