简介

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

文章持续更新,微信搜索「万猫学社」第一时间阅读,关注后回复「电子书」,免费获取12本Java必读技术书籍。

使用示例

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

spring.datasource.password=js8sbAwkduzPTEWQrlDbTw==

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

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

文章持续更新,微信搜索「万猫学社」第一时间阅读,关注后回复「电子书」,免费获取12本Java必读技术书籍。

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

文章持续更新,微信搜索「万猫学社」第一时间阅读,关注后回复「电子书」,免费获取12本Java必读技术书籍。

单元测试

下面介绍本文的重点:怎么做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;
}
}

文章持续更新,微信搜索「万猫学社」第一时间阅读,关注后回复「电子书」,免费获取12本Java必读技术书籍。

附:加解密工具类代码

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;
}
}

微信公众号:万猫学社

微信扫描二维码

关注后回复「电子书」

获取12本Java必读技术书籍

最新文章

  1. RealProxy实现AOP编程(1)
  2. Jquery判断数组中是否包含某个元素$.inArray()的用法
  3. 妙味5:document.cookie 操作
  4. ruby代码重构第一课
  5. C#语言基础01
  6. javascript跨域请求RESTful Web Service
  7. Form实现无刷新上传文件并返回自定义值
  8. php install extension
  9. CodeForces 621A Wet Shark and Odd and Even
  10. 在java中的Try Catch块-------------异常处理(2)
  11. [Oracle]审计Audit
  12. kafka使用场景
  13. LightGBM算法(转载)
  14. .NET开发工程师职业规划
  15. JAVA正则表达式匹配,替换,查找,切割(转)
  16. ID基本操作(复制页面)(移动页面)(调整跨页页数)(版面调整)5.16
  17. 程序编译运行和exe运行之文件位置的区别
  18. Oracle层次查询和分析函数在号段选取中的应用
  19. 框架: require.js
  20. beta冲刺————第三天(3/5)

热门文章

  1. Private jre vs Public jre
  2. Scala中的IO操作及ArrayBuffer线程安全问题
  3. react第十三单元(react路由-react路由的跳转以及路由信息) #课程目标
  4. AWVS批量导入网站(刷漏洞入门)
  5. C# NPOI Excel多级表头导出多个表
  6. Windows Server 2016介绍与安装
  7. Redis的内存淘汰
  8. JavaScript--总结二(流程控制+调试)
  9. C#中的格式
  10. .NET 云原生架构师训练营(模块二 基础巩固 MongoDB API重构)--学习笔记