springboot配置方法
springboot应用application基本配置
springboot的配置文件application.properties存放在resources目录下。当然也可以用yml文件来配置。
配置内容如下:
server.port=8686server.context-path=/happybks
我们在上一文章介绍的示例项目上继续实验。启动项目,发现原先的url已经无法访问了。注意,不是404,是无法访问。也就是整个web服务都没有访问到,因为刚才的web应用已经映射到了/happybks下。
我们按照配置的端口和应用目录访问/hello,就好了。
使用yml文件作为配置文件
前面说了,我们还可以使用yml文件来做配置,这种方式也是springboot推荐的方式:
server: port: 8686 context-path: /happybks
这里需要注意的是,yml文件的语法要求冒号前是配置键,冒号后空格加是配置值。注意一定要有空格,否则不符合yml的语法。子配置项需要另起一行缩进一格。
用idea的好处是,如果你忘记空格,高亮语法能帮你及时识别出来:
正确:(本文出自osc博主happyBKs的博文https://my.oschina.net/happyBKs/blog/edit/1622275)
错误:
获取配置文件中的配置值
springboot访问配置文件也十分方便,直接通过@Value注解${配置键}的方式注入到特定控制器类的属性上。
首先我们需要在配置yml文件中添加一个配置项:
server: port: 8686 context-path: /happybkspictureDir: D:/Test/pic/pets/
然后我们在控制器类中尝试加一个属性,并注解@Value("${pictureDir}")注入配置项pictureDir的值到控制器类的petsPictureDir属性中。
package com.happybks.pets;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class HelloHappyBKsController { @Value("${pictureDir}") private String petsPictureDir; @RequestMapping(value = "/hello",method = RequestMethod.GET) public String sayHello(){ return "Hello! I am HappyBKs, from Oschina! pictureDir="+petsPictureDir; }}
运行:
各类型配置值的获取封装方法
配置文件里的配置项可以通过@Value注解(注解值为配置文件中的配置键名,如果有多级用"."连接)注入到bean(controller等)的属性上。@Value注解支持注入String,int,double等基本类型。但如果想将配合文件中的一组配置项封装成一个属性对象注入是不可以的。例如:
server: port: 8686 context-path: /happybkspictureDir: D:/Test/pic/pets/configPet: breed: n/a price: 0.0 age: 0
如果我在刚才的控制器类中加入属性和注解,spring是无法注入的。springboot虽然强大,但是你的想象力更强大,欢迎聪明的你以后为springboot贡献新特性。
//不正确的用法 @Value("${configPet}") private ConfigPet configPet;
说道这里,如果我们就是想把这种封装好的配置项作为一个bean对象注入怎么办呢?
新建一个bean类ConfigPet,然后在类定义的各个属性上注解@Value将各个属性注入,类本身同时用spring的bean注解(@Component、@Service等都可以实现)。
package com.happybks.pets;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;@Componentpublic class ConfigPet { @Value("${configPet.breed}") private String breed; @Value("${configPet.price}") private double price; @Value("${configPet.age}") private int age; public String getBreed() { return breed; } public void setBreed(String breed) { this.breed = breed; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "ConfigPet{" + "breed='" + breed + '\'' + ", price=" + price + ", age=" + age + '}'; }}
然后在刚才用到配置值的控制器类中加入一个ConfigPet属性,并用@Autowired自动注入属性为ConfigPet对象。
package com.happybks.pets;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class HelloHappyBKsController { @Value("${pictureDir}") private String petsPictureDir; @Autowired private ConfigPet configPet; @RequestMapping(value = "/hello",method = RequestMethod.GET) public String sayHello(){ return "Hello! I am HappyBKs, from Oschina! pictureDir="+petsPictureDir+"\n"+configPet; }}
运行后访问http://localhost:8686/happybks/hello
那也许你说,如果springboot不能自己按照yml文件中配置的两级配置键自动封装成一个bean,那如果二级键的个数很多,那我们就不得不一个属性一个属性的@Value注解了。springboot作为一个一切为了简化配置的新存在,怎么可能不提供一步搞定的方法呢?代价的“约定大于配置”,首先我们还是要创建一个专门的存放各个配置项的bean,然后在bean类定义上注解@ConfigurationProperties的prefix属性上配置为yml文件中的一级键名为前缀,同时,该bean中的所有属性名要与yml文件中的二级配置键名称相同:
package com.happybks.pets;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;@Component@ConfigurationProperties(prefix = "pet")public class PetProperties { private String breed; private double price; private int age; public String getBreed() { return breed; } public void setBreed(String breed) { this.breed = breed; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "PetProperties{" + "breed='" + breed + '\'' + ", price=" + price + ", age=" + age + '}'; }}
然后在使用的地方:
@Autowired private PetProperties petProperties; @RequestMapping(value = "/hello",method = RequestMethod.GET) public String sayHello(){ return petProperties.toString(); }
运行结果:
在配置中引用配置
前面提到,注入配置值到属性,我们用的是@Value注解。但实际上从配置里取值,是通过${配置键名称}表达式。如果是通过表达式取值,那当与@Value注解的注入操作是相互独立的两步操作,即第一步,通过${配置键名称}表达式取配置值;第二步,通过@Value注解将值注入所注解的属性。
你可能会问:啰嗦这么多有什么意义?那么请你联想一下,如果取值真的只是通过${配置键名称}表达式,那么表达式出现和使用的位置可能有多种:1、在配置值中引入其他配置值,可以解决配置项间的值依赖问题。2、在@Value中注入的可以是一个包含${配置键名称}表达式的字符串值,相应的取值操作会由${配置键名称}表达式自行完成。
下面我们在上面的例子中加入两个新的配置项来说明这种量情况:
在配置值中引入其他配置值,例如petDefaultInfo
server: port: 8686 context-path: /happybkspictureDir: D:/Test/pic/pets/configPet: breed: n/a price: 0.0 age: 0petDefaultInfo: "I have a ${configPet.breed}, ${configPet.age} years old. price:$${configPet.price}"
然后在控制器中加入以下两个属性,一个注入这个引入其他配置值的配置值;另一个,直接在@Value注解的赋值操作上做文章。
package com.happybks.pets;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class HelloHappyBKsController { @Value("${pictureDir}") private String petsPictureDir; @Autowired private ConfigPet configPet; @Value("${petDefaultInfo}") private String petDefaultInfo; @Value("${configPet.breed} is $${configPet.price} now!") private String priceBroad; @RequestMapping(value = "/hello",method = RequestMethod.GET) public String sayHello(){ return "Hello! I am HappyBKs, from Oschina! pictureDir="+petsPictureDir+"\n"+configPet+""+petDefaultInfo+""+priceBroad; }}
然后我们运行结果:
多环境配置
在实际开发部署中,我们往往需要根据开发环境、测试环境、生产环境来分别修改一大堆配置项。尤其是移交部署时,向运维部署同事说明不同环境不同的修改地方的时候,总是看他们脸色,开发宝宝十分不爽;当然,混熟了自然好商量,但是需要修改的地方多的时候自己也嫌麻烦。
springboot为我们提供了多种多环境配置的解决办法。今天先介绍第一种,后面的文章再介绍其他的。
我们可以定义多个yml文件。例如,开发和生产,以-xxx为后缀标注
里面的配置的项的键都是一样的,不同的只是配置值。
此时,application.yml文件中的内容就不再是各类配置项了,而是只配置当前的环境是什么,springboot会自己去选择对应的环境的配置文件。
例如:application.yml
spring: profiles: active: dev
或者
spring: profiles: active: prod
application-dev.yml
server: port: 8686 context-path: /happybkspictureDir: D:/Test/pic/pets/configPet: breed: n/a price: 0.0 age: 0petDefaultInfo: "I have a ${configPet.breed}, ${configPet.age} years old. price:$${configPet.price}"
application-prod.yml
server: port: 8689 context-path: /happybkspictureDir: D:/Test/pic/pets/configPet: breed: n/a price: 1.0 age: 1petDefaultInfo: "I have a ${configPet.breed}, ${configPet.age} years old. price:$${configPet.price}"
我们切到dev环境运行:
我们再切到prod环境: