千家信息网

springboot中application.yml的文件配置是怎样的

发表于:2025-01-27 作者:千家信息网编辑
千家信息网最后更新 2025年01月27日,今天就跟大家聊聊有关springboot中application.yml的文件配置是怎样的,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。spr
千家信息网最后更新 2025年01月27日springboot中application.yml的文件配置是怎样的

今天就跟大家聊聊有关springboot中application.yml的文件配置是怎样的,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

springboot配置文件

springboot最简便的地方,一是开箱即用,二是配置简单,配置文件路径一般在/src/main/resources 下,主要配置有两种形式,一种是properties文件,一种是springboot官方推荐的yml后缀的文件

一、properties/yml文件配置spring
  1. 使用properties文件springboot配置

#配置内置tomcat启动端口server.port=8080#给程序起个名字spring.application.name=boot-helloworld

properties文件作为众多配置文件中的佼佼者,使用比较方便,格式简单,等号左边为属性,右边为值,可以说是简单方便

  1. 使用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
二、springboot常用配置项

以下摘自 spring-boot-autoconfiguration.jar中的spring-configuration-metadata.json,另外,springboot默认配置都在此包中的spring-autoconfigure-metadata.properties文件中有指定,有需要的同学可以去翻阅,不同springboot版本,默认的属性有区别

  1. 指定项目启动端口

server:  port: 8081
  1. 给项目指定名称

spring:  application:    name: boot-helloworld
  1. 日志级别

#默认情况下springboot使用Logback作为日志框架#logging.level开头,指定一个依赖的groupId然后,指定日志级别logging:  level:    org.springframeword.web: debug
  1. 多个环境下指定启动使用的配置环境

#只需要在application.properties中设置spring.profiles.active=prod来指定活动的profile即可#如下表示采用application-test.yml#默认使用default(application.yml),这样可以区分开发、线上、测试,preview等环境spring:  profiles:    active: test
  1. 指定项目路径

#在所有接口访问路径之前需要加/bootserver:  servlet:    context-path: /boot
三、基于@Value注解读取自定义属性

以下代码仅供演示

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-helloworldlogging:  level:    org.springframeword.web: debugserver:  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中,具体方法此处不做调研

四、yml配置文件加载源码解读

源码解读非常不容易,笔者也是参考了网上很多资源,才撰写出这部分,希望大家多多指教,此处就不画图了,画图功底不好

1: 调用链
BootApplication.main()

//main方法,程序入口SpringApplication.run(BootApplication.class, args);

SpringApplication.run():1202

//实例化SpringApplication并且调用run方法return new SpringApplication(primarySources).run(args);

SpringApplication.run():304

//此方法中实例化EnvironmentConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

SpringApplication.prepareEnvironment():338

//因为我是Web程序,用的是spring-boot-starter-web依赖,所以是StandardServletEnvironmentprivate 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

@Overrideprotected 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()

@Overridepublic 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 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

/** *

*

  • 此类负责加载["properties", "xml"]后缀的配置文件 *
  • loadProerties负责读取配置文件中的内容 *

    */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> load(String name, Resource resource) throws IOException { Map properties = loadProperties(resource); if (properties.isEmpty()) { return Collections.emptyList(); } return Collections.singletonList(new OriginTrackedMapPropertySource(name, properties)); } @SuppressWarnings({ "unchecked", "rawtypes" }) private Map 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

    /** *

    *

  • 此类负责加载["yml", "yaml"]后缀的配置文件 *
  • load负责读取配置文件中的内容 *

    */public class YamlPropertySourceLoader implements PropertySourceLoader { @Override public String[] getFileExtensions() { return new String[] { "yml", "yaml" }; } @Override public List> 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> loaded = new OriginTrackedYamlLoader(resource).load(); if (loaded.isEmpty()) { return Collections.emptyList(); } List> 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> load() {        final List> result = new ArrayList<>();        process((properties, map) -> result.add(getFlattenedMap(map)));        return result;}

    YamlProcessor.java

    private void buildFlattenedMap(Map result, Map 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 map = (Map) value;                        buildFlattenedMap(result, map, key);                }                else if (value instanceof Collection) {                        // Need a compound key                        @SuppressWarnings("unchecked")                        Collection collection = (Collection) 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的文件配置是怎样的有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注行业资讯频道,感谢大家的支持。

    0