基于EnvironmentPostProcessor实现自定义配置中心

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 接口的作用如下:

  1. 定义对象的执行顺序:当在 Spring 上下文中有多个对象实现了 Ordered 接口时,它们可以根据 getOrder 方法返回的值来确定它们的执行顺序。较小的整数值表示较高的执行顺序,即具有较小值的对象将在具有较大值的对象之前执行。

  2. 自定义执行顺序:通过实现 Ordered 接口并提供适当的整数值,您可以自定义 Spring 中各种组件的执行顺序。这对于确保某些操作在其他操作之前或之后执行非常有用。

  3. 控制生命周期回调顺序:例如,在使用 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注解的顺序去被调用。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值