今天就跟大家聊聊有关springboot中application.yml的文件配置是怎样的,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
springboot最简便的地方,一是开箱即用,二是配置简单,配置文件路径一般在/src/main/resources 下,主要配置有两种形式,一种是properties文件,一种是springboot官方推荐的yml后缀的文件
使用properties文件springboot配置
#配置内置tomcat启动端口
server.port=8080
#给程序起个名字
spring.application.name=boot-helloworld
properties文件作为众多配置文件中的佼佼者,使用比较方便,格式简单,等号左边为属性,右边为值,可以说是简单方便
使用yml文件作为springboot配置
#配置内置tomcat启动端口
server:
port: 8080
#应用名称
spring:
application:
name: boot-helloworld
yml文件中,有着严格的层级关系,以空格或者Tab缩进表示,英文冒号结尾,末尾层级最后空至少一个英文空格,然后填写该属性的值,优点在于,层级表示可以简单的连着写多个值,例如:
spring:
application:
name: boot-helloworld
#redis连接池配置, 可以换行保持缩进,连着写
redis:
host: localhost
port: 6379
password: password
timeOut: 5000
maxIdle: 50
maxWaitMillis: 5000
maxTotal: 500
以下摘自 spring-boot-autoconfiguration.jar中的spring-configuration-metadata.json,另外,springboot默认配置都在此包中的spring-autoconfigure-metadata.properties文件中有指定,有需要的同学可以去翻阅,不同springboot版本,默认的属性有区别
指定项目启动端口
server:
port: 8081
给项目指定名称
spring:
application:
name: boot-helloworld
日志级别
#默认情况下springboot使用Logback作为日志框架
#logging.level开头,指定一个依赖的groupId然后,指定日志级别
logging:
level:
org.springframeword.web: debug
多个环境下指定启动使用的配置环境
#只需要在application.properties中设置spring.profiles.active=prod来指定活动的profile即可
#如下表示采用application-test.yml
#默认使用default(application.yml),这样可以区分开发、线上、测试,preview等环境
spring:
profiles:
active: test
指定项目路径
#在所有接口访问路径之前需要加/boot
server:
servlet:
context-path: /boot
以下代码仅供演示
1: User.java
/**
* 当作一个配置项,简单处理
* @author Calvin
* @date 2019/07/24
*/
public class User {
/**
* ID
*/
private String id;
/**
* 名字
*/
private String userName;
/**
* 年龄
*/
private Integer age;
/**
* 性别
*/
private String gender;
/**
* 所使用的操作系统
*/
private String systemName;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getSystemName() {
return systemName;
}
public void setSystemName(String systemName) {
this.systemName = systemName;
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", userName='" + userName + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", systemName='" + systemName + '\'' +
'}';
}
}
2: application.yml
spring:
application:
name: boot-helloworld
logging:
level:
org.springframeword.web: debug
server:
servlet:
context-path: /boot
# admin年龄的值配置
admin:
age: 20
3:ValueController.java
/**
* 测试@Value注解的各种方式
* @author Calvin
* @date 2019/07/24
*/
@RestController
@RequestMapping("/value")
public class ValueController {
/**
* ID 采用表达式注解
*/
@Value("#{ T(java.util.UUID).randomUUID()}")
private String adminId;
/**
* name 采用值注解获取一个UUID
*/
@Value("Calvin")
private String adminName;
/**
* gender 给定一个默认值,如果没有定义,就采用默认值
*/
@Value("${admin.gender: 男}")
private String adminGender;
/**
* 注入yml文件中的admin.age
*/
@Value("${admin.age}")
private Integer adminAge;
/**
* 采用表达式获取系统变量
*/
@Value("#{systemProperties['os.name']}")
private String systemName;
/**
* 获取用户信息
* @return
*/
@GetMapping("/getUserInfo")
public User getAdminInfo(){
User admin = new User();
admin.setId(adminId);
admin.setUserName(adminName);
admin.setAge(adminAge);
admin.setGender(adminGender);
admin.setSystemName(systemName);
return admin;
}
}
4:调用结果
springboot配置文件不仅可以使用@Value注解, 还有很多好玩的方式,另一个可以使用@ConfigurationProperties去注入到某个Bean中,具体方法此处不做调研
源码解读非常不容易,笔者也是参考了网上很多资源,才撰写出这部分,希望大家多多指教,此处就不画图了,画图功底不好
1: 调用链
BootApplication.main()
//main方法,程序入口
SpringApplication.run(BootApplication.class, args);
SpringApplication.run():1202
//实例化SpringApplication并且调用run方法
return new SpringApplication(primarySources).run(args);
SpringApplication.run():304
//此方法中实例化Environment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
SpringApplication.prepareEnvironment():338
//因为我是Web程序,用的是spring-boot-starter-web依赖,所以是StandardServletEnvironment
private ConfigurableEnvironment getOrCreateEnvironment() {
//省略部分代码
return new StandardServletEnvironment();
}
//因为StandardServletEnviroment extends AbstractEnvironment
//而AbstractEnvironment构造器中中调用了customizePropertySources()方法
public AbstractEnvironment() {
//模板方法,大家都懂得
customizePropertySources(this.propertySources);
}
StandardServletEnvironment.customizePropertySources():54
protected void customizePropertySources(MutablePropertySources propertySources) {
//将servletConfigInitParams添加进PropertySource中
propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
//将servletContextInitParams添加进PropertySource中
propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
}
//调用AbstractEnvironment.customizePropertySources()方法
super.customizePropertySources(propertySources);
}
AbstractEnvironment.customizePropertySources():77
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
//添加systemProperties, 添加的是System.getProperties(),是一个Native方法,主要属性是
//java.runtime.name | java.vm.version等系统配置的属性
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
//添加systemEnvironment, 也就是系统环境变量, 其中包含JAVA_HOME等属性
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
至此结束,ConfigurableEnvironment对象中添加了四个propertySource,分别为:
[servletConfigInitParams, servletContextInitParams , systemProperties, systemEnvironment]
看完 SpringApplication.prepareEnvironment():338所作的事情, 接着看listeners.environmentPrepared(environment):340,这也是yml文件配置的入口
SpringApplicationRunListeners.environmentPremared
public void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
这个方法中加载了很多个Listener,每个的调用链也非常深,这里简短点,
SpringApplicationRunListeners.java
environmentPrepared-->
SimpleApplcationEventMulticaster.java
multicastEvent():126&131-->
invokeListener():159-->
doInvokeListener(listener, event) -->
CofigFileApplicationListener.java
onApplicationEvent():-->
addPostProcessors();-->
new Loader(environment, resourceLoader).load():202-->
load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,DocumentConsumer consumer):429-->
前方高能,笔者跟代码跟的非常之辛苦,废话少说,ConfigFileApplicationListener.java中核心代码
ConfigFileApplicationListener.onApplicationEvent()
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
ConfigFileApplicationListener.load()
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
if (!StringUtils.hasText(name)) {
for (PropertySourceLoader loader : this.propertySourceLoaders) {
if (canLoadFileExtension(loader, location)) {
load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
return;
}
}
}
Set<String> processed = new HashSet<>();
//遍历propertySourceLoaders
for (PropertySourceLoader loader : this.propertySourceLoaders) {
for (String fileExtension : loader.getFileExtensions()) {
if (processed.add(fileExtension)) {
//寻找配置文件
loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,
consumer);
}
}
}
}
this.propertySourceLoaders中有两个类,都是PropertySourceLoader的实现类,分别是
[
org.springframework.boot.env.PropertiesPropertySourceLoader,
org.springframework.boot.env.YamlPropertySourceLoader
]
PropertiesPropertySourceLoader.java
/**
*<p>
* <li>此类负责加载["properties", "xml"]后缀的配置文件
* <li>loadProerties负责读取配置文件中的内容
*</p>
*/
public class PropertiesPropertySourceLoader implements PropertySourceLoader {
private static final String XML_FILE_EXTENSION = ".xml";
@Override
public String[] getFileExtensions() {
return new String[] { "properties", "xml" };
}
@Override
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
Map<String, ?> properties = loadProperties(resource);
if (properties.isEmpty()) {
return Collections.emptyList();
}
return Collections.singletonList(new OriginTrackedMapPropertySource(name, properties));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Map<String, ?> loadProperties(Resource resource) throws IOException {
String filename = resource.getFilename();
if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
return (Map) PropertiesLoaderUtils.loadProperties(resource);
}
return new OriginTrackedPropertiesLoader(resource).load();
}
}
YamlPropertySourceLoader.java
/**
*<p>
* <li>此类负责加载["yml", "yaml"]后缀的配置文件
* <li>load负责读取配置文件中的内容
*</p>
*/
public class YamlPropertySourceLoader implements PropertySourceLoader {
@Override
public String[] getFileExtensions() {
return new String[] { "yml", "yaml" };
}
@Override
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
throw new IllegalStateException(
"Attempted to load " + name + " but snakeyaml was not found on the classpath");
}
List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();
if (loaded.isEmpty()) {
return Collections.emptyList();
}
List<PropertySource<?>> propertySources = new ArrayList<>(loaded.size());
for (int i = 0; i < loaded.size(); i++) {
String documentNumber = (loaded.size() != 1) ? " (document #" + i + ")" : "";
propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber, loaded.get(i)));
}
return propertySources;
}
}
至此结束,找到了加载application.yml文件的位置,接着往下跟 会在此方法中加载,具体调用了Yaml类构造器,StreamReader去读取文件
public List<Map<String, Object>> load() {
final List<Map<String, Object>> result = new ArrayList<>();
process((properties, map) -> result.add(getFlattenedMap(map)));
return result;
}
YamlProcessor.java
private void buildFlattenedMap(Map<String, Object> result, Map<String, Object> source, @Nullable String path) {
source.forEach((key, value) -> {
if (StringUtils.hasText(path)) {
if (key.startsWith("[")) {
key = path + key;
}
else {
key = path + '.' + key;
}
}
if (value instanceof String) {
result.put(key, value);
}
else if (value instanceof Map) {
// Need a compound key
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) value;
buildFlattenedMap(result, map, key);
}
else if (value instanceof Collection) {
// Need a compound key
@SuppressWarnings("unchecked")
Collection<Object> collection = (Collection<Object>) value;
if (collection.isEmpty()) {
result.put(key, "");
}
else {
int count = 0;
for (Object object : collection) {
buildFlattenedMap(result, Collections.singletonMap(
"[" + (count++) + "]", object), key);
}
}
}
else {
result.put(key, (value != null ? value : ""));
}
});
}
来点废话,这个加载的调用链非常深,六七个类,不少于十几个方法,有兴趣的同学可以研究研究,学习使用的同学请不要过于关注这个,不过读源码有助于更好的理解框架,如有不同理解或者文中有误,欢迎多多指正。
1:简单解释springboot常用两种配置文件
2:基于两种配置文件,分别做实现
3:@Value注解常用方式
4:yml文件加载源码解读
看完上述内容,你们对springboot中application.yml的文件配置是怎样的有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注亿速云行业资讯频道,感谢大家的支持。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/devilsblog/blog/3080295