

























Spring的事务传播机制,主要是用于控制多个事务方法相互调用时的事务行为。
在后端复杂的业务场景中,多个事务之间的调用可能会导致事务的不一致,例如:数据重复提交,数据丢失等问题,使用事务传播机制可以避免这些问题的发生,从而保证事务的一致性和数据的完整性。
Spring 通过 @Transactional 注解的 propagation 属性来设置传播级别
REQUIRES_NEW 方法之前的状态)。REQUIRES_NEW 区别:NESTED 是“子事务”,依赖于外部事务;REQUIRES_NEW 是“独立事务”,与外部事务并列。面试官问:一个长的事务方法a,在读写分离的情况下,里面既有读库操作,也有写库操作,再调用个读库方法b,方法b该用什么传播机制呢?
答:这种情况,读方法如果是最后一步,直接not_supported就行了,避免读报错导致数据回滚。如果是中间步骤,最好还是要required,因为异常失败需要回滚一下。
例如:A B C三个操作,C就是最后一步,B就是中间步骤如果一个读操作在中间(如B操作)失败了,那么就需要让A做回滚,因为C还没执行,所以A必须回滚才能保证一致性。
首先,容易造成事务失效的方式是通过@Transactional注解方式的声明式事务。
@Transactional是基于Spring的AOP来实现的,而AOP机制又是基于动态代理实现的,如果代理失效那么事务也就失效了。
@Service
public class UserService {
@Transactional
private void updateUserData() { // private方法
// ...
}
}
由于代理机制会为 public 方法创建拦截器,事务可以正常生效。而非public得方法,JDK代理是不会创建拦截器的,虽然CGLIB可能支持,但行为不一致,不保证生效。
因此在使用时还是强烈建议放到public方法上。
@Service
public class UserService {
public void businessMethod() {
// 1. 执行一些业务逻辑
// 2. 调用本类的事务方法
this.transactionalMethod(); // 自调用,事务失效
}
@Transactional
public void transactionalMethod() {
// 数据库操作
}
}
public class OuterClass{
private class InnerClass {
@Transactional
public void doSomething() {
System.out.println("Doing something in inner class...");
}
}
public void invokeInnerClassMethod() {
InnerClass innerclass = new InnerClass();
innerclass.doSomething();//调用内部类方法,事务失效
}
}
在对象内部调用其他方法,就会用对象直接调用了,而不是用代理对象,因此代理会失效。
由于static方法是属于类级别的对象,所以代理对象无法代理,因此AOP也是无效的,因此@Transactional修饰这种方法时,事务也是会失效的。
final方法,是固定形式,而AOP的代理是通过子类或实现接口来实现的,final方法无法被子类覆盖,也无法通过实现类覆盖。因此如果将@Transactional修饰这种方法时,事务也是会失效的。
没有使用Spring管理bean,因此也就不会存在使用AOP来创建代理对象来保证事务。
public class UserService{
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void notSupportedMethod() {
// 此方法不会运行在事务中
}
}
不同的Propagation属性决定了事务的创建和参与方式。例如:Propagation.NOT_SUPPORTED或Propagation.NEVER会挂起或拒绝当前事务。
public class UserService{
@Transactional(rollbackFor = FileNotFoundException.class)
public void someMethod(){
// 抛出 IOException
throw new IOException("文件读取失败");
// 事务不会回滚
}
}
rollbackFor配置的异常类型需和方法抛出的异常一致,事务才会进行回滚。改成使用@Transactional(rollbackFor = Exception.class)或@Transactional(rollbackFor = IOException.class)即可。
有时候,在写代码的时候,由于手快也没有注意@Transactional注解的引用来源,直接就用了,等出现问题的时候,排查了很久发现写的都没问题,但是还是不生效,然后找别人来帮你看,他上来就看了一下你用的@Transactional,发现并不是Spring中的,而是其他什么地方的,比如javax.transaction.Transactional ,这样也会导致事务失效。
@EnableTransactionManagement。<tx:annotation-driven />。public class UserService{
@Transactional(rollbackFor = Exception.class)
public void doSomething() {
try {
// doSomething...
}catch (Exception e){
System.out.println("Exception:"+e);
}
}
}
异常被捕获后,不会抛出,也就走不到rollbackFor这样也就不会进行回滚了。
@Transactional的事务管理使用的是ThreadLocal来存储事务上下文,ThreadLocal存储的变量是线程隔离的,因此每个线程都有自己的事务上下文副本。所以Spring的声明式事务在多线程环境下会失效的风险。
如果使用的数据库表引擎不支持事务(如MySQL的MyISAM引擎),那么即使Spring配置了事务,也无法回滚。
解决方案:确保数据库表使用支持事务的引擎,如MySQL的InnoDB。
从名字上看BeanFactory和FactoryBean看着很相似,但是实际上它俩没什么关系,是完全不相关的两个接口。
BeanFactory就是Bean的工厂,是整个Spring的IOC其中的一部分,管理Bean的创建和生命周期。
BeanFactory提供了一系列的方法,可以让我们获取到具体的Bean实例。
你可能没有直接用过BeanFactory,但是你肯定间接的使用或者看到过。
applicationContent.getBean(type);
applicationContent.getBean(name);
这些代码通常用在一些测试用例,或者需要手动从IOC容器中获取指定的Bean的时候使用。
通过上面的代码使用示例也说明了,BeanFactory是IOC容器的一个接口,用来获取Bean以及管理Bean的依赖注入和生命周期。
FactoryBean本质是一个特殊的Bean,用于定义一个工厂Bean,可以用来生成某些特定的Bean。
当项目中定义了某一个Bean的时候,如果这个Bean实现了FactoryBean这个接口,那么使用这个Bean的时候,Spring的IOC容器不会直接返回这个Bean实例,而是返回FactoryBean的getObject()方法返回的实体对象。(获取FactoryBean本身:需要在ID前加&符号(如&myFactoryBean))
// 定义一个FactoryBean
public class MyFactoryBean implements FactoryBean<MyObject> {
public MyObject getObject() {
return new MyObject(); // 返回实际对象
}
public Class<?> getObjectType() {
return MyObject.class;
}
public boolean isSingleton() {
return true;
}
}
// 使用
BeanFactory beanFactory = new DefaultListableBeanFactory();
// 注册MyFactoryBean
beanFactory.registerSingleton("myFactoryBean", new MyFactoryBean());
// 获取FactoryBean创建的对象
MyObject obj = (MyObject) beanFactory.getBean("myFactoryBean"); // 返回MyObject实例
// 获取FactoryBean本身
FactoryBean<MyObject> factoryBean = (FactoryBean<MyObject>) beanFactory.getBean("&myFactoryBean");
FactoryBean常用于创建需要特殊初始化逻辑的Bean,如Spring AOP代理、JNDI数据源,kafka,Dubbo中都用FactoryBean与Spring做集成。
| 特性 | BeanFactory | FactoryBean |
|---|---|---|
| 本质 | Spring容器核心接口 | 一个特殊Bean |
| 名称含义 | Bean的工厂 | 工厂类型的Bean |
| 获取对象 | 获取Bean实例 | 获取getObject()返回的对象 |
| 是否为容器 | 是 | 否(它是容器中的一个Bean) |
| 主要用途 | 管理所有Bean | 自定义特定Bean的创建逻辑 |
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。