EnvironmentPostProcessor怎么做单元测试?阿里P7告诉你

简介: 从Spring Boot 1.3开始,我们可以在应用程序上下文刷新之前使用EnvironmentPostProcessor来自定义应用程序的Environment。Environment表示当前应用程序运行的环境,它可以统一访问各种属性源中的属性,如属性文件、JVM系统属性、系统环境变量和Servlet上下文参数。使用EnvironmentPostProcessor可以在bean初始化之前对Environment进行修改。

简介

从Spring Boot 1.3开始,我们可以在应用程序上下文刷新之前使用EnvironmentPostProcessor来自定义应用程序的EnvironmentEnvironment表示当前应用程序运行的环境,它可以统一访问各种属性源中的属性,如属性文件、JVM系统属性、系统环境变量和Servlet上下文参数。使用EnvironmentPostProcessor可以在bean初始化之前对Environment进行修改。

使用示例

让我们设想一个需求,配置文件中的数据库密码是加密后的密文,如:

spring.datasource.password=js8sbAwkduzPTEWQrlDbTw==

在应用启动时,对密文进行解密后再进行数据库的连接。

针对这种需求,就可以通过EnvironmentPostProcessor对密文进行解密,重新放到Environment中。

1.实现EnvironmentPostProcessor

package one.more;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;

import java.util.Properties;

public class DecodeEnvironmentPostProcessor implements EnvironmentPostProcessor {

    public static final String SPRING_DATASOURCE_PASSWORD = "spring.datasource.password";
    public static final String AES_SECRET = "OneMore";

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        String password = environment.getProperty(SPRING_DATASOURCE_PASSWORD);
        Properties properties = new Properties();
        properties.setProperty(SPRING_DATASOURCE_PASSWORD, AESUtil.decrypt(password, AES_SECRET));
        PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource(SPRING_DATASOURCE_PASSWORD,
                properties);
        environment.getPropertySources().addFirst(propertiesPropertySource);
    }
}

如果你希望EnvironmentPostProcessor按照特定的顺序被调用,可以实现Ordered接口,或者使用@Order注解。

2.注册实现类

想要在Spring Boot启动过程中调用这个实现类,我们还需要在META-INF/ Spring .factories中注册这个实现类:

org.springframework.boot.env.EnvironmentPostProcessor=
  one.more.DecodeEnvironmentPostProcessor

单元测试

下面介绍本文的重点:怎么做EnvironmentPostProcessor实现类的单元测试,话不多说,直接上代码:

package one.more;

import org.junit.Assert;
import org.junit.Test;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;

import java.util.Properties;

public class DecodeEnvironmentPostProcessorTest {

    @Test
    public void testPostProcessEnvironment() {
        DecodeEnvironmentPostProcessor processor = new DecodeEnvironmentPostProcessor();
        String password = "one-more";
        Properties properties = new Properties();
        properties.setProperty(DecodeEnvironmentPostProcessor.SPRING_DATASOURCE_PASSWORD,
                AESUtil.encrypt(password, DecodeEnvironmentPostProcessor.AES_SECRET));
        ConfigurableEnvironment environment = getEnvironment(processor, properties);

        Assert.assertEquals(password,
                environment.getProperty(DecodeEnvironmentPostProcessor.SPRING_DATASOURCE_PASSWORD));

    }

    /**
     * 获取一个经过EnvironmentPostProcessor处理过的Environment
     *
     * @param processor  EnvironmentPostProcessor实现类的实例
     * @param properties 预置准备做单元测试的属性
     * @return 处理过的Environment
     */
    private ConfigurableEnvironment getEnvironment(EnvironmentPostProcessor processor, Properties properties) {
        // 创建一个SpringApplication
        SpringApplication springApplication = new SpringApplicationBuilder()
                .sources(DecodeEnvironmentPostProcessor.class)
                .web(WebApplicationType.NONE).build();
        // 获取应用上下文
        ConfigurableApplicationContext context = springApplication.run();
        // 获取Environment
        ConfigurableEnvironment environment = context.getEnvironment();
        //添加准备做单元测试的属性
        environment.getPropertySources()
                .addFirst(new PropertiesPropertySource("test", properties));
        processor.postProcessEnvironment(environment, springApplication);
        context.close();
        return environment;
    }
}

