Spring 系列
- IoC 容器
- AOP
- SpringMVC
- Spring 事务
- Spring 源码故事(瞎编版)
- Spring 整体脉络
- Spring 类解析
- Spring 自定义标签解析
- Spring Scan 包扫描
- Spring 注解工具类
- Spring 别名注册
- Spring 标签解析类
- Spring ApplicationListener
- Spring messageSource
- Spring 自定义属性解析器
- Spring 排序工具
- Spring-import 注解
- Spring-定时任务
- Spring StopWatch
- Spring 元数据
- Spring 条件接口
- Spring MultiValueMap
- Spring MethodOverride
- Spring BeanDefinitionReaderUtils
- Spring PropertyPlaceholderHelper
- Spring-AnnotationFormatterFactory
- Spring-Formatter
- Spring-Parser
- Spring-Printer
- Spring5 新特性
- Spring RMI
- Spring Message
- SpringBoot
- SpringBootBatch
- Spring Cloud
- SpringSecurity
MyBatis
- 基础支持层
- 核心处理层
- 类解析
Netty
- 网络 IO 技术基础
- JDK1.8 NIO 包 核心组件源码剖析
- Netty 粘拆包及解决方案
- Netty 多协议开发
- 基于 Netty 开发服务端及客户端
- Netty 主要组件的源码分析
- Netty 高级特性
- Netty 技术细节源码分析
Dubbo
- 架构设计
- SPI 机制
- 注册中心
- 远程通信
- RPC
- 集群
Tomcat
- Servlet 与 Servlet 容器
- Web 容器
Redis
Nacos
Sentinel
RocketMQ
- RocketMQ NameServer 与 Broker 的通信
- RocketMQ 生产者启动流程
- RocketMQ 消息发送流程
- RocketMQ 消息发送存储流程
- RocketMQ MappedFile 内存映射文件详解
- RocketMQ ConsumeQueue 详解
- RocketMQ CommitLog 详解
- RocketMQ IndexFile 详解
- RocketMQ 消费者启动流程
- RocketMQ 消息拉取流程
- RocketMQ Broker 处理拉取消息请求流程
- RocketMQ 消息消费流程
番外篇(JDK 1.8)
- 基础类库
- 集合
- 并发编程
学习心得
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
SpringBoot 配置加载解析
Spring Boot application 文件加载
- Author: HuiFer
- 源码阅读仓库: SourceHot-spring-boot
如何找到这个加载的过程
创建配置文件
application.yml
全局搜索 yml
换成
properties
搜索我们以
yml
为例打上断点开始源码追踪
看到调用堆栈
- 一步一步回上去看如何调用具体方法的
ConfigFileApplicationListener
- 配置文件监听器
调用过程
org.springframework.boot.context.config.ConfigFileApplicationListener#addPropertySources
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
// 加载器加载信息
new Loader(environment, resourceLoader).load();
}
Loader
- 配置资源加载器
构造方法
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
// 环境配置
this.environment = environment;
// 占位符处理器
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
// 资源加载器
this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
// 配置信息加载器初始化
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
getClass().getClassLoader());
}
熟悉的老朋友
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader())
, 看看spring.factories
有什么搜索目标:
org.springframework.boot.env.PropertySourceLoader
观察发现里面有一个YamlPropertySourceLoader
和我们之前找 yml 字符串的时候找到的类是一样的。说明搜索方式没有什么问题。
初始化完成,后续进行解析了
load 方法
void load() {
FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
(defaultProperties) -> {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
// 初始化配置文件
initializeProfiles();
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
if (isDefaultProfile(profile)) {
addProfileToEnvironment(profile.getName());
}
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
applyActiveProfiles(defaultProperties);
});
}
initializeProfiles
- 初始化
private Deque<Profile> profiles;
属性
load
org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#load(org.springframework.boot.context.config.ConfigFileApplicationListener.Profile, org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentFilterFactory, org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentConsumer)
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
getSearchLocations().forEach(
// 本地路径
(location) -> {
// 是不是文件夹
boolean isFolder = location.endsWith("/");
// 文件名,默认application
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
// 循环加载
names.forEach((name) -> {
load(location, name, profile, filterFactory, consumer);
});
});
}
- 资源路径可能性
该方法采用循环每个路径下面都去尝试一遍
- 中间过程省略,我们直接看最后的加载行为
org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#loadDocuments
private List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource)
throws IOException {
// 文档的缓存key
DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
// 文档信息
List<Document> documents = this.loadDocumentsCache.get(cacheKey);
if (documents == null) {
// 执行加载,将配置文件读取返回
List<PropertySource<?>> loaded = loader.load(name, resource);
// 数据转换
documents = asDocuments(loaded);
// 缓存设置
this.loadDocumentsCache.put(cacheKey, documents);
}
return documents;
}
此处的loader.load()
调用具体的 loader 实现类进行执行方法
yml 解析
@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,
Collections.unmodifiableMap(loaded.get(i)), true));
}
return propertySources;
}
PropertiesPropertySourceLoader
解析同理不在次展开描述了
asDocuments
/**
* 将 {@link PropertySource} 转换成 {@link Document}
* @param loaded
* @return
*/
private List<Document> asDocuments(List<PropertySource<?>> loaded) {
if (loaded == null) {
return Collections.emptyList();
}
return loaded.stream().map(
// 循环创建新对象
(propertySource) -> {
// 对象创建
Binder binder = new Binder(ConfigurationPropertySources.from(propertySource),
this.placeholdersResolver);
/**
* 通过 {@link Binder} 将数据进行绑定,创建 {@link Document}进行返回
*/
return new Document(propertySource, binder.bind("spring.profiles", STRING_ARRAY).orElse(null),
getProfiles(binder, ACTIVE_PROFILES_PROPERTY),
getProfiles(binder, INCLUDE_PROFILES_PROPERTY));
}).collect(Collectors.toList());
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论