热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【熵增教育】SpringBoot的配置外部化——熵增学院

熵,增,教育,springboot,的,配置,外

在前面的课程中,我们给大家分享过SpringBoot精妙的启动配置,主要阐述的是spring的IoC容器在SpringBoot中的加载过程,并与传统项目中Spring的IoC容器加载过程进行了一个对比.我们在开发的过程中,除了IoC容器的配置之外,当然还有许多其他的配置,诸如数据库的链接信息,端口,以及项目的内部使用的一些个性化信息等.那SpringBoot是如何管理这些配置呢?我今天呢,就从以下这三个方面来给大家分享一下SpringBoot是如何管理配置信息的.

  1. 配置文件和属性获取

  2. 配置文件的名字、目录和优先级

  3. 传统的properties与YAML

1.配置文件和属性获

1.1 传统配置文件的值获取与SpringBoot中的值获取

在传统的项目里,我们的配置信息一般都写在配置文件中,然后关于spring需要的信息,我们就在spring的xml文件里引用,大略如下所示:

classpath下的config里创建一个jdbc.properties

jdbc.driverClassName=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/testdb?serverTimezOne=UTC&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true jdbc.username=develop jdbc.password=&dT$BvYdOlH4*m9G

然后在我们的application.xml里引入我们需要的这个数据源:

                                                     ... ...其他配置 

当然除了spring的数据源信息之外,我们往往也会有一些其他的信息,比如项目返回给前端的一些报错信息等,此时我们通常的做法是使用java的原生方法去加载。如以下所示:

在classpath的config下有个webreslt.properties

0=SUCCESS 100000=参数有误 100001=AppKey不能为空 100002=Signature不能为空 100003=参数列表(paramMap)不能为空 100004=secret不能包含在参数列表中 100005=参数签名不合法 100006=手机号不能为空 100007=验证码不能为空 100008=邮件内容不能为空 100009=收件人不能为空 100010=邮件主题不能为空 200000=应用无权限 200001=应用未注册 300000=Api异常 300001=短信发送失败 300002=短信验证失败 300003=邮件发送失败 300004=短信发送超过最大条数限制

获取值的方法如下:

package com.ailu.paas.common.utils; import org.apache.commons.lang3.StringUtils; import java.util.Locale; import java.util.ResourceBundle; public class PropertyFileReader {     public static String getItem(String key) { return getItem(key, ""); } public static String getItem(String key, String defaultValue) { ResourceBundle rb = ResourceBundle.getBundle("config/webresult"); String value = "";         try {          value = new String(rb.getString(key).getBytes("ISO-8859-1"), "UTF-8");         } catch (Exception e) {          e.printStackTrace();         }         if (StringUtils.isEmpty(value)) {          value = defaultValue;         }         return value.trim();     } }

然后在其他的地方,我们就可以直接使用以下这样的方式去获取属性文件中的值了.

String value=PropertyFileReader.getItem(key);

而在SpringBoot中呢,我们则可以使用@Component+@Value这两个组合,来快速的读取配置文件中的值,

仍然是在classpath下,我们在配置文件里写上如下配置:

name="Lianmengtu"

然后我们创建一个java类,并加上@Component和@Value,如下所示:

package top.lianmengtu.testprofile.common; import lombok.Getter; import lombok.Setter; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component @Getter @Setter public class TestProperty {    @Value("${name}")    private String name; }

其中@Component是为了把TestProperty作为组件放入到Spring的IoC容器中,而@Value("${name}")则是为了从配置文件中取值,${name}中的name即是配置文件中的key.然后我们就可以在其他地方通过注入直接使用了:

@Autowired private TestProperty testProperty; public void test(){     System.out.println(testProperty.getName()); }

1.2 随机值的绑定

在某些场景下,我们可能需要在项目的配置中添加一些随机值,并且这些值在我们项目启动后就自动的初始化,而SpringBoot就考虑到了这种情况,于是给我们准备了一些工具,方便我们的使用.使用情况如下:

# 随机字符串 secret=${random.value} #随机数 setup=${random.int} #0-10之间的随机数 range-int=${random.int[0,10]} #生成uuid uuid=${random.uuid}

获取方式和其他的属性相同,如下所示:

package top.lianmengtu.testprofile.common; import lombok.Getter; import lombok.Setter; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component @Getter @Setter public class TestProperty {     @Value("${secret}")     private String secret;     @Value("${setup}")     private Integer setup;     @Value("${range-int}")     private Integer rangeInt;     @Value("${uuid}")     private String uuid; }

1.3 变量的引用与占位符

有些时候我们会在配置项中引用另一项的值,当然,是以变量的形式进行引用.如下所示:

protocal=http:// domain=${protocal}ask.lianmengtu.top

这里我们看到,在springBoot中使用的变量占位符是${key}.这时候就有一个问题,因为我们现在大多数的开发环境都是maven,我们都知道maven也是支持变量占位的,我们可以在打包不同环境的时候可以激活不同的profile,然后对变量值进行替换,而我们往常在使用Maven的时候,用的变量占位符正好是${key},那此时,我们该怎么办呢?SpringBoot是不是不支持Maven的变量占位呢?我们必须要二选其一吗?

当然不是.SpringBoot也考虑到了这个问题,因此给maven占位提供了另外一个符号即@key@,如下所示:

protocal=http:// domain=${protocal}ask.lianmengtu.top username=@username@ password=@password@

然后我们就可以在我们的pom里这么写了,而当我们激活某一个profile的时候,相应的maven变量就会被替换了

              dev                      dev             dev123                            test                      test             test123               
2. yml与properties

SpringBoot是除了支持properties这种方式之外,它也支持YAML这种方式,并且因为yml结构清晰,又可以继承,所以使用yml这种方式的人也越来越多了.而我就是这么对yml路转粉的.

2.1 yml结构化

yml的第一个好处是结构化,这与properties是明显的差别.这里放上两份同样的配置文件来个视觉对比,首先是properties

environments.dev.url=  environments.dev.name=Developer Setup environments.prod.url=  environments.prod.name=My Cool App my.servers[0]=dev.example.com my.servers[1]=another.example.com

对比yml

environments:     dev:         url: http://dev.example.com         name: Developer Setup     prod:         url: http://another.example.com         name: My Cool App my:     servers: - dev.example.com - another.example.com

这里只是举个例子,所以可能这两三行大家看起来没什么感觉,但要知道在实际的项目中,我们的配置可能包含各种各样的信息,数据库的,缓存的,第三方平台的等等,那时候,如果我们还用properties,那么看着将会非常头大.

2.2 yml的继承

在我们使用配置文件的时候,尤其是分环境使用的时候,常常会碰到这么一个问题: 大多数的项目配置都一样,只有少数的不一样,此时,如果是使用properties,那么我们就只能每个文件各写一份,而在yml里,我们就不用,我们只需要将通用的那部分写到application-common.yml里,然后少量不同的,我们在分环境进行描述,application-dev.yml,application-prod.yml里,这样我们只需要激活一份文件,剩下的就会自动的进行继承和使用了,具体方式如下:

application.yml

app:   name: "lianmengtu"   country: "China"   username: "melon"   password: "melon123"

application-dev.yml:

app:   username: "jacobdev"   password: "dev123456"

application-prod.yml

app:   username: "LMTprod"   password: "prod456"

这样当我们在启动时,激活不同的配置时,username和password会不同,但name和country则是从默认的yml中继承过来的.

2.3 指定配置文件的名字和地址

刚刚我们提到过多种环境,配置文件之所以要区分环境,就是因为有些信息是需要保密的,无法公开.而SpringBoot则允许从外部,通过命令行的形式,对配置文件进行指定,当然也可以指定变量.

2.3.1 指定配置文件的名字

我们可以使用--spring.config.name=xxx 这样的参数形式指定配置文件的名字:

$ java -jar myproject.jar --spring.config.name=myproject

2.3.2 配置指定目录下的配置文件

我们可以使用--spring.config.location=xxxxx这样的参数形式来配置指定目录下的配置文件,如下文则指定了classpath下的config目录下的test.yml

