不学无数——SpringBoot入门Ⅲ

SpringBoot-外部资源的配置

1.外部资源的配置优先级

为了能够在不同的环境运行不同的配置文件,或者有些代码的一些变量是跟随着环境的改变而改变的,这时候就需要在外部做一些配置。SpringBoot允许这么做,并且有一套配置规则。可以通过@Value注解进行将一些变量动态的取出来。SpringBoot支持多种的外部配置的方式,其中的优先级如下:

  1. 全局的设置在根目录中(~/.spring-boot-devtools.properties)
  2. @TestPropertySource在Test模块中的注解
  3. @SpringBootTest#properties也是在Test模块中
  4. 命令行
  5. 命令行中的SPRING_APPLICATION_JSONJSON字符串, 例如java -Dspring.application.json='{"foo":"bar"}' -jar myapp.jar
  6. ServletConfig 初始化参数,可在代码进行配置
  7. ServletContext 初始化参数,可在代码进行配置
  8. 来自java:comp/env的JNDI属性
  9. Java系统属性(System.getProperties())
  10. 操作系统的环境变量
  11. RandomValuePropertySource配置的random.*属性值
  12. jar外部的带指定profile的application.yml,比如application-{profile}.yml
  13. jar内部的带指定profile的application.yml,比如application-{profile}.yml
  14. jar外部的application.yml
  15. jar内部的application.yml
  16. 在自定义的@Configuration类中定于的@PropertySource
  17. 启动的main方法中,定义的默认配置。SpringApplication.setDefaultProperties

下面给出具体的实例:

@SpringBootApplication
@RestController
public class FirstSpringBootApplication {

    @Value("${name}")
    private String name;
    
    @RequestMapping("/")
    String home() {
        return name;
    }

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(FirstSpringBootApplication.class);
        app.run(args);
    }
}

可以在application.yml或者application.properties配置文件中设置name的值

application.yml

name: 不学无数

注意在name: 的冒号后面要加空格

application.properties

name=不学无数

这样在启动项目的时候然后访问http:localhost:8080就可以取出了name的值

不学无数

2.配置随机值

当想要一些随机值的时候SpringBoot也提供了一些方法,随机数具体有哪些应用呢,例如可以防止浏览器认为是相同的资源而去读取缓存,或者是生成验证码,在application.properties配置如下

my.secret=${random.value} --生成随机字符串
my.number=${random.int}   --生成随机数
my.uuid=${random.uuid}    --生成uuid
my.number.less.than.ten=${random.int(10)} -- 生成10以内的随机数
my.number.in.range=${random.int[1024,65536]} --生成1024到65536的随机数

取出的代码如下:

    @Value("${my.secret}")
    private String secret;
    @Value("${my.number}")
    private String number;
    @Value("${my.uuid}")
    private String uuid;
    @Value("${my.number.less.than.ten}")
    private Integer ten;
    @Value("${my.number.in.range}")
    private Integer range;

    @RequestMapping("/")
    String home() {
        StringBuffer stringBuffer=new StringBuffer();
        stringBuffer.append("my.secret:"+secret+"<br/>");
        stringBuffer.append("my.number:"+number+"<br/>");
        stringBuffer.append("my.uuid:"+uuid+"<br/>");
        stringBuffer.append("my.number.less.than.ten}:"+ten+"<br/>");
        stringBuffer.append("my.number.in.range:"+range+"<br/>");
        return stringBuffer.toString();
    }

然后访问http:localhost:8080可以查看到如下的页面


不学无数

3.命令行配置

SpingApplication默认的能够转变任何命令行的参数然后将其配置在Spring中,例如--server.port=9000,那么web启动的端口号就变为了9000。


端口.jpeg

然后启动项目就会发现端口号改变了。

如果不想让命令行的配置加入到Spring环境中的话,可以SpringApplication.setAddCommandLineProperties(false)进行设置

4.资源文件配置

SpringApplication会从application.properties进行加载系统的配置,并且将其配置加入到Spring的环境中

