springboot自定义yml配置文件及其外部部署过程

目录
  • 1、序
  • 2、加载自定义yml文件
    • 2.1、使用@PropertiesSource注解读取yml配置文件-简单版
    • 2.2、使用@PropertiesSource注解读取yml配置文件-不简单版?
    • 2.3、加前缀可行版
  • 3、外部部署
    • 3.1、spring boot核心配置文件外部加载
    • 3.2、在@PropertySource中添加路径
    • 3.3、通过YamlPropertiesFactoryBean添加路径
    • 3.4、自定义yaml文件资源加载类

1、序

背景:有个小项目需要后台,俺顶着Java菜逼的头衔接下来了,被男票疯狂安利spring boot,于是(被逼无奈)开始了边学边开发的躺坑之路……真香,资料超多,超好用!!!

电厂的项目,用了公司自己开发的实时数据库,后台这边就涉及到很多测点的信息需要存储到配置文件(为什么不是关系数据库真的不要问我),并且希望在部署的时候方便修改,考虑到内容颇多,放在application-pro.yml中实在不合适,就加了个point.yml。倒不是因为现场测点信息会变才需要更改,更多的是突然一拍脑袋,发现手抖写错了?

首先,因为一不小心变成了xxx.yml玩家,好好用哦,没能回去xxx.properties,传说中官方不支持像加载xxx.properties配置文件那样使用注解@PropertySource("classpath:xxx.properties")的方式加载yml配置文件,这里要说的就是加载自定义yml文件的方法。

官方说明看一下

加载自定义xxx.properties文件的方法参考这篇文章:

springBoot中的properties配置解析

注意:之前在找多数据源配置的资料时,就因为资料对应的spring boot版本差异搞得很郁闷,请务必注意俺用的版本是:

spring boot 2.13

2、加载自定义yml文件

spring boot的资料非常多,多到非常容易不用动脑就解决了问题呢~项目做完之后冷静下来,觉得还是应该验证一下,毕竟打脸是为了以后有头有脸。

2.1、使用@PropertiesSource注解读取yml配置文件-简单版

按照上面给出的官宣,这条路是不行的。因为没看到文档对应的版本号,还是试一下:

# 配置文件 point.yml
id: 2233
name: Ellie