java -jar myproject.jar --spring.config.location=classpath:/config/test.yml

当然从1.x转过来的人可能更喜欢指定目录,这里要特别说明一下,如果--spring.config.location是以目录结尾的,则必须加/ 如下所示:

java -jar myproject.jar --spring.config.location=classpath:/config/

2.3.3 配置的优先级

在我们没有明确指定文件名字的时候,springBoot会按着以下顺序进行考虑

  1. 当前目录下的config目录里的application.properties

  2. 当前目录下的application.properties

  3. classpath下的config目录下的application.properties

  4. classpath下的application.properties

当然除了这些之外,命令行也是可以传参数的,并且命令行参数的优先级是最高的.

3. 一些比较复杂的配置

使用@Value()来进行属性注入有些时候会显得比较笨重,尤其是使用多个配置或者我们的配置项是呈垂直结构化的数据时,更是这样.SpringBoot提供了另外一种方法来处理这类比较复杂的数据.这就是我们要说的@ConfigurationProperties.

首先我们有这样一个配置文件:

app:   name: "lianmengtu"   enabled: false   security:     username: "jacob"     password: "jacob123"     roles:       - USER       - ADMIN

我们看到在这个配置文件里,app下有name属性,有enabled属性,其中较为特殊的是security,因为他还包含了些其他的属性,包括username,包括password,还有一个类型为String的roles集合,那么此时,我们可以对应的写成下面这个属性类

package top.lianmengtu.testprofile.common; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Collections; import java.util.List; @ConfigurationProperties("app") public class ComplexProperty {     private String name;     private boolean enabled;     private final Security security=new Security();     public String getName() {         return name;     }     public void setName(String name) {         this.name = name;     }     public boolean isEnabled() {         return enabled;     }     public void setEnabled(boolean enabled) {         this.enabled = enabled;     }     public Security getSecurity() {         return security;     }     public static class Security {         private String username;         private String password;         private List roles =new ArrayList<>(Collections.singleton("USER"));         public String getUsername() {             return username;         }         public void setUsername(String username) {             this.username = username;         }         public String getPassword() {             return password;         }         public void setPassword(String password) {             this.password = password;         }         public List getRoles() {             return roles;         }         public void setRoles(List roles) {             this.roles = roles;         }     } }

之后,我们可以在我们的service层引用它:

package top.lianmengtu.testprofile.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import top.lianmengtu.testprofile.common.ComplexProperty; import java.util.List; @Service public class PropertyService  {     private final ComplexProperty complexProperty;     @Autowired     public PropertyService(ComplexProperty complexProperty){         this.complexProperty=complexProperty;     }     public String name(){         return complexProperty.getName();     }     public boolean enabled(){         return complexProperty.isEnabled();     }     public String userName(){         return complexProperty.getSecurity().getUsername();     }     public String password(){         return complexProperty.getSecurity().getPassword();     }     public String roles(){         StringBuffer roles=new StringBuffer();         List roleArray=complexProperty.getSecurity().getRoles();         roleArray.forEach(role->{             roles.append(role).append("----");         });         return roles.toString();     } }

这里的构造函数注入,我们也可以换成相应的@Autowried注入.为了使这个配置生效,我们需要在Application上加上@EnableConfigurationProperties(ComplexProperty.class)

package top.lianmengtu.testprofile; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; import top.lianmengtu.testprofile.common.ComplexProperty; @SpringBootApplication @EnableConfigurationProperties(ComplexProperty.class) public class Application {     public static void main(String[] args) {         SpringApplication.run(Application.class,args);     } }

刚刚举的那种类型可以说是比较复杂的一个类型了,除了那种内嵌的形式之外,如果说我们只想得到内嵌类里面的属性或者说只有内嵌类里面有属性,则我们可以写成以下这种形式,其他的地方都不用变.这种形式我们称之为relaxed binding

@ConfigurationProperties("app.security") public class ComplexProperty {     private String username;     private String password;     private List roles;     public String getUsername() {         return username;     }     public void setUsername(String username) {         this.username = username;     }     public String getPassword() {         return password;     }     public void setPassword(String password) {         this.password = password;     }     public List getRoles() {         return roles;     }     public void setRoles(List roles) {         this.roles = roles;     } }

并且也支持map类型,配置文件如下所示,其中key可以有两种指定方式,一种是"[/key]",一种是/key:

app:   name: "lianmengtu"   enabled: false   security:     username: "jacob"     password: "jacob123"     roles:       - USER       - ADMIN     work:       "[/position]": "CEO"       "[/company]": "bat"       /address: "BeiJing"

而我们的配置类则如下所示:

package top.lianmengtu.testprofile.common; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.List; import java.util.Map; @ConfigurationProperties("app.security") public class ComplexProperty {     private String username;     private String password;     private List roles;     private Map work;     public String getUsername() {         return username;     }     public void setUsername(String username) {         this.username = username;     }     public String getPassword() {         return password;     }     public void setPassword(String password) {         this.password = password;     }     public List getRoles() {         return roles;     }     public void setRoles(List roles) {         this.roles = roles;     }     public Map getWork() {         return work;     }     public void setWork(Map work) {         this.work = work;     } }

这两种方式都是Ok的,

总结

今天的内容,其实到这里就已经结束了,在使用配置文件的过程中,我们还碰到了一些问题,首先,我们在使用这种配置方式,无论是@Component+@Value还是后来的@ConfigurationProperties这两种方式,他都是在进入spring的时候进行初始化的,这也就意味着,如果我们没有从Spring的容器中去取我们的属性容器的话,那么我们的属性值是没有办法注入的,这一点希望大家能够注意,其次,今天只是讲了主要的几种方式,还有一些像复杂类型的属性合并,以及属性验证,这些希望大家可以研究一下,如果有不明白的,欢迎大家在论坛上提出来,我们可以一起探讨.以下是@ConfigurationProperties和@Value的一些对比:

Feature @ConfigurationProperties @Value

Relaxed binding

Yes

No

Meta-data support

Yes

No

SpEL evaluation

No

Yes

转载请注明出处:联盟兔


推荐阅读
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • Activiti7流程定义开发笔记
    本文介绍了Activiti7流程定义的开发笔记,包括流程定义的概念、使用activiti-explorer和activiti-eclipse-designer进行建模的方式,以及生成流程图的方法。还介绍了流程定义部署的概念和步骤,包括将bpmn和png文件添加部署到activiti数据库中的方法,以及使用ZIP包进行部署的方式。同时还提到了activiti.cfg.xml文件的作用。 ... [详细]
  • POCOCLibraies属于功能广泛、轻量级别的开源框架库,它拥有媲美Boost库的功能以及较小的体积广泛应用在物联网平台、工业自动化等领域。POCOCLibrai ... [详细]
  • 精讲代理设计模式
    代理设计模式为其他对象提供一种代理以控制对这个对象的访问。代理模式实现原理代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色ÿ ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了使用postman进行接口测试的方法,以测试用户管理模块为例。首先需要下载并安装postman,然后创建基本的请求并填写用户名密码进行登录测试。接下来可以进行用户查询和新增的测试。在新增时,可以进行异常测试,包括用户名超长和输入特殊字符的情况。通过测试发现后台没有对参数长度和特殊字符进行检查和过滤。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文讨论了在Spring 3.1中,数据源未能自动连接到@Configuration类的错误原因,并提供了解决方法。作者发现了错误的原因,并在代码中手动定义了PersistenceAnnotationBeanPostProcessor。作者删除了该定义后,问题得到解决。此外,作者还指出了默认的PersistenceAnnotationBeanPostProcessor的注册方式,并提供了自定义该bean定义的方法。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
  • Hibernate延迟加载深入分析-集合属性的延迟加载策略
    本文深入分析了Hibernate延迟加载的机制,特别是集合属性的延迟加载策略。通过延迟加载,可以降低系统的内存开销,提高Hibernate的运行性能。对于集合属性,推荐使用延迟加载策略,即在系统需要使用集合属性时才从数据库装载关联的数据,避免一次加载所有集合属性导致性能下降。 ... [详细]
author-avatar
没有水的鱼0713
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有