Spring Boot 2.7中如何在公共模块读取主模块的application.yml配置属性
看起来你现在遇到的核心问题有两个:一是公共模块缺少Spring上下文相关依赖,导致@Configuration、@Value这些注解用不了;二是你的OurDataSource是手动new出来的,不属于Spring管理的Bean,所以没法自动注入配置。结合你的场景(多个服务模块复用公共数据源逻辑,每个服务要创建7个数据源),我给你整理几个可行的方案:
第一步:先给公共模块补上Spring依赖
你的common-module的pom.xml里没有引入Spring Context相关依赖,所以才会出现package org.springframework.context.annotation does not exist的错误。先在common模块的pom里添加这个依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <!-- 用provided是因为各个服务模块已经引入了这个依赖,避免重复打包 --> <scope>provided</scope> </dependency>
如果你的Spring Boot版本是2.7.x,这个依赖会自动引入对应版本的Spring Context,这样@Configuration、@Value这些注解就能正常使用了。
第二步:把公共数据源逻辑纳入Spring上下文管理
现在你是手动new OurDataSource(),Spring没法给这个类注入配置。我们需要让数据源的配置逻辑被Spring管理,同时读取主模块的application.yml配置。
方案1:用@ConfigurationProperties统一管理Hikari配置(推荐)
这种方式更优雅,后续加新的Hikari配置也不用改多处:
- 在公共模块创建Hikari配置属性类:
package com.us.ourapp; import org.springframework.boot.context.properties.ConfigurationProperties; import lombok.Data; // 用Lombok的@Data自动生成getter/setter,你已经引入了Lombok依赖 @Data @ConfigurationProperties(prefix = "spring.datasource.hikari") public class HikariPoolProperties { private int minimumIdle; private int maximumPoolSize; }
- 在公共模块创建配置类,启用这个属性绑定:
package com.us.ourapp; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; @Configuration @EnableConfigurationProperties(HikariPoolProperties.class) public class CommonDataSourceConfig { }
- 修改
OurDataSource,让它接收Hikari配置参数:
package com.us.ourapp; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Properties; public class OurDataSource { private static final Logger log = LoggerFactory.getLogger(OurDataSource.class); private HikariDataSource dataSource; public OurDataSource(String url, String user, String password, HikariPoolProperties hikariProps) { String jdbcUrl = String.format("jdbc:postgresql://%s", url); String[] pieces = url.split("/"); if (pieces.length < 2) { throw new IllegalArgumentException("Invalid DB url format, expected 'server/dbname'"); } Properties props = new Properties(); props.setProperty("dataSourceClassName", "org.postgresql.ds.PGSimpleDataSource"); props.setProperty("dataSource.user", user); props.setProperty("dataSource.password", password); props.setProperty("dataSource.serverName", pieces[0]); props.setProperty("dataSource.databaseName", pieces[1]); HikariConfig config = new HikariConfig(props); config.setMinimumIdle(hikariProps.getMinimumIdle()); config.setMaximumPoolSize(hikariProps.getMaximumPoolSize()); this.dataSource = new HikariDataSource(config); log.info("DataSource({}, user={}) created minIdle={}, max={}", jdbcUrl, user, dataSource.getMinimumIdle(), dataSource.getMaximumPoolSize()); } public HikariDataSource getDataSource() { return dataSource; } }
- 在各个服务模块(比如api-module)创建数据源Bean:
package com.us.ourapp; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppDataSourceConfig { private final HikariPoolProperties hikariPoolProperties; // 自动注入公共模块的Hikari配置属性 @Autowired public AppDataSourceConfig(HikariPoolProperties hikariPoolProperties) { this.hikariPoolProperties = hikariPoolProperties; } // 第一个数据源 @Bean public OurDataSource db1DataSource() { return new OurDataSource("db-server-1/user-db", "db-user-1", "db-pass-1", hikariPoolProperties); } // 第二个数据源,以此类推直到7个 @Bean public OurDataSource db2DataSource() { return new OurDataSource("db-server-2/log-db", "db-user-2", "db-pass-2", hikariPoolProperties); } }
方案2:用@Value直接注入配置(简单场景适用)
如果你的Hikari配置不多,也可以直接在公共模块的配置类里用@Value注入:
package com.us.ourapp; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; @Configuration public class OurDataSourceConfig { @Value("${spring.datasource.hikari.minimum-idle}") private int hikariMinIdle; @Value("${spring.datasource.hikari.maximum-pool-size}") private int hikariMaxPoolSize; // 提供创建OurDataSource的工厂方法 public OurDataSource createDataSource(String url, String user, String password) { return new OurDataSource(url, user, password, hikariMinIdle, hikariMaxPoolSize); } }
然后修改OurDataSource的构造器,接收这两个参数,后续逻辑和方案1类似。
第三步:确保服务模块能加载到application.yml
你提到application.yml在project-root下,Spring Boot默认不会自动加载这个位置的配置文件。需要在每个服务模块的pom.xml里配置资源复制,把根目录的application.yml复制到服务模块的classpath中:
<build> <resources> <!-- 先复制根目录的共享配置 --> <resource> <directory>${project.parent.basedir}/src/main/resources</directory> </resource> <!-- 再复制服务模块自己的配置(如果有的话) --> <resource> <directory>src/main/resources</directory> </resource> </resources> </build>
或者,你也可以把共享的application.yml放到common-module/src/main/resources下,这样服务模块依赖common时会自动加载这个配置,不过如果服务模块有自己的配置,会覆盖共享配置的相同属性。
最后要注意的点
- 确保服务模块的启动类能扫描到公共模块的配置类:比如启动类的
@SpringBootApplication加上scanBasePackages = "com.us.ourapp",这样Spring会扫描整个com.us.ourapp包下的所有Spring组件。 - 如果你用了
@ConfigurationProperties,记得在服务模块的启动类上加上@EnableConfigurationProperties,或者公共模块已经用@EnableConfigurationProperties启用了,就不用重复加。
这样改造后,你就不用在每个服务模块里重复传递Hikari的配置参数,所有配置都统一在application.yml里管理,公共模块复用逻辑,也不会有冗余代码啦。
内容来源于stack exchange

