静态工具类读取springboot配置文件:通过ConfigurableEnvironment和SpringApplicationRunListener

原创 临窗旋墨 2024-04-19 阅读:4011 分类: 专题: SpringBoot进阶实践 标签: 源码,springboot

静态工具类读取springboot配置文件:通过ConfigurableEnvironment和SpringApplicationRunListener

springboot版本:2.7.3

  1. 有的时候需要在静态工具方法中访问到系统配置的springboot参数:包括application.preperties以及不同的环境或者其他方式import到spring的参数。

  2. springboot的配置文件位置可能是不确定的,有可能在jar中,jar的config中,或者jar的同目录下,或者jar的同目录下的config文件夹中

  3. 所以读取springboot配置文件的工具类无法写死配置文件的目录。

  4. 因此,需要借助springboot去读取这些配置参数。

一 、借助Environment读取参数配置项

Spring Boot 中 Environment 中的配置源于多个配置源,有的来自系统环境变量,有的来自命令行参数,有的来自用户配置。Environment 屏蔽了各个配置源间不同的配置获取方式,对外提供统一的调用接口。并且相关配置的优先级已经处理好,我们只需要从Environment中获取最终的数据即可。

所以,只需要在工具类中SpringBootApplicationPropertiesUtil获取到Environment就能获取到相关配置项

复制代码
public class SpringBootApplicationPropertiesUtil  {

    private static ConfigurableEnvironment environment;
}    

获取的方式有很多,比如 实现ApplicationContextAware接口,重写setApplicationContext, 通过ApplicationContext获取Environment。然后把工具类注册到spring。或者把setEnvironment置为static,在spring启动的合适的时候调用,以注入ConfigurableEnvironment。

二、借助SpringApplicationRunListener提前注入Environment到工具类

  • 我希望Environment的注入时机能更早,在它准备完毕的时候就能注入到工具类中,这些在其他Bean初始化的时候就能可以使用SpringBootApplicationPropertiesUtil,而无法担心Environment尚未注入。

  • 所以这里通过SpringApplicationRunListener#environmentPrepared回调注入Environment

三、springboot(2.7.3)run部分源码

java 复制代码
	public ConfigurableApplicationContext run(String... args) {
		....
        //获取 SpringApplicationRunListener  监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting(bootstrapContext, this.mainApplicationClass);
			
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        	//在prepareEnvironment方法中会调用listeners.environmentPrepared(bootstrapContext, environment);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			configureIgnoreBeanInfo(environment);
			......
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
		.....
		return context;
	}


private ConfigurableEnvironment prepareEnvironment(...) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
        // 通知监听器,环境准备好,  (也是我们需要把environment注入到工具类的时机)  
		listeners.environmentPrepared(bootstrapContext, environment);
		......
		return environment;
	}

如上文代码,springboot在启动的时候,会先准备好ConfigurableEnvironment,然后调用监听器的environmentPrepared方法,做一些回调,这里就是通过这个回调初始化我们的静态工具类。

SpringBootApplicationPropertiesUtil代码

这里直接用工具类本身实现SpringApplicationRunListener,而不再额外的写一个监听器。

使用方式:在resources/META-INFO/spring.factories中配置此监听器:

org.springframework.boot.SpringApplicationRunListener=cn.xuqiudong.common.util.SpringBootApplicationPropertiesUtil

注意注入时机比一般的Bean初始化时机更早,但是在EnvironmentPostProcessor之后

复制代码
package cn.xuqiudong.common.util;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 描述: 通过ConfigurableEnvironment读取springboot 配置文件中的值
 *    比一般的Bean初始化时机更早,但是在EnvironmentPostProcessor之后
 * <p>
 *     通过SpringApplicationRunListener
 *    在spring上下文准备好之前,为SpringBootApplicationPropertiesUtil 工具类设置 ConfigurableEnvironment
 *    在resources/META-INFO/spring.factories中配置此监听器:
 *    org.springframework.boot.SpringApplicationRunListener=cn.xuqiudong.common.util.SpringBootApplicationPropertiesUtil
 * </p>
 * @author Vic.xu
 * @since 2024-04-18 10:33
 */
public class SpringBootApplicationPropertiesUtil implements SpringApplicationRunListener {

    private static ConfigurableEnvironment environment;

    private static final String DELIMITER = ",";

    private static final Logger LOGGER = LoggerFactory.getLogger(SpringBootApplicationPropertiesUtil.class);

    /**
     * 初始化:should declare a public constructor that accepts a {@link SpringApplication}
     *  * instance and a {@code String[]} of arguments.
     * @see SpringApplicationRunListener
     */
    public SpringBootApplicationPropertiesUtil(SpringApplication application, String[] args) {
        LOGGER.info("SpringBootApplicationPropertiesUtil initialized  as  a SpringApplicationRunListener !!!");
    }

    /**
     * 在spring上下文准备好之前,为本工具类设置 ConfigurableEnvironment
     */
    @Override
    public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
        setEnvironment(environment);
    }

    public static void setEnvironment(ConfigurableEnvironment environment) {
        SpringBootApplicationPropertiesUtil.environment = environment;
    }

    /**
     *  获取 String
     * @param key key
     */
    public static String getString(String key) {
        return environment.getProperty(key);
    }


    /**
     *  获取 String
     * @param key key
     * @param defaultValue the default value to return if no value is found
     */
    public static String getString(String key, String defaultValue) {
        return environment.getProperty(key, defaultValue);
    }

    /**
     *  获取 Boolean
     * @param key key
     */
    public static Boolean getBoolean(String key) {
        return environment.getProperty(key, Boolean.class);
    }

    /**
     *  获取 Boolean
     * @param key key
     * @param defaultValue the default value to return if no value is found
     */
    public static Boolean getBoolean(String key, Boolean defaultValue) {
        return environment.getProperty(key, Boolean.class, defaultValue);
    }

    /**
     *  获取 Integer
     * @param key key
     */
    public static Integer getInteger(String key) {
        return environment.getProperty(key, Integer.class);
    }

    /**
     *  获取 Integer
     * @param key key
     * @param defaultValue the default value to return if no value is found
     */
    public static Integer getInteger(String key, Integer defaultValue) {
        return environment.getProperty(key, Integer.class, defaultValue);
    }

    /**
     *  获取 BigDecimal
     * @param key key
     */
    public static BigDecimal getBigDecimal(String key) {
        return environment.getProperty(key, BigDecimal.class);
    }

    /**
     *  获取 BigDecimal
     * @param key key
     * @param defaultValue the default value to return if no value is found
     */
    public static BigDecimal getBigDecimal(String key, BigDecimal defaultValue) {
        return environment.getProperty(key, BigDecimal.class, defaultValue);
    }

    /**
     *  获取 Double
     * @param key key
     */
    public static Double getDouble(String key) {
        return environment.getProperty(key, Double.class);
    }

    /**
     *  获取 Double
     * @param key key
     * @param defaultValue the default value to return if no value is found
     */
    public static Double getDouble(String key, Double defaultValue) {
        return environment.getProperty(key, Double.class, defaultValue);
    }

    /**
     *  获取long
     * @param key key
     * @param defaultValue the default value to return if no value is found
     */
    public static Long getLong(String key, Long defaultValue) {
        return environment.getProperty(key, Long.class, defaultValue);
    }

    /**
     *  获取long
     * @param key key
     */
    public static Long getLong(String key) {
        return environment.getProperty(key, Long.class);
    }

    /**
     * 获取逗号分隔的字符串为List
     * @param key key
     */
    public static List<String> getStringAsList(String key) {
        String property = environment.getProperty(key);
        if (StringUtils.isBlank(property)) {
            return new ArrayList<>();
        }
        return Stream.of(property.split(DELIMITER)).collect(Collectors.toList());
    }

    /**
     * 获取 Map
     * (此处不缓存, 兼容以后动态刷新环境变量的情境)
     * @param prefix key prefix
     */
    public static Map<String, Object> getMapConfig(String prefix) {
        Map<String, Object> result = new HashMap<>();
        if (StringUtils.isBlank(prefix)) {
            return result;
        }
        for (PropertySource<?> propertySource : environment.getPropertySources()) {
            if (propertySource instanceof MapPropertySource) {
                MapPropertySource mapPropertySource = (MapPropertySource) propertySource;
                Map<String, Object> sourceMap = mapPropertySource.getSource();
                for (Map.Entry<String, Object> entry : sourceMap.entrySet()) {
                    String propertyName = entry.getKey();
                    prefix += ".";
                    if (propertyName.startsWith(prefix)) {
                        String keyWithoutPrefix = propertyName.substring(prefix.length());
                        result.put(keyWithoutPrefix, entry.getValue());
                    }
                }
            }
        }
        return result;
    }
}

2024-04-19

源码地址:lcxm-common-util

评论区

avatar
未登录

暂无评论,来发表第一条评论吧~