个人Spring问题总结
1. 循环依赖
先看看 Bean 生命周期
A 依赖 B,B 依赖 A,就成了循环依赖。
1
2
3
4
5
6
7
8
9
10
11
12
13
@Service
public class CircularServiceA {
@Autowired
private CircularServiceB circularServiceB;
}
@Service
public class CircularServiceB {
@Autowired
private CircularServiceA circularServiceA;
}看看 Spring 的三级缓存
只有 A 时,创建流程为
引入 B,变为
B populatedBean 查找依赖项 A 的时候,从一级缓存中虽然未获取到 A,但是发现 A 在创建中。
此时,从三级缓存中获取 A 的 singletonFactory 调用工厂方法(singletonObject = singletonFactory.getObject();),创建 getEarlyBeanReference A 的早期引用并返回。
B 引用到 A ,B 就可以初始化完毕,然后 A 同样也可以初始化完毕了。
假如 A 需要 Aop 代理呢?
此时对于单独的 A 来说,其初始化过程变成了
即通过后置处理器返回 A 的代理对象。
假如此时 A、B 循环依赖
B 从三级缓存拿 A 的时候,singletonFactory.getObject()返回的其实是 A 的代理对象,然会将这个代理对象放到二级缓存里面去,到 A 自身进行创建的时候,他也会执行后置处理器,但此时会判断代理缓存(二级缓存)里面是否有 A,有的话执行拿到缓存里面的代理 A,所以不会再生成一次 A 的代理对象。
可以看到,循环依赖下,有没有代理情况下的区别就在:
singletonObject = singletonFactory.getObject();
在循环依赖发生的情况下 B 中的 A 赋值时:
无代理:getObject 直接返回原来的 Bean
有代理:getObject 返回的是代理对象
假如去掉三级缓存
去掉三级缓存之后,Bean 直接创建 earlySingletonObjects, 看着好像也可以。
如果有代理的时候,在 earlySingletonObjects 直接放代理对象就行了。
但是会导致一个问题:在实例化阶段就得执行后置处理器,判断有 AnnotationAwareAspectJAutoProxyCreator 并创建代理对象。
这么一想,是不是会对 Bean 的生命周期有影响。
同样,先创建 singletonFactory 的好处就是:在真正需要实例化的时候,再使用 singletonFactory.getObject() 获取 Bean 或者 Bean 的代理。相当于是延迟实例化。假如去掉二级缓存
如果去掉了二级缓存,则需要直接在 singletonFactory.getObject() 阶段初始化完毕,并放到一级缓存中。假如 A、B、C 循环依赖,B、C 调用 singletonFactory.getObject() 返回的代理对象 A 是不同的,就会导致 B 和 C 依赖了不同的 A。
2. 事务机制
Spring 框架中,事务管理相关最重要的 3 个接口如下:
- PlatformTransactionManager:(平台)事务管理器,Spring 事务策略的核心。
- TransactionDefinition:事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)。
- TransactionStatus:事务运行状态。
我们可以把 PlatformTransactionManager接口可以被看作是事务上层的管理者,而 TransactionDefinition和 TransactionStatus这两个接口可以看作是事务的描述。
PlatformTransactionManager 会根据 TransactionDefinition 的定义比如事务超时时间、隔离级别、传播行为等来进行事务管理 ,而 TransactionStatus接口则提供了一些方法来获取事务相应的状态比如是否新事务、是否可以回滚等等。
各个平台想实现自己的事务机制,则需要实现PlatformTransactionManager这个接口
1
2
3
4
5
6
7
8
9
10
11
12
package org.springframework.transaction;
import org.springframework.lang.Nullable;
public interface PlatformTransactionManager {
//获得事务
TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
//提交事务
void commit(TransactionStatus var1) throws TransactionException;
//回滚事务
void rollback(TransactionStatus var1) throws TransactionException;
}事务属性包含了 5 个方面:
- 隔离级别
- 传播行为
- 回滚规则
- 是否只读
- 事务超时
TransactionDefinition 接口中定义了 5 个方法以及一些表示事务属性的常量比如隔离级别、传播行为等等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package org.springframework.transaction;
import org.springframework.lang.Nullable;
public interface TransactionDefinition {
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
int TIMEOUT_DEFAULT = -1;
// 返回事务的传播行为,默认值为 REQUIRED。
int getPropagationBehavior();
//返回事务的隔离级别,默认值是 DEFAULT
int getIsolationLevel();
// 返回事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
int getTimeout();
// 返回是否为只读事务,默认值为 false
boolean isReadOnly();
@Nullable
String getName();
}TransactionStatus接口用来记录事务的状态 该接口定义了一组方法,用来获取或判断事务的相应状态信息。PlatformTransactionManager.getTransaction(…)方法返回一个 TransactionStatus 对象。
1
2
3
4
5
6
7
public interface TransactionStatus{
boolean isNewTransaction(); // 是否是新的事务
boolean hasSavepoint(); // 是否有恢复点
void setRollbackOnly(); // 设置为只回滚
boolean isRollbackOnly(); // 是否为只回滚
boolean isCompleted; // 是否已完成
}事务传播行为
- TransactionDefinition.PROPAGATION_REQUIRED(默认)
- 如果外部方法没有开启事务的话,
Propagation.REQUIRED修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。 - 如果外部方法开启事务并且被
Propagation.REQUIRED的话,所有Propagation.REQUIRED修饰的内部方法和外部方法均属于同一事务 ,只要一个方法回滚,整个事务均回滚。
- 如果外部方法没有开启事务的话,
- TransactionDefinition.PROPAGATION_REQUIRES_NEW
创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。
例如 A 调用 B,B 加了这个属性,那么 A 回滚不会影响 B,B 也不会影响 A,但是假如 B 抛出了异常并且没有捕获,那还是会导致 A 回滚。
- TransactionDefinition.PROPAGATION_NESTED
如果当前存在事务,就在嵌套事务内执行;如果当前没有事务,就执行与TransactionDefinition.PROPAGATION_REQUIRED类似的操作。- 在外部方法开启事务的情况下,在内部开启一个新的事务,作为嵌套事务存在。
- 如果外部方法无事务,则单独开启一个事务,与 PROPAGATION_REQUIRED 类似。
还是上面那个例子,B 回滚,A 不会回滚,A 回滚,B 也会回滚
- TransactionDefinition.PROPAGATION_MANDATORY
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性) - TransactionDefinition.PROPAGATION_SUPPORTS
如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。 - TransactionDefinition.PROPAGATION_NOT_SUPPORTED
以非事务方式运行,如果当前存在事务,则把当前事务挂起。 - TransactionDefinition.PROPAGATION_NEVER
以非事务方式运行,如果当前存在事务,则抛出异常。
@Transactional
如果一个类或者一个类中的 public 方法上被标注@Transactional 注解的话,Spring 容器就会在启动的时候为其创建一个代理类,在调用被@Transactional 注解的 public 方法的时候,实际调用的是,TransactionInterceptor 类中的 invoke()方法。这个方法的作用就是在目标方法之前开启事务,方法执行过程中如果遇到异常的时候回滚事务,方法调用完成之后提交事务。
TransactionInterceptor 类中的 invoke()方法内部实际调用的TransactionAspectSupport 类的 invokeWithinTransaction()方法。
事务失效
当一个方法被标记了@Transactional 注解的时候,Spring 事务管理器只会在被其他类方法调用的时候生效,而不会在一个类中方法调用生效。这是因为 Spring AOP 工作原理决定的。因为 Spring AOP 使用动态代理来实现事务的管理,它会在运行的时候为带有 @Transactional 注解的方法生成代理对象,并在方法调用的前后应用事物逻辑。如果该方法被其他类调用我们的代理对象就会拦截方法调用并处理事务。但是在一个类中的其他方法内部调用的时候,我们代理对象就无法拦截到这个内部调用,因此事务也就失效了。


