PropertySource
添加 PropertySource
- < context:property-placeholder >
把property文件中的属性,变成spring可以获取到的配置项,它的背后就是帮我们去注册一个propertyplaceholderconfigure 是spring3.1之后 提供的 - PropertySourcesPlaceholderConfigurer
• PropertyPlaceholderConfigurer - @PropertySource
在类上加这个@PropertySource注解,就可以配置配置的来源,将property文件配置在@PropertySource注解里面,也可以在里面添加如果找不到这个配置项 之后 的做法 - @PropertySources
如果有多个@PropertySource,在类上添加@PropertySources 的集合,是多个@PropertySource的注解
Spring Boot 中的 @ConfigurationProperties
-
可以将属性绑定到结构化对象上
-
支持 Relaxed Binding
-
支持安全的类型转换
-
在配置类上加上@EnableConfigurationProperties 注解,这个注解就可以开启@ConfigurationProperties注解的支持
以JdbcProperties源码为例:
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;
@ConfigurationProperties(
prefix = "spring.jdbc"
)//通过prefix 说明所有的spring.jdbc打头的属性 都会被绑定到JdbcProperties里面
public class JdbcProperties {
private final JdbcProperties.Template template = new JdbcProperties.Template();
//结构化就是通过对象进行属性的绑定 比如说 spring.jdbc.template 打头的这些属性会被绑定到Template 里面的这些属性上的 比如说 spring.jdbc.template.fetchSize 都会绑定到fetchSize 上
public JdbcProperties() {
}
public JdbcProperties.Template getTemplate() {
return this.template;
}
public static class Template {
private int fetchSize = -1;
private int maxRows = -1;
@DurationUnit(ChronoUnit.SECONDS) //支持安全的类型转换 只要是Unit支持的 就可以写上去
private Duration queryTimeout;
public Template() {
}
public int getFetchSize() {
return this.fetchSize;
}
public void setFetchSize(int fetchSize) {
this.fetchSize = fetchSize;
}
public int getMaxRows() {
return this.maxRows;
}
public void setMaxRows(int maxRows) {
this.maxRows = maxRows;
}
public Duration getQueryTimeout() {
return this.queryTimeout;
}
public void setQueryTimeout(Duration queryTimeout) {
this.queryTimeout = queryTimeout;
}
}
}
定制 PropertySource
主要步骤
- 实现 PropertySource<T>的bean
- 把 PropertySource注册到EnvironmentPropertySource 从 Environment 取得 PropertySources
- 将自己的 PropertySource添加到合适的位置
切入位置 - EnvironmentPostProcessor(springboot)
- BeanFactoryPostProcessor (spring)
以源码RandomValuePropertySource为例:
public class RandomValuePropertySource extends PropertySource<Random> {//提供随机数的PropertySource 实现 PropertySource<T<T>>的bean
public static final String RANDOM_PROPERTY_SOURCE_NAME = "random";
private static final String PREFIX = "random.";
private static final Log logger = LogFactory.getLog(RandomValuePropertySource.class);
public RandomValuePropertySource(String name) {
super(name, new Random());//使用父类构造方法 传入一个名字 一个Source 一个Random对象
}
public RandomValuePropertySource() {
this("random");
}
public Object getProperty(String name) {
if (!name.startsWith("random.")) {//判断是否以random.打头
return null;
} else {
if (logger.isTraceEnabled()) {
logger.trace("Generating random property for '" + name + "'");
}
return this.getRandomValue(name.substring("random.".length()));//去掉random. 截取后面的一段
}
}
private Object getRandomValue(String type) {
if (type.equals("int")) {//如果是int类型的
return ((Random)this.getSource()).nextInt();//在getSource()中取下一个int
} else if (type.equals("long")) {
return ((Random)this.getSource()).nextLong();
} else {
String range = this.getRange(type, "int");//也可以是一个范围 使用getRange取得一个范围
if (range != null) {
return this.getNextIntInRange(range);
} else {
range = this.getRange(type, "long");
if (range != null) {
return this.getNextLongInRange(range);
} else {
return type.equals("uuid") ? UUID.randomUUID().toString() : this.getRandomBytes();
}
}
}
}
private String getRange(String type, String prefix) {
if (type.startsWith(prefix)) {
int startIndex = prefix.length() + 1;
if (type.length() > startIndex) {
return type.substring(startIndex, type.length() - 1);
}
}
return null;
}
private int getNextIntInRange(String range) {
String[] tokens = StringUtils.commaDelimitedListToStringArray(range);
int start = Integer.parseInt(tokens[0]);
return tokens.length == 1 ? ((Random)this.getSource()).nextInt(start) : start + ((Random)this.getSource()).nextInt(Integer.parseInt(tokens[1]) - start);
}
private long getNextLongInRange(String range) {
String[] tokens = StringUtils.commaDelimitedListToStringArray(range);
if (tokens.length == 1) {
return Math.abs(((Random)this.getSource()).nextLong() % Long.parseLong(tokens[0]));
} else {
long lowerBound = Long.parseLong(tokens[0]);
long upperBound = Long.parseLong(tokens[1]) - lowerBound;
return lowerBound + Math.abs(((Random)this.getSource()).nextLong() % upperBound);
}
}
private Object getRandomBytes() {
byte[] bytes = new byte[32];
((Random)this.getSource()).nextBytes(bytes);
return DigestUtils.md5DigestAsHex(bytes);
}
public static void addToEnvironment(ConfigurableEnvironment environment) {
environment.getPropertySources().addAfter("systemEnvironment", new RandomValuePropertySource("random"));
logger.trace("RandomValuePropertySource add to Environment");
}
}
例子
@SpringBootApplication
@Slf4j
public class PropertySourceDemoApplication implements ApplicationRunner {
@Value("${geektime.greeting}")//从配置文件中取得
private String greeting;
public static void main(String[] args) {
SpringApplication.run(PropertySourceDemoApplication.class, args);
}
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("{}", greeting);
}
}
@Slf4j
public class YapfEnvironmentPostProcessor implements EnvironmentPostProcessor {
private PropertiesPropertySourceLoader loader = new PropertiesPropertySourceLoader();
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
MutablePropertySources propertySources = environment.getPropertySources();//从 Environment 取得 PropertySources
Resource resource = new ClassPathResource("yapf.properties");//创建一个自己的PropertySource
try {
PropertySource ps = loader.load("YetAnotherPropertiesFile", resource)//通过PropertiesPropertySourceLoader 去load一个resource
.get(0);
propertySources.addFirst(ps); //添加到第一个位置上 即 完成yapf.properties的注册
} catch (Exception e) {
log.error("Exception!", e);
}
}
}
spring.factories
org.springframework.boot.env.EnvironmentPostProcessor=spring.hello.YapfEnvironmentPostProcessor
yapf.properties
geektime.greeting=hello
结果
在控制台打印出hello