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 ...
随机推荐
- GCC在C语言中内嵌汇编 asm __volatile__
2012-11-26 22:20 17958人阅读 评论(2) 收藏 举报 分类: linux(59) 架构管理(24) C/C++(59) 目录(?)[+] 在内嵌汇编中,可以将C语言表达式 ...
- Swift3.0 字符串(string)
string常用的一些操作方式 //字符串 //1.初始化字符串 //1.1通过字面量赋值的方式初始化字符串 let tempStrig = "this is temp string&quo ...
- 机器学习--DIY笔记与感悟--②决策树(1)
在完成了K临近之后,今天我们开始下一个算法--->决策树算法. 一.决策树基础知识 如果突然问你"有一个陌生人叫X,Ta今天需要带伞吗?", 你一定会觉得这个问题就像告诉你& ...
- Linux--------------mysql的安装
工具: 1>Centos6.8 2>Jdk1.7 3>Mysql5.7 1.下载mysql wget http://dev.mysql.com/get/Download ...
- 继续(3n+1)猜想 (25)
#include <algorithm> #include <iostream> using namespace std; int main(){ ] = { }; ], nu ...
- Arthur and Table CodeForces - 557C
Arthur and Table CodeForces - 557C 首先,按长度排序. 长度为p的桌腿有a[p]个. 要使得长度为p的桌腿为最长,那么要按照代价从小到大砍掉sum{长度不到p的腿的数 ...
- ios学习笔记 UITableView(纯代码) (一)
参考 “https://www.cnblogs.com/ai-developers/p/4557487.html” UITableViewCell 有一个代码重用 减少资源的浪费 参考 https: ...
- 【转】数据库CRUD操作
数据库CRUD操作 一.删除表 drop table 表名称 二.修改表 alter table 表名称 add 列名 数据类型 (add表示添加一列) alter table 表名 ...
- [转]Intellij Idea自动添加注释的方法
Intellij Idea自动添加注释的方法 阿历Ali 关注 2017.08.20 21:22* 字数 914 阅读 2741评论 0喜欢 6 程序媛阿历终于要写第一篇关于开发的文章了!!! 阿历用 ...
- 高阶函数之filter 和 sorted
filter函数 接受一个函数和序列,把传入的函数依次作用于每个序列,然后根据返回值时True还是False保留或舍弃元素. def func(n): if n%2 == 0: return n m ...