SpringBoot源码解析之Config

来源:互联网 发布:乎的用法 编辑:程序博客网 时间:2024/05/16 23:40

SpringBoot配置文件加载

1. ConfigFileApplicationListener

  1. 配置相关的属性名基本都可以在这个类里面找到. 例如 spring.profiles.active,application(配置文件的名字;不过后缀名存储在其他位置)等.
  2. 另外该类的注释已经解释得比较清楚了.
  3. 该类的加载位置是 spring-boot-1.5.7.RELEASE.jar下的META-INF/spring.factories中的 ApplicationListener, 而加载时机是构造SpringApplication实例时(参见本人博客SpringBoot源码研究之Start中的第二节).

ConfigFileApplicationListener继承链如下
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. 亮点

  1. ConfigFileApplicationListener类同时实现了EnvironmentPostProcessor接口和SmartApplicationListener接口,而在监听回调中又调用所有EnvironmentPostProcessor接口实现类, 这样就将配置文件的解析工作从主流程中剥离出来了,但其实又是集中在一个类里面的。
  2. ConfigFileApplicationListener类中的内部类Loader类专职了配置文件的解析和加载工作,这样ConfigFileApplicationListener类的职责就更加单一了。

6. 总结

  1. ConfigFileApplicationListener类的实例通过spring-boot-1.5.7.RELEASE.jar下的META-INF/spring.factories配置文件加载进容器。
  2. ConfigFileApplicationListener类通过监听ApplicationEnvironmentPreparedEvent事件来进行自定义配置文件的读取。
  1. http://blog.csdn.net/liaokailin/article/details/48878447
  2. http://blog.csdn.net/liaokailin/article/details/48864737
  3. http://blog.csdn.net/liaokailin/article/details/48423847
  4. Spring Boot属性配置文件详解 - 所以如果不是特殊应用场景,就只需要在application.properties中完成一些属性配置就能开启各模块的应用。
阅读全文
0 0
原创粉丝点击