Springboot源码分析第二弹 - 自动配置实现

文章接上文哈,请先熟读自动装配篇,不然本文比较难以理解。

预先准备

  • 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;
}

读取配置断点图

读取配置断点图

概括

  1. 通过SpringApplication构造方法时会手动初始化listeners,即spring.factories中可以为org.springframework.context.ApplicationListener的值,最终保存到本地变量List<ApplicationListener<?>> listeners
  2. 初始化事件监听类EventPublishingRunListener
  3. EventPublishingRunListener类中获取到构造方法保存的监听,然后进行回调
  4. 后面回调到文件处理的监听ConfigFileApplicationListener类,然后通过加载配置中PropertySourceLoader类名的值得到PropertiesPropertySourceLoader,YamlPropertySourceLoader两个配置解析类
  5. 最终交由这两个类分别解析properties后缀和yaml后缀的配置文件

自动配置实现分析就到这里了,这个比自动装配简单多了,主要是要了解spring.factories其中的Listener配置

以上就是本章的全部内容了。

上一篇:Springboot源码分析第一弹 - 自动装配实现
下一篇:Springboot源码分析第三弹 - 自动装配扩展,手动实现一个starter

熟读唐诗三百首,不会作诗也会吟

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值