SpringBoot源码解析之Config
来源:互联网 发布:乎的用法 编辑:程序博客网 时间:2024/05/16 23:40
SpringBoot配置文件加载
1. ConfigFileApplicationListener
类
- 配置相关的属性名基本都可以在这个类里面找到. 例如
spring.profiles.active
,application
(配置文件的名字;不过后缀名存储在其他位置)等. - 另外该类的注释已经解释得比较清楚了.
- 该类的加载位置是
spring-boot-1.5.7.RELEASE.jar
下的META-INF/spring.factories
中的ApplicationListener
, 而加载时机是构造SpringApplication
实例时(参见本人博客SpringBoot源码研究之Start中的第二节).
ConfigFileApplicationListener
继承链如下
1.1 实现了SmartApplicationListener
接口
// ------- SmartApplicationListener接口实现@Overridepublic boolean supportsEventType(Class<? extends ApplicationEvent> eventType) { // 只支持`ApplicationEnvironmentPreparedEvent`和`ApplicationPreparedEvent`事件 return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType) || ApplicationPreparedEvent.class.isAssignableFrom(eventType);}@Overridepublic boolean supportsSourceType(Class<?> aClass) { // 支持所有的SourceType return true;}// -------- SmartApplicationListener接口的基接口ApplicationListener<ApplicationEvent>的实现@Overridepublic void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent( (ApplicationEnvironmentPreparedEvent) event); } if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent(event); }}// ApplicationEnvironmentPreparedEvent 类型事件 private void onApplicationEnvironmentPreparedEvent( ApplicationEnvironmentPreparedEvent event) { List<EnvironmentPostProcessor> postProcessors = loadPostProcessors(); // 将自身加载到集合中, 因为`ConfigFileApplicationListener`自身就实现了`EnvironmentPostProcessor`接口 postProcessors.add(this); AnnotationAwareOrderComparator.sort(postProcessors); for (EnvironmentPostProcessor postProcessor : postProcessors) { postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()); }}List<EnvironmentPostProcessor> loadPostProcessors() { /* 从classpath下的每个jar中的"META-INF/spring.factories"中加载指定类, 例如这里的条件下从spring-boot-1.5.7.RELEASE.jar中加载到的类如下:# Environment Post Processorsorg.springframework.boot.env.EnvironmentPostProcessor=\org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor */ return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());}// ApplicationPreparedEvent 类型事件private void onApplicationPreparedEvent(ApplicationEvent event) { this.logger.replayTo(ConfigFileApplicationListener.class); addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext());}/** * Add appropriate post-processors to post-configure the property-sources. * @param context the context to configure */protected void addPostProcessors(ConfigurableApplicationContext context) { // 看PropertySourceOrderingPostProcessor名称应该是对PropertySource进行排序 // 注意PropertySourceOrderingPostProcessor实现了Ordered接口, 而且优先级是最高的. context.addBeanFactoryPostProcessor( new PropertySourceOrderingPostProcessor(context));}
1.2 实现了EnvironmentPostProcessor
接口
@Overridepublic void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { // RandomValuePropertySource就是在这里被加入到environment中的 // 配置文件中可以通过`${random.xx}`来获取随机值. 相关的类就是这个`RandomValuePropertySource`. // 细节可以看看 RandomValuePropertySource实现的 getProperty方法 addPropertySources(environment, application.getResourceLoader()); configureIgnoreBeanInfo(environment); bindToSpringApplication(environment, application);}// Add config file property sources to the specified environment.protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { RandomValuePropertySource.addToEnvironment(environment); // 就是在这里将配置文件进行加载 new Loader(environment, resourceLoader).load();}
2. 时机
在继续深入ConfigFileApplicationListener.Loader
之前, 让我们回头看看Spring是如何执行到这里的, 如下图堆栈所示
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 这里的参数listeners的来源: // spring-boot-1.5.7.RELEASE.jar中的"META-INF/spring.factories"中的SpringApplicationRunListener实现. // Create and configure the environment // 这里的getOrCreateEnvironment()方法会创建一个StandardServletEnvironment实例(web环境下,如何判断请参考之前的文章). ConfigurableEnvironment environment = getOrCreateEnvironment(); // 根据用户传入的参数对environment进行配置. configureEnvironment(environment, applicationArguments.getSourceArgs()); // 触发ApplicationEnvironmentPreparedEvent事件. 这也和上面的逻辑吻合上了. listeners.environmentPrepared(environment); if (!this.webEnvironment) { // 优秀的源码读到一定的量之后, 应该就会有这样一个感受: // 它们很少将功能集中在一起, 而是进行拆分为一个个小的功能点; // 这也是新手在阅读源码时会碰到比较大的问题之一, 跟着跳来跳去就晕了. // 这里也是一样, 当发现当前不是web环境时, 就委托给一个专门的类去处理转换当前的environment. 这里使用的类名和方法名已经很清楚地表明了所做的事情. environment = new EnvironmentConverter(getClassLoader()) .convertToStandardEnvironmentIfNecessary(environment); } return environment;}
3. ConfigFileApplicationListener.Loader
类
作为ConfigFileApplicationListener
类的内部类, Loader
负责对配置文件进行加载.
public void load() { /* 这里的PropertySourcesLoader会从`spring-boot-1.5.7.RELEASE.jar`下的`META-INF/spring.factories`中加载# PropertySource Loaders 属性文件加载org.springframework.boot.env.PropertySourceLoader=\org.springframework.boot.env.PropertiesPropertySourceLoader,\org.springframework.boot.env.YamlPropertySourceLoader */ this.propertiesLoader = new PropertySourcesLoader(); this.activatedProfiles = false; this.profiles = Collections.asLifoQueue(new LinkedList<Profile>()); this.processedProfiles = new LinkedList<Profile>(); // Pre-existing active profiles set via Environment.setActiveProfiles() // are additional profiles and config files are allowed to add more if // they want to, so don't call addActiveProfiles() here. Set<Profile> initialActiveProfiles = initializeActiveProfiles(); this.profiles.addAll(getUnprocessedActiveProfiles(initialActiveProfiles)); if (this.profiles.isEmpty()) { for (String defaultProfileName : this.environment.getDefaultProfiles()) { Profile defaultProfile = new Profile(defaultProfileName, true); if (!this.profiles.contains(defaultProfile)) { this.profiles.add(defaultProfile); } } } // The default profile for these purposes is represented as null. We add it // last so that it is first out of the queue (active profiles will then // override any settings in the defaults when the list is reversed later). this.profiles.add(null); while (!this.profiles.isEmpty()) { Profile 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); } } } this.processedProfiles.add(profile); } // 将解析过后的资源信息放置进Enviroment中propertySources属性集合中 addConfigurationProperties(this.propertiesLoader.getPropertySources());}
5. 亮点
ConfigFileApplicationListener
类同时实现了EnvironmentPostProcessor
接口和SmartApplicationListener
接口,而在监听回调中又调用所有EnvironmentPostProcessor
接口实现类, 这样就将配置文件的解析工作从主流程中剥离出来了,但其实又是集中在一个类里面的。ConfigFileApplicationListener
类中的内部类Loader
类专职了配置文件的解析和加载工作,这样ConfigFileApplicationListener
类的职责就更加单一了。
6. 总结
ConfigFileApplicationListener
类的实例通过spring-boot-1.5.7.RELEASE.jar
下的META-INF/spring.factories
配置文件加载进容器。ConfigFileApplicationListener
类通过监听ApplicationEnvironmentPreparedEvent
事件来进行自定义配置文件的读取。
7. Links
- http://blog.csdn.net/liaokailin/article/details/48878447
- http://blog.csdn.net/liaokailin/article/details/48864737
- http://blog.csdn.net/liaokailin/article/details/48423847
- Spring Boot属性配置文件详解 - 所以如果不是特殊应用场景,就只需要在
application.properties
中完成一些属性配置就能开启各模块的应用。
阅读全文
0 0
- SpringBoot源码解析之Config
- SpringBoot-自动配置源码解析
- webpack.config.js源码解析
- SpringBoot源码研究之Start
- 【Spring Boot】SpringBoot-自动配置源码解析
- 第二十三章 SpringBoot @SpringBootApplication注解源码解析
- Springboot 源码解析(启动时)
- Redis源码解析(十六)--- config配置文件
- CI框架源码解析六之配置类文件Config.php
- springboot源码分析2-springboot 之banner定制以及原理
- springboot源码分析4-springboot之SpringFactoriesLoader使用
- springboot源码分析5-springboot之命令行参数以及原理
- springboot源码分析6-springboot之PropertySource类初探
- springboot-cloud-6-config
- SpringBoot入门之HelloWorld(含源码)
- SpringBoot源码分析之CommandLineRunner、ApplicationRunner
- CodeIgniter源码分析之Config.php
- struts1源码解析_struts加载struts-config.xml
- 02-线性结构1 两个有序链表序列的合并(15 分)
- mysql之InnoDB存储引擎(1)——InnoDB存储引擎体系
- 新建的类对象,在重写equals方法后为什么要重写hashcode方法?
- flexbox 剩余空间分配规则
- HDU 1166 敌兵布阵
- SpringBoot源码解析之Config
- JavaScript同步、异步、回调执行顺序之经典闭包(setTimeout面试题分析)
- 质数判断程序
- Unity_Lua_VS环境下入门操作
- 【单调队列】BZOJ 1047 [HAOI2007]理想的正方形
- 算法爱好者——重复子串 ? 待解决
- spring mvc上传带参数的文件
- A1048. Find Coins
- python 的 DEAP框架学习