SpringBoot支持动态的读取文件,留下的扩展接口org.springframework.boot.env.EnvironmentPostProcessor
。这个接口是spring包下的,使用这个进行配置文件的集中管理,而不需要每个项目都去配置配置文件。这种方法也是springboot框架留下的一个扩展(可以自己去扩展)
介绍
`EnvironmentPostProcessor` 是 Spring Framework 中的一个接口,用于在 Spring 应用程序的环境配置加载之后,但在应用程序上下文创建之前,对环境进行进一步的自定义处理。这个接口允许开发人员在 Spring 应用程序启动时修改应用程序的环境配置。
通常,Spring 应用程序的环境配置是通过属性文件、YAML 文件、系统属性、命令行参数等方式定义的。`EnvironmentPostProcessor` 提供了一个扩展点,让开发人员可以在应用程序启动时动态修改这些配置,例如,可以根据运行时条件来添加、修改或删除属性值,从而实现更灵活的配置管理。
`EnvironmentPostProcessor` 接口的主要方法是 `postProcessEnvironment`,开发人员需要实现这个方法,以执行他们希望在环境配置加载后进行的自定义处理逻辑。在这个方法中,您可以访问和修改应用程序的环境对象,通常是 `ConfigurableEnvironment` 接口的实例,这允许您操作配置属性、激活的配置文件、属性源等。
使用 `EnvironmentPostProcessor` 可以用于各种场景,例如:
1. 动态配置属性:根据运行时条件设置属性值。
2. 安全性配置:根据环境或安全需求来动态配置密码、密钥等。
3. 多租户支持:为不同租户的应用程序动态配置属性。
4. 特定环境下的配置:根据开发、测试和生产环境的需要,动态修改配置。
`EnvironmentPostProcessor` 是一个高级的 Spring 功能,通常在定制化的场景中使用。要使用它,您需要创建一个实现了该接口的类,并将它注册到 Spring 应用程序的配置中。在注册后,`postProcessEnvironment` 方法将会在 Spring 应用程序启动时执行,允许您修改应用程序的环境配置。
实现demo
自定义CxmRPCConfigEnvironmentPostProcessor类实现EnvironmentPostProcessor接口
public class CxmRPCConfigEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
/**
* The default order for the processor.
*/
public static final int DEFAULT_ORDER = ConfigFileApplicationListener.DEFAULT_ORDER + 1;
private int order = DEFAULT_ORDER;
@Override
public int getOrder() {
return this.order;
}
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
HttpClientUtil httpClientUtil = HttpClientUtil.getInstance();
String userName = environment.getProperty("config.userName");
String password = environment.getProperty("config.password");
String profile = environment.getProperty("spring.profiles.active");
if (profile.equals("local")) {
return;
}
try {
String host = "";
// 获取配置
String configUrl = host + "/getConfig?profile=" + profile + "&userName=" + userName + "&password=" + password;
RpcRequest rpcRequest = new RpcRequest();
rpcRequest.setExecutorApiType("GET");
rpcRequest.setUrl(configUrl);
String config = httpClientUtil.doWithRequest(rpcRequest).getResult().toString();
if (StringUtils.isNotBlank(config)) {
// 递归解析Map
Map<String, Object> configtMap = JsonUtil.json2propMap(config);
// 开启HTTP响应报文压缩
configtMap.put("server.compression.enabled", true);
configtMap.put("server.compression.mime-types", "application/json,application/xml");
MapPropertySource propertySource = new MapPropertySource("cxmcap", configtMap);
// 优先使用远端的服务配置
environment.getPropertySources().addFirst(propertySource);
}
} catch (ParseException | IOException e) {
e.printStackTrace();
throw new RuntimeException("get config error");
} catch (Exception e) {
e.printStackTrace();
}
return;
}
}
在classpath定义一个META-INF
文件夹然后在其下面先建spring.factories
文件,在其中指定:
org.springframework.boot.env.EnvironmentPostProcessor=com.cxm.config.CxmRPCConfigEnvironmentPostProcessor
从远端http服务器上获取到对应的配置文件后,需要将配置文件递归解析成map,然后通过配置开启,开启方法如下:
// 递归解析Map
Map<String, Object> configtMap = JsonUtil.json2propMap(config);
// 开启HTTP响应报文压缩
configtMap.put("server.compression.enabled", true);
configtMap.put("server.compression.mime-types", "application/json,application/xml");
MapPropertySource propertySource = new MapPropertySource("cxmcap", configtMap);
// 优先使用远端的服务配置
environment.getPropertySources().addFirst(propertySource);
其中递归解析map的工具方法源码:
/**
* @throws IOException
* @throws IOException
*
* @Title: json2propMap
* @Description: 解析json 转换 Map
* @param jsonObject
* @return
* @throws
*
*/
public static Map<String, Object> json2propMap(String config) throws IOException {
Map<String, Object> configMap = new HashMap<>();
json2prop(mapper.readTree(config), "", "", configMap);
return configMap;
}
private static void json2prop(JsonNode jsonNode, String tmpKey, String tmpKeyPre, Map<String, Object> configMap) {
Iterator<Map.Entry<String, JsonNode>> iterator = jsonNode.fields();
while (iterator.hasNext()) {
Map.Entry<String, JsonNode> entry = iterator.next();
// 获得 key 和 value 节点
String key = entry.getKey();
JsonNode valueNode = entry.getValue();
if (valueNode.isObject()) {
// 如果 value 是一个对象,则递归解析
json2prop(valueNode, tmpKey + key + ".", tmpKeyPre + key + ".", configMap);
} else if (valueNode.isArray()) {
// 如果 value 是一个数组,则遍历解析其中每个元素
Iterator<JsonNode> elements = valueNode.elements();
int i = 0;
while (elements.hasNext()) {
JsonNode element = elements.next();
json2prop(element, tmpKey + key + "[" + (i++) + "]" + ".", tmpKeyPre + key + ".", configMap);
}
} else {
// 如果是基本类型,则直接放入 Map 中
configMap.put(tmpKey + key, valueNode.asText());
}
}
}
EnvironmentPostProcessor和Ordered的源码说明
1. EnvironmentPostProcessor接口源码:
* Copyright 2012-2019 the original author or authors.
package org.springframework.boot.env;
import org.springframework.boot.SpringApplication;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
/**
* Allows for customization of the application's {@link Environment} prior to the
* application context being refreshed.
* <p>
* EnvironmentPostProcessor implementations have to be registered in
* {@code META-INF/spring.factories}, using the fully qualified name of this class as the
* key.
* <p>
* {@code EnvironmentPostProcessor} processors are encouraged to detect whether Spring's
* {@link org.springframework.core.Ordered Ordered} interface has been implemented or if
* the {@link org.springframework.core.annotation.Order @Order} annotation is present and
* to sort instances accordingly if so prior to invocation.
*
* @author Andy Wilkinson
* @author Stephane Nicoll
* @since 1.3.0
*/
@FunctionalInterface
public interface EnvironmentPostProcessor {
/**
* Post-process the given {@code environment}.
* @param environment the environment to post-process
* @param application the application to which the environment belongs
*/
void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application);
}
里面重点的接口是postProcessEnvironment,我们只需要实现该接口即可
order接口源码:
public interface Ordered {
/**
* Useful constant for the highest precedence value.
* @see java.lang.Integer#MIN_VALUE
*/
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
/**
* Useful constant for the lowest precedence value.
* @see java.lang.Integer#MAX_VALUE
*/
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
/**
* Get the order value of this object.
* <p>Higher values are interpreted as lower priority. As a consequence,
* the object with the lowest value has the highest priority (somewhat
* analogous to Servlet {@code load-on-startup} values).
* <p>Same order values will result in arbitrary sort positions for the
* affected objects.
* @return the order value
* @see #HIGHEST_PRECEDENCE
* @see #LOWEST_PRECEDENCE
*/
int getOrder();
}
在 Spring Framework 中,Ordered
接口是一个标记接口,用于表示对象的顺序(order)。它定义了一个单一的方法 getOrder
,该方法返回一个整数值,表示对象的顺序。Ordered
接口通常与 Spring 中的对象排序和执行顺序相关的功能一起使用,例如,BeanPostProcessor、ApplicationContextInitializer、EnvironmentPostProcessor 等。
具体来说,Ordered
接口的作用如下:
-
定义对象的执行顺序:当在 Spring 上下文中有多个对象实现了
Ordered
接口时,它们可以根据getOrder
方法返回的值来确定它们的执行顺序。较小的整数值表示较高的执行顺序,即具有较小值的对象将在具有较大值的对象之前执行。 -
自定义执行顺序:通过实现
Ordered
接口并提供适当的整数值,您可以自定义 Spring 中各种组件的执行顺序。这对于确保某些操作在其他操作之前或之后执行非常有用。 -
控制生命周期回调顺序:例如,在使用 BeanPostProcessor 进行定制处理时,您可以通过实现
Ordered
接口来指定这些处理器的执行顺序。这对于确保在初始化或销毁 Bean 之前或之后执行特定逻辑非常重要。
EnvironmentPostProcessor接口官网说明
官方文档如下
Allows for customization of the application's {@link Environment} prior to the application context being refreshed.
允许定制应用的上下文的应用环境优于应用的上下文之前被刷新。(意思就是在spring上下文构建之前可以设置一些系统配置)
EnvironmentPostProcessor implementations have to be registered in
META-INF/spring.factories, using the fully qualified name of this class as the key.
EnvironmentPostProcessor的实现类必须要在META-INF/spring.factories
文件中去注册,并且注册的是全类名。
EnvironmentPostProcessor processors are encouraged to detect whether Spring's
org.springframework.core.Ordered
Ordered interface has been implemented or if the@org.springframework.core.annotation.Order
Order annotation is present and to sort instances accordingly if so prior to invocation.
鼓励EnvironmentPostProcessor处理器检测Org.springframework.core.Ordered
注解,这样相应的实例也会按照@Order注解的顺序去被调用。