work hard work smart

专注于Java后端开发。 不断总结,举一反三。
随笔 - 1158, 文章 - 0, 评论 - 153, 阅读 - 186万
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

SpringBoot Environment使用

Posted on   work hard work smart  阅读(11961)  评论(0编辑  收藏  举报

在上一篇中,我们的类加载器使用environment获取一些属性,如下图

 

 下面我们介绍下environment的使用

1、进入启动方法run,定位到prepareEnvironment方法

 

 

 

2、进到prepareEnvironment方法

 

 

 

3、进入getOrCreateEnvironment方法。实例化了StandardServletEnvironment

 

 

 

进入父类AbstractEnvironment的构造函数

 

 

 

1)、进入customizePropertySources方法。

增加servletConfigInitParams属性源servletContextInitParams属性源,添加jndi属性源

然后调用父类的super.customizePropertySources(propertySources);

1
2
3
4
5
6
7
8
9
protected void customizePropertySources(MutablePropertySources propertySources) {
    propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
    propertySources.addLast(new StubPropertySource("servletContextInitParams"));
    if(JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
        propertySources.addLast(new JndiPropertySource("jndiProperties"));
    }
 
    super.customizePropertySources(propertySources);
}

  

2)、进入customizePropertySources,增加两个属性源(系统属性源和系统环境属性源

1
2
3
4
5
6
protected void customizePropertySources(MutablePropertySources propertySources) {
    propertySources.addLast(
            new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
    propertySources.addLast(
            new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}

 里面getSystemProperties方法,主要有Java的版本号,Java虚拟机名称等

 

 

 

4、然后回到configureEnvironment方法

1
2
3
4
5
6
7
8
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    if (this.addConversionService) {
        ConversionService conversionService = ApplicationConversionService.getSharedInstance();
        environment.setConversionService((ConfigurableConversionService) conversionService);
    }
    configurePropertySources(environment, args);
    configureProfiles(environment, args);
}

  

1) 进入configurePropertySources(environment, args);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
    MutablePropertySources sources = environment.getPropertySources();
    if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
        sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
    }
    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 {
            sources.addFirst(new SimpleCommandLinePropertySource(args));
        }
    }
}

  这个方法主要是添加defaultProperties默认属性源(sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));

)和命令属性源sources.addFirst(new SimpleCommandLinePropertySource(args));

进入SimpleCommandLinePropertySource的构造函数

1
2
3
public SimpleCommandLinePropertySource(String... args) {
    super(new SimpleCommandLineArgsParser().parse(args));
}

  里面调用了parse函数。命令的定义是我们前面介绍过的。解析命令的时候,判断是否以“--”开头

 

 

 

2) 进入configureProfiles。profile我们后面在进行介绍  SpringBoot Profile源码介绍

1
2
3
4
5
6
7
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
    environment.getActiveProfiles(); // ensure they are initialized
    // But these ones should go first (last wins in a property key clash)
    Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
    profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
    environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}

  

3、 listeners.environmentPrepared(environment)

发布environmentPrepared事件,这里就是监听器的实现。进入environmentPrepared方法

1
2
3
4
5
public void environmentPrepared(ConfigurableEnvironment environment) {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.environmentPrepared(environment);
    }
}

 进入listener.environmentPrepared(environment);里面调用广播器广播一个事件

1
2
3
4
5
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
    this.initialMulticaster
            .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}

 进入广播事件方法。

1
2
3
4
5
6
7
8
9
10
11
12
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    Executor executor = getTaskExecutor();
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            invokeListener(listener, event);
        }
    }
}

  在此方法中,获得对该事件感兴趣的监听器,遍历监听器,调用invokeListener方法。

 

监听器的部分在前面已经介绍过了,这里我们主要看下监听器都做了什么?

