1.spring循环依赖
1.普通bean生成过程
- getBean 依次从一级缓存,二级缓存,三级缓存中找。如果一级缓存中没有并且正在被创建(当在缓存中没有找到时会标记为正在被创建)才会去二级缓存中找,如果二级缓存中没有并且bean运行循环引用才会去三级缓存中找。
- createBean
- doCreateBean
- 实例化,将bean的
ObjectFactory
对象放入三级缓存(如果没有循环依赖,二级缓存和三级缓存都没有作用) - 属性注入
- 后置处理器的前置方法
- 初始化
- 后置处理器的后置方法(如果要生成代理,这里会生成代理对象)放入一级缓存
singletonObjeacts
中 - 销毁
2.对于存在循环依赖但不存在aop的情况
假设A中聚合了B对象,B中聚合了A对象 - getBean(A)
- createBean(A)
- doCreateBean(A)
- 实例化,将bean的
ObjectFactory
对象放入三级缓存 - 属性注入发现依赖于B,执行getBean(B)
- 三级缓存中都没有B,因此执行createBean(B)
- doCreateBean(B)
- 属性注入,发现依赖于A,执行getBean(A)
- 在三级缓存中找到了半成品的A对象,执行三级缓存中存放的lambda表达式,并将返回的bean放入二级缓存,删除三级缓存中的
ObjectFactory
,并返回 - 完成B的属性注入,初始化,最终将B放入一级缓存
singletoObjects
中 - 完成A的属性注入,初始化,最终将A放入一级缓存
2.对于存在循环依赖和aop的情况
假设A中聚合了B对象,B中聚合了A对象,且A对象需要aop
- getBean(A)
- createBean(A)
- doCreateBean(A)
- 实例化,将bean的
ObjectFactory
对象(一个lambda表达式)放入三级缓存 - 属性注入发现依赖于B,执行getBean(B)
- 三级缓存中都没有B,因此执行createBean(B)
- doCreateBean(B)
- 属性注入,发现依赖于A,执行getBean(A)
- 在三级缓存中找到了半成品的A对象,执行三级缓存中存放的lambda表达式,这里会将原始的A对象放入一个
earlyProxyReference
的map中,表示里面的bean执行了aop,但里面存放的依然是原始对象,不是执行了aop的对象。并将代理对象放入二级缓存,删除三级缓存中的ObjectFactory
,并返回A - 完成B的属性注入,初始化,最终将B放入一级缓存
singletoObjects
中 - 完成A的属性注入,初始化
- 当A要执行aop的时候,进行判断,如果
earlyProxyReference
中存在原始对象,就不再执行aop - 将A放入一级缓存
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从一级缓存获取,key=beanName value=bean
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 从二级缓存获取,key=beanName value=bean
singletonObject = this.earlySingletonObjects.get(beanName);
// 是否允许循环引用
if (singletonObject == null && allowEarlyReference) {
/**
* 三级缓存获取,key=beanName value=objectFactory,objectFactory中存储getObject()方法用于获取提前曝光的实例
*
* 而为什么不直接将实例缓存到二级缓存,而要多此一举将实例先封装到objectFactory中?
* 主要关键点在getObject()方法并非直接返回实例,而是对实例又使用
* SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法对bean进行处理
*
* 也就是说,当spring中存在该后置处理器,所有的单例bean在实例化后都会被进行提前曝光到三级缓存中,
* 但是并不是所有的bean都存在循环依赖,也就是三级缓存到二级缓存的步骤不一定都会被执行,有可能曝光后直接创建完成,没被提前引用过,
* 就直接被加入到一级缓存中。因此可以确保只有提前曝光且被引用的bean才会进行该后置处理
*/
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
/**
* 通过getObject()方法获取bean,通过此方法获取到的实例不单单是提前曝光出来的实例,
* 它还经过了SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法处理过。
* 这也正是三级缓存存在的意义,可以通过重写该后置处理器对提前曝光的实例,在被提前引用时进行一些操作
*/
singletonObject = singletonFactory.getObject();
// 将三级缓存生产的bean放入二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
// 删除三级缓存
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
2.对IOC AOP的理解
IOC
- IoC(Inverse of Control:控制反转)是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理。 IoC 在其他语言中也有应用,并非 Spring 特有。
- 当我们需要一个对象时,只要注入进来.否则一个对象可能依赖许多对象,这样创建对象会造成很大的麻烦
- 依赖倒置 当存在A依赖于B,B依赖于C,C依赖于D 那么D的改动会让依赖于它的类都要改动.而使用IOC只需要将改动后的D放入IOC容器中
- 降低耦合度 不同IOC就类似于许多耦合在一起的齿轮,而IOC就相当于一个容器,与每个bean只有一条连接线
AOP
将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
3.spring bean的生命周期
4.spring IOC
5.spring AOP
使用
- 导入spring-aspects依赖,定义业务类和切面类
依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.8</version>
</dependency>
//其他依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.8</version>
</dependency>
业务类:
public class MathCalculate {
public int div(int i,int j){
System.out.println("div方法执行内部");
return i/j;
}
}
切面类:
@Aspect
public class LogAspect {
//如果在其他类使用该切面,可以加修饰符,再加该函数
@Pointcut("execution(public int com.atguigu.bean.MathCalculate.*(..))")
public void pointCut(){
}
@Before("execution(public int com.atguigu.bean.MathCalculate.div(int,int))")
//@Before("pointCut()")
public void before(){
System.out.println("执行前。。。");
}
@After("pointCut()")
public void after(){
System.out.println("执行后。。。");
}
@AfterReturning("pointCut()")
public void afterReturning(){
System.out.println("执行返回。。。");
}
@AfterThrowing("pointCut()")
public void afterThrowing(){
System.out.println("执行异常。。。");
}
}
- 抽取切入点,@PointCut,加前置通知、后置通知、返回通知、异常通知
- 将业务逻辑类和切面类加入到容器中
- 告诉spring哪个是切面类 @Aspect
- 开启基于注解版的切面功能@EnableAspectJAutoProxy要标注在配置类,而不是切面类
aop中的一些概念
目标对象target:被增强的对象。
代理类:增强后的对象。
通知Advice:就是增强类,增强代码所在的类。
织入:是个动词,就是把增强的代码织入到被代理对象的过程
连接点:目标对象中的每一个方法都是连接点。
切入点:目标对象中被增强的方法是切入点。
切面:切面就是切入点和通知的组合。
原理
- 导入组件
-
@EnableAspectJAutoProxy
通过@Import
给容器中添加了一个类AspectJAutoProxyRegistrar
- 该类实现了
ImportBeanDefinitionRegistrar
接口,该接口的作用也是给容器中添加组件,实际是添加了AnnotationAwareAspectJAutoProxy
类 -
AnnotationAwareAspectJAutoProxy
类实现了BeanFactoryAware
和SmartInstantiationAwareBeanPostProcessor
接口
- 先将AnnotationAwareAspectJAutoProxy加入到容器中
在spring容器启动过程中,有一个阶段是注册Bean的后置处理器的,即registerBeanPostProcessors(beanFactory)
.在这个阶段会将完成所有后置处理器的Bean的实例化,初始化,最后放入到IOC容器中 - 执行
SmartInstantiationAwareBeanPostProcessor
接口的postProcessBeforeInstantiation
方法
在spring容器启动过程中的finishBeanFactoryInitialization(beanFactory)
阶段,在Bean被调用doCreateBean
之前,会执行该方法。该方法的作用是:
判断当前bean是否是基础类型 Advice PointCut Advisor AopInfrastructureBean @Aspect 如果是就加入到advisedBean集合中。 - 执行
SmartInstantiationAwareBeanPostProcessor
接口的postProcessAfterInitialization
方法
- 如果
earlyProxyReference
中没有原始Bean,则执行AOP,即return wrapIfNecessary
获取当前bean的增强器(通知方法) - 找到所有的增强器
List<Advisor> candidateAdvisors
找到能在当前bean使用的候选的增强器(找那些方法需要切入当前bean) - 找到能在当前Bean使用的增强器
List<Advisor> eligibleAdvisors
获取到能在当前bean使用的增强器(这里根据切入点表达式进行判断) - 给增强器排序
- 保存当前bean在advisedBeans
- 如果需要增强创建代理对象(即有可以使用的增强器),则执行创建代理的逻辑
- 将所有增强器保存在代理工厂
proxyFactory
中,并使用代理工厂创建代理proxyFactory.getProxy
调用createAopProxy.getProxy
创建代理对象 - spring自动决定jdk代理对象和cglib代理对象,给容器中返回当前bean的代理对象
- 获取拦截器链
对于需要切入的方法,获取该方法的拦截器链.
获取拦截器链的过程:
- 获取5个增强器,一个默认的,其他的都是通知方法
- 遍历增强器,封装成拦截器
- 如果增强器是MethodInvocation,直接加入,否则使用AdvisorAdapter转为MethodInterceptor( before 和 afterReturning是需要转的)
如果没有拦截器链,直接执行目标方法。如果有拦截器链,把需要执行的拦截器链,目标方法,目标对象传入创建CglibMethodInvocation,并调用它的proceed
方法
- 拦截器链的执行
首先拦截器链中共有5个拦截器
拦截器链.png
执行过程:
image.png
- 使用
currentInterceptorIndex
记录拦截器的索引,初始值为-1,如果拦截器链中拦截器的个数是0,那么会直接执行目标方法 - 当前长度
len -1 = 4 != -1,index++,index = 0
执行ExposeInvocationInterceptor的invoke方法 - ExposeInvocationInterceptor的invoke主要用来存储CglibMethodInvocation到threadlocal里面,以便后续可以取出来,并继续调用CglibMethodInvocation的proceed方法
- 当前长度
len - 1= 4 != 0,index++,index = 1
执行异常通知的invoke方法 - 异常通知的invoke方法先执行
mi.proceed
,执行过程中有异常,则执行异常通知 - 当前长度
len -1 = 4 != 1,index++,index = 2
执行返回通知的invoke方法 - 异常通知的invoke方法先执行
mi.proceed
,再执行返回通知 - 当前长度
len -1 = 4 != 2,index++,index = 3
执行后置通知的invoke方法 - 后置通知的invoke方法先执行
mi.proceed
,再执行后置通知 - 当前长度
len -1 = 4 != 3,index++,index = 4
执行前置通知的invoke方法 - 前置通知的invoke方法先执行前置通知输出,再执行
mi.proceed
- 当前长度
len -1 = 4 != 4,
执行目标方法 - 后置通知执行完了
mi.proceed
,继续执行后置通知 - 返回通知执行完了
mi.proceed
,继续执行返回通知 - 异常通知执行完了
mi.proceed
,如果存在异常继续执行异常通知
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
ExposeInvocationInterceptor的invoke方法
public Object invoke(MethodInvocation mi) throws Throwable {
MethodInvocation oldInvocation = invocation.get();
invocation.set(mi);
try {
return mi.proceed();
}
finally {
invocation.set(oldInvocation);
}
}
MethodBeforeAdviceInterceptor的invoke方法
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
AspectJAfterAdvice的invoke方法
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
AfterReturningAdviceInterceptor的invoke方法
public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
AspectJAfterThrowingAdvice的invoke方法
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (Throwable ex) {
if (shouldInvokeOnThrowing(ex)) {
invokeAdviceMethod(getJoinPointMatch(), null, ex);
}
throw ex;
}
}
6.@Transactional
- NEVER
如果laoda
有@Transactional
,那么直接抛出不允许laoda
方法有@Transactional
注解的异常。如果如果laoda
没有@Transactional
,则以非事务方式执行。
使用propagation = Propagation.NEVER
的意义在于如果不标准@Transaction
会将xiaodi
当作laoda
的函数中的一部分,整体放入一个事务中执行。
@Transactional(propagation = Propagation.REQUIRED)
public void laoda(Money from, Money to, int count){
from.sub(count);
try{
test.xiaodi(to,count);
}catch (Exception e){
e.printStackTrace();
}
int x = 10;
if(x == 10)
throw new RuntimeException("老大异常!");
}
@Transactional(propagation = Propagation.NEVER)
public void xiaodi(Money to,int count){
to.add(count);
int x = 10;
if(x == 10)
throw new RuntimeException("小弟异常!");
}
2.NOT_SUPPORTED
laoda
没有事务就以非事务执行,有就先将laoda
的事务挂起,自己以非事务执行,执行结束后回去执行laoda
的事务。下面的例子中,由于xiaodi
没有事务,因此转入成功,由于laoda
有事务,因此回滚。
@Transactional(propagation = Propagation.REQUIRED)
public void laoda(Money from, Money to, int count){
from.sub(count);
try{
test.xiaodi(to,count);
}catch (Exception e){
e.printStackTrace();
}
int x = 10;
if(x == 10)
throw new RuntimeException("老大异常!");
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void xiaodi(Money to,int count){
to.add(count);
int x = 10;
if(x == 10)
throw new RuntimeException("小弟异常!");
}
3.SUPPORTS
laoda
没有事务就以非事务执行,laoda
有事务就加入。因此下面代码由于xiaodi
有异常造成laoda
和xiaodi
都回滚。
@Transactional(propagation = Propagation.REQUIRED)
public void laoda(Money from, Money to, int count){
from.sub(count);
try{
test.xiaodi(to,count);
}catch (Exception e){
e.printStackTrace();
}
int x = 9;
if(x == 10)
throw new RuntimeException("老大异常!");
}
@Transactional(propagation = Propagation.SUPPORTES)
public void xiaodi(Money to,int count){
to.add(count);
int x = 10;
if(x == 10)
throw new RuntimeException("小弟异常!");
}
4.REQUIRES_NEW
laoda
没有新事务,则新建一个事务。laoda
有事务,则xiaodi
新建一个事务,将laoda
的事务挂起,自己执行完后laoda
再执行。这种情况是两个事务,回滚与否互不影响。
@Transactional(propagation = Propagation.REQUIRED)
public void laoda(Money from, Money to, int count){
from.sub(count);
try{
test.xiaodi(to,count);
}catch (Exception e){
e.printStackTrace();
}
int x = 9;
if(x == 10)
throw new RuntimeException("老大异常!");
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void xiaodi(Money to,int count){
to.add(count);
int x = 10;
if(x == 10)
throw new RuntimeException("小弟异常!");
}
5.NESTED
如果laoda
没有事务,则新建事务。如果laoda
有事务,则在laoda
嵌套当前事务。即如果外层事务laoda
回滚,那么内层事务xiaodi
也会回滚,但如果内层事务xiaodi
回滚,外层事务不会回滚。
@Transactional(propagation = Propagation.REQUIRED)
public void laoda(Money from, Money to, int count){
from.sub(count);
try{
test.xiaodi(to,count);
}catch (Exception e){
e.printStackTrace();
}
int x = 9;
if(x == 10)
throw new RuntimeException("老大异常!");
}
@Transactional(propagation = Propagation.NESTED)
public void xiaodi(Money to,int count){
to.add(count);
int x = 10;
if(x == 10)
throw new RuntimeException("小弟异常!");
}
6.REQUIRED
没有就新建,有就加入(即在同一个事务中)
@Transactional(propagation = Propagation.REQUIRED)
public void laoda(Money from, Money to, int count){
from.sub(count);
try{
test.xiaodi(to,count);
}catch (Exception e){
e.printStackTrace();
}
int x = 9;
if(x == 10)
throw new RuntimeException("老大异常!");
}
@Transactional(propagation = Propagation.REQUIRED)
public void xiaodi(Money to,int count){
to.add(count);
int x = 10;
if(x == 10)
throw new RuntimeException("小弟异常!");
}
7.MANDATORY
@Transactional(propagation = Propagation.REQUIRED)
public void laoda(Money from, Money to, int count){
from.sub(count);
try{
test.xiaodi(to,count);
}catch (Exception e){
e.printStackTrace();
}
int x = 9;
if(x == 10)
throw new RuntimeException("老大异常!");
}
@Transactional(propagation = Propagation.MANDATORY)
public void xiaodi(Money to,int count){
to.add(count);
int x = 10;
if(x == 10)
throw new RuntimeException("小弟异常!");
}