赞
踩
在spring中我们要读取一个配置文件中的属性通常会使用@Value来注入对应的属性值,但@Value一次性只能注入一个属性值,不太方便统一管理。由此springboot中提供了一个可通过前缀分组的的注解对同一特征的属性统一管理。这就是@ConfigurationProperties
- @Target({ElementType.TYPE, ElementType.METHOD})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface ConfigurationProperties {
- @AliasFor("prefix")
- String value() default "";
-
- @AliasFor("value")
- String prefix() default "";
-
- boolean ignoreInvalidFields() default false;
-
- boolean ignoreUnknownFields() default true;
- }
可以看到@ConfigurationProperties中的属性并不多:
@ConfigurationProperties通常用以标识一个属性配置类,其本身只是一个标识,读取配置文件并提取注入对应值的过程则在spring的生命周期中-【准确的说是通过BeanPostProcessor,InitializingBean。这里不做详细描述,感兴趣的胖友可以自己挖坑】完成,所以要想使用@ConfigurationProperties所标识的属性配置类还需要将对应的类注入到spring容器中。由此我们得出springboot属性注入的两个步骤:
springboot属性注入的几种方式
为了测试不同注入方式的效果,我们首先创建一个application.yml文件,它就是属性配置文件:
- user:
- user-name: kell
- password: 888888
- email: kell@qq.com
- sex: man
- phone: 18345678900
为了后面我们需要测试一波,我们在创建一个Application类和一个测试类:
- @SpringBootApplication
- public class PropApplication {
- public static void main(String[] args) {
- SpringApplication.run(PropApplication.class,args);
- }
- }
- @Slf4j
- public class UserService {
- private UserProperties userProperties;
-
- public UserService(UserProperties userProperties) {
- this.userProperties = userProperties;
- }
-
- public void getUser(){
- if(userProperties==null){
- log.info("[user properties is null]");
- return;
- }
- log.info("[user info:userName:{}]",userProperties.getUserName());
- }
- }

- @RunWith(SpringJUnit4ClassRunner.class)
- @SpringBootTest(classes = PropApplication.class)
- public class PropTest {
- @Resource
- private UserService userService;
-
- @Test
- public void test_getUser(){
- userService.getUser();
- }
- }
- @Component
- @ConfigurationProperties(prefix = "user")
- public class UserProperties {
- private String userName;
- private String password;
- private String email;
- private String sex;
- private String phone;
-
- //省略get,set方法
- }
使用@Component注解直接将属性配置类注入到spring中。
这种方式是最简单,但是不提倡这样使用。通常情况下我们可能会将项目中的所有配置类都放在一起,可能是通过依赖包的形式,这个时候我们配置spring扫描的时候,可能并不会扫描到这些路径,这就会导致@Component不起作用。
- @ConfigurationProperties(prefix = "user")
- public class UserProperties {
- private String userName;
- private String password;
- private String email;
- private String sex;
- private String phone;
-
- //省略get,set方法
- }
- @Configuration
- public class PropConfig {
- @Bean
- public UserProperties userProperties(){
- return new UserProperties();
- }
-
- @Bean
- public UserService userService(){
- return new UserService(this.userProperties());
- }
- }
这是一种可选的方式,但是不够简练需要new一个配置类然后再注入
- @ConfigurationProperties(prefix = "user")
- public class UserProperties {
- private String userName;
- private String password;
- private String email;
- private String sex;
- private String phone;
-
- //省略get,set方法
- }
- @Configuration
- @EnableConfigurationProperties(UserProperties.class)
- public class PropConfig {
-
- @Bean
- public UserService userService(UserProperties userProperties){
- return new UserService(userProperties);
- }
- }
使用@EnableConfigurationProperties和使用@Component的区别在于:
@EnableConfigurationProperties能够将属性类的定义和使用区分开。属性类可能在其他项目中定义,只要在使用到该类的地方使用@EnableConfigurationProperties开启某个配置,该配置类即可以被注入到spring中。
这也是最为提倡的方式,springboot自动配置实现中就是采用这种方式。
- user-name : kell
- user_name: kell
- USER_NAME: kell
- userName:kell
- UserName:kell
Spring使用一些宽松的绑定属性规则。因此,以上变体都将绑定到userName属性上。
如果我们想给user添加一个集合类型的属性(如:一个user他有多本书籍),该怎么办呢?
这里有两种写法:
- user:
- user-name: kell
- books[0]: 书名1
- books[1]: 书名2
- user:
- user-name: kell
- books:
- - 书名1
- - 书名2
运行结果:
如果我们想给user定义一个体重的属性,这个属性是带有单位的,那我们怎么自动解析对应的值呢?如下
- user:
- user-name: kell
- books[0]: 书名1
- books[1]: 书名2
- weight: 60KG
当然你可以直接通过字符串接受对应值。除此之外我们还可以通过Converter集中处理那些属性。
- public class Weight {
- private Double weight;
- private String unit;
-
- public Weight(Double weight, String unit) {
- this.weight = weight;
- this.unit = unit;
- }
-
- //省略get,set方法
- }
-
我们定义了一个weight的类,这个类包含重量和单位。
- public enum WeightUnitEnum {
- KG(1),
- TON(2);
- private int unit;
-
- WeightUnitEnum(int unit) {
- this.unit = unit;
- }
-
- public int getUnit() {
- return unit;
- }
-
- public void setUnit(int unit) {
- this.unit = unit;
- }
-
- //将字符串转化为weight
- public static Weight getWeight(String weightStr){
- Weight weight=new Weight(0D,KG.name());
- if(StringUtils.isEmpty(weightStr)){
- return weight;
- }
- for(WeightUnitEnum weightUnit:WeightUnitEnum.values()){
- if(weightStr.endsWith(weightUnit.name())){
- double weightNum=Double.parseDouble(weightStr.substring(0,weightStr.indexOf(weightUnit.name())));
- weight.setUnit(weightUnit.name());
- weight.setWeight(weightNum);
- }
- }
- return weight;
- }
- }

我们定义了一个枚举,这个枚举中定义类单位。并且里面新增了一个将字符解析为weight的方法。
- public class WeightConverter implements Converter<String, Weight> {
- @Override
- public Weight convert(String s) {
- return WeightUnitEnum.getWeight(s);
- }
- }
-
通过实现Converter,我们定义了一个自动解析转化属性类型的方法。为了使该方法起作用,我们还需要将他注入到spring容器中:
- @Configuration
- @EnableConfigurationProperties(UserProperties.class)
- public class PropConfig {
-
-
- @Bean
- public UserService userService(UserProperties userProperties){
- return new UserService(userProperties);
- }
-
- //注入属性转化器
- @Bean
- @ConfigurationPropertiesBinding
- public WeightConverter weightConverter(){
- return new WeightConverter();
- }
-
- }

运行结果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。