spring boot实战(第六篇)加载application资源文件源码分析
前言
在上一篇中了解了spring配置资源的加载过程,本篇在此基础上学习spring boot如何默认加载application.xml等文件信息的。
ConfigFileApplicationListener
在spring boot实战(第三篇)事件监听源码分析中可知在构造SpringApplication时加载相关的监听器,其中存在一个监听器ConfigFileApplicationListener,其定义如下:
[html] view plain copy
- public class ConfigFileApplicationListener implements
- ApplicationListener<ApplicationEvent>, Ordered {
- @Override
- public void onApplicationEvent(ApplicationEvent event) {
- if (event instanceof ApplicationEnvironmentPreparedEvent) {
- onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
- }
- if (event instanceof ApplicationPreparedEvent) {
- onApplicationPreparedEvent((ApplicationPreparedEvent) event);
- }
- }
- }
监听ApplicationEvent事件,在触发所有其子类以及本身事件时会执行其onApplicationEvent方法。在执行
[html] view plain copy
- for (SpringApplicationRunListener runListener : runListeners) {
- runListener.environmentPrepared(environment);
- }
时会触发到
[html] view plain copy
- if (event instanceof ApplicationEnvironmentPreparedEvent) {
- onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
- }
中;
[html] view plain copy
- private void onApplicationEnvironmentPreparedEvent(
- ApplicationEnvironmentPreparedEvent event) {
- Environment environment = event.getEnvironment();
- if (environment instanceof ConfigurableEnvironment) {
- onApplicationEnvironmentPreparedEvent((ConfigurableEnvironment) environment,
- event.getSpringApplication());
- }
- }
在上一篇中可以知道enviroment为StandardServletEnvironment实例,因此执行onApplicationEnvironmentPreparedEvent方法
[html] view plain copy
- private void onApplicationEnvironmentPreparedEvent(
- ConfigurableEnvironment environment, SpringApplication application) {
- addPropertySources(environment, application.getResourceLoader());
- bindToSpringApplication(environment, application);
- }
首先来看addPropertySources相关信息
[html] view plain copy
- protected void addPropertySources(ConfigurableEnvironment environment,
- ResourceLoader resourceLoader) {
- RandomValuePropertySource.addToEnvironment(environment);
- try {
- new Loader(environment, resourceLoader).load();
- }
- catch (IOException ex) {
- throw new IllegalStateException("Unable to load configuration files", ex);
- }
- }
RandomValuePropertySource.addToEnvironment(environment)将随机方法放入到PropertySources中
[html] view plain copy
- public static void addToEnvironment(ConfigurableEnvironment environment) {
- environment.getPropertySources().addAfter(
- StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
- new RandomValuePropertySource("random"));
- logger.trace("RandomValuePropertySource add to Environment");
- }
如何从Random中获取值是需要看getProperty方法:
[html] view plain copy
- public Object getProperty(String name) {
- if (!name.startsWith("random.")) {
- return null;
- }
- if (logger.isTraceEnabled()) {
- logger.trace("Generating random property for '" + name + "'");
- }
- if (name.endsWith("int")) {
- return getSource().nextInt();
- }
- if (name.startsWith("random.long")) {
- return getSource().nextLong();
- }
- if (name.startsWith("random.int") && name.length() > "random.int".length() + 1) {
- String range = name.substring("random.int".length() + 1);
- range = range.substring(0, range.length() - 1);
- return getNextInRange(range);
- }
- byte[] bytes = new byte[32];
- getSource().nextBytes(bytes);
- return DigestUtils.md5DigestAsHex(bytes);
- }
其中的getSource()表示Random类。
接下来看
[html] view plain copy
- new Loader(environment, resourceLoader).load()
看load方法
[html] view plain copy
- public void load() throws IOException {
- ...//处理profiles信息
- while (!this.profiles.isEmpty()) {
- String profile = this.profiles.poll();
- for (String location : getSearchLocations()) {
- if (!location.endsWith("/")) {
- // location is a filename already, so don't search for more
- // filenames
- load(location, null, profile);
- }
- else {
- for (String name : getSearchNames()) {
- load(location, name, profile);
- }
- }
- }
- }
- addConfigurationProperties(this.propertiesLoader.getPropertySources());
- }
看getSearchLocations()方法
[html] view plain copy
- private Set<String> getSearchLocations() {
- Set<String> locations = new LinkedHashSet<String>();
- // User-configured settings take precedence, so we do them first
- if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
- for (String path : asResolvedSet(
- this.environment.getProperty(CONFIG_LOCATION_PROPERTY), null)) {
- if (!path.contains("$")) {
- if (!path.contains(":")) {
- path = "file:" + path;
- }
- path = StringUtils.cleanPath(path);
- }
- locations.add(path);
- }
- }
- locations.addAll(asResolvedSet(
- ConfigFileApplicationListener.this.searchLocations,
- DEFAULT_SEARCH_LOCATIONS));
- return locations;
- }
首先看CONFIG_LOCATION_PROPERTY(spring.config.location)是否存在配置,无则走默认配置路径DEFAULT_SEARCH_LOCATIONS(classpath:/,classpath:/config/,file:./,file:./config/)
继续来看getSearchNames()方法
[html] view plain copy
- private Set<String> getSearchNames() {
- if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
- return asResolvedSet(this.environment.getProperty(CONFIG_NAME_PROPERTY),
- null);
- }
- return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
- }
优先看CONFIG_NAME_PROPERTY(spring.config.name)配置,否则走DEFAULT_NAMES(application)
解析完路径和配置文件名以后,将开始判断路径+名称组合是否存在 执行load(...)方法
[html] view plain copy
- private void load(String location, String name, String profile)
- throws IOException {
- String group = "profile=" + (profile == null ? "" : profile);
- if (!StringUtils.hasText(name)) {
- // Try to load directly from the location
- loadIntoGroup(group, location, profile);
- }
- else {
- // Search for a file with the given name
- for (String ext : this.propertiesLoader.getAllFileExtensions()) {
- if (profile != null) {
- // Try the profile specific file
- loadIntoGroup(group, location + name + "-" + profile + "." + ext,
- null);
- // Sometimes people put "spring.profiles: dev" in
- // application-dev.yml (gh-340). Arguably we should try and error
- // out on that, but we can be kind and load it anyway.
- loadIntoGroup(group, location + name + "-" + profile + "." + ext,
- profile);
- }
- // Also try the profile specific section (if any) of the normal file
- loadIntoGroup(group, location + name + "." + ext, profile);
- }
- }
- }
this.propertiesLoader.getAllFileExtensions()方法获取文件后缀
[html] view plain copy
- public Set<String> getAllFileExtensions() {
- Set<String> fileExtensions = new HashSet<String>();
- for (PropertySourceLoader loader : this.loaders) {
- fileExtensions.addAll(Arrays.asList(loader.getFileExtensions()));
- }
- return fileExtensions;
- }
loader.getFileExtensions()获取所有支持的文件后缀,其中loader在执行load方法时实例化
[html] view plain copy
- public void load() throws IOException {
- this.propertiesLoader = new PropertySourcesLoader();
- ...}
调用其构造方法
[html] view plain copy
- public PropertySourcesLoader(MutablePropertySources propertySources) {
- Assert.notNull(propertySources, "PropertySources must not be null");
- this.propertySources = propertySources;
- this.loaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
- null);
- }
可以看出this.loaders是由SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,null)得到
[html] view plain copy
- public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
- Assert.notNull(factoryClass, "'factoryClass' must not be null");
- ClassLoader classLoaderToUse = classLoader;
- if (classLoaderToUse == null) {
- classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
- }
- List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
- if (logger.isTraceEnabled()) {
- logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
- }
- List<T> result = new ArrayList<T>(factoryNames.size());
- for (String factoryName : factoryNames) {
- result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
- }
- AnnotationAwareOrderComparator.sort(result);
- return result;
- }
加载META-INF/spring.factories文件下对应内容
[html] view plain copy
- # PropertySource Loaders
- org.springframework.boot.env.PropertySourceLoader=\
- org.springframework.boot.env.PropertiesPropertySourceLoader,\
- org.springframework.boot.env.YamlPropertySourceLoader
因此加载了PropertiesPropertySourceLoader以及YamlPropertySourceLoader类实例;
- PropertiesPropertySourceLoader 支持文件后缀格式 "properties","xml"
[html] view plain copy
- @Override
- public String[] getFileExtensions() {
- return new String[] { "properties", "xml" };
- }
- YamlPropertySourceLoader 支持文件后缀格式 "yml","yaml"
[html] view plain copy
- @Override
- public String[] getFileExtensions() {
- return new String[] { "yml", "yaml" };
- }
两者覆写的load方法实现如何处理资源为PropertySource对象。
获取完文件后缀后调用loadIntoGroup方法将资源信息转化为PropertySource,其实质为调用PropertySourcesLoader中load方法
[html] view plain copy
- private PropertySource<?> loadIntoGroup(String identifier, String location,
- String profile) throws IOException {
- Resource resource = this.resourceLoader.getResource(location);
- PropertySource<?> propertySource = null;
- if (resource != null) {
- String name = "applicationConfig: [" + location + "]";
- String group = "applicationConfig: [" + identifier + "]";
- propertySource = this.propertiesLoader.load(resource, group, name,
- profile);
- if (propertySource != null) {
- maybeActivateProfiles(propertySource
- .getProperty(ACTIVE_PROFILES_PROPERTY));
- addIncludeProfiles(propertySource
- .getProperty(INCLUDE_PROFILES_PROPERTY));
- }
- }
- StringBuilder msg = new StringBuilder();
- msg.append(propertySource == null ? "Skipped " : "Loaded ");
- msg.append("config file ");
- msg.append("'").append(location).append("'");
- if (StringUtils.hasLength(profile)) {
- msg.append(" for profile" + profile);
- }
- if (resource == null || !resource.exists()) {
- msg.append(" resource not found");
- }
- this.debug.add(msg);
- return propertySource;
- }
最后调用addConfigurationProperties(this.propertiesLoader.getPropertySources())方法将解析过后的资源信息放置进Enviroment中propertySources属性集合中
[html] view plain copy
- private void addConfigurationProperties(MutablePropertySources sources) {
- List<PropertySource<?>> reorderedSources = new ArrayList<PropertySource<?>>();
- for (PropertySource<?> item : sources) {
- reorderedSources.add(item);
- }
- // Maybe we should add before the DEFAULT_PROPERTIES if it exists?
- this.environment.getPropertySources().addLast(
- new ConfigurationPropertySources(reorderedSources));
- }
至此 application.xml等文件的加载分析结束。
时序图
简单的画了一下时序图,可能和实际调用存在出入,仅作参考使用
spring boot实战(第六篇)加载application资源文件源码分析相关推荐
- 图片加载框架Picasso - 源码分析
简书:图片加载框架Picasso - 源码分析 前一篇文章讲了Picasso的详细用法,Picasso 是一个强大的图片加载缓存框架,一个非常优秀的开源库,学习一个优秀的开源库,,我们不仅仅是学习它的 ...
- 【Spring源码】ClassPathResource 资源文件源码分析
上一篇文章我们主要介绍了开发 Spring 应用涉及到的一些核心组件,在文章的最后搭建了开发环境.那么接下来我们开始分析 Spring 源码部分,本篇文章首先分析 Spring 是如何封装资源文件的. ...
- os引导程序boot从扇区拷贝os加载程序loader文件到内存(boot copy kernel to mem in the same method)
[0]README 0.1) 本代码旨在演示 在boot 代码中,如何 通过 loader文件所在根目录条目 找出该文件的 在 软盘所有全局扇区号(簇号),并执行内存中的 loader 代码: 0.2 ...
- android资源加载流程6,FrameWork源码解析(6)-AssetManager加载资源过程
之前一段时间项目比较忙所以一直没有更新,接下来准备把插件化系列的文章写完,今天我们就先跳过ContentProvider源码解析来讲资源加载相关的知识,资源加载可以说是插件化非常重要的一环,我们很有必 ...
- Spark动态加载外部资源文件
Spark动态加载外部资源文件 1.spark-submit --files 动态加载外部资源文件 之前做一个关于Spark的项目时,因项目中需要读取某个静态资源文件,然后在本地IDEA测试一切皆正常 ...
- OSG —— 笔记2 - 加载模型(附源码)
效果 相关文章 OSG -- 笔记1 - 指令调用模型 OSG -- 笔记2 - 加载模型(附源码) OSG -- 笔记3 - 绘制矩形(附源码) ...
- 【java毕业设计】基于Spring Boot+mysql的酒店管理系统设计与实现(程序源码+毕业论文)-酒店管理系统
基于Spring Boot+mysql的酒店管理系统设计与实现(程序源码+毕业论文) 大家好,今天给大家介绍基于Spring Boot+mysql的酒店管理系统设计与实现,本论文只截取部分文章重点,文 ...
- bytebuf池_Netty篇:ByteBuf之内存池源码分析
Netty的内存池的实现有些复杂,使用了大量的位运算,晦涩难懂,不过万能的博客上好多大神已经介绍的非常详细,推荐四篇很详细很棒的源码分析的文章链接,本文根据自己的理解顺一下思路,内容主要也是出自以下四 ...
- 深入理解DOM事件类型系列第六篇——加载事件
前面的话 提到加载事件,可能想到了window.onload,但实际上,加载事件是一大类事件,本文将详细介绍加载事件 load load事件是最常用的一个事件,当页面完全加载后(包括所有图像.java ...
最新文章
- C++语言学习(十二)——C++语言常见函数调用约定
- java中堆与栈的区别 彻底理解
- 基于python的游戏设计与实现-python五子棋游戏的设计与实现
- python处理pdf提取指定数据_python从PDF中提取数据的示例
- 大一java期末考笔试_大学java期末考试试题和答案
- React Router路由详解
- Qt工作笔记-QGraphicsView框架容易忽视的坐标问题-“画布”QgraphicsScene到底放在了哪儿?
- pycharm安装教程,超详细
- 力扣242.有效的字母异位词(JavaScript)
- 使用ExposedObject对Asp.net MVC中匿名类型的JsonResult做单元测试
- 新手学Docker(1)Hello World
- CSS3---3.相对父元素的伪类
- WPS简历模板的图标怎么修改_HR眼里的优秀简历模板长这样!30份中英文优秀模板,可一键修改!...
- Oracle11g Dataguard配置
- 新电脑如何进行磁盘分区?
- no accounts with itunes connect access问题排查解决
- python海龟库写名字
- 深入springboot怎么启动tomcat
- 基于深度学习对皮肤病进行识别设计与实现
- 猪是坚强的,所以取名朱坚强!