附:加解密工具类代码

package one.more;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

public class AESUtil {

    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
    private static final String KEY_ALGORITHM = "AES";

    public static String encrypt(String content, String password) {
        try {
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            byte[] byteContent = content.getBytes("utf-8");
            cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(password));
            byte[] result = cipher.doFinal(byteContent);
            return Base64.encodeBase64String(result);
        } catch (Exception ex) {

        }
        return null;
    }

    public static String decrypt(String content, String password) {
        try {
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, getSecretKey(password));
            byte[] result = cipher.doFinal(Base64.decodeBase64(content));
            return new String(result, "utf-8");
        } catch (Exception ex) {
        }
        return null;
    }

    private static SecretKeySpec getSecretKey(final String password) {
        KeyGenerator kg = null;
        try {
            kg = KeyGenerator.getInstance(KEY_ALGORITHM);
            kg.init(128, new SecureRandom(password.getBytes()));
            SecretKey secretKey = kg.generateKey();
            return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);
        } catch (NoSuchAlgorithmException ex) {

        }
        return null;
    }
}
相关文章
|
4月前
|
人工智能 搜索推荐 Serverless
使用金庸的著作,来测试阿里通义千问最新开放的长文档处理功能
使用金庸的著作,来测试阿里通义千问最新开放的长文档处理功能
使用金庸的著作,来测试阿里通义千问最新开放的长文档处理功能
|
4月前
|
缓存 运维 容灾
入行5年,谈谈我在阿里做测试开发的经验
作者在阿里一直从事测试开发相关工作,这几年学习很多、收获很多,作者希望给还在该方向摸爬滚打的同学一些启发和方向。
|
4月前
|
运维 负载均衡 网络协议
函数计算FC报错问题之测试报错如何解决
函数计算(Function Compute,FC)是一个事件驱动的全托管计算服务,允许用户编写并上传代码,而无需管理服务器运行和维护;在使用过程中,可能会遇到各种报错,本合集聚焦于函数计算FC常见的报错问题,提供一系列的故障排查指导和解决建议,帮助用户优化云端函数执行
|
1月前
|
缓存 运维 容灾
入行5年,谈谈我在阿里做测试开发的经验
作者在阿里一直从事测试开发相关工作,这几年学习很多、收获很多,作者希望给还在该方向摸爬滚打的同学一些启发和方向。
|
3月前
|
Linux 测试技术 开发工具
CentOS Linux 8使用阿里源(安装jdk11、git测试)
CentOS Linux 8使用阿里源(安装jdk11、git测试)
360 1
|
4月前
|
算法 测试技术 项目管理
阿里十年总结之软件测试的价值
本文是作者十几年工作经验的总结,也对“软件测试的价值”做个探讨,希望有机会跟团队一起走出当前的周期。
|
4月前
|
Kubernetes 架构师 测试技术
软件测试|测试专家(前阿里P8)聊测试职业发展常见瓶颈
软件测试|测试专家(前阿里P8)聊测试职业发展常见瓶颈
97 0
|
机器学习/深度学习 人工智能 自然语言处理
牛刀小试:我用自创的测试集参加了阿里中文竞技场双模型评测
8月我自己创建了一个包含320个问题的大语言模型测试集,刚好阿里魔搭社区正在举办中文模型评测活动,本着对这些模型效果的好奇,刚好手里也有“验丹指南”,所以就抽时间来玩一把模型测试。
|
人工智能 达摩院 自然语言处理
阿里版ChatGPT「通义千问」邀请测试,我们第一时间试了试
阿里版ChatGPT「通义千问」邀请测试,我们第一时间试了试
1114 2
|
测试技术 UED
带你读《2022技术人的百宝黑皮书》——我在阿里做测试,入职5个月的回顾与总结(1)
带你读《2022技术人的百宝黑皮书》——我在阿里做测试,入职5个月的回顾与总结(1)