使用DefaultLockRepository升级到Spring Boot 3后出现错误

问题描述 投票:0回答:1

我目前正在将遗留代码从 spring-boot v2.7.1 升级到 spring-boot v3.1.0。 不幸的是,我从未直接接触过 JDBC。我主要使用JPA。

我得到的错误如下:

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

JDBC 的使用方式如下:

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

现在,DefaultLockRepository 类由于升级而发生了变化:

  • 不再用
    @Repository
    注释
  • 添加了以下字段,必须为每个设置器设置:
    • 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?通常,Spring 处理 Bean 的注入,因此处理 ApplicationContext 本身? 我对这些变化感到相当困惑。测试进行得很艰难。

java spring spring-boot jdbc
1个回答
0
投票

发生这种情况可能是因为有代码实例化了一些依赖于 JdbcLockRegistry 的单例 bean 并开始使用它。 问题是

org.springframework.integration.jdbc.lock.DefaultLockRepository#afterSingletonsInstantiated
在单例预实例化阶段结束时调用,并保证所有常规单例 bean 都已创建。

例如,下面的代码将导致类似的错误:

@Bean
public LockRegistryLeaderInitiator leaderInitiator(LockRegistry lockRegistry) {
  var lockRegistryLeaderInitiator = new LockRegistryLeaderInitiator(lockRegistry);
  lockRegistryLeaderInitiator.start();
  return lockRegistryLeaderInitiator;
}

要修复它,我们需要将

lockRegistryLeaderInitiator.start()
移到外面。一种可能的解决方案是在 Spring 上下文初始化后运行它:

@EventListener
public void onApplicationEvent(ContextRefreshedEvent event) {
    lockRegistryLeaderInitiator.start();
}
© www.soinside.com 2019 - 2024. All rights reserved.