2、Spring Boot中属性的获取
在本节我们来讨论下如何获取配置文件里面的属性
1、属性的获取
Spring Boot要从application.yml(application.properties)中获取变量的值,比如application.properties中有下面的属性:
huahua=lili
那么要获得这个属性,有以下几种方式
1.1、通过context获取
@SpringBootApplication
public class SpringBootConfigApplication {
public static void main(String[] args) {
ConfigurableApplicationContext application =
SpringApplication.run(
SpringBootConfigApplication.class, args);
System.out.println(application.getEnvironment()
.getProperty("huahua"));
application.close();
}
}
1.2、通过注入Environment对象获取
@Component
public class UserConfig {
@Autowired
private Environment env;
public void show(){
System.out.println("name = " + env.getProperty("huahua"));
}
}
@SpringBootApplication
public class SpringBootConfigApplication {
public static void main(String[] args) {
ConfigurableApplicationContext application =
SpringApplication.run(
SpringBootConfigApplication.class, args);
application.getBean(UserConfig.class).show();
application.close();
}
}
通过env.getProperty()方法可以获取一个整数,比如huahua属性是一个整数。那么可以通过evn的重载方法getProperty(String prop, Class clazz)获取。
System.out.println(env.getProperty("huahua", Integer.class));
来获取,同样也可以获取boolean类型的值
1.3、通过@Value()注解
@Component
public class UserConfig {
@Value("${huahua}")
private String huahua;
public void show(){
System.out.println("name = " + huahua);
}
}
SpringBootConfigApplication类保持不变。然后运行它,仍然可以达到统一的效果。
需要注意的是如果通过@Value注解的形式注解一个没有的属性,那么是会启动报错的,而通过Environment对象获取一个没有的属性,会返回null。
1.4、属性的默认值
比如我们的classpath下没有host的这个属性,那么我们运行下面的代码
@SpringBootApplication
public class SpringBootConfigApplication {
@Value("${host:com.dragon}")
private String host;
public static void main(String[] args) {
ConfigurableApplicationContext application =
SpringApplication.run(
SpringBootConfigApplication.class, args);
System.out.println(application.getEnvironment()
.getProperty("host"));
System.out.println(application.getBean(
SpringBootConfigApplication.class).host);
application.close();
}
}
将会有如下的打印
在上面的示例中,我们将@Value注解了一个默认值。同样Environment对象也是可以有默认值的。看下面的代码
@SpringBootApplication
public class SpringBootConfigApplication {
@Value("${host:com.dragon}")
private String host;
public static void main(String[] args) {
ConfigurableApplicationContext application = SpringApplication.run(SpringBootConfigApplication.class, args);
System.out.println(application.getEnvironment().getProperty("host","dragon.com.cn"));
System.out.println(application.getBean(SpringBootConfigApplication.class).host);
application.close();
}
}
除了上面的2种设置默认值,我们还有下面的方式可以设置默认值
SpringApplication application = new SpringApplication(SpringBootConfigApplication.class);
Map<String, Object> defaultProperties = new HashMap<>();
defaultProperties.put("host", "192.168.1.100");
application.setDefaultProperties(defaultProperties);
然后我们来看启动类
@SpringBootApplication
public class SpringBootConfigApplication {
@Value("${host:com.dragon}")
private String host;
public static void main(String[] args) {
SpringApplication application = new SpringApplication(SpringBootConfigApplication.class);
Map<String, Object> defaultProperties = new HashMap<>();
defaultProperties.put("host", "192.168.1.100");
application.setDefaultProperties(defaultProperties);
ConfigurableApplicationContext context = application.run(args);
System.out.println(context.getEnvironment().getProperty("host","dragon.com.cn"));
System.out.println(context.getBean(SpringBootConfigApplication.class).host);
context.close();
}
}
运行该类,其打印如下
也就是说通过application.setDefaultProperties()设置的方式,优先级最高:
获取属性,但是指定默认值,有3种方式:
a、@Value("${host:com.dragon}")
b、context.getEnvironment().getProperty("host","dragon.com.cn")
c、
SpringApplication application = new SpringApplication(SpringBootConfigApplication.class);
Map<String, Object> defaultProperties = new HashMap<>();
defaultProperties.put("host", "192.168.1.100");
application.setDefaultProperties(defaultProperties);
1.5、配置文件里面获取配置项
配置文件里面除了配置普通的外,还可以使用当前配置的文件。如application.yml有如下配置:
propera: a
properb: 20
content: "propera: ${propera}, properb: ${properb}"
那么content的值就是 propera: a, properb: 20
2、配置文件及其文件路径
Spring Boot默认的配置文件是application.yml(application.properties),
默认的位置是有4个:classpath根目录、classpath:/config、file:/、file:config/。
如果我们的配置文件不叫application.properties,而是叫app.properties,那么很显然这种情况默认是无法启动的。比如说我们的目录结构如下
在我们打成jar包后(这里需要注意的是,默认maven的打包是要执行test里面的逻辑,我们上面的代码里面的src/test/java中是有代码的,所以打包的命令要是clean compile package -Dmaven.test.skip=true),通过spring.config.name可以来指定启动的配置文件:
java -jar spring-boot-config-0.0.1-SNAPSHOT.jar --spring.config.name=app
也可以运行打包完成后的jar。这种情况就是app.properties在classpath目录下。可以只指定配置文件的名称从而忽略后缀。
但是也还有一种情况就是指定外部的配置文件,比如配置文件在某个目录下,那么这时候需要使用spring.config.location来指定:
java -jar spring-boot-config-0.0.1-SNAPSHOT.jar --spring.config.location=file:/Users/aa/Downloads/app.properties
需要注意的是,通过spring.config.location是需要指定配置文件的绝对路径(包含后缀)。同时,也可以指定多个配置文件,多个配置文件用逗号分隔,如:
java -jar spring-boot-config-0.0.1-SNAPSHOT.jar --spring.config.location=file:/Users/aa/Downloads/app.properties,file:/Users/aa/Downloads/app2.properties
这里需要注意一点:逗号之间不能有空格。
通过上面的讲解,我们知道文件的指定方式有两种:1、classpath. 2、file:/ 。第一种是classpath,第二种是文件系统加载。
2.1、其他的配置文件
除了上面的spring boot需要的配置文件,我们可能还有其他的配置文件,比如jdbc.properties,那么这个时候可以通过下面的方式读取:
@Configuration
@PropertySource(value = "classpath:config/jdbc.properties")
public class JdbcConfig {
@Value("$spring.datasource.driverClassName}")
private String driverClassName;
public void show(){
System.out.println("driverClassName : " + driverClassName);
}
}
@PropertySource可以指定多个配置文件。如
@PropertySource(value = {"classpath:config/jdbc.properties", "classpath:config/redis.properties"})
2.2、带有前缀规律的配置文件
比如我们有个book.properties的配置文件,其内容如下:
book.name=spring boot
book.price=1
那么可以通过如下的配置来获取对应的属性
@Configuration
@PropertySource(value = "classpath:config/book.properties")
@ConfigurationProperties(prefix = "book")
public class BookConfig {
private String name;
private String price;
// 省略 getter/setter
}
2.3、其他类型的注入
spring boot除了上面的简单注入属性外,还有其他注入属性。
2.3.1、注入List数据
ds.hosts[0] = 192.168.1.100
ds.hosts[1] = 192.168.1.101
ds.hosts[2] = 192.168.1.102
ds.hosts[3] = 192.168.1.103
上面的方式配置在application.properties(或者能够读取到的其他配置文件)中,然后通过
private List<String> hosts ;
就可以注入就去
2.3.2、注入数组
数组在配置文件中的写法和List一致:
ds.ports[0] = 8000
ds.ports[1] = 8001
ds.ports[2] = 8002
只需要在类文件中声明一个数组就可以获取了:
private String[] ports ;
2.4、动态注入
前面几小节讲解的都是静态文件的注入,Spring Boot也还支持动态文件的注入,这里时候的动态文件的注入是指的通过代码来进行注入。要实现注入动态文件需要一个接口名叫EnvironmentPostProcessor的支持。EnvironmentPostProcessor接口的主要作用就是在Bean的装配期进行回调。
比如说我们在/Users/aa/Downloads/目录下有个app.properties的文件,这个文件是需要加载到Spring Boot中的,那么我们可以这样做
在/Users/aa/Downloads/app.properties文件中输入下面的属性
spring.name=zhangsanaaa声明一个实现了EnvironmentPostProcessor接口的类
package com.dragon.config;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.stereotype.Component;
@Component
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
try(InputStream input = new FileInputStream("/Users/aa/Downloads/app.properties")){
Properties source = new Properties();
source.load(input);
//PropertiesPropertySource构造函数接收2个参数。第一个是取个名称,第二个是数据源
PropertiesPropertySource propertySource = new PropertiesPropertySource("my", source);
environment.getPropertySources().addLast(propertySource);
}catch(IOException e){
}
}
}
3、启动类读取
然后我们在启动类读取这个类.
@SpringBootApplication
public class SpringBootConfigApplication {
public static void main(String[] args) {
ConfigurableApplicationContext application = SpringApplication.run(SpringBootConfigApplication.class, args);
System.out.println(application.getEnvironment().getProperty("spring.name"));
application.close();
}
}
我们的理想是从/Users/aa/Downloads/app.properties这个文件中读取spring.name的属性。但是很遗憾的是取不到该值的。
我们查看EnvironmentPostProcessor的源码会发现下面的注释:
<p>
* EnvironmentPostProcessor implementations have to be registered in
* {@code META-INF/spring.factories}, using the fully qualified name of this class as the
* key.
* <p>
也就是说EnvironmentPostProcessor的实现类必须注册在META-INF/spring.factories文件中。
4、注册EnvironmentPostProcessor实现类
我们在src/main/resources里面新建一个名词为META-INF的文件夹,再新建一个名称为spring.factories的文件,然后在该文件中加入下面的内容:
org.springframework.boot.env.EnvironmentPostProcessor=com.dragon.config.MyEnvironmentPostProcessor
然后再启动SpringBootConfigApplication就可以看到如下的信息
之所以要加入spring.factories文件,是因为EnvironmentPostProcessor是Spring Boot的processor,而不是Spring的,是Spring原生不支持的。
通过了上面的几个步骤,我们就实现了动态注入。这种方式的好处是可以将配置文件集中管理,我们只需要指定配置文件的路径即可,可以实现多个工程的配置文件集中管理,而不需要各个工程各自维护一个配置文件。然后各个工程去相应的目录下读取即可。也可以通过HttpClient将远程的配置文件读取进来。(有点类似Spring Cloud的config-server的功能)
2.5、Profile
一般来说,都会有本地环境、测试环境和公网环境。那么不同的环境对应的配置文件肯定不一样的。这里的Profile就是指的不同的环境。
假设我们的classpath下面有application-test.properties、application-prod.properties这2个配置文件,代表了测试和正式环境,那么我们如何来加载不同的配置环境呢?
可以通过程序指定的代码实现
@SpringBootApplication
public class SpringBootConfigApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(SpringBootConfigApplication.class);
application.setAdditionalProfiles("test");
ConfigurableApplicationContext context = application.run(args);
System.out.println(context.getEnvironment().getProperty("profile"));
}
}
通过application.setAdditionalProfiles(“test”);就可以指定环境为test环境,那么加载的就是application-test.properties。
除了通过代码的方式,还可以通过spring.application.active来指定特定的环境。比如说–spring.profiles.active=test。
下一篇: 实现多线程的第三种方式
推荐阅读
-
spring boot tomcat jdbc pool的属性绑定
-
Spring中如何获取request的方法汇总及其线程安全性分析
-
spring boot 打jar包,获取resource路径下的文件
-
LNAMP架构中后端Apache获取用户真实IP地址的2种方法
-
CSS3中的transform属性进行2D和3D变换的基本用法
-
C#实现获取不同对象中名称相同属性的方法
-
Spring Boot 2.X整合Spring-cache(让你的网站速度飞起来)
-
Spring Boot 2 - 初识与新工程的创建
-
Python中动态获取对象的属性和方法的教程
-
十、Spring boot 简单优雅的整合 Swagger2