如何在Spring Boot Starter中加载Maven项目属性并添加至Environment?
我们有多个Spring Boot应用,配置属性使用@...@占位符引用Maven项目属性,示例如下:
spring.application.name=@project.name@ version=@project.version@ ...
这些属性是所有应用共用的,为了避免重复代码,我们想创建一个自定义Spring Boot Starter,把这些属性以最低优先级添加到Environment中。
在自定义Starter里,我们把属性写入custom.properties文件,并创建了CustomEnvironmentPostProcessor来加载这些属性:
public class CustomEnvironmentPostProcessor implements EnvironmentPostProcessor { private final PropertiesPropertySourceLoader loader = new PropertiesPropertySourceLoader(); @Override public void postProcessEnvironment( ConfigurableEnvironment environment, SpringApplication application) { ClassPathResource path = new ClassPathResource("custom.properties"); PropertySource<?> propertySource = loadProperties(path); environment.getPropertySources().addLast(propertySource); } private PropertySource<?> loadProperties(ClassPathResource path) { Assert.isTrue(path.exists(), () -> "Resource " + path + " does not exist"); try { return this.loader.load("custom-properties", path).get(0); } catch (IOException ex) { throw new IllegalStateException("Failed to load properties configuration from " + path, ex); } } }
但问题是,这些属性并没有被资源过滤覆盖。应用的pom.xml构建配置如下:
<build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> </build>
现在想咨询两个问题:
- 能否让Starter的属性文件被资源过滤覆盖?
- 有没有其他更优的方法,在Spring启动前从pom.xml加载属性并添加至Environment?
一、能不能让Starter的属性文件被资源过滤覆盖?
不行。Maven的资源过滤只在构建当前应用项目时生效,只会处理当前项目src/main/resources下的文件,对于依赖的Starter包里的资源文件,Maven不会做二次替换。如果在Starter自己的pom里配置资源过滤,打包时会用Starter自身的Maven属性替换占位符,而不是你期望的应用项目的属性,完全不符合需求,所以这个路子走不通。
二、更优的替代方案
直接读取Maven构建时生成的属性文件,或者通过Maven插件生成自定义属性文件后再加载,比自己维护custom.properties靠谱得多。
方案1:读取Spring Boot默认生成的pom.properties
Spring Boot构建时会自动在target/classes/META-INF/maven/${groupId}/${artifactId}/pom.properties生成文件,里面包含project.name、project.version等核心属性。直接在EnvironmentPostProcessor里读取这个文件即可:
public class CustomEnvironmentPostProcessor implements EnvironmentPostProcessor { private final PropertiesPropertySourceLoader loader = new PropertiesPropertySourceLoader(); @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { Class<?> mainClass = application.getMainApplicationClass(); // 这里可以根据实际场景调整groupId/artifactId的获取逻辑,比如读取Manifest信息更可靠 String groupId = mainClass.getPackageName().split("\\.")[0]; String artifactId = mainClass.getSimpleName(); String mavenPropsPath = String.format("META-INF/maven/%s/%s/pom.properties", groupId, artifactId); ClassPathResource resource = new ClassPathResource(mavenPropsPath); if (resource.exists()) { try { PropertySource<?> propertySource = loader.load("maven-project-props", resource).get(0); environment.getPropertySources().addLast(propertySource); // 设置为最低优先级 } catch (IOException e) { throw new IllegalStateException("Failed to load Maven project properties", e); } } } }
如果觉得通过包名/类名定位不够稳定,可以遍历classpath:META-INF/maven下的目录,找到对应的pom.properties文件,逻辑稍复杂但容错性更强。
方案2:用Maven插件生成自定义属性文件
在应用的pom.xml里添加properties-maven-plugin,把需要的Maven属性写入指定文件:
<build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>properties-maven-plugin</artifactId> <version>1.0.0</version> <executions> <execution> <phase>generate-resources</phase> <goals> <goal>write-project-properties</goal> </goals> <configuration> <outputFile>${project.build.outputDirectory}/shared-project.properties</outputFile> <!-- 可指定只输出需要的属性,避免冗余 --> <properties> <property>project.name</property> <property>project.version</property> <property>project.groupId</property> </properties> </configuration> </execution> </executions> </plugin> </plugins> </build>
然后在Starter的EnvironmentPostProcessor里直接读取这个文件,路径明确更可控:
@Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { ClassPathResource resource = new ClassPathResource("shared-project.properties"); if (resource.exists()) { try { PropertySource<?> propertySource = loader.load("shared-project-props", resource).get(0); environment.getPropertySources().addLast(propertySource); } catch (IOException e) { throw new IllegalStateException("Failed to load shared project properties", e); } } }
这个方案更灵活,能精准控制要暴露给Spring环境的Maven属性。
内容的提问来源于stack exchange,提问作者Toni

