SpringBoot获取由于@Transactional注解获取bean失败

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 {
}

@Transactional方法时:

img


@Transactional注释掉:

img


img


这样就可以正常调用
这个问题好像和注入方式无关,我使用@Resource注解注入再这样测试,结果是一样的

可以看下源码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

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 这篇博客也许可以解决你的问题👉 :Spring Boot中@Transactional 注解
  • 除此之外, 这篇博客: 【Java 基础】SpringBoot 中 @Transactional 注解的使用与实践中的 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
    隔离类型脏读不可重复读幻读描述
    ISOLATION_READ_UNCOMMITTEDRead uncommitted读未提交,就是一个事务可以读取另一个未提交事务的数据。
    ISOLATION_READ_COMMITTED×Read committed读提交,就是一个事务要等另一个事务提交后才能读取数据。解决了脏读,但不能解决不可重复读和幻读。
    ISOLATION_REPEATABLE_READ××Repeatable read重复读,就是在开始读取数据(事务开启)时,不再允许修改操作解决了不可重复读,但不能解决幻读。
    ISOLATION_SERIALIZABLE×××Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。

    脏读:指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据

    可重复读:指在事务1内,读取了一个数据,事务1还没有结束时,事务2也访问了这个数据,修改了这个数据,并提交。紧接着,事务1又读这个数据。由于事务2的修改,那么事务1两次读到的数据可能是不一样的,因此称为是不可重复读。

    幻读:指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。InnoDB 存储引擎通过多版本并发控制(MVCC)解决了幻读的问题。幻读和不可重复读的区别是,前者是一个范围,后者是本身


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^