Spring Boot读取自定义配置文件
@Value
首先,会想到使用@Value注解,该注解只能去解析yaml文件中的简单类型,并绑定到对象属性中去。
felord: phone: 182******32 def: name: 码农小胖哥 blog: felord.cn we-chat: MSW_623 dev: name: 码农小胖哥 blog: felord.cn we-chat: MSW_623 type: JUEJIN
对于上面的yaml配置,如果我们使用@Value注解的话,冒号后面直接有值的key才能正确注入对应的值。例如felord.phone我们可以通过@Value获取,但是felord.def不行,因为felord.def后面没有直接的值,它还有下一级选项。另外@Value不支持yaml松散绑定语法,也就是说felord.def.weChat获取不到felord.def.we-chat的值。
@Value是通过使用Spring的SpEL表达式来获取对应的值的:
// 获取 yaml 中 felord.phone的值 并提供默认值 UNKNOWN @Value("${felord.phone:UNKNOWN}") private String phone;
@Value的使用场景是只需要获取配置文件中的某项值的情况下,如果我们需要将一个系列的值进行绑定注入就建议使用复杂对象的形式进行注入了。
@ConfigurationProperties
@ConfigurationProperties注解提供了我们将多个配置选项注入复杂对象的能力。它要求我们指定配置的共同前缀。比如我们要绑定felord.def下的所有配置项:
package cn.felord.yaml.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import static cn.felord.yaml.properties.FelordDefProperties.PREFIX; /** * @author felord.cn */ @Data @ConfigurationProperties(PREFIX) public class FelordDefProperties { static final String PREFIX = "felord.def"; private String name; private String blog; private String weChat; }
我们注意到我们可以使用weChat接收we-chat的值,因为这种形式支持从驼峰camel-case到短横分隔命名kebab-case的自动转换。
如果我们使用@ConfigurationProperties的话建议配置类命名后缀为Properties,比如Redis的后缀就是RedisProperties,RabbitMQ的为RabbitProperties。
另外我们如果想进行嵌套的话可以借助于@NestedConfigurationProperty注解实现。也可以借助于内部类。这里用内部类实现将开头yaml中所有的属性进行注入:
package cn.felord.yaml.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import static cn.felord.yaml.properties.FelordProperties.PREFIX; /** * 内部类和枚举配置. * * @author felord.cn */ @Data @ConfigurationProperties(PREFIX) public class FelordProperties { static final String PREFIX = "felord"; private Def def; private Dev dev; private Type type; @Data public static class Def { private String name; private String blog; private String weChat; } @Data public static class Dev { private String name; private String blog; private String weChat; } public enum Type { JUEJIN, SF, OSC, CSDN } }
单独使用@ConfigurationProperties的话依然无法直接使用配置对象FelordDefProperties,因为它并没有被注册为Spring Bean。我们可以通过两种方式来使得它生效。
显式注入 Spring IoC
你可以使用@Component、@Configuration等注解将FelordDefProperties注入Spring IoC使之生效。
package cn.felord.yaml.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import static cn.felord.yaml.properties.FelordDefProperties.PREFIX; /** * 显式注入Spring IoC * @author felord.cn */ @Data @Component @ConfigurationProperties(PREFIX) public class FelordDefProperties { static final String PREFIX = "felord.def"; private String name; private String blog; private String weChat; }
@EnableConfigurationProperties
我们还可以使用注解@EnableConfigurationProperties进行注册,这样就不需要显式声明配置类为Spring Bean了。
package cn.felord.yaml.configuration; import cn.felord.yaml.properties.FelordDevProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; /** * 使用 {@link EnableConfigurationProperties} 注册 {@link FelordDevProperties}使之生效 * @author felord.cn */ @EnableConfigurationProperties({FelordDevProperties.class}) @Configuration public class FelordConfiguration { }
该注解需要显式的注册对应的配置类。
@ConfigurationPropertiesScan
在Spring Boot 2.2.0.RELEASE中提供了一个扫描注解@ConfigurationPropertiesScan。它可以扫描特定包下所有的被@ConfigurationProperties标记的配置类,并将它们进行IoC注入。
package cn.felord.yaml; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.ConfigurationPropertiesScan; import org.springframework.boot.context.properties.EnableConfigurationProperties; /** * {@link ConfigurationPropertiesScan} 同 {@link EnableConfigurationProperties} 二选一 * * @see cn.felord.yaml.configuration.FelordConfiguration * @author felord.cn */ @ConfigurationPropertiesScan @SpringBootApplication public class SpringBootYamlApplication { public static void main(String[] args) { SpringApplication.run(SpringBootYamlApplication.class, args); } }
这非常适合自动注入和批量注入配置类的场景,但是有版本限制,必须在2.2.0及以上。
@PropertySource注解
@PropertySource可以用来加载指定的配置文件,默认它只能加载*.properties文件,不能加载诸如yaml等文件。
@PropertySource相关属性介绍
- value:指明加载配置文件的路径。
- ignoreResourceNotFound:指定的配置文件不存在是否报错,默认是false。当设置为 true 时,若该文件不存在,程序不会报错。实际项目开发中,最好设置 ignoreResourceNotFound 为 false。
- encoding:指定读取属性文件所使用的编码,我们通常使用的是UTF-8。
@Data @AllArgsConstructor @NoArgsConstructor @Builder @Configuration @PropertySource(value = {"classpath:common.properties"},ignoreResourceNotFound=false,encoding="UTF-8") @ConfigurationProperties(prefix = "author") public class Author { private String name; private String job; private String sex; }
有小伙伴也许发现示例上的@ConfigurationProperties注解了。当我们使用@Value需要注入的值较多时,代码就会显得冗余。我们可以使用@ConfigurationProperties 中的 prefix 用来指明我们配置文件中需要注入信息的前缀
前边提到了用@PropertySource只能加载*.properties文件,但如果我们项目的配置文件不是*.properties这种类型,而是其他类型,诸如yaml,此时我们可以通过实现PropertySourceFactory接口,重写createPropertySource方法,就能实现用@PropertySource也能加载yaml等类型文件。
public class YamlPropertySourceFactory implements PropertySourceFactory { @Override public PropertySource<?> createPropertySource(String sourceName, EncodedResource resource) throws IOException { Properties propertiesFromYaml = loadYaml(resource); if(StringUtils.isBlank(sourceName)){ sourceName = resource.getResource().getFilename();; } return new PropertiesPropertySource(sourceName, propertiesFromYaml); } private Properties loadYaml(EncodedResource resource) throws FileNotFoundException { try { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources(resource.getResource()); factory.afterPropertiesSet(); return factory.getObject(); } catch (IllegalStateException e) { // for ignoreResourceNotFound Throwable cause = e.getCause(); if (cause instanceof FileNotFoundException) throw (FileNotFoundException) e.getCause(); throw e; } } }
@Data @AllArgsConstructor @NoArgsConstructor @Builder @Configuration @PropertySource(factory = YamlPropertySourceFactory.class,value = {"classpath:user.yml"},ignoreResourceNotFound=false,encoding="UTF-8") @ConfigurationProperties(prefix = "user") public class User { private String username; private String password; }
使用EnvironmentPostProcessor加载自定义配置文件
1、实现EnvironmentPostProcessor接口,重写postProcessEnvironment方法
@Slf4j public class CustomEnvironmentPostProcessor implements EnvironmentPostProcessor { @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { Properties properties = new Properties(); try { properties.load(new InputStreamReader(CustomEnvironmentPostProcessor.class.getClassLoader().getResourceAsStream("custom.properties"),"UTF-8")); PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource("custom",properties); environment.getPropertySources().addLast(propertiesPropertySource); } catch (IOException e) { log.error(e.getMessage(),e); } } }
2、在META-INF下创建spring.factories
spring.factories文件内容如下: org.springframework.boot.env.EnvironmentPostProcessor=com.github.lybgeek.env.CustomEnvironmentPostProcessor
1、2步骤实现完后,就可以在代码中直接用@Value的方式获取自定义配置文件内容了
读取的自定义配置文件内容的实现方法有多种多样,除了上面的方法,还可以在以-jar方式启动时,执行形如下命令
java -jar project.jar --spring.config.location=classpath:/config/custom.yml
也能实现。还可以干脆自定义配置文件都以application-*为前缀,比如application-custom,然后在application.properties,使用spring.profiles.include=custom或者spring.profiles.active=custom也可以实现
总结
日常开发中单个属性推荐使用@Value,如果同一组属性为多个则推荐@ConfigurationProperties。需要补充一点的是@ConfigurationProperties还支持使用 JSR303 进行属性校验。好了今天的教程就到这里
相关的demo地址
https://gitee.com/felord/spring-boot-yml.git
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-outside-config
以上就是Spring Boot读取自定义配置文件的详细内容,更多关于Spring Boot读取自定义配置文件的资料请关注我们其它相关文章!