SpringBoot工程中,如果使用@Transactional标注一个方法,通过ApplicationContext就会获取不到bean,以下是代码和执行结果:
@RestController
@RequestMapping("/pii")
@DependsOn("springContextUtil")
public class PiiMiddleController {
@Resource
private ClAcctSequenceCreateService clAcctSequenceCreateService;
private TestService testService = (TestService)SpringContextUtil.getBean("testService");
@GetMapping("/test")
private String test() {
return testService.toString();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void test2() {
System.out.println(testService.toString());
}
}
@Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return context;
}
public static Object getBean(String name) {
return context.getBean(name);
}
}
@Service
public class TestService {
}
可以看下源码HandlerMethod中的第377行(spring 5.3.29),意思就在匹配到HandlerMethod方法后再通过beanname去IOC获取bean,此时获取到的是代理对象,所以得到注入字段为null。因为test方法是private修饰,所以就不会被cglib代理,也就是说在调用的时候不走方法拦截器,直接走的是代理对象,所以使用的注入对象为null,如果走方法拦截器即DynamicAdvisedInterceptor,则调用的是实际封装在TargetSource中的目标对象所以注入的字段就不为null
public HandlerMethod createWithResolvedBean() {
Object handler = this.bean;
if (this.bean instanceof String) {
Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
String beanName = (String) this.bean;
handler = this.beanFactory.getBean(beanName);
}
return new HandlerMethod(this, handler);
}
事物注解 要放置在 服务类的方法上,并且服务类被注册为spring 的bean
使用@Transactional注解时,Spring会通过AOP技术为该方法创建一个代理对象。在运行时,实际调用的是这个代理对象的方法,而不是原始对象的方法
如果获取不到的话,试试ApplicationContext获取bean
隔离类型 | 脏读 | 不可重复读 | 幻读 | 描述 |
---|---|---|---|---|
ISOLATION_READ_UNCOMMITTED | √ | √ | √ | Read uncommitted读未提交,就是一个事务可以读取另一个未提交事务的数据。 |
ISOLATION_READ_COMMITTED | × | √ | √ | Read committed读提交,就是一个事务要等另一个事务提交后才能读取数据。解决了脏读,但不能解决不可重复读和幻读。 |
ISOLATION_REPEATABLE_READ | × | × | √ | Repeatable read重复读,就是在开始读取数据(事务开启)时,不再允许修改操作解决了不可重复读,但不能解决幻读。 |
ISOLATION_SERIALIZABLE | × | × | × | Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。 |
脏读:指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据
可重复读:指在事务1内,读取了一个数据,事务1还没有结束时,事务2也访问了这个数据,修改了这个数据,并提交。紧接着,事务1又读这个数据。由于事务2的修改,那么事务1两次读到的数据可能是不一样的,因此称为是不可重复读。
幻读:指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。InnoDB 存储引擎通过多版本并发控制(MVCC)解决了幻读的问题。幻读和不可重复读的区别是,前者是一个范围,后者是本身