(呃,这种信息为啥要叫point啊啊啊!

// 配置对应的config类
@Data
@Configuration
@PropertySource(value = {"classpath:point.yml"})
@ConfigurationProperties()
public class TestPoint {
    private int id;
    private String name;
    @Override
    public String toString() {
        return "TestPoint{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

随手糊了个controller来测试

@RestController
public class TestConfigController {
    @Resource
    TestPoint testPoint;
    @ApiOperation("测试 配置文件")
    @RequestMapping(value = "/config")
    public ResultBean<String> testConfig() {
        return ResultBeanUtil.makeOkResp(testPoint.toString());
    }
}

postman搞起来

都挺好!

所以如果只是要读取这样简单的信息的话,直接使用注解@PropertiesSource是可以的,官方说的不确定的影响我也不知道是啥哦。

2.2、使用@PropertiesSource注解读取yml配置文件-不简单版?

加个list<基础类型>看看。

# point.yml
id: 2233
name: Ellie
cards:
  - XD02101263
  - ZY8965
  - GX0009

 // 配置类
 @Data
@Configuration
@PropertySource(value = {"classpath:point.yml"})
@ConfigurationProperties()
public class TestPoint {
    private int id;
    private String name;
    private List<String> cards;
    @Override
    public String toString() {
        return "TestPoint{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", cards=" + cards +
                '}';
    }
}

postman

装逼失败,不行了哦。使用@Value("id")注解也是不行的呢,因为这个注解是用来匹配变量名称和配置文件不一致的情况。

按照其他博客里讲的(才糊代码一个月根本没有深入看原理的我只好是:大佬说啥就是啥),是因为使用@PropertySource注解只能加载yml配置文件,但不能将其配置信息暴露给spring environment,需要手动暴露。方法就是在让application启动的时候把下面的bean加载。

@Bean
	public static PropertySourcesPlaceholderConfigurer properties() {
		PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
		YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
		yaml.setResources(new ClassPathResource("point.yml"));
		configurer.setProperties(yaml.getObject());
		return configurer;
	}

偷懒的我直接丢到了main函数所在的.java文件。运行:

真的不是我截错图哦。

2.3、加前缀可行版

毕竟我这么机智(无脑分析!),悄咪咪加了个前缀,前缀的名字随意取哈,与配置类中对应即可,我只是偷懒叫做prefix。

# point.yml
prefix:
  id: 2233
  name: Ellie
  cards:
    - XD02101263
    - ZY8965
    - GX0009

 // config类
 @Data
@Configuration
@PropertySource(value = {"classpath:point.yml"})
@ConfigurationProperties(prefix = "prefix")
public class TestPoint {
    private int id;
    private String name;
    private List<String> cards;
    @Override
    public String toString() {
        return "TestPoint{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", cards=" + cards +
                '}';
    }
}

都挺好大结局?

求助:

这样为什么可行,俺是一点都不晓得的,如果有大佬路过,请帮忙解答!!!跪谢orz

顺便说一句,出于好奇,试了下某些博文里说的前缀加yml分隔符---配合的方式,感觉上是一本正经胡说八道,实际上也没读出来。读取List<类>也是同样可行的。

# point.yml
prefix:
  id: 2233
  name: Ellie
  cards:
    - name: XD
      code: XD02101263
    - name: ZY
      code: ZY8965
    - name: GX
      code: GX0009

// config 类
@Data
@Configuration
@PropertySource(value = {"classpath:point.yml"})
@ConfigurationProperties(prefix = "prefix")
public class TestPoint {
    private int id;
    private String name;
    private List<Card> cards;
    @Override
    public String toString() {
        return "TestPoint{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", cards=" + cards +
                '}';
    }
}
// 并不需要有什么与众不同的card类
@Data
public class Card {
    private String name;
    private String code;
    @Override
    public String toString() {
        return "Card{" +
                "name='" + name + '\'' +
                ", code='" + code + '\'' +
                '}';
    }
}

请求

查找资料的过程中还看到了一种别致的写法,是为了解决多层嵌套的yml的读写,未验证,因为有选择的话,我不愿意这样写,不过写法确实很别致,哈哈哈!https://www.jb51.net/article/242026.htm

3、外部部署

其实就是把配置文件部署在jar包外部,方便修改而不必重新打包。

3.1、spring boot核心配置文件外部加载

希望外部加载自定义配置文件,需要先了解spring默认的文件加载方式。

spring程序会按优先级从下面这些路径来加载application.properties配置文件:

  • 当前目录下的/config目录
  • 当前目录
  • classpath里的/config目录
  • classpath 根目录

idea中,在源码下的classpath对应src/main/resources很明确,打包后的classpath在哪里俺是不知道的,然后就把打包后的jar包解压看了下,在BOOT-INF\classes下看到了application.yml和point.yml。所以要想覆盖配置文件,我再jar包同级目录下建了config文件夹,修改配置文件内容看覆盖是否生效。

具体操作:

  • 打包的时候默认将application.yml和point.yml打包到jar中(classpath)
  • 部署时,jar包同级目录下建立config文件夹,修改application.yml中端口号和point.yml内容,看修改是否生效。

修改后的point.yml文件如下:

prefix:
  id: 2233
  name: FakeEllie
  cards:
    - name: NONE
      code: 00000001

测试结果:端口号修改生效(application.yml修改生效),修改后的point.yml并未生效。

毕竟自定义配置文件,一厢情愿希望spring boot按照核心文件加载方式加载point.yml,没有生效也在意料之中,不过路并没有堵死。

3.2、在@PropertySource中添加路径

查资料的时候注意到还有这种写法:

@Data
@Configuration
@PropertySource(value = {"file:config/point.yml"})
@ConfigurationProperties(prefix = "prefix")
public class TestPoint {
    private int id;
    private String name;
    private List<Card> cards;
    @Override
    public String toString() {
        return "TestPoint{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", cards=" + cards +
                '}';
    }
}

就是通过file来指定文件路径,之前是classpath来指定资源相对路径,说来神奇,这种方式没有报错,但读取的内容却是classpath下的point.yml,而不是config下的point.yml。

想来是通过@ConfigurationProperties(prefix = "prefix")指定的前缀去classpath下匹配到的。跟@PropertySource(value = {"file:config/point.yml"})大概是没有关系了,忘崽牛奶真好喝。

3.3、通过YamlPropertiesFactoryBean添加路径

回想上面的描述,YamlPropertiesFactoryBean是将配置文件暴露给spring环境的,可以考虑使用它来指定文件路径。

修改bean,添加new FileSystemResource("config/point.yml")来指定config文件夹下的配置。

@Bean
	public static PropertySourcesPlaceholderConfigurer properties() {
		PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
		YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
		yaml.setResources(new ClassPathResource("point.yml"), new FileSystemResource("config/point.yml"));
		configurer.setProperties(yaml.getObject());
		return configurer;
	}

此时配置类上使用@PropertySource(value = {"file:config/point.yml"})这种写法,返回的是

成功了?但是好像搞笑了。不过也说明了配置文件读取的顺序。config文件夹下的有最终决定权。

为了直观些,俺顺手修改jar包同级目录下config文件夹中point.yml配置文件,保证list元素个数相同:

prefix:
  id: 2233
  name: FakeEllie
  cards:
    - name: NONE
      code: 00000001
    - name: NONE
      code: 00000002
    - name: NONE
      code: 00000003

不搞笑了。

但是,改成此时配置类上使用@PropertySource(value = {"classpath:point.yml"})后,返回并没有变化。所以YamlPropertiesFactoryBean将配置文件暴露给spring环境,说的应该就是将文件添加到spring的classpath下了,先读默认的,再读新添加这样子的。

然鹅这样就没有办法在不进行外部配置的时候使用默认的classpath下的配置文件了。

此外,通过YamlPropertiesFactoryBean添加配置文件的方式,就需要保证config/point.yml一定要存在,要想达到不进行外部配置的时候读取默认classpath下point.yml,在进行外部配置的时候读取config/point.yml。那就只好耍流氓了。

@Data
@Configuration
@PropertySource(value = {"file:config/point.yml", "classpath:point.yml"}, ignoreResourceNotFound = true)
@ConfigurationProperties(prefix = "prefix")
public class TestPoint {
    private int id;
    private String name;
    private List<Card> cards;
    @Override
    public String toString() {
        return "TestPoint{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", cards=" + cards +
                '}';
    }
}

重点:然后!在不进行外部配置的时候,config/point.yml内容为空,或者干脆跟classpath下的point.yml内容保持一致。

小孩子才做选择题,我全都想要

虽然看上去像个意外,但是好在意啊啊啊啊,遏制不住的好奇心啊!就是刚刚那个拼起来的返回值。

想看看是不是application.yml覆盖list也会这样,俺把配置类对应的内容举家搬迁到了application.yml中。如下:

// 配置类
@Data
@Configuration
@ConfigurationProperties(prefix = "prefix")
public class TestPoint {
    private int id;
    private String name;
    private List<Card> cards;
    @Override
    public String toString() {
        return "TestPoint{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", cards=" + cards +
                '}';
    }
}

配置类默认读取application.yml。

# classpath:application.yml
prefix:
  id: 2233
  name: Ellie
  cards:
    - name: XD
      code: XD02101263
    - name: ZY
      code: ZY8965
    - name: GX
      code: GX0009
#config/application.yml
prefix:
  id: 2233
  name: FakeEllie
  cards:
    - name: NONE
      code: 00000001

测试结果:

并没有进行拼接啊喂!!!

在各种调换顺序看影响的时候,修改了YamlPropertiesFactoryBean添加source的顺序,返回结果发生了变化。

@Bean
	public static PropertySourcesPlaceholderConfigurer properties() {
		PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
		YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
		yaml.setResources(new FileSystemResource("config/point.yml"), new ClassPathResource("point.yml"));
		configurer.setProperties(yaml.getObject());
        configurer.setIgnoreResourceNotFound(true);
		return configurer;
	}

返回结果

有一种简单的在运行时通过命令参数指定配置文件的方式效果与此类似。

java -jar demo.jar --Dspring.config.location=point.yml

俺的原则时,代码能解决的,就不要交给人来解决。

虽然没有解决任何问题,但是顺便知道了读取的先后顺序就是setResources的先后顺序。卒

所以目前的结论是,对于有list的配置,并且个数发生变化的时候,这种方式并不适用。

3.4、自定义yaml文件资源加载类

在注解@PropertySource中,有个属性factory主要用来声明解析配置文件的类,这个类必须是PropertySourceFactory接口的实现。从这里入手。

参考资料:

Spring Boot自定义加载yml实现,附源码解读

默认调用的是PropertySourceFactory的实现DefaultPropertySourceFactory,因此可以自定义factory实现PropertySourceFactory接口,也可以扩展DefaultPropertySourceFactory类。两种写法的效果是一样的,列出来。

直接实现PropertySourceFactory接口

public class YamlPropertyLoaderFactory implements PropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) throws IOException {
        List<PropertySource<?>> sources = name != null ? new YamlPropertySourceLoader().load(name, encodedResource.getResource()) : new YamlPropertySourceLoader().load(
                getNameForResource(encodedResource.getResource()), encodedResource.getResource());
        if (sources.size() == 0) {
            return null;
        }
        return sources.get(0);
    }
    private static String getNameForResource(Resource resource) {
        String name = resource.getDescription();
        if (!StringUtils.hasText(name)) {
            name = resource.getClass().getSimpleName() + "@" + System.identityHashCode(resource);
        }
        return name;
    }
}

扩展DefaultPropertySourceFactory

 public class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        if (resource == null) {
            return super.createPropertySource(name, resource);
        }
        List<PropertySource<?>> sources = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource());
        if (sources.size() == 0) {
            return super.createPropertySource(name, resource);
        }
        return sources.get(0);
    }
}

