自定义 PropertySourcesPlaceholderConfigurer,实现加载自定义属性配置

本文将介绍怎样自定义 PropertySourcesPlaceholderConfigurer 类,根据自己需要去加载属性配置文件。

背景

当我们项目下面有很多个模块,而且每个模块都是独立运行时,我们将面临一个问题 “解决配置繁琐问题”(通常需要在每个模块配置数据库、Redis、MongoDB等信息)。

方案

最容易想到的解决方法就是搭建一个配置中心,但是项目已经成型,再去搭建配置中心,成本和工期不允许(就拿 nacos 为例,我们需要将 @Value 注解修改为 @NacosValue,还有其他配置和注解需要引入)。

那么有没有更简单的方法呢!有的,自定义 PropertySourcesPlaceholderConfigurer 类,根据自己的需要从一个默认的全局配置文件加载所有项目模块公用的配置信息。

本文将介绍怎样自定义 PropertySourcesPlaceholderConfigurer,然后实现配置公用。

示例

建立约定

本项目使用 Spring Boot 2.3.1.RELEASE 为基础

本项目采用属性文件 properties 作为默认配置文件

本项目默认将读取 D:\application.properties 配置文件,用户可以通过 my_config 环境变量修改配置文件路径

编写代码

pom.xml

项目依赖信息如下:

<?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.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.hxstrive</groupId>
    <artifactId>springboot_hello_world</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot_hello_world</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.7</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

MyPropertySourcesPlaceholderConfigurer.java

直接继承 PropertySourcesPlaceholderConfigurer 类的 loadProperties() 方法,代码如下:

package com.hxstrive.hello_world.customer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;

/**
 * 自定义属性资源加载器
 * @author huangxin 2022/8/16
 */
@Component
public class MyPropertySourcesPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer {
    private static final Logger LOG = LoggerFactory.getLogger(MyPropertySourcesPlaceholderConfigurer.class);

    /**
     * 默认配置文件路径
     */
    private static final String DEFAULT_CONFIG_PATH = "D:\\application.properties";

    /**
     * 系统配置的环境变量
     */
    private static final String CFG_ENVIRONMENT_VARIABLE = "my_config";

    /**
     * 这里重新加载
     * @param props 元素
     * @throws IOException 异常
     */
    @Override
    protected void loadProperties(Properties props) throws IOException {
        super.loadProperties(props);

        // 开始解析自定义的配置文件
        // 1.获取配置文件路径,如果没有配置环境变量,则采用默认配置文件
        String cfgPath = System.getenv(CFG_ENVIRONMENT_VARIABLE);
        if(StringUtils.isEmpty(cfgPath)) {
            cfgPath = DEFAULT_CONFIG_PATH;
        }

        // 2.判断配置文件是否存在、是否有效
        File file = new File(cfgPath);
        if(!file.exists()) {
            LOG.info("Config file does not exist. path={}", cfgPath);
            return;
        }

        if(!file.isFile()) {
            LOG.info("Config file is not a file. path={}", cfgPath);
            return;
        }

        // 3.开始解析配置文件
        Properties properties = new Properties();
        properties.load(new FileReader(file));
        Enumeration<?> propKeys = properties.propertyNames();
        while(propKeys.hasMoreElements()) {
            String name = String.valueOf(propKeys.nextElement());
            String value = properties.getProperty(name);
            // 在这里可以对加密的 VALUE 进行解密操作
            // 将自定义的配置放到 Spring 标准配置中
            props.put(name, value);
        }
    }

}

MyPropertySourcesPlaceholderConfigurerTest.java

测试代码,使用 @SpringBootTest 注解创建一个简单的测试类,该类通过 @Value 自动注入我们在默认配置文件中配置的属性。代码如下:

package com.hxstrive.hello_world.customer;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class MyPropertySourcesPlaceholderConfigurerTest {

    @Value("${key1}")
    private String key1;

    @Value("${key2}")
    private String key2;

    @Test
    void contextLoads() {
        System.out.println("key1=" + key1);
        System.out.println("key2=" + key2);
    }

}

运行&效果

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.1.RELEASE)

2022-08-17 13:46:11.475  INFO 13388 --- [           main] PropertySourcesPlaceholderConfigurerTest : Starting MyPropertySourcesPlaceholderConfigurerTest on hxstrive with PID 13388 (started by Administrator in D:\learn\demo_workspace\demo_springboot\springboot_hello_world)
2022-08-17 13:46:11.507  INFO 13388 --- [           main] PropertySourcesPlaceholderConfigurerTest : No active profile set, falling back to default profiles: default
2022-08-17 13:46:14.163  INFO 13388 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2022-08-17 13:46:14.600  INFO 13388 --- [           main] PropertySourcesPlaceholderConfigurerTest : Started MyPropertySourcesPlaceholderConfigurerTest in 4.247 seconds (JVM running for 6.795)
key1=value1
key2=def value2
2022-08-17 13:46:15.210  INFO 13388 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

Process finished with exit code 0

扩展

有时,项目鉴于安全的目的,不能在配置文件中以明文的方式配置数据库、Redis、第三方系统的密码。此时,我们可以通过自定义 PropertySourcesPlaceholderConfigurer,实现加载自定义属性配置。在加载自定义属性配置时,根据一些规则,对某些配置进行解密即可。这样就能很好的解决明文密码配置问题,当然还有其他方法,读者可以留言,一起探讨。

注意:使用该种方式加载的属性不能在其他属性配置文件中使用 ${} 进行引用。

如果要解决该问题,可以参考“Spring Boot 加载自定义属性文件(@PropertySource注解)

不是每一次努力都有收获,但是,每一次收获都必须努力。
0 不喜欢
说说我的看法 -
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号