@Value注解可以在不需要配置文件的情况下注入属性,经常会看到两种写法 ${}
和 #{}
:
${properties名称}
的表示获取properties的值,注入配置文件中的属性的值。
#{表达式}
括号里面的表达式必须是SpEL表达式的格式。通过 #{表达式}
方式可以在不需要使用配置文件的情况下,动态的将外部值注入到bean中。
@Component
@ToString
public class InjectWithoutConfigurationFile {
// 注入普通字符串
@Value("diego")
private String name;
// 注入表达式
@Value("#{T(java.lang.Math).random()}")
private double random;
// 注入系统属性
@Value("#{systemProperties['os.name']}")
private String defaultLocale;
// 注入其他bean的属性值
@Value("#{rectangle.length}")
private int rectangleLength;
// 注入url
@Value("http://www.baidu.com")
private Resource baiduUrls;
}
测试结果:
InjectWithoutConfigurationFile(name=diego, random=0.20947710881441184, defaultLocale=Mac OS X, rectangleLength=20, baiduUrls=URL [http://www.baidu.com])
在Spring Boot项目中,先在全局配置文件中配置好属性值,在项目启动时这些属性自动注入到对应bean对应属性中。
全局配置文件:
这两个都是全局配置文件,但properties的优先级高于yaml,当两个文件中对统一属性配置不同的值时,以properties文件中的为准,两个文件中的不同配置互补。
实现配置文件中的属性自动注入有一下几种方式:
value.from.file=Value got from the file
priority=Properties file
listOfValues=A,B,C
@ToString
@Component
public class Properties {
@Value("${value.from.file}")
private String valueFromFile;
@Value("${priority}")
private String prioritySystemProperty;
@Value("${listOfValues}")
private String[] valuesArray; // 注入数组
}
在测试中如果将listOfValues卸载yaml配置文件中,形如下面的样子,再用@Value("${listOfValues}") 注入的时候会报错。
listOfValues:
- A
- B
- C
在开发过程中,如果需要将配置文件中的单个属性注入bean中,采用@Value是最简单的方式。当然@Value注解也可以将object注入,但总觉得操作有限。
当配置文件中的object很复杂时,选用@ConfigurationProperties将object注入会很方便。@ConfigurationProperties注解的使用也有好几种方式,这里主要介绍两种:
Spring Boot 2中,可以使用@ConfigurationProperties和@ConfigurationPropertiesScan两个注解实现配置注入。
mail:
hostname: [email protected]
port: 9000
from: [email protected]
defaultRecipients:
- [email protected]
- [email protected]
additionalHeaders:
redelivery: true
secure: true
p3: value
@SpringBootApplication
@ConfigurationPropertiesScan("com.yang.config")
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
@ConfigurationProperties(prefix = "mail")
@ToString
@Setter
public class ConfigProperties {
private String hostName;
private int port;
private String from;
private List<String> defaultRecipients;
private Map<String, String> additionalHeaders;
}
@ConfigurationPropertiesScan(“com.yang.config”)表示扫描com.yang.config包下的@ConfigurationProperties注解,@ConfigurationProperties(prefix = “mail”)表示将配置文件中前缀为mail的属性与ConfigProperties中的对应的成员变量映射起来。必须为要映射的成员变量创建setter方法,才能将配置文件中的属性值映射成功。
测试结果:
ConfigProperties(hostName=host@mail.com, port=9000, from=mailer@mail.com, defaultRecipients=[admin@mail.com, owner@mail.com], additionalHeaders={redelivery=true, secure=true, p3=value})
使用@ConfigurationProperties和@Component注解
@Component
@ConfigurationProperties(prefix = "mail")
@ToString
@Setter
public class ConfigProperties {
private String hostName;
private int port;
private String from;
private List<String> defaultRecipients;
private Map<String, String> additionalHeaders;
}
当然还有其他的的注解也能实现复杂的object的注入,我觉得上面这两种使用简单,理解也很容易。
有时候我们可能会自定义配置文件,专门用来保存某个类的配置,这时需要使用@PropertySource注解先将自定义配置文件加载进来。
# customConfigProperties.properties配置文件
#Simple properties
[email protected]
custom.port=9000
[email protected]
#List properties
custom.defaultRecipients[0][email protected]
custom.defaultRecipients[1][email protected]
#Map Properties
custom.additionalHeaders.redelivery=true
custom.additionalHeaders.secure=true
custom.additionalHeaders.p3=value
@PropertySource("classpath:/customConfigProperties.properties")
@ConfigurationProperties(prefix = "custom")
@Component
@ToString
@Setter
public class CustomConfigProperties {
private String hostName;
private int port;
private String from;
private List<String> defaultRecipients;
private Map<String, String> additionalHeaders;
}
可以看出@PropertySource注解将自定义的customConfigProperties.properties配置文件加载进来。之后用@ConfigurationProperties和@Component组合的方式将配置文件的中的object注入。
注:@PropertySource注解不支持加载自定义yaml格式的配置文件。
测试结果:
CustomConfigProperties(hostName=host@mail.com, port=9000, from=mailer@mail.com, defaultRecipients=[admin@mail.com, owner@mail.com], additionalHeaders={redelivery=true, secure=true, p3=value})
EnvironmentPostProcessor接口允许用户在Spring Boot应用启动之前操作 Environment
,官方参考资料。
这个链接介绍如何载入和转换自定义的属性到 Environment
中,并且最后再访问自定义的属性。
可以用这个接口加载自定义yaml配置文件。
创建person.yml
文件,内容很简单。
personconfig:
name: lee
age: 20
这段代码来自官方参考文档。
public class PersonConfigProcessor implements EnvironmentPostProcessor {
private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
// 加载自定义的配置文件
Resource path = new ClassPathResource("persons.yml");
PropertySource<?> propertySource = loadYaml(path);
environment.getPropertySources().addLast(propertySource);
}
private PropertySource<?> loadYaml(Resource path) {
if (!path.exists()) {
throw new IllegalArgumentException("Resource " + path + " does not exist");
}
try {
return this.loader.load("custom-person", path).get(0);
}
catch (IOException ex) {
throw new IllegalStateException("Failed to load yaml configuration from " + path, ex);
}
}
}
PersonConfigProcessor
必须注册到 META-INF/spring.factories
文件中,目的是让Spring Boot在启动时能扫描到这个类,之后才能加载 。
org.springframework.boot.env.EnvironmentPostProcessor=com.yang.config.PersonConfigProcessor
配置文件的目的就是为将其定义的属性注入到java object中,所以还需要构建一个类用来“接”配置文件定义的内容。
@Component
@ConfigurationProperties(prefix = "personconfig")
@ToString
@Setter
public class PersonConfig {
private String name;
private int age;
}
这里的@Setter
不能省略,这种方式也是:先调用无参构造函数构造对象,再调用属性的set方法初始化。使用PersonConfig时,直接装配就可以了。
@Autowired
private PersonConfig personConfig;
测试结果:
PersonConfig(name=lee, age=20)
@Value
注解。application.properties/application.yml
中,用@Value ${}
方式注入。application.properties/application.yml
中,用 @ConfigurationProperties
注入。@PropertySource
注入。其实,4和5的注入方式都是先将配置文件加载进来,再用@ConfigurationProperties绑定类。只是不同类型的文件,加载的方式不一样。
github