建议用第二种方式。

用factory的方式来实现的话,前面莫名其妙加个prefix就可以正常读取的诡异操作也不需要了哦。

使用方式如下:

@Data
@Configuration
@PropertySource(value = {"classpath:point.yml", "file:config/point.yml"}, factory = YamlPropertyLoaderFactory.class, ignoreResourceNotFound = true)
@ConfigurationProperties
public class TestPoint{
    private int id;
    private String name;
    private List<Card> cards;
    @Override
    public String toString() {
        return "TestPoint{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", cards=" + cards +
                '}';
    }
}
# config/point.yml
id: 2233
name: FakeEllie
cards:
  - name: NONE
    code: 00000001

测试结果:

自定义factory的方式,读取多种路径的配置文件时,也是有先后顺序的,就是@PropertySource中value属性指定的顺序,与使用YamlPropertiesFactoryBean将资源暴露给spring环境不同,这个不会有前面出现的“拼接”效果出现,棒呆~

以解决问题为目标和以写清楚文章为目标去看同样的问题,真的是不一样的探索路径呢,凑字数和为了flag不倒的文写的远远超出自己最初的预期,真好,超喜欢!

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • SpringBoot动态修改yml配置文件的方法详解

    目录 前言 具体实现 实现代码 测试 源码 总结 前言 记录下SpringBoot修改yml配置文件后无需重启服务的方式(打包后生效),效果如下: 具体实现 实现代码 pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </d

  • SpringBoot实现yml配置文件为变量赋值

    目录 yml配置文件为变量赋值 1. 创建person类和Car类 2. 为person类创建yml配置文件 3.创建启动类 在yml文件中配置变量 例如:二维码的内容 yml配置文件为变量赋值 1. 创建person类和Car类 在person类上加注释 @ConfigurationProperties(prefix = "person"),表明这个类的成员变量的值从配置类注入. 注意这里的person类的成员变量需要有get/set方法. import org.springfram

  • Springboot打包部署修改配置文件的方法

    一般情况下SpringBoot以Jar包的形式进行打包 打包 - jar包方式 1.pom文件引入插件 <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </bu

  • SpringBoot加载配置文件的实现方式总结

    目录 一.简介 二.代码实践 2.1.通过@value注解实现参数加载 2.2.通过@ConfigurationProperties注解实现参数加载 2.3.通过@PropertySource注解实现配置文件加载 2.4.通过自定义环境处理类,实现配置文件的加载 2.5.最后,我们来介绍一下yml文件读取 三.小结 一.简介 在实际的项目开发过程中,我们经常需要将某些变量从代码里面抽离出来,放在配置文件里面,以便更加统一.灵活的管理服务配置信息.比如,数据库.eureka.zookeeper.r

  • SpringBoot实现多环境配置文件切换教程详解

    目录 背景 解决方案 一.新建配置文件 二. 服务调用测试 2.1 新建调用类 2.2 使用样例项目 三.扩展练习 3.1 使用注解标记配置,首先定义一个接口 3.2 分别定义俩个实现类来实现它 3.3 修改application.yml文件激活配置 3.4 新增查询方法 3.5 使用一个或多个配置文件及激活标记文件 3.6 切换日志文件 背景 很多时候,我们项目在开发环境和生成环境的环境配置是不一样的,例如,数据库配置,在开发的时候,我们一般用测试数据库,而在生产环境的时候,我们是用正式的数据

  • springboot自定义yml配置文件及其外部部署过程

    目录 1.序 2.加载自定义yml文件 2.1.使用@PropertiesSource注解读取yml配置文件-简单版 2.2.使用@PropertiesSource注解读取yml配置文件-不简单版? 2.3.加前缀可行版 3.外部部署 3.1.spring boot核心配置文件外部加载 3.2.在@PropertySource中添加路径 3.3.通过YamlPropertiesFactoryBean添加路径 3.4.自定义yaml文件资源加载类 1.序 背景:有个小项目需要后台,俺顶着Java菜

  • SpringBoot在yml配置文件中配置druid的操作

    最新版的druid和旧版在filter配置方面有些不同,以下是旧版druid中配置filter: spring: ##数据库连接信息 datasource: url: jdbc:mysql://localhost:3306/young username: root password: root driver-class-name: com.mysql.jdbc.Driver ###################以下为druid增加的配置########################### t

  • springboot 使用yml配置文件给静态变量赋值教程

    声明: 此处需求是修改封装的clickhouseUtil数据查询引擎连接工具类.由于此类中的方法都是静态方法.连接地址等参数需要根据不同环境改变.例如开发下地址,测试下地址,生产地址等,所有通过配置文件来获取不同环境下的配置参数,但是使用的方法是静态的,所有不能使用一般情况下的@value直接给变量赋值,需要用到spring 属性的set方法来给静态变量赋值,然后静态方法使用静态变量即可 方法: 第一步:在yml文件中配置需要的参数 clickhouse: address: jdbc:click

  • springboot的yml配置文件通过db2的方式整合mysql的教程

    springboot整合MySQL很简单,多数据源就master,slave就行了,但是在整合DB2就需要另起一行,以下是同一个yml文件 先配置MySQL,代码如下 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource druid: # 主库数据源 master: url: jdbc:mysql://localhost:3308/<数据库名>?useUnicode=true&characterEncoding

  • springboot注入yml配置文件 list报错的解决方案

    目录 springboot注入yml配置文件 list报错 注入list的正确方法 springboot yml 配置文件注入Map,List springboot注入yml配置文件 list报错 springboot中yml配置注入一般使用@Value注解可注入String类型数据,比如: @Value("${config}") String stringConfig; 即可注入属性,而注入list使用此方法则会报错提示Could not resolve placeholder xx

  • SpringBoot从yml配置文件中读常用参数值实例方法

    SpringBoot现在基本上都是使用application-XXX.yml(生产环境为pro,开发测试环境为dev)来配置项目中的一些配置条件,在springboot中还可以通过从yml文件中将yml中的数据直接读取出来. 1.yml文件(这里设置参数的时候往往设置两层前缀,避免在调用的时候该属性中的部分参数名与计算机中的某些名字冲突) test: person: lastName: hello age: 18 boss: false birth: 2017/12/12 maps: {k1:

  • Springboot 读取 yml 配置文件里的参数值

    目录 方式一 方式二 总结 方式一 1.yml配置 yml配置(示例): api: mes: MES_SOCKET: http://192.168.99.140:8081 2.读取 代码如下(示例): package com.jack.modules.wms.api.common.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; impor

  • Java基于自定义类加载器实现热部署过程解析

    热部署: 热部署就是在不重启应用的情况下,当类的定义即字节码文件修改后,能够替换该Class创建的对象.一般情况下,类的加载都是由系统自带的类加载器完成,且对于同一个全限定名的java类,只能被加载一次,而且无法被卸载.可以使用自定义的 ClassLoader 替换系统的加载器,创建一个新的 ClassLoader,再用它加载 Class,得到的 Class 对象就是新的(因为不是同一个类加载器),再用该 Class 对象创建一个实例,从而实现动态更新.如:修改 JSP 文件即生效,就是利用自定

  • Springboot的yml配置文件用法

    目录 一.服务配置 二.数据库连接 三.Mybatis-Plus 四.日志打印 五.Redis 总结 一.服务配置 server: port: 8989 servlet: # context-path就是端口号与服务之间要加的一段路径,比如localhost:8080/wcm/login/login的/wcm就是 context-path: / 二.数据库连接 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url:

随机推荐