SpringBoot分别提供3中方式读取项目的application.properties配置文件的内容。这个方式分别为:Environment类、@Value注解以及@ConfigurationProperties注解。
你必须要知道的事情:下面提供的三种方式,都可以拿到配置文件的信息,不要纠结那种方式好与坏。你爱用中方式就用那种方式。只要能解决问题就可以了。
1、Environment
Environment是用来读取应用程序运行时的环境变量的类,可以通过key-value的方式读取application.properties和系统环境变量,命令行输入参数,系统属性等,具体如下:
在application.yml文件定义如下:
# 属性配置类的
server:
port: 8082
spring:
main:
banner-mode: console
# 自定义
alipay:
pay:
appid: 123456
notify: http://www.xxx.com
定义读取的类如下:
package com.kuangstudy.web.properties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* Description:
* Author: yykk Administrator
* Version: 1.0
* Create Date Time: 2021/12/11 21:25.
* Update Date Time:
*
* @see
*/
@RestController
public class ReadPropertiesEnvironment {
@Autowired
private Environment environment;
@GetMapping("/read/file")
public Map<String,Object> readInfo(){
Map<String,Object> map = new HashMap<>();
map.put("port",environment.getProperty("server.port"));
map.put("appid",environment.getProperty("alipay.pay.appid"));
map.put("notify",environment.getProperty("alipay.pay.notify"));
map.put("javaversion",environment.getProperty("java.version"));
map.put("javahome",environment.getProperty("JAVA_HOME"));
map.put("mavenhome",environment.getProperty("MAVEN_HOME"));
return map;
}
public static void main(String[] args) {
Properties properties = System.getProperties();
Set<String> strings = properties.stringPropertyNames();
for (String string : strings) {
System.out.println(string+"===>"+properties.get(string));
}
}
}
在浏览器访问
http://localhost:8082/read/file
2、@Value
使用@Value注解读取配置文件内容,具体如下:
package com.kuangstudy.web.properties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* Description:
* Author: yykk Administrator
* Version: 1.0
* Create Date Time: 2021/12/11 21:25.
* Update Date Time:
*
* @see
*/
@RestController
public class ReadPropertiesValue {
@Value("${server.port}")
private Integer port;
@Value("${alipay.pay.appid}")
private String appid;
@Value("${alipay.pay.notify}")
private String notify;
@Value("${java.version}")
private String javaVersion;
@Value("${JAVA_HOME}")
private String javaHome;
@Value("${MAVEN_HOME}")
private String mavenHome;
@GetMapping("/read/value")
public Map<String, Object> readInfo() {
Map<String, Object> map = new HashMap<>();
map.put("port", port);
map.put("appid", appid);
map.put("notify", notify);
map.put("javaversion", javaVersion);
map.put("javahome", javaHome);
map.put("mavenhome", mavenHome);
return map;
}
}
浏览器如下:
结论:其实@Value底层就是Environment.java
3、@ConfigurationProperties (属性配置类的底层原理)
使用@ConfigurationProperties首先建立配置文件与对象的映射关系,然后在控制器方法中使用@Autowired注解将对象注入。具体如下:
我们指定:@SpringBootApplication 注解是一个复合注解:其中有两个注解是和配置类和属性配置类有关:如下:
1、@SpringBootConfiguration :对@Configuration注解的升级版本。一般用来扩展和覆盖内部的配置的。自定义starter的时候使用的一种机制(springboot提供让开发人员扩展使用的一直机制,因为官方提供配置类不可能满足未来所有业务场景和需求,或者一些新的技术的诞生,springboot团队是不可能马上就开发所谓starter,这个时候就只能去扩展或者自定义,未来所讲的starter其实就通过配置类和属性配置完成这个过程其中使用的原理就是:@SpringBootConfiguration+@Bean 或者 @Configuration + @Bean)
2、@EnableAutoConfiguration:去加载springBoot官方提供的starter包提供的配置类和属性配置类。官方提供了一系列的starters的依赖,这些依赖分别方在:spring-boot-autoconfigure-2.5.7.jar
这个思想:java9中新特性:SPI,把项目中一些类的初始化放在一个文件中,然后去加载和读取这个文件,将其实例化的过程。
每个配置类一般都搭配一个属性配置类:
- DataSourceConfiguration + DataSourceProperties
- JacksonAutoConfiguration + JacksonProperties
- KafkaAutoConfiguration + KafkaProperties
属性配置类的的作用就是让你去扩展和动态修改属性值的一个机制,如果没有这个机制其实starter其实毫无意义。比如:我的:kafka: 158.12.41.41:9092 内部:localhost:9092 .
同时告诉我们一个道理:每天在全局配置文件中配置其实都是在给starter属性配置类在进行赋值。也是就说没个配置都会对应对一个属性配置类。每个值都对应属性配置类的属性。比如:
server.port=8080 对应的配置类:ServerProperties port就是属性:port
ServerProperties serverp = new ServerProperties();
serverp.setPort(8080)
一句话:配置文件在每天配置,其实背后都有一个类,定义值其实都是给类的属性赋值。你就想象成调用set方法。只不过这个过程创建对象的过程,和赋值你过程看不到而已,但是绝对是java基础,创建对象给属性赋值。
4、为什么要用属性配置
传统的代码编写就通过耦合的方式编写如下:
@GetMapping("/alipay")
public String alipay(){
String appid = "2021003100625328";
String mkey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC9kGK4VMbYm";
String ckey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx5i5LhEtDZw6Q6mUxkC5f6sAvZm9OncAkRXwfoBdDeKk";
String callbackurl = "https://www.txnh.net/api/alipay/returnUrl";
String charset = "utf-8";
log.info("你支付是的appid:{},{},{},{}",appid, ckey,mkey,callbackurl,charset);
return "success";
}
- 上面的代码没有错误的,是正确的
- 上面是耦合的,如果我换了公司,换企业就必须重新在代码进行修改,就不方便进行调整。
5、使用@Value的方式进行定义
第一步:在application.yml文件中自定义属性:
# 自定义属性
ksd:
alipay:
appid: 2021003100625328
mkey: MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC9kGK4VMbYm
ckey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx5i5LhEtDZw6Q6mUxkC5f6sAvZm9OncAkRXwfoBdDeKk
callbackurl: https://www.txnh.net/api/alipay/returnUrl
charset: utf-8
第二步:在代码中使用@Value的方式进行获取全局配置文件中对应的值如下:
package com.kuangstudy.web.properties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.crypto.MacSpi;
@RestController
@Slf4j
public class AlipayController {
@Value("${ksd.alipay.appid}")
private String appid;
@Value("${ksd.alipay.mkey}")
private String mkey;
@Value("${ksd.alipay.ckey}")
private String ckey;
@Value("${ksd.alipay.callback}")
private String callbackurl;
@Value("${ksd.alipay.charset}")
private String charset;
@GetMapping("/alipay")
public String alipay(){
log.info("你支付是的appid:{},{},{},{}",appid, ckey,mkey,callbackurl,charset);
return "success";
}
}
第三步:在浏览器访问如下
http://localhost:8083/alipay
- 通过上面可以很清楚看到@Value可以读取全局配置文件对应的属性的值。
- 好处:封装和统一管理,解决耦合性。
- 缺点1:不具备面向对象特征,如果属性过多,而且有一类的很难区分,
- 缺点2:如果@Value(key)使用的key如果在配置文件中没有定义,直接报错
6、springboot官方提供的starter配置类为什么不用@Value
- 属性太多,而且用@Value不具备面向对象的特征和行为,所以就用@ConfigurationProerpties去取代@Value的方式。
- @Value有毛病,就是无法在全部配置文件中进行自动提示
7、springBoot取而代之的是@Configuration + @ConfigurationProperties
-
具有面向对象的特征
-
可以在代码中自动提示
-
维护起来很方便。在配置文件也可以很方便的控制和提示
具体实现步骤:
第一步:在application.yml自定义支付属性
# 自定义属性
ksd:
alipay:
appid: 2021003100625328
mkey: MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC9kGK4VMbYm
ckey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx5i5LhEtDZw6Q6mUxkC5f6sAvZm9OncAkRXwfoBdDeKk
callback: https://www.txnh.net/api/alipay/returnUrl
charset: utf-8
第二步:定义一个属性配置类如下:
package com.kuangstudy.web.properties;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Description:
* Author: yykk Administrator
* Version: 1.0
* Create Date Time: 2021/12/14 20:53.
* Update Date Time:
*
* @see
*/
@ConfigurationProperties(prefix = "ksd.alipay")//这个注解是用找到类
@Data
public class AlipayProperties {
private String appid;
private String mkey;
private String ckey;
private String callback;
private String charset;
}
- @ConfigurationProperties(prefix = “ksd.alipay”)//这个注解的作用是:找到类
- prefix = “ksd.alipay” 前缀未来可以在全局配置文件中进行自动提示。
第三步:属性配置类必须找到一个配置类进行注册如下:
package com.kuangstudy.web.properties;
import lombok.Data;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* Description:
* Author: yykk Administrator
* Version: 1.0
* Create Date Time: 2021/12/14 20:53.
* Update Date Time:
*
* @see
*/
// 用配置类去注册:属性配置类
@EnableConfigurationProperties(AlipayProperties.class)
@SpringBootConfiguration
public class AlipayConfiguration {
}
建议:一个属性配置类 – 找到自己对应配置类进行注册, @EnableConfigurationProperties(AlipayProperties.class)
问:可以在启动类注册吗?可以,因为启动类本身也是一个配置类,但是不推荐。考虑后续的维护以及可插拔性。
8、总结
- 在开发中不要纠结到底使用那种好那种坏,你可以解决业务问题就可以了
- 如果属性仅仅就1~3个其实就用@Value就可以。但是如果很多还是建议大家使用@ConfigurationProperties + @Configuration机制会更好,可以更加清晰底层原来在做什么事情。
- 这也是为什么底层不用@Value的原因。 @Configuration + @Value 这种理论是可以,但是有毛病不能在全局配置文件中进行自动提示。
9、关于属性配置类的自动提示的问题
第一步:pom.xml中增加配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
第二步:关闭所有
-
把idea打开的所有的类和全局配置文件全部关闭。(很重要)
-
然后打开maven的控制,执行clean和compile即可
-
重新开applicaiton.yml文件进行查看是否自动提示,如果可以就说明生效了:
10、@PropertySource(不推荐)
开发者希望读取项目的其他配置文件,而不是全局配置文件中的application.properties,该如何实现呢?可以使用@PropertySource注解找到项目的其他的配置文件。
方式:@PropertySource + @Value
具体如下:
定义两个属性配置文件a.yml和b.yml
a.yml
user.nickname: zhangsan
b.yml
user.age: 32
第二步:定义类
package com.kuangstudy.web.properties;
import com.kuangstudy.web.properties.config.AlipayProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
@PropertySource({"classpath:a.yml","classpath:b.yml"})
public class AlipayController3 {
@Value("${user.nickname}")
private String userName;
@Value("${user.age}")
private String userage;
@GetMapping("/alipay3")
public String alipay3(){
log.info("你支付是的:{},{}",userName,userage);
return "success";
}
}
第三步:访问测试
http://localhost:8083/alipay3
总结:
- 这种@PropertySource机制是一种扩展属性配置文件的机制。
- 一般不建议大家去使用,学习目的是未来能看得懂别人写的逻辑和代码。