1

1. Background

Apollo Configuration Center was used in the previous project. After docking with the Apollo Configuration Center, the properties of the Configuration Center can be used in the program. So how is this achieved? When are the properties of the configuration center loaded into the program? So if we find out how this is implemented, can we 从任何地方加载配置属性 , 配置属性的加解密功能呢 ?

2. Demand

需求
从上图中得知,我们的需求很简单,即我们自己定义的属性需要比配置文件中的优先级更高。

3. Analysis

1. When to add our own configuration properties to SpringBoot

When we want to use configuration properties in beans, then our configuration properties must be put into Spring to Environment before the bean is instantiated. That is, our interface needs to be called before application context refreshed , and EnvironmentPostProcessor can just achieve this function.

2. Get the priority of configuration properties

We know that getting properties in Spring has priority.
For example, we have the following configuration properties username

 ├─application.properties
│   >> username=huan
├─application-dev.properties
│   >> username=huan.fu

So what is the value of username at this time? Here is a picture of Apollo to explain this problem.

Reference link: https://www.apolloconfig.com/#/zh/design/apollo-design
配置的优先级
Spring has added ConfigurableEnvironment and PropertySource since version 3.1:

ConfigurableEnvironment

  • Spring's ApplicationContext will contain an Environment (implementing the ConfigurableEnvironment interface)
  • ConfigurableEnvironment itself contains many PropertySource

PropertySource

  • property source
  • It can be understood as the property configuration of many Key-Value

From the diagram above, key most began to appear PropertySource higher priority in the above example in SpringBoot in username The value of username is huan.fu .

3. When to add our own configuration

From the second step 获取配置属性的优先级 , we can see that PropertySource is executed first, so for our configuration to take effect, it must be placed as far ahead as possible.
在这里插入图片描述
As can be seen from the above figure, SpringBoot loads various configurations through EnvironmentPostProcessor to achieve, and the specific implementation is ConfigDataEnvironmentPostProcessor to achieve. Then we write an implementation class of EnvironmentPostProcessor cad11a75de97d59ff64f4474432d9028---, and then execute it after ConfigDataEnvironmentPostProcessor , and add it to the first place in Environment .
保证我们自己的PropertySource在第一位

4. Realization

1. Introduce SpringBoot dependencies

 <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.huan.springcloud</groupId>
    <artifactId>springboot-extension-point</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-extension-point</name>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>

2. Configure properties in application.properties

vim application.properties

 username=huan

3. Write custom properties and add them to Spring Environment

在这里插入图片描述
Notice:
1. If it is found that the log in the program is not output, check whether slf4j is used to output the log. At this time, the log cannot be output because the log system is not initialized. 解决方法如下:

 SpringBoot版本
        >= 2.4 可以参考上图中的使用 DeferredLogFactory 来输出日志
        < 2.4
            1、参考如下链接 https://stackoverflow.com/questions/42839798/how-to-log-errors-in-a-environmentpostprocessor-execution
            2、核心代码:
                @Component
                public class MyEnvironmentPostProcessor implements
                        EnvironmentPostProcessor, ApplicationListener<ApplicationEvent> {
                    private static final DeferredLog log = new DeferredLog();
                    @Override
                    public void postProcessEnvironment(
                            ConfigurableEnvironment env, SpringApplication app) {
                        log.error("This should be printed");
                    }
                    @Override
                    public void onApplicationEvent(ApplicationEvent event) {
                        log.replayTo(MyEnvironmentPostProcessor.class);
                    }
                }

4. Make the custom configuration take effect through SPI

1. Create a new file under src/main/resources META-INF/spring.factories
创建spring.factories文件
2. Configuration

 org.springframework.boot.env.EnvironmentPostProcessor=\
  com.huan.springcloud.extensionpoint.environmentpostprocessor.CustomEnvironmentPostProcessor

5. Write a test class and output the value of the defined username attribute

 @Component
public class PrintCustomizeEnvironmentProperty implements ApplicationRunner {

    private static final Logger log = LoggerFactory.getLogger(PrintCustomizeEnvironmentProperty.class);

    @Value("${username}")
    private String userName;

    @Override
    public void run(ApplicationArguments args) {
        log.info("获取到的 username 的属性值为: {}", userName);
    }
}

6. Running results

运行结果

Five, matters needing attention

1. The log cannot be output

Refer to the solution provided above 3、编写自定义属性并加入Spring Environment中 .

2. The configuration does not take effect

  • Check the priority of EnvironmentPostProcessor to see if @Order or Ordered returns the wrong priority value.
  • See if it is implemented elsewhere EnvironmentPostProcessor or ApplicationContextInitializer or BeanFactoryPostProcessor or BeanDefinitionRegistryPostProcessor etc PropertySource The order of PropertySource .
  • Understanding the order in which Spring gets properties is referenced 2、获取配置属性的优先级

3. How to initialize the log system

The following code initializes the logging system

 org.springframework.boot.context.logging.LoggingApplicationListener

6. Complete code

https://gitee.com/huan1993/spring-cloud-parent/tree/master/springboot/springboot-extension-point/src/main/java/com/huan/springcloud/extensionpoint/environmentpostprocessor

7. Reference link

1. https://github.com/apolloconfig/apollo/blob/master/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/boot/ApolloApplicationContextInitializer.java
2. https://github.com/apolloconfig/apollo/blob/master/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/PropertySourcesProcessor.java

3. https://www.apolloconfig.com/#/zh/design/apollo-design

4. Solve the problem that the log cannot be output in EnvironmentPostProcessor

5. https://docs.spring.io/spring-boot/docs/2.6.6/reference/htmlsingle/#howto.application.customize-the-environment-or-application-context


huan1993
207 声望31 粉丝

java工程师