你也可以使用YAML('.yml')来代替.properties

如果不想使用application.properties作为配置文件的名字的话,那么可以选择其他名字的文件作为配置文件。可以具体设置spring.config.name环境变量进行设置。也可以使用spring.config.location后面跟文件的全路径名。

$ java -jar myproject.jar --spring.config.name=myproject
$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

spring.config.name和spring.config.location这两个配置因为是要设置哪个配置文件起作用的,所以必须在程序启动之前设置,例如设置在系统的环境变量中,或者是设置在启动的参数中。

5.独特的配置(Profile-specific Properties)

不知道该怎么翻译,就把英文给加在后面了,以免误人子弟。在公司进行开发的过程中,也许我们会碰到这种情况,例如在开发环境中用的一套配置,而在测试环境是另一套的配置。所以在不同的环境需要有不同的配置文件。SpringBoot也提供了这样一套的配置规则。application-{profile}.properties。其中的profile可以进行配置化,如果没有配置的话那么就会以默认的application-default.properties为配置文件。其中profile的属性可以在application.properties进行配置spring.profiles.active的值。
application.properties配置如下

spring.profiles.active=test --即加载application-test.properties

或者spring.profiles.active=dev --即加载application-dev.properties

application-test.properties

name=BuXueWuShu---Test

application-dev.properties

name=BuXueWuShu---Dev

这样在application.properties中就可以加载不同的配置文件

    @Value("${name}")
    private String name;
    @RequestMapping("/")
    String home() {
        return name;
    }

通过不同的环境配置就可以访问不同值

当然你也可以通过上一节的spring.config.location这个启动的环境变量进行设置想要启动的时候加载具体哪个配置文件

6.资源文件中的占位符

当在资源文件中定义过一些变量的时候,如果在同样的资源文件中要复用,那么也可以进行引用。

application.properties配置

my.name=BuXueWuShu
my.description=My name is ${my.name}

7.使用YAML

YAML是针对于JSON的一种扩展,所以做配置文件是非常不错的选择。并且在SpringApplication中如果你引入了spring-boot-starter的包,那么SpringApplication会自动的支持YAML。

7.1解析YAML

Spring框架提供了两个简便的类能够解析YAML文档。YamlPropertiesFactoryBean解析YAML作为Properties。YamlMapFactoryBean解析YAML作为Map。例如下面的YAML文件中

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

刚才的例子这个YAML将会被解析成

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

YAML列表将会被替代成数组的类型,根据[index]进行取值

my:
servers:
    - dev.example.com
    - another.example.com

上面的例子将会被解析成下面这种形式的

my.servers[0]=dev.example.com
my.servers[1]=another.example.com

如果你想讲这些数据进行取出来的话,新建一个配置类,使用SpringBoot提供的@ConfigurationProperties的注解,然后在此类中得有一个List或者Set集合,然后设置get方法就能将值取出来了,具体实现如下:

@ConfigurationProperties(prefix="my")
public class Config {

    private List<String> servers = new ArrayList<String>();

    public List<String> getServers() {
        return this.servers;
    }
}

7.2 多环境配置YAML

在讲解application.properties的时候如果想使用多环境的配置那么就是设置不同的application-{profile}.properties的文件具体哪个文件生效可以在application.properties中设置spring.profiles.active属性即可。但是在YAML如何在不同的环境中生效呢?当然第一种办法就是和application.properties一样设置不同的yml文件,然后进行设置具体哪个生效即可。YAML也有另一种的方法。如下所示

server:
    address: 192.168.1.100
spring:
    profiles:
        active:在此设置具体哪个profiles生效即可(例如此处填写 test) adress就是192.168.1.120
---
spring:
    profiles: development
server:
    address: 127.0.0.1
---
spring:
    profiles: test
server:
    address: 192.168.1.120

通过"---"设置不同的环境的配置属性

如果你在配置文件中没有设置spring.profiles.active属性那么配置文件就不知道哪个生效,所以他会找一个默认的进行设置。只需要设置spring.profiles:default 即可

server:
  port: 8000
---
spring:
  profiles: default
  security:
    user:
      password: weak

8.通过类型匹配属性(Type-safe Configuration Properties)

通过@Value("${property}")可以将配置中的属性在类中取出来,这样是可以的,但是如果有多个类型要取或者是要取的有着严格的等级。那么取的时候会有些麻烦,SpringBoot提供了一套可以通过类型匹配在相应的Bean实体中取出配置中的属性。而这也是SpringBoot的一项宗旨,尽量减少xml的配置。

在yml文件中的配置如下

acme:
  remote-address: 192.168.1.1
  security:
    username: admin
    roles:
      - USER
      - ADMIN

接收这些配置信息的类如下

package com.example.FirstSpringBoot.Configuration;

import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@ConfigurationProperties("acme")
public class AcmeProperties {

    private boolean enabled;

    private String remoteAddress;

    private final Security security = new Security();

    public boolean isEnabled() { return enabled; }

    public void setEnabled(boolean enabled) { this.enabled=enabled; }

    public String getRemoteAddress() { return remoteAddress; }

    public void setRemoteAddress(String remoteAddress) { this.remoteAddress=remoteAddress; }

    public Security getSecurity() { return security; }

    public static class Security {

        private String username;

        private String password;

        private List<String> 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<String> getRoles() { return roles; }

        public void setRoles(List<String> roles) { this.roles=roles; }

    }
}

然后再写一个类将上面的类进行注册进Spring中

@Configuration
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}

然后在任何类中就可以进行配置了

@Service
public class MyService {

    private final AcmeProperties acmeProperties;

    @Autowired
    public MyService(AcmeProperties acmeProperties){
        this.acmeProperties=acmeProperties;
    }

    @PostConstruct
    public void openConnection() {
        System.out.println("remote-address:"+acmeProperties.getRemoteAddress());
        System.out.println("username:"+acmeProperties.getSecurity().getUsername());
        System.out.println("role:"+acmeProperties.getSecurity().getRoles());
    }
}

这样就可以将配置文件中的属性从bean中取出来,而不是一个一个@Value进行取了。其中@PostConstruct注解意思是在加载Servlet的时候进行运行,并且只运行一次。

2018-07-18 17:55:51.593  INFO 17487 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]
2018-07-18 17:55:51.599  INFO 17487 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-07-18 17:55:51.599  INFO 17487 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-07-18 17:55:51.599  INFO 17487 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-07-18 17:55:51.599  INFO 17487 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
remote-address:192.168.1.1
username:admin
role:[USER, ADMIN]

查看启动信息就可以知道,他是在加载Servlet的时候运行的。

8.1第三方的配置

如果你的配置想要作为一个jar包供第三方进行使用,那么可以在与配置进行类型匹配的类中即上面提到的AcmeProperties类上面加上@Bean注解。这样就可以将配置进行打包传输了。

@ConfigurationProperties(prefix = "acme")
@Bean
public class AcmeProperties() {
    ...
}

8.2对于资源文件的校验

SpringBoiot提供了一套对资源文件的校验。假如在资源文件中某个字段是不可或缺的,只要如下配置即可。

@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {

    @NotNull
    private String remoteAddress;

    // ... getters and setters

}

此时如果资源文件中没有定义acme.remoteAddress的值的话那么在项目启动的时候就会报错

Description:

Binding to target com.example.FirstSpringBoot.Configuration.AcmeProperties@2eadc9f6 failed:

    Property: acme.remoteAddress
    Value: null
    Reason: 不能为null

当复杂的资源文件想要校验怎么办?就如下配置即可

@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {

    @NotNull
    private String remoteAddress;

    @Valid
    private final Security security = new Security();

    // ... getters and setters

    public static class Security {

        @NotEmpty
        public String username;

        // ... getters and setters

    }

}

当然这个@Validated注解是SpringMvc中的注解,这里就不详细解释了。关于@Validated的参考资料:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容