如何在Spring Boot中触发接口后再创建数据源而非启动时?
动态创建并缓存HikariDataSource实现方案(Spring Boot + JPA)
你的需求核心是延迟初始化数据源,不在启动阶段绑定配置,而是在首次请求时根据请求头动态获取数据库信息、创建数据源并缓存复用。以下是具体实现步骤:
1. 禁用Spring Boot自动数据源配置
首先阻止Spring Boot启动时强制加载数据源配置,在启动类上排除相关自动配置类:
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class }) public class DynamicDatasourceApplication { public static void main(String[] args) { SpringApplication.run(DynamicDatasourceApplication.class, args); } }
2. 实现数据源缓存管理器
创建单例组件,负责缓存已创建的数据源,确保同一请求标识(比如请求头中的X-DB-Key)复用同一个数据源:
@Component public class DataSourceCache { private final ConcurrentHashMap<String, HikariDataSource> dataSourceCache = new ConcurrentHashMap<>(); // 注入获取数据库配置的服务(需自行实现,根据请求头调用外部接口拿配置) private final DatabaseConfigService configService; public DataSourceCache(DatabaseConfigService configService) { this.configService = configService; } public HikariDataSource getDataSource(String dbKey) { // 不存在则创建并缓存,线程安全 return dataSourceCache.computeIfAbsent(dbKey, this::createDataSource); } private HikariDataSource createDataSource(String dbKey) { // 调用服务获取数据库配置 DatabaseConfig dbConfig = configService.fetchDbConfig(dbKey); HikariDataSource dataSource = new HikariDataSource(); dataSource.setJdbcUrl(dbConfig.getJdbcUrl()); dataSource.setUsername(dbConfig.getUsername()); dataSource.setPassword(dbConfig.getPassword()); dataSource.setDriverClassName("org.postgresql.Driver"); dataSource.setMaximumPoolSize(10); dataSource.setMinimumIdle(2); return dataSource; } // 应用关闭时清理数据源,避免资源泄漏 @PreDestroy public void cleanUp() { dataSourceCache.values().forEach(HikariDataSource::close); } }
3. 动态创建EntityManagerFactory(适配JPA)
因为Spring Data JPA默认依赖启动时的EntityManagerFactory,所以需要动态创建对应数据源的实例并缓存:
@Component public class EntityManagerFactoryProvider { private final ConcurrentHashMap<String, EntityManagerFactory> emfCache = new ConcurrentHashMap<>(); private final DataSourceCache dataSourceCache; private final EntityManagerFactoryBuilder emfBuilder; public EntityManagerFactoryProvider(DataSourceCache dataSourceCache, EntityManagerFactoryBuilder emfBuilder) { this.dataSourceCache = dataSourceCache; this.emfBuilder = emfBuilder; } public EntityManagerFactory getEntityManagerFactory(String dbKey) { return emfCache.computeIfAbsent(dbKey, key -> { HikariDataSource dataSource = dataSourceCache.getDataSource(key); return emfBuilder.dataSource(dataSource) .packages("com.yourproject.entity") // 替换为你的实体类包路径 .persistenceUnit(key) // 每个数据源对应独立的持久化单元 .build(); }); } @PreDestroy public void cleanUp() { emfCache.values().forEach(EntityManagerFactory::close); } }
4. 控制器中动态使用数据源
在接口方法中,通过请求头获取标识,拿到对应的EntityManagerFactory执行数据库操作:
@RestController @RequestMapping("/demo") public class DemoController { private final EntityManagerFactoryProvider emfProvider; public DemoController(EntityManagerFactoryProvider emfProvider) { this.emfProvider = emfProvider; } @GetMapping("/list") public List<DemoEntity> getDemoList(@RequestHeader("X-DB-Key") String dbKey) { EntityManagerFactory emf = emfProvider.getEntityManagerFactory(dbKey); // 每次请求创建EntityManager,用完自动关闭 try (EntityManager em = emf.createEntityManager()) { return em.createQuery("SELECT e FROM DemoEntity e", DemoEntity.class) .getResultList(); } } }
额外注意事项
- 事务支持:如果需要事务,可创建
PlatformTransactionManager缓存并绑定对应数据源,手动管理事务或通过AOP动态切换事务管理器。 - Spring Data JPA Repository适配:若想继续使用Spring Data的Repository接口,需自定义Repository工厂,根据当前请求的数据源动态生成代理实例,可参考
RepositoryFactorySupport实现。 - 资源清理:务必在应用关闭时调用数据源和EntityManagerFactory的
close方法,避免连接池泄漏。
内容的提问来源于stack exchange,提问作者Praveen Kumar
相关产品推荐
相关产品推荐