进入doInvokeListener方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    try {
        listener.onApplicationEvent(event);
    }
    catch (ClassCastException ex) {
        String msg = ex.getMessage();
        if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
            // Possibly a lambda-defined listener which we could not resolve the generic event type for
            // -> let's suppress the exception and just log a debug message.
            Log logger = LogFactory.getLog(getClass());
            if (logger.isTraceEnabled()) {
                logger.trace("Non-matching event type for listener: " + listener, ex);
            }
        }
        else {
            throw ex;
        }
    }
}

  

然后进入listener.onApplicationEvent(event);方法

 

 

 然后进入onApplicationEnvironmentPreparedEvent方法

 

 

 方法loadPostProcessors是获得属性文件中对该监听器的实现类有哪些。

1
2
3
List<EnvironmentPostProcessor> loadPostProcessors() {
    return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());
}  

 该配置文件配置如下:

 

 

 

配置文件由三个EnvironmentPostProcessor,在把当前的添加进去postProcessors.add(this);就有四个了。然后依次遍历这四个EnvironmentPostProcessor

 

 

 

我们进入其中一个EnvironmentPostProcessor,如ConfigFileApplicationListener。里面调用了addPropertySources(environment, application.getResourceLoader());

1
2
3
4
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
    addPropertySources(environment, application.getResourceLoader());
}

  然后进入addPropertySources方法

1
2
3
4
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
    RandomValuePropertySource.addToEnvironment(environment);
    new Loader(environment, resourceLoader).load();
}

  RandomValuePropertySource.addToEnvironment(environment);这个方法添加了一个随机的属性源

1
2
3
4
5
public static void addToEnvironment(ConfigurableEnvironment environment) {
    environment.getPropertySources().addAfter(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
            new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME));
    logger.trace("RandomValuePropertySource add to Environment");
}

  然后进入new Loader(environment, resourceLoader).load();的load方法。添加application-profile.(properties|yml)属性集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void load() {
    this.profiles = new LinkedList<>();
    this.processedProfiles = new LinkedList<>();
    this.activatedProfiles = false;
    this.loaded = new LinkedHashMap<>();
    initializeProfiles();
    while (!this.profiles.isEmpty()) {
        Profile profile = this.profiles.poll();
        if (profile != null && !profile.isDefaultProfile()) {
            addProfileToEnvironment(profile.getName());
        }
        load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false));
        this.processedProfiles.add(profile);
    }
    resetEnvironmentProfiles(this.processedProfiles);
    load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
    addLoadedPropertySources();
}

  关于profile留在后面介绍。  SpringBoot Profile源码介绍

 

 

 

4、bindToSpringApplication(environment);

进入bindToSpringApplication方法

1
2
3
4
5
6
7
8
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
    try {
        Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
    }
    catch (Exception ex) {
        throw new IllegalStateException("Cannot bind to SpringApplication", ex);
    }
}

  

 

5、environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,  deduceEnvironmentClass());

使用判断convertEnvironmentIfNecessary当前环境是否是指定类型,是的话就返回。否的话再new一个环境,把值赋值到新的实例化的环境中

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
StandardEnvironment convertEnvironmentIfNecessary(ConfigurableEnvironment environment,
        Class<? extends StandardEnvironment> type) {
    if (type.equals(environment.getClass())) {
        return (StandardEnvironment) environment;
    }
    return convertEnvironment(environment, type);
}
 
private StandardEnvironment convertEnvironment(ConfigurableEnvironment environment,
        Class<? extends StandardEnvironment> type) {
    StandardEnvironment result = createEnvironment(type);
    result.setActiveProfiles(environment.getActiveProfiles());
    result.setConversionService(environment.getConversionService());
    copyPropertySources(environment, result);
    return result;
}

  

 

6、ConfigurationPropertySources.attach(environment); 增加configurationProperties属性源

 

 

 

 

 

  

 
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
历史上的今天:
2019-03-01 备忘录模式
2014-03-01 XAMPP 在windows下无法启动Apache解决方案
点击右上角即可分享
微信分享提示