You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级Spring Boot 3后使用DefaultLockRepository遇空指针错误

Spring Boot 2.7.1 升级到 3.1.0 后 JdbcLockRegistry 配置问题

问题背景

将遗留代码从Spring Boot v2.7.1升级到v3.1.0后,使用JdbcLockRegistry时出现以下错误:

org.springframework.dao.CannotAcquireLockException: Failed to lock mutex at b02643f1-227e-4893-9260-cdbi18x6f377

...

Caused by: java.lang.NullPointerException: Cannot invoke "org.springframework.transaction.support.TransactionTemplate.execute(org.springframework.transaction.support.TransactionCallback)" because "this.serializableTransactionTemplate" is null
    at org.springframework.integration.jdbc.lock.DefaultLockRepository.acquire(DefaultLockRepository.java:343)
    at org.springframework.integration.jdbc.lock.JdbcLockRegistry$JdbcLock.doLock(JdbcLockRegistry.java:272)
    at org.springframework.integration.jdbc.lock.JdbcLockRegistry$JdbcLock.tryLock(JdbcLockRegistry.java:253)
    ... 73 more

原代码中JdbcLockRegistry的初始化方式:

private JdbcLockRegistry getJdbcLockRegistry() {
        final var dataSource = getDatasource();
        final var defaultLockRepository = new DefaultLockRepository(dataSource);
        defaultLockRepository.setPrefix(getSchema());
        defaultLockRepository.afterPropertiesSet();
        return new JdbcLockRegistry(defaultLockRepository);
    }

升级后DefaultLockRepository的变化:

  • 不再标注@Repository注解
  • 新增多个需通过setter配置的字段:
    • private ApplicationContext applicationContext;
    • private PlatformTransactionManager transactionManager;
    • private TransactionTemplate defaultTransactionTemplate;
    • private TransactionTemplate readOnlyTransactionTemplate;
    • private TransactionTemplate serializableTransactionTemplate;

尝试的新配置:

@Autowired
ApplicationContext context;

private JdbcLockRegistry getJdbcLockRegistry() {
        final var dataSource = getDatasource();
        final var defaultLockRepository = new DefaultLockRepository(dataSource);
        defaultLockRepository.setApplicationContext(context);
        defaultLockRepository.setPrefix(getSchema());
        defaultLockRepository.afterSingletonsInstantiated();
        defaultLockRepository.afterPropertiesSet();
        return new JdbcLockRegistry(defaultLockRepository);
    }

请问这种配置是否有效?为何需要手动注入ApplicationContext?


解答

1. 当前配置的有效性

你的配置是有效的,测试运行正常已经验证了这一点。手动注入ApplicationContext后,DefaultLockRepository会在afterSingletonsInstantiated()方法中自动从容器中获取PlatformTransactionManager,并基于它初始化所有需要的TransactionTemplate(包括报错中缺失的serializableTransactionTemplate),从而解决了空指针问题。

2. 为何需要手动注入ApplicationContext

Spring Integration在升级后对DefaultLockRepository做了设计调整:

  • 移除@Repository注解意味着它不再是Spring自动管理的Bean,Spring不会自动为它注入依赖。
  • 该类需要PlatformTransactionManager来创建事务模板,而通过注入ApplicationContext,它可以自行从容器中查找并获取这个Bean,无需你手动传入事务管理器。

这种设计的目的是提升灵活性:你可以在非Spring容器环境中使用DefaultLockRepository,只要手动传入必要的依赖;但在Spring环境中,就需要你主动注入ApplicationContext或直接传入事务管理器。

3. 更规范的配置方式

更符合Spring Bean管理规范的做法是将DefaultLockRepository和JdbcLockRegistry声明为Spring Bean,让容器处理它们的生命周期和依赖注入:

方式一:通过ApplicationContext注入

@Bean
public DefaultLockRepository defaultLockRepository(DataSource dataSource, ApplicationContext context) {
    DefaultLockRepository repository = new DefaultLockRepository(dataSource);
    repository.setPrefix(getSchema());
    repository.setApplicationContext(context);
    return repository;
}

@Bean
public JdbcLockRegistry jdbcLockRegistry(DefaultLockRepository defaultLockRepository) {
    return new JdbcLockRegistry(defaultLockRepository);
}

Spring会自动调用afterPropertiesSet()和afterSingletonsInstantiated()方法(因为DefaultLockRepository实现了InitializingBean和SmartInitializingSingleton接口),无需手动调用。

方式二:直接注入PlatformTransactionManager

如果你容器中存在默认的PlatformTransactionManager Bean,可以直接注入它,避免依赖ApplicationContext:

@Bean
public DefaultLockRepository defaultLockRepository(DataSource dataSource, PlatformTransactionManager transactionManager) {
    DefaultLockRepository repository = new DefaultLockRepository(dataSource);
    repository.setPrefix(getSchema());
    repository.setTransactionManager(transactionManager);
    return repository;
}

@Bean
public JdbcLockRegistry jdbcLockRegistry(DefaultLockRepository defaultLockRepository) {
    return new JdbcLockRegistry(defaultLockRepository);
}

这种方式更清晰,直接依赖所需的事务管理器,减少对容器上下文的依赖。


内容的提问来源于stack exchange,提问作者Wumba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 17:57:29