1 概述
开发一个服务,配置是经常要用到的。如果把配置分一下类,可以分为系统配置、Springboot/Spring自带的配置、业务服务自定义的配置。Spring提供了一整套配置文件的加载,需要了解一下这些配置文件的加载原理,以便更好地使用这些配置文件。这里先关注系统配置。
2 原理
2.1 配置资源加载过程
系统启动的时候会执行SpringApplication.run()方法,Spring使用Environment的概念承载配置资源数据,在创建和准备Environment对象的时候进行配置资源的加载。
这里称为“配置资源”,一是大家熟悉把“配置”写到application.properties文件里,二是源码里使用了PropertyResouce这个词来代表这些配置,Resource是“资源”的意思。可以理解为跟使用application.properties文件里的配置一样对待这里加载的所有“配置资源”。
// 源码位置:org.springframework.boot.SpringApplication
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 1. 准备环境对象
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
// 省略其它代码
}
// 源码位置:org.springframework.boot.SpringApplication
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 2. 根据application情况创建环境对象,一般有三种:ApplicationServletEnvironment(Web)、ApplicationReactiveWebEnvironment(相应式)、ApplicationEnvironment(普通)
// 这里以第一种ApplicationServletEnvironment为例
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
// 源码位置:org.springframework.boot.SpringApplication
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
// 3. 使用工厂来创建环境对象
// webApplicationType为上面所的“应用情况”,根据依赖包不同得到不同的值,比如依赖了spring-web相关的包得到的是webApplicationType=SERVLET
// applicationContextFactory为DefaultApplicationContextFactory
ConfigurableEnvironment environment = this.applicationContextFactory.createEnvironment(this.webApplicationType);
if (environment == null && this.applicationContextFactory != ApplicationContextFactory.DEFAULT) {
environment = ApplicationContextFactory.DEFAULT.createEnvironment(this.webApplicationType);
}
// 如果不是web,创建的就是普通的ApplicationEnvironment
return (environment != null) ? environment : new ApplicationEnvironment();
}
// 源码位置:org.springframework.boot.DefaultApplicationContextFactory
public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
// 4. 把创建环境对象的funtion=ApplicationContextFactory::createEnvironment作为参数传入以获取环境对象
return getFromSpringFactories(webApplicationType, ApplicationContextFactory::createEnvironment, null);
}
private <T> T getFromSpringFactories(WebApplicationType webApplicationType, BiFunction<ApplicationContextFactory, WebApplicationType, T> action, Supplier<T> defaultResult) {
for (ApplicationContextFactory candidate : SpringFactoriesLoader.loadFactories(ApplicationContextFactory.class, getClass().getClassLoader())) {
// 5. 调用创建环境对象的function以创建环境对象
// SpringFactoriesLoader.loadFactories()得到的是两种工厂,分别用于创建ReactiveWeb、ServletWeb对应的环境对象,这里以ServletWeb为例:
// (1) org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext$Factory
// (2) org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext$Factory
// 根据webApplicationType不同,只有匹配的那个工厂才会最终创建出环境对象
T result = action.apply(candidate, webApplicationType);
if (result != null) {
return result;
}
}
return (defaultResult != null) ? defaultResult.get() : null;
}
// 源码位置:org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext.Factory
public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
// 6. 创建ApplicationReactiveWebEnvironment对象
// 继承关系:ApplicationServletEnvironment < StandardServletEnvironment < StandardEnvironment < AbstractEnvironment
// new对象的时候,前面几个没有构造方法,执行的是AbstractEnvironment构造方法
return (webApplicationType != WebApplicationType.REACTIVE) ? null : new ApplicationReactiveWebEnvironment();
}
// 源码位置:org.springframework.core.env.AbstractEnvironment
public AbstractEnvironment() {
// 7. 创建一个用于承载所有资源文件的MutablePropertySources对象作为参数
this(new MutablePropertySources());
}
protected AbstractEnvironment(MutablePropertySources propertySources) {
this.propertySources = propertySources; // 存放MutablePropertySources对象
this.propertyResolver = createPropertyResolver(propertySources);
// 8. 自定义初始化资源对象,不同子类增加不同的资源对象
// ApplicationServletEnvironment没有重载此方法,StandardServletEnvironment、StandardEnvironment重载了此方法
customizePropertySources(propertySources);
}
// 源码位置:org.springframework.web.context.support.StandardServletEnvironment
protected void customizePropertySources(MutablePropertySources propertySources) {
// 9. 增加2个资源对象,jndi比较少用一般没有,这里忽略
// SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams"
// SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams"
propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
if (jndiPresent && JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
}
// 10. 调用父类方法继续增加资源对象
super.customizePropertySources(propertySources);
}
// 源码位置:org.springframework.core.env.StandardEnvironment
protected void customizePropertySources(MutablePropertySources propertySources) {
// 11. 增加2个资源对象
// SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties"
// SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment"
propertySources.addLast(new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
// 源码位置:org.springframework.core.env.AbstractEnvironment
protected void customizePropertySources(MutablePropertySources propertySources) {
// 这里没有实现,由于子类重载了,也没有调到此方法
}
// 上面初始化完资源对象后,环境对象也创建完成,一路return到prepareEnvironment()的configureEnvironment()
// 源码位置:org.springframework.boot.SpringApplication
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 2. 根据application情况创建环境对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 12. 配置环境对象
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 省略其它代码
}
// 源码位置:org.springframework.boot.SpringApplication
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
environment.setConversionService(new ApplicationConversionService());
}
// 13. 继续增加资源对象,传入的是从命令行来的参数数组args
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
// 源码位置:org.springframework.boot.SpringApplication
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
if (!CollectionUtils.isEmpty(this.defaultProperties)) {
DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources);
}
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
// 14. 增加命令行资源对象,注意用的是addFirst,代表顺序在之前加载的资源对象前面
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
// 回到SpringApplication.prepareEnvironment()
// 源码位置:org.springframework.boot.SpringApplication
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 2. 根据application情况创建环境对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 12. 配置环境对象
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 15. attach资源对象
ConfigurationPropertySources.attach(environment);
// 省略其它代码
}
// 源码位置:org.springframework.boot.context.properties.source.ConfigurationPropertySources
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
PropertySource<?> attached = getAttached(sources);
if (attached == null || !isUsingSources(attached, sources)) {
// 16. 增加资源对象,ATTACHED_PROPERTY_SOURCE_NAME = "configurationProperties"
// 这个资源对象没有新的信息,而是封装了一个SpringConfigurationPropertySources,传入了整个MutablePropertySources对象,也就是从它这里也能访问到所有的资源信息
attached = new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME, new SpringConfigurationPropertySources(sources));
}
sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
// 注意位置,目前它排第一位
sources.addFirst(attached);
}
// 回到SpringApplication.prepareEnvironment()
// 源码位置:org.springframework.boot.SpringApplication
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 2. 根据application情况创建环境对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 12. 配置环境对象
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 15. attach资源对象
ConfigurationPropertySources.attach(environment);
// 17. 调用各个listener,根据情况添加资源对象,listeners为SpringApplicationRunListeners类型
listeners.environmentPrepared(bootstrapContext, environment);
// 省略其它代码
}
// 源码位置:org.springframework.boot.SpringApplicationRunListeners
void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
// 18. 用doWithListeners封装了一些调用各个listener这个操作,这里有个lambda表达式,把它整体看做一个参数
doWithListeners("spring.boot.application.environment-prepared", (listener) -> listener.environmentPrepared(bootstrapContext, environment));
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
// 19. doWithListeners继续封装,listenerAction为上面传入的lambda表达式
doWithListeners(stepName, listenerAction, null);
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction) {
StartupStep step = this.applicationStartup.start(stepName);
// 20. 遍历各个listener,调用listener的environmentPrepared()方法,listenerAction为上面传入的lambda表达式提供了environmentPrepared()方法调用
// listeners里面只有一个listener:org.springframework.boot.context.event.EventPublishingRunListener
this.listeners.forEach(listenerAction);
if (stepAction != null) {
stepAction.accept(step);
}
step.end();
}
// 源码位置:org.springframework.boot.context.event.EventPublishingRunListener
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
// 21. 发送ApplicationEnvironmentPreparedEvent事件
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}
// 源码位置:org.springframework.context.event.SimpleApplicationEventMulticaster
public void multicastEvent(ApplicationEvent event) {
// 22. 调用私有方法发送事件
this.multicastEvent(event, this.resolveDefaultEventType(event));
}
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
Executor executor = this.getTaskExecutor(); // 如果获取到线程池则异步发送事件
// 23. 遍历ApplicationListener,触发监听器处理事件,getApplicationListeners()获取到多个listener,
// 这里只关注EnvironmentPostProcessorApplicationListener处理与资源对象有关的内容
for(ApplicationListener<?> listener : this.getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> this.invokeListener(listener, event));
} else {
this.invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = this.getErrorHandler();
if (errorHandler != null) {
try {
this.doInvokeListener(listener, event);
} catch (Throwable err) {
errorHandler.handleError(err);
}
} else {
// 24. 触发单个ApplicationListener处理事件
this.doInvokeListener(listener, event);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
// 25. 调用listener的onApplicationEvent接口
// 这里只关注EnvironmentPostProcessorApplicationListener处理ApplicationEnvironmentPreparedEvent事件
listener.onApplicationEvent(event);
} catch (ClassCastException ex) {
// 省略部分代码
}
}
// 源码位置:org.springframework.boot.env.EnvironmentPostProcessorApplicationListener
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
// 26. 处理ApplicationEnvironmentPreparedEvent事件
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent();
}
if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
SpringApplication application = event.getSpringApplication();
// 27. 遍历多个EnvironmentPostProcessor,后处理环境对象,EnvironmentPostProcessor有下面几个:
// (1) org.springframework.boot.env.RandomValuePropertySourceEnvironmentPostProcessor
// (2) org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
// (3) org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor
// (4) org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor
// (5) org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor
// (6) org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
// (7) org.springframework.boot.autoconfigure.integration.IntegrationPropertiesEnvironmentPostProcessor
// 这里只关注前两个和ConfigDataEnvironmentPostProcessor,其它的是某些情况才用到,用到的时候再摸索,也就是当发现有新的资源,可以翻翻看是哪个processor添加的
for (多个EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(), event.getBootstrapContext())) {
postProcessor.postProcessEnvironment(environment, application);
}
}
// 源码位置:org.springframework.boot.env.RandomValuePropertySourceEnvironmentPostProcessor
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
// 28. 通过RandomValuePropertySource增加random资源对象
RandomValuePropertySource.addToEnvironment(environment, this.logger);
}
// 源码位置:org.springframework.boot.env.RandomValuePropertySource
static void addToEnvironment(ConfigurableEnvironment environment, Log logger) {
MutablePropertySources sources = environment.getPropertySources();
PropertySource<?> existing = sources.get(RANDOM_PROPERTY_SOURCE_NAME);
if (existing != null) {
logger.trace("RandomValuePropertySource already present");
return;
}
RandomValuePropertySource randomSource = new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME);
if (sources.get(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME) != null) {
sources.addAfter(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, randomSource);
}
else {
// 29. 增加random资源对象,RANDOM_PROPERTY_SOURCE_NAME = "random"
sources.addLast(randomSource);
}
logger.trace("RandomValuePropertySource add to Environment");
}
// 源码位置:org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
// SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment"
String sourceName = StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME;
PropertySource<?> propertySource = environment.getPropertySources().get(sourceName);
if (propertySource != null) {
// 30. 调用私有方法替换资源对象
replacePropertySource(environment, sourceName, propertySource, application.getEnvironmentPrefix());
}
}
private void replacePropertySource(ConfigurableEnvironment environment, String sourceName,
PropertySource<?> propertySource, String environmentPrefix) {
Map<String, Object> originalSource = (Map<String, Object>) propertySource.getSource();
// 31. 没有增加资源,只是把资源对象类型把SystemEnvironmentPropertySource替换为OriginAwareSystemEnvironmentPropertySource
SystemEnvironmentPropertySource source = new OriginAwareSystemEnvironmentPropertySource(sourceName, originalSource, environmentPrefix);
environment.getPropertySources().replace(sourceName, source);
}
// 源码位置:org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
// 32. 自定义的配置文件加载,都从这里加载,这块另外细讲
postProcessEnvironment(environment, application.getResourceLoader(), application.getAdditionalProfiles());
}
2.2 系统属性
在上面加载过程的第11步,进行系统属性的加载,这个是比较熟悉的System.getProperties()操作:
// 源码位置:org.springframework.core.env.StandardEnvironment
protected void customizePropertySources(MutablePropertySources propertySources) {
// 增加系统属性信息资源对象,SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties"
propertySources.addLast(new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
// 源码位置:org.springframework.core.env.AbstractEnvironment
public Map<String, Object> getSystemProperties() {
try {
// 获取JVM的系统属性集合,如操作系统名称、Java 版本等。
return (Map) System.getProperties();
}
catch (AccessControlException ex) {
// 异常情况下返回一个只读map,等获取具体属性的时候再调System.getProperty
return (Map) new ReadOnlySystemAttributesMap() {
@Override
@Nullable
protected String getSystemAttribute(String attributeName) {
try {
return System.getProperty(attributeName);
}
catch (AccessControlException ex) {
// 省略部分代码
}
}
};
}
}
2.3 系统环境变量
在上面加载过程的第11步,进行系统环境变量的加载,这个是通过System.getenv()获取:
// 源码位置:org.springframework.core.env.StandardEnvironment
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
// 增加系统环境变量资源对象,SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment"
propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
// 源码位置:org.springframework.core.env.AbstractEnvironment
public Map<String, Object> getSystemEnvironment() {
if (suppressGetenvAccess()) {
return Collections.emptyMap();
}
try {
// 获取系统环境变量
return (Map) System.getenv();
}
catch (AccessControlException ex) {
return (Map) new ReadOnlySystemAttributesMap() {
@Override
@Nullable
// 异常情况下返回一个只读map,等获取具体属性的时候再调System.getenv
protected String getSystemAttribute(String attributeName) {
try {
return System.getenv(attributeName);
}
catch (AccessControlException ex) {
// 省略部分代码
}
}
};
}
}
2.4 随机变量
在上面加载过程的第29步还提供了一个随机变量的配置资源,可以代替平时用的new Random(),也就是可以按正常的用@Value注入一个随机数,每次使用的都是一个新的随机数:
// 源码位置:org.springframework.boot.env.RandomValuePropertySource
static void addToEnvironment(ConfigurableEnvironment environment, Log logger) {
MutablePropertySources sources = environment.getPropertySources();
PropertySource<?> existing = sources.get(RANDOM_PROPERTY_SOURCE_NAME);
if (existing != null) {
logger.trace("RandomValuePropertySource already present");
return;
}
// 增加random资源对象,RANDOM_PROPERTY_SOURCE_NAME = "random"
RandomValuePropertySource randomSource = new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME);
if (sources.get(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME) != null) {
sources.addAfter(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, randomSource);
}
else {
sources.addLast(randomSource);
}
logger.trace("RandomValuePropertySource add to Environment");
}
public Object getProperty(String name) {
// PREFIX = "random.",变量必须以random.作为前缀
if (!name.startsWith(PREFIX)) {
return null;
}
logger.trace(LogMessage.format("Generating random property for '%s'", name));
return getRandomValue(name.substring(PREFIX.length()));
}
private Object getRandomValue(String type) {
// 支撑的随机数类型:int、long、uuid、字节,每调用一次都会产生不同的随机数
if (type.equals("int")) {
return getSource().nextInt();
}
if (type.equals("long")) {
return getSource().nextLong();
}
String range = getRange(type, "int");
if (range != null) {
return getNextIntInRange(Range.of(range, Integer::parseInt));
}
range = getRange(type, "long");
if (range != null) {
return getNextLongInRange(Range.of(range, Long::parseLong));
}
if (type.equals("uuid")) {
return UUID.randomUUID().toString();
}
return getRandomBytes();
}
private Object getRandomBytes() {
byte[] bytes = new byte[32];
getSource().nextBytes(bytes);
return DigestUtils.md5DigestAsHex(bytes);
}
可以这样使用:
// 使用方法一:直接用@Value注入
// 产生一个在10到20直接的随机整数
@Value("${random.int(10,20)}")
private int randomInt;
// 产生一个不指定范围的随机长整形数
@Value("${random.long}")
private long randomLong;
// 产生一个不指定范围的随机长整形数
@Value("${random.uuid}")
private String randomUuid;
// 产生一个不指定范围的随机长整形数
@Value("${random.other}")
private Object randomOtherType;
输出:
randomInt=12
randomLong=7441928871694155576
randomUuid=e0543df9-7623-42bf-9fe5-1ad7a180e74d
randomOtherType=b57ed309eb16895fc360ea4bd4f35277
// 注意:如果服务是一个单例的,则第一次赋值之后不会再变化。
// 使用方法二:通过Environment获取
// 在配置文件application.properties里配置
srvpro.random.int=${random.int(10,20)}
// 在代码里注入Environment
@Autowired
private Environment environment;
// 通过Environment获取
environment.getProperty("srvpro.random.int")
// 注意:由于environment.getProperty最终会触发上面源码RandomValuePropertySource的getProperty(),
// 这个方法每次调用都会产生一个新的随机数,所以每次的数据都是不同的。
3 架构一小步
(1) 使用@Value的方式获取配置,简单清晰;
(2) 如果使用Environment获取配置,最好封装一个服务,既可以隔离一下容易测试,也可以统一管理用到的系统变量。