Spring Boot手动Autowiring失效原因:多数据库延迟连接问题
现有配置
我正在开发一个Spring Boot应用,已配置多数据库(Oracle、Teradata等),通过@Entity、Repository类及带有@EnableJpaRepositories注解的配置类完成初始化,示例配置类如下:
@Configuration @EnableJpaRepositories( basePackages = "com.baeldung.multipledb.dao.product", entityManagerFactoryRef = "productEntityManager", transactionManagerRef = "productTransactionManager" ) public class PersistenceProductConfiguration { @Bean public LocalContainerEntityManagerFactoryBean productEntityManager() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(productDataSource()); em.setPackagesToScan( new String[] { "com.baeldung.multipledb.model.product" }); em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); HashMap<String, Object> properties = new HashMap<>(); properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); properties.put("hibernate.dialect", env.getProperty("hibernate.dialect")); em.setJpaPropertyMap(properties); return em; } @Bean @ConfigurationProperties(prefix = "datasource.dbprops") public DataSource productDataSource() { return DataSourceBuilder.create().build(); } @Bean public PlatformTransactionManager productTransactionManager() { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory( productEntityManager().getObject()); return transactionManager; } }
需求
现有配置会在启动时连接数据库,我希望在启动后,通过Controller接收用户请求时再建立数据库连接,示例Controller逻辑如下:
/*LOGIC BELOW IS ALTERNATIVE TO CLASS PersistanceProductConfiguration ABOVE*/ @RestController public class RestController { @GetMapping(value = "/student/{studentId}") public Student getTestData(@PathVariable Integer studentId) { ensureConnectionExists(); return ResponseEntity.ok(""); } public void ensureConnectionExists() { if(!connectionExists){ //================ // ESTABLISH THE CONNECTION HERE //================= } } }
尝试的实现
@Autowired private ApplicationContext applicationContext; public void ensureConnectionExists() { if(!connectionExists){ DataSource datasource = DataSourceBuilder.create() .username("jdbc.user") .password("jdbc.pass") .url("jdbc.url") .driverClassName("jdbc.driver") .build(); LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(productDataSource()); em.setPackagesToScan( new String[] { "com.baeldung.multipledb.model.product" }); em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); HashMap<String, Object> properties = new HashMap<>(); properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); properties.put("hibernate.dialect", env.getProperty("hibernate.dialect")); em.setJpaPropertyMap(properties); JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory( productEntityManager().getObject()); AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory(); factory.autowireBean(transactionManager); factory.initializeBean(transactionManager, "JpaTransactionManager"); } }
遇到的问题
上述手动Autowiring的代码未生效,断点能命中,但Bean未完成自动装配,也未成功建立数据库连接。遗漏了什么?
你当前的实现存在几个关键问题,导致Bean无法正确初始化和数据库连接无法建立:
EntityManagerFactory未完成初始化
LocalContainerEntityManagerFactoryBean是FactoryBean,直接创建实例后必须调用afterPropertiesSet()触发初始化流程,否则无法生成可用的EntityManagerFactory实例,后续事务管理器拿到的是无效对象。DataSource引用错误
你手动构建了datasource,但后续em.setDataSource(productDataSource())仍调用原配置类的Bean方法,会重新创建一个新的DataSource,而非你自定义的实例。未将Bean注册到Spring容器
手动创建的EntityManagerFactory和TransactionManager没有注册到Spring容器,导致Repository无法关联到这些Bean,@EnableJpaRepositories也无法识别新实例。Environment未注入
代码中直接使用env.getProperty(),但Environment未注入到Controller,会触发空指针异常,无法获取Hibernate配置参数。
修正后的实现步骤
步骤1:完善Controller依赖注入
@RestController public class RestController { @Autowired private ApplicationContext applicationContext; @Autowired private Environment env; private boolean connectionExists = false; // 缓存已初始化的Bean实例 private LocalContainerEntityManagerFactoryBean productEntityManager; private JpaTransactionManager productTransactionManager; @GetMapping(value = "/student/{studentId}") public ResponseEntity<String> getTestData(@PathVariable Integer studentId) { ensureConnectionExists(); return ResponseEntity.ok(""); } }
步骤2:修正ensureConnectionExists方法
// 添加同步锁避免多请求重复初始化 public synchronized void ensureConnectionExists() { if(!connectionExists){ // 1. 构建自定义DataSource DataSource datasource = DataSourceBuilder.create() .username(env.getProperty("jdbc.user")) .password(env.getProperty("jdbc.pass")) .url(env.getProperty("jdbc.url")) .driverClassName(env.getProperty("jdbc.driver")) .build(); // 2. 构建并初始化EntityManagerFactoryBean productEntityManager = new LocalContainerEntityManagerFactoryBean(); productEntityManager.setDataSource(datasource); productEntityManager.setPackagesToScan("com.baeldung.multipledb.model.product"); productEntityManager.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); HashMap<String, Object> properties = new HashMap<>(); properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); properties.put("hibernate.dialect", env.getProperty("hibernate.dialect")); // 添加延迟连接配置,避免提前建立连接 properties.put("hibernate.connection.provider_disables_autocommit", true); properties.put("hibernate.hikari.auto-commit", false); productEntityManager.setJpaPropertyMap(properties); // 触发FactoryBean初始化 productEntityManager.afterPropertiesSet(); // 3. 构建并初始化事务管理器 productTransactionManager = new JpaTransactionManager(); productTransactionManager.setEntityManagerFactory(productEntityManager.getObject()); AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory(); factory.autowireBean(productTransactionManager); factory.initializeBean(productTransactionManager, "productTransactionManager"); // 4. 将Bean注册到Spring容器,供Repository关联使用 factory.registerSingleton("productEntityManager", productEntityManager.getObject()); factory.registerSingleton("productTransactionManager", productTransactionManager); connectionExists = true; } }
步骤3:调整原配置类为懒加载
原配置类需要设置懒加载,避免启动时自动初始化:
@Configuration @Lazy @EnableJpaRepositories( basePackages = "com.baeldung.multipledb.dao.product", entityManagerFactoryRef = "productEntityManager", transactionManagerRef = "productTransactionManager" ) public class PersistenceProductConfiguration { @Bean @Lazy public LocalContainerEntityManagerFactoryBean productEntityManager() { // 原代码保持不变 } @Bean @Lazy @ConfigurationProperties(prefix = "datasource.dbprops") public DataSource productDataSource() { // 原代码保持不变 } @Bean @Lazy public PlatformTransactionManager productTransactionManager() { // 原代码保持不变 } }
额外注意事项
- 线程安全:必须给
ensureConnectionExists()添加synchronized关键字,防止多请求同时触发重复初始化。 - 连接池配置:确保DataSource连接池设置了合理的超时时间和最小连接数,避免连接闲置失效。
- Repository懒加载:Repository接口需配合
@Lazy注解使用,避免启动时触发数据库连接校验。
内容的提问来源于stack exchange,提问作者10101010

