小架构step系列22:加载系统配置

发布于:2025-07-25 ⋅ 阅读:(15) ⋅ 点赞:(0)

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获取配置,最好封装一个服务,既可以隔离一下容易测试,也可以统一管理用到的系统变量。


网站公告

今日签到

点亮在社区的每一天
去签到