文章接上文哈,请先熟读自动装配篇,不然本文比较难以理解。
预先准备
- MVC项目的web.xml,找到本地文件扫描配置
<context-param>
<param-name>contextConfigLocation</param-name>
<!--指定路径下的配置文件 -->
<param-value>classpath*:META-INF/spring/applicationContext*.xml</param-value>
</context-param>
但是在springboot中是没有这种指向配置文件的入口的,那么springboot中的配置文件又是怎么加载到系统里面的呢?
今天来一探究竟吧
自动配置源码分析
- 入口当然还是main方法,前面的就不多了,一直往下在
SpringApplication.run
方法里面找到这行代码,从这里开始分析
//找到这里 解析配置文件入口 自动装配之前 ###
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
- 接下来到这里
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
//获取或者创建环境 这里就无其他web mvc的依赖包 所有得到的是 sevlet
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
//然后进入监听 接下来往这看 ###
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
- 一直往下实现了就一个
EventPublishingRunListener
,看下从那里获取的
//从run方法中就可以看出listeners
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
//从 spring.factories 找到SpringApplicationRunListener类名的配置
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
//其实就这一个 注意加载该类时,会调用该类的构造方法初始化
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
- 接下来来到
EventPublishingRunListener
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(
new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}
//multicastEvent一直往下到SimpleApplicationEventMulticaster
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
//找到ApplicationListeners的监听列表 接下来先看这个 才知道回调到了哪个类 ###
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
//监听的调用
invokeListener(listener, event);
}
}
}
- 接下来先看下获取监听列表
getApplicationListeners
,找到最后一行代码
//获取对应类型的监听
return retrieveApplicationListeners(eventType, sourceType, newRetriever)
//AbstractApplicationEventMulticaster.retrieveApplicationListeners找到这行
//这里就是返回对应的监听了
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
- this.defaultRetriever.applicationListeners的值怎么来的呢? 是什么?
- 前面
spring.factories
自动加载时会加载EventPublishingRunListener类,那么肯定会通过构造方法初始化,回到EventPublishingRunListener的构造方法
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
//getListeners 获取的listener
for (ApplicationListener<?> listener : application.getListeners()) {
//在这里添加的 那listeners又是什么呢
this.initialMulticaster.addApplicationListener(listener);
}
}
//先看 application.getListeners()取的是SpringApplication里面的this.listeners
//在new SpringApplication(primarySources).run(args);能看到
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//this.listeners = new ArrayList<>(listeners);
//其实就是从spring.factories取的ApplicationListener类名的配置
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
//spring.factories中找到ApplicationListener类名的配置 那就是这些了
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener
- 到目前为止,已经获取到自动装配的listener类了,接下来开始分析监听的回调
- 回到
SimpleApplicationEventMulticaster.multicastEvent
//监听的调用 从这里开始
invokeListener(listener, event);
//一直往下 最终到这个
listener.onApplicationEvent(event);
- 上面说到实现ApplicationListener监听的类比较多,我也是一个一个断点调试,最终找到EnvironmentPostProcessorApplicationListener类是分析配置文件的,其他的类就不一一展开了有兴趣的可以自己多做了解
EnvironmentPostProcessorApplicationListener#onApplicationEvent
//这行代码往下
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
SpringApplication application = event.getSpringApplication();
//先看getEnvironmentPostProcessors 获取需要回调集合
for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
event.getBootstrapContext())) {
//回调
postProcessor.postProcessEnvironment(environment, application);
}
}
//获取需要回调的监听类
List<EnvironmentPostProcessor> getEnvironmentPostProcessors(ResourceLoader resourceLoader,
ConfigurableBootstrapContext bootstrapContext) {
ClassLoader classLoader = (resourceLoader != null) ? resourceLoader.getClassLoader() : null;
//这里扫描对应的类 ###
EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);
//通过newInstance初始化并返回
return postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext);
}
- 先看下
postProcessorsFactory
本地变量
private final Function<ClassLoader, EnvironmentPostProcessorsFactory> postProcessorsFactory;
//前面说了该类时自动装配进来的 无参的就会走这个构造方法
public EnvironmentPostProcessorApplicationListener() {
//postProcessorsFactory 传参classLoader 执行的是后面这段
this((classLoader) -> EnvironmentPostProcessorsFactory.fromSpringFactories(classLoader), new DeferredLogs());
}
//fromSpringFactories
static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {
//从配置文件里面取 EnvironmentPostProcessor这个类的
return new ReflectionEnvironmentPostProcessorsFactory(classLoader,
SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader));
}
//查看spring.factories 发现并没有我们想找的实现类 经过断点调试才发现当前类也是它的实现类
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
- 在回到当前类
EnvironmentPostProcessorApplicationListener#onApplicationEnvironmentPreparedEvent
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
SpringApplication application = event.getSpringApplication();
for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
event.getBootstrapContext())) {
//开始回调
postProcessor.postProcessEnvironment(environment, application);
}
}
- 经过断点回调到
ConfigFileApplicationListener#postProcessEnvironment
,它也是EnvironmentPostProcessor的实现类
//一直往下到这
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
//用loader封装后 在加载
new Loader(environment, resourceLoader).load();
}
//先看构造方法
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader(null);
//加载PropertySourceLoader配置的类
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
this.resourceLoader.getClassLoader());
}
//那就是这两个类
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
- 接下来继续分析
ConfigFileApplicationListener#load()
方法
void load() {
FilteredPropertySource.apply(this.environment, DefaultPropertiesPropertySource.NAME, LOAD_FILTERED_PROPERTY,
this::loadWithFilteredProperties);
}
private void loadWithFilteredProperties(PropertySource<?> defaultProperties) {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
//这里就是加载坏境的 spring.profiles.active 和 spring.config.name
initializeProfiles();
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
if (isDefaultProfile(profile)) {
addProfileToEnvironment(profile.getName());
}
//接下来load 一直往下 外层的load也是一样的功能 ###
load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
applyActiveProfiles(defaultProperties);
}
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
//先看下getSearchLocations() 我猜应该是对应路径遍历
getSearchLocations().forEach((location) -> {
String nonOptionalLocation = ConfigDataLocation.of(location).getValue();
boolean isDirectory = location.endsWith("/");
//getSearchNames() 获取需要加载的配置名称
//进去可以看到默认名称 String DEFAULT_NAMES = "application";
Set<String> names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES;
//接下来遍历名称去加载 ###
names.forEach((name) -> load(nonOptionalLocation, name, profile, filterFactory, consumer));
});
}
//果不其然 拿到配置所在目录 以后面试问配置加载顺序 直接背下这个类这一行就完事了
private Set<String> getSearchLocations() {
if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
return getSearchLocations(CONFIG_LOCATION_PROPERTY);
}
Set<String> locations = getSearchLocations(CONFIG_ADDITIONAL_LOCATION_PROPERTY);
//private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
locations.addAll(
asResolvedSet(ConfigFileApplicationListener.this.searchLocations, DEFAULT_SEARCH_LOCATIONS));
return locations;
}
- 再看下一个load
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
//省略部分代码
......
Set<String> processed = new HashSet<>();
for (PropertySourceLoader loader : this.propertySourceLoaders) {
//getFileExtensions这里获取文件后缀
for (String fileExtension : loader.getFileExtensions()) {
if (processed.add(fileExtension)) {
//location/路径 name/配置名称 fileExtension/后缀
loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,
consumer);
}
}
}
}
//前面说了 loader就两个类 先直接看YamlPropertySourceLoader类里的
loader.getFileExtensions()
@Override
public String[] getFileExtensions() {
return new String[] { "yml", "yaml" };
}
//接下来到 loadForFileExtension
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
if (profile != null) {
// Try profile-specific file & profile section in profile file (gh-340)
//如果环境为test 那这里就是"classpath:/application + "-" + "test" + .yml
String profileSpecificFile = prefix + "-" + profile + fileExtension;
load(loader, profileSpecificFile, profile, defaultFilter, consumer);
load(loader, profileSpecificFile, profile, profileFilter, consumer);
// Try profile specific sections in files we've already processed
for (Profile processedProfile : this.processedProfiles) {
if (processedProfile != null) {
String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
load(loader, previouslyLoaded, profile, profileFilter, consumer);
}
}
}
// Also try the profile-specific section (if any) of the normal file
//如果环境为null 那就是"classpath:/application.yml
load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
- 再继续load到loadDocuments
//找到这行代码往下
List<Document> documents = loadDocuments(loader, name, resource);
private List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource)
throws IOException {
DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
List<Document> documents = this.loadDocumentsCache.get(cacheKey);
if (documents == null) {
//接下来看这个 实现类就是上PropertySourceLoader配置的了
//我们这里是yml那么直接到YamlPropertySourceLoader类吧
List<PropertySource<?>> loaded = loader.load(name, resource);
documents = asDocuments(loaded);
this.loadDocumentsCache.put(cacheKey, documents);
}
return documents;
}
- 接下来到
YamlPropertySourceLoader#load
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", getClass().getClassLoader())) {
throw new IllegalStateException(
"Attempted to load " + name + " but snakeyaml was not found on the classpath");
}
//这个地方就是解析yaml文件了 得到的果然是一个list map 断点图在下面
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;
}
读取配置断点图
概括
- 通过SpringApplication构造方法时会手动初始化listeners,即
spring.factories
中可以为org.springframework.context.ApplicationListener
的值,最终保存到本地变量List<ApplicationListener<?>> listeners
中 - 初始化事件监听类
EventPublishingRunListener
- 在
EventPublishingRunListener
类中获取到构造方法保存的监听,然后进行回调 - 后面回调到文件处理的监听
ConfigFileApplicationListener
类,然后通过加载配置中PropertySourceLoader
类名的值得到PropertiesPropertySourceLoader,YamlPropertySourceLoader
两个配置解析类 - 最终交由这两个类分别解析properties后缀和yaml后缀的配置文件
自动配置实现分析就到这里了,这个比自动装配简单多了,主要是要了解spring.factories其中的Listener配置
以上就是本章的全部内容了。
上一篇:Springboot源码分析第一弹 - 自动装配实现
下一篇:Springboot源码分析第三弹 - 自动装配扩展,手动实现一个starter
熟读唐诗三百首,不会作诗也会吟