最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring如何自定義加載配置文件(分層次加載)

 更新時(shí)間:2023年07月27日 10:49:31   作者:恐龍弟旺仔  
這篇文章主要介紹了Spring如何自定義加載配置文件(分層次加載)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

前言

Spring會(huì)默認(rèn)加載application.properties文件,我們一般可以將配置寫(xiě)在此處。這基本可以滿(mǎn)足我們的常用demo項(xiàng)目使用。   

但是在實(shí)際項(xiàng)目開(kāi)發(fā)中,我們會(huì)將配置文件外置,這樣在我們需要修改配置的時(shí)候就不用將項(xiàng)目重新打包部署了。   

下面我們來(lái)看一下實(shí)際項(xiàng)目開(kāi)發(fā)的需求。

針對(duì)配置分層次加載的需求    

舉給例子

1.我們希望項(xiàng)目啟動(dòng)后會(huì)加載內(nèi)部配置文件(統(tǒng)一命名為env.properties)

2.如果有外置配置文件的話(huà)(路徑設(shè)置為/envconfig/${app.name}/env.properties),則加載外置配置文件,并覆蓋內(nèi)部配置文件的相同key的項(xiàng)

3.如果在項(xiàng)目啟動(dòng)時(shí)候指定了命令行參數(shù),則該參數(shù)級(jí)別最高,可以覆蓋外置配置文件相同key的項(xiàng)

以上這個(gè)需求,我們用目前Spring的加載配置的方式就有點(diǎn)難以完成了。

所以這時(shí)候我們需要自定義加載方式。

環(huán)境準(zhǔn)備    

筆者新建了一個(gè)SpringBoot項(xiàng)目,maven基本配置如下:

?? ?<parent>
? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? <artifactId>spring-boot-starter-parent</artifactId>
? ? ? ? <version>2.2.5.RELEASE</version>
? ? ? ? <relativePath/> <!-- lookup parent from repository -->
? ? </parent>
? ? <dependencies>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? <artifactId>spring-boot-starter</artifactId>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? <artifactId>spring-boot-devtools</artifactId>
? ? ? ? ? ? <scope>runtime</scope>
? ? ? ? ? ? <optional>true</optional>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? <artifactId>spring-boot-starter-test</artifactId>
? ? ? ? ? ? <scope>test</scope>
? ? ? ? </dependency>
? ? ? ? <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.apache.commons</groupId>
? ? ? ? ? ? <artifactId>commons-lang3</artifactId>
? ? ? ? ? ? <version>3.4</version>
? ? ? ? </dependency>
? ? </dependencies>

自定義配置加載器

1.配置加載器processor

/**
?* 客戶(hù)端自定義加載配置
?*
?* @author lucky
?* @create 2020/3/7
?* @since 1.0.0
?*/
public class CustomerConfigLoadProcessor implements EnvironmentPostProcessor {
? ? @Override
? ? public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
? ? ? ? // 我們將主要邏輯都放在ConfigLoader去做
? ? ? ? environment.getPropertySources().addFirst(new ConfigLoader().getPropertySource());
? ? }
}? ? 

2.在/resources/META-INF/下創(chuàng)建spring.factories文件

并添加

org.springframework.boot.env.EnvironmentPostProcessor=com.xw.study.configload.processor.CustomerConfigLoadProcessor? ??

3.實(shí)現(xiàn)配置加載邏輯        

以上spring environment框架搭建好之后,在項(xiàng)目啟動(dòng)時(shí)候就會(huì)去加載ConfigLoader對(duì)應(yīng)的Properties信息到當(dāng)前運(yùn)行環(huán)境中。

下面就來(lái)看下加載邏輯:

/**
?* 配置加載器
?*
?* @author lucky
?* @create 2020/3/7
?* @since 1.0.0
?*/
public class ConfigLoader {
? ? private static Properties prop = new Properties();
? ? public static final String DEFAULT_CONFIG_FILE_NAME = "env.properties";
? ? public static final String SLASH = File.separator;
? ? public ConfigLoader() {
? ? ? ? loadProperties();
? ? }
? ? /**
? ? ?* 加載配置文件分為三個(gè)層次
? ? ?* 1.加載項(xiàng)目?jī)?nèi)置classpath:env.properties
? ? ?* 2.加載外部配置文件env.properties(會(huì)給定一個(gè)默認(rèn)路徑)
? ? ?* 3.加載JVM命令行參數(shù)
? ? ?*/
? ? private void loadProperties() {
? ? ? ? loadLocalProperties();
? ? ? ? loadExtProperties();
? ? ? ? loadSystemEnvProperties();
? ? }
? ? /**
? ? ?* 加載JVM命令行參數(shù)、Environment參數(shù)
? ? ?*/
? ? private void loadSystemEnvProperties() {
? ? ? ? prop.putAll(System.getenv());
? ? ? ? prop.putAll(System.getProperties());
? ? }
? ? /**
? ? ?* 加載外部配置文件env.properties(會(huì)給定一個(gè)默認(rèn)路徑)
? ? ?* 筆者所在公司,會(huì)根據(jù)不同的項(xiàng)目名,統(tǒng)一路徑設(shè)置為
? ? ?* /envconfig/{app.name}/env.properties
? ? ?*/
? ? private void loadExtProperties() {
? ? ? ? // 獲取全路徑
? ? ? ? // 所以需要首先在內(nèi)部env.properties中配置上app.name
? ? ? ? if (prop.containsKey("app.name")) {
? ? ? ? ? ? String appName = prop.getProperty("app.name");
? ? ? ? ? ? String path = SLASH + "envconfig" + SLASH + appName + SLASH + DEFAULT_CONFIG_FILE_NAME;
? ? ? ? ? ? Properties properties = ConfigUtil.loadProperties(path);
? ? ? ? ? ? if (null != properties) {
? ? ? ? ? ? ? ? prop.putAll(properties);
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? /**
? ? ?* 對(duì)外提供的方法,獲取配置信息
? ? ?* @param key key
? ? ?* @return 配置值
? ? ?*/
? ? public static String getValue(String key) {
? ? ? ? return prop.getProperty(key);
? ? }
? ? /**
? ? ?* 加載項(xiàng)目?jī)?nèi)置classpath:env.properties
? ? ?*/
? ? private void loadLocalProperties() {
? ? ? ? Properties properties = ConfigUtil.loadProperties(ConfigUtil.CLASSPATH_FILE_FLAG + DEFAULT_CONFIG_FILE_NAME);
? ? ? ? if (null != properties) {
? ? ? ? ? ? prop.putAll(properties);
? ? ? ? }
? ? }
? ? // 提供給environment.getPropertySources()的加載方法
? ? public PropertiesPropertySource getPropertySource() {
? ? ? ? return new PropertiesPropertySource("configLoader", prop);
? ? }
}

工具類(lèi):ConfigUtil

/**
?* 工具類(lèi)
?* 直接從Sentinel項(xiàng)目拷貝過(guò)來(lái)的
?*
?* @author lucky
?* @create 2020/3/7
?* @since 1.0.0
?*/
public class ConfigUtil {
? ? public static final String CLASSPATH_FILE_FLAG = "classpath:";
? ? /**
? ? ?* <p>Load the properties from provided file.</p>
? ? ?* <p>Currently it supports reading from classpath file or local file.</p>
? ? ?*
? ? ?* @param fileName valid file path
? ? ?* @return the retrieved properties from the file; null if the file not exist
? ? ?*/
? ? public static Properties loadProperties(String fileName) {
? ? ? ? if (StringUtils.isNotBlank(fileName)) {
? ? ? ? ? ? if (absolutePathStart(fileName)) {
? ? ? ? ? ? ? ? return loadPropertiesFromAbsoluteFile(fileName);
? ? ? ? ? ? } else if (fileName.startsWith(CLASSPATH_FILE_FLAG)) {
? ? ? ? ? ? ? ? return loadPropertiesFromClasspathFile(fileName);
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? return loadPropertiesFromRelativeFile(fileName);
? ? ? ? ? ? }
? ? ? ? } else {
? ? ? ? ? ? return null;
? ? ? ? }
? ? }
? ? private static Properties loadPropertiesFromAbsoluteFile(String fileName) {
? ? ? ? Properties properties = null;
? ? ? ? try {
? ? ? ? ? ? File file = new File(fileName);
? ? ? ? ? ? if (!file.exists()) {
? ? ? ? ? ? ? ? return null;
? ? ? ? ? ? }
? ? ? ? ? ? try (BufferedReader bufferedReader =
? ? ? ? ? ? ? ? ? ? ? ? ?new BufferedReader(new InputStreamReader(new FileInputStream(file), getCharset()))) {
? ? ? ? ? ? ? ? properties = new Properties();
? ? ? ? ? ? ? ? properties.load(bufferedReader);
? ? ? ? ? ? }
? ? ? ? } catch (Throwable e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return properties;
? ? }
? ? private static boolean absolutePathStart(String path) {
? ? ? ? File[] files = File.listRoots();
? ? ? ? for (File file : files) {
? ? ? ? ? ? if (path.startsWith(file.getPath())) {
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return false;
? ? }
? ? private static Properties loadPropertiesFromClasspathFile(String fileName) {
? ? ? ? fileName = fileName.substring(CLASSPATH_FILE_FLAG.length()).trim();
? ? ? ? List<URL> list = new ArrayList<>();
? ? ? ? try {
? ? ? ? ? ? Enumeration<URL> urls = getClassLoader().getResources(fileName);
? ? ? ? ? ? list = new ArrayList<>();
? ? ? ? ? ? while (urls.hasMoreElements()) {
? ? ? ? ? ? ? ? list.add(urls.nextElement());
? ? ? ? ? ? }
? ? ? ? } catch (Throwable e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? if (list.isEmpty()) {
? ? ? ? ? ? return null;
? ? ? ? }
? ? ? ? Properties properties = new Properties();
? ? ? ? for (URL url : list) {
? ? ? ? ? ? try (BufferedReader bufferedReader =
? ? ? ? ? ? ? ? ? ? ? ? ?new BufferedReader(new InputStreamReader(url.openStream(), getCharset()))) {
? ? ? ? ? ? ? ? Properties p = new Properties();
? ? ? ? ? ? ? ? p.load(bufferedReader);
? ? ? ? ? ? ? ? properties.putAll(p);
? ? ? ? ? ? } catch (Throwable e) {
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return properties;
? ? }
? ? private static Properties loadPropertiesFromRelativeFile(String fileName) {
? ? ? ? return loadPropertiesFromAbsoluteFile(fileName);
? ? }
? ? private static ClassLoader getClassLoader() {
? ? ? ? ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
? ? ? ? if (classLoader == null) {
? ? ? ? ? ? classLoader = ConfigUtil.class.getClassLoader();
? ? ? ? }
? ? ? ? return classLoader;
? ? }
? ? private static Charset getCharset() {
? ? ? ? // avoid static loop dependencies: SentinelConfig -> SentinelConfigLoader -> ConfigUtil -> SentinelConfig
? ? ? ? // so not use SentinelConfig.charset()
? ? ? ? return Charset.forName(System.getProperty("csp.sentinel.charset", StandardCharsets.UTF_8.name()));
? ? }
? ? public static String addSeparator(String dir) {
? ? ? ? if (!dir.endsWith(File.separator)) {
? ? ? ? ? ? dir += File.separator;
? ? ? ? }
? ? ? ? return dir;
? ? }
? ? public ConfigUtil() {
? ? }
}

代碼不算復(fù)雜,筆者不再詳述。

根據(jù)以上的加載順序,就可以實(shí)現(xiàn) 命令行 > 外部配置文件 > 內(nèi)部配置文件的需求。   

4.測(cè)試

這個(gè)比較簡(jiǎn)單了,用戶(hù)可自行測(cè)試       

1)只有內(nèi)部配置文件           

在/resources下創(chuàng)建env.properties文件       

2)內(nèi)部配置文件、外部配置文件均存在           

滿(mǎn)足1)的同時(shí)(注意有一個(gè)必備項(xiàng)為app.name,筆者自定義為configload),在本地磁盤(pán)創(chuàng)建/envconfig/configload/env.properties文件       

3)添加命令行參數(shù)

在滿(mǎn)足2)的同時(shí),在啟動(dòng)行添加參數(shù)(-D的方式)

筆者測(cè)試代碼:

@SpringBootTest(classes = ConfigloadApplication.class)
@RunWith(SpringRunner.class)
public class ConfigloadApplicationTests {
? ? @Test
? ? public void contextLoads() {
? ? ? ? String s = ConfigLoader.getValue("zookeeper.serverList");
? ? ? ? System.out.println(s);
? ? }
}

總結(jié)

在中大型公司,統(tǒng)一項(xiàng)目配置文件路徑和日志路徑都是一項(xiàng)政治正確的事。

統(tǒng)一這些基本規(guī)范后,可以避免很多奇奇怪怪的問(wèn)題。

這樣就滿(mǎn)足了嘛?

就目前看來(lái)這個(gè)是基本滿(mǎn)足了需求,略微修改下,打成一個(gè)jar包,就可以直接使用了。

但是目前的這種方式,在需要修改配置的時(shí)候,還是需要關(guān)閉應(yīng)用然后修改外部配置文件或者命令行參數(shù)后,再重啟的。

有沒(méi)有那種可以即時(shí)生效的方案呢?答案是:肯定是有的。那就是配置中心。

我們可以引入配置中心,比如開(kāi)源的Apollo,在上述我們的配置加載中,再加一層,從配置中心中加載配置,就可以實(shí)現(xiàn)配置即時(shí)生效。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 對(duì)Java字符串與整形、浮點(diǎn)類(lèi)型之間的相互轉(zhuǎn)換方法總結(jié)

    對(duì)Java字符串與整形、浮點(diǎn)類(lèi)型之間的相互轉(zhuǎn)換方法總結(jié)

    今天小編就為大家分享一篇對(duì)Java字符串與整形、浮點(diǎn)類(lèi)型之間的相互轉(zhuǎn)換方法總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • 淺談java的守護(hù)線程與非守護(hù)線程

    淺談java的守護(hù)線程與非守護(hù)線程

    這篇文章主要介紹了淺談java的守護(hù)線程與非守護(hù)線程,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • Spring?Boot與graphql-java如何構(gòu)建高效GraphQL服務(wù)

    Spring?Boot與graphql-java如何構(gòu)建高效GraphQL服務(wù)

    文章介紹如何在SpringBoot中集成graphql-java庫(kù),通過(guò)定義GraphQLSchema、構(gòu)建實(shí)例及注冊(cè)Web端點(diǎn)實(shí)現(xiàn)高效數(shù)據(jù)查詢(xún)與突變操作,解決RESTAPI的過(guò)度獲取問(wèn)題,提升開(kāi)發(fā)靈活性與性能,并結(jié)合工具如ApolloClient優(yōu)化前端數(shù)據(jù)管理,感興趣的朋友跟隨小編一起看看吧
    2025-09-09
  • 區(qū)分java中String+String和String+char

    區(qū)分java中String+String和String+char

    這篇文章主要向大家詳細(xì)區(qū)分了java中String+String和String+char,感興趣的小伙伴們可以參考一下
    2016-01-01
  • Redis使用RedisTemplate模板類(lèi)的常用操作方式

    Redis使用RedisTemplate模板類(lèi)的常用操作方式

    這篇文章主要介紹了Redis使用RedisTemplate模板類(lèi)的常用操作方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • SpringBoot整合EasyPoi實(shí)現(xiàn)復(fù)雜多級(jí)表頭Excel導(dǎo)出的完整方案

    SpringBoot整合EasyPoi實(shí)現(xiàn)復(fù)雜多級(jí)表頭Excel導(dǎo)出的完整方案

    Easypoi是一款基于Java的開(kāi)源項(xiàng)目,專(zhuān)為簡(jiǎn)化POI操作而設(shè)計(jì),它與SpringBoot的集成使得在處理Excel數(shù)據(jù)時(shí)變得更加便捷高效,在本案例中,我們將深入探討SpringBoot整合EasyPoi實(shí)現(xiàn)復(fù)雜多級(jí)表頭Excel導(dǎo)出的完整方案,需要的朋友可以參考下
    2025-08-08
  • IDEA 2020 2全家桶安裝激活超詳細(xì)圖文教程

    IDEA 2020 2全家桶安裝激活超詳細(xì)圖文教程

    這篇文章主要介紹了IDEA-2020-2 全家桶安裝激活超詳細(xì)教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Springboot文件上傳功能簡(jiǎn)單測(cè)試

    Springboot文件上傳功能簡(jiǎn)單測(cè)試

    這篇文章主要介紹了Springboot文件上傳功能簡(jiǎn)單測(cè)試,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • springboot集成camunda的實(shí)現(xiàn)示例

    springboot集成camunda的實(shí)現(xiàn)示例

    本文主要介紹了springboot集成camunda的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • Java Scoket實(shí)現(xiàn)雙向通信代碼詳解

    Java Scoket實(shí)現(xiàn)雙向通信代碼詳解

    這篇文章主要介紹了Java Scoket實(shí)現(xiàn)雙向通信代碼詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06

最新評(píng)論

垣曲县| 德格县| 略阳县| 林州市| 政和县| 阜阳市| 黄平县| 常山县| 栾川县| 太仆寺旗| 米泉市| 梅州市| 阿拉善右旗| 友谊县| 玛沁县| 平顶山市| 浪卡子县| 格尔木市| 娄底市| 夹江县| 孟州市| 隆林| 普陀区| 容城县| 桐乡市| 霍山县| 玉龙| 奉贤区| 墨脱县| 章丘市| 陈巴尔虎旗| 敖汉旗| 白玉县| 阿巴嘎旗| 松潘县| 朝阳市| 望谟县| 右玉县| 资溪县| 延长县| 临颍县|