Springboot默认加载application.yml原理以及扩展
SpringApplication.run(...)默认会加载classpath下的application.yml或application.properties配置文件。公司要求搭建的框架默认加载一套默认的配置文件demo.properties,让开发人员实现“零”配置开发,但是前提如果开发人员在application.yml或application.properties文件中自定义配置,则会“覆盖”默认的demo.properties文件,按照Springboot外部化配置的特性(优先使用先加载的),只要demo.properties配置在application.yml或application.properties 配置之后加载到environment中即可。
一、SpirngApplication.run(...)源码分析
通过源码分析,得知Springboot加载配置文件,是利用Spring的事件机制,通过EventPublishingRunListener取发布准备资源事件ApplicationEnvironmentPreparedEvent,被ConfigFileApplicationListener监听到,从而来实现资源的加载
具体源码如下:
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); //这里是扩展的关键点 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); //这里是加载资源的关键 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); .... } //从方法名称来看就是准备environment的即配置信息 private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); //这里默认EventPublishingRunListener发布ApplicationEnvironmentPreparedEvent事件 //让监听器ConfigFileApplicationListener加载配置文件 //这个listeners就是我们扩展的地方 listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (this.webApplicationType == WebApplicationType.NONE) { environment = new EnvironmentConverter(getClassLoader()) .convertToStandardEnvironmentIfNecessary(environment); } ConfigurationPropertySources.attach(environment); return environment; }SpirngApplication.run(...)方法中有个重要的扩展点方法getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)); } //可扩展的关键点SpringFactoriesLoader //SpringFactoriesLoader会去加载META-INF/spring.factories文件,并根据 //type过滤出符合要求的类 //比如这里的type对应的是:SpringApplicationRunListener private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }Springboot默认提供的META-INF/spring.factories,这里就是我们可以扩展的地方
# Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener至此资源加载的大概流程就分析完了,下面是我们的扩展
二、扩展——自定义加载配置文件(demo.properties)
通过上述源码分析得知:只需要在项目中添加META-INF/spring.factories,并配置SpringApplicationRunListener为我们自定义的来即可
1、在项目中的resources下创建META-INF/spring.factories
org.springframework.boot.SpringApplicationRunListener=\ com.demo.module.ApplicatonEnvironDemoListener2、ApplicatonEnvironDemoListener的代码
package com.chyjr.hyboot.demo.module; import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplicationRunListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.PriorityOrdered; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertiesPropertySource; import org.springframework.core.env.PropertySource; import java.io.IOException; import java.util.Properties; public class ApplicatonEnvironDemoListener implements SpringApplicationRunListener,PriorityOrdered { private SpringApplication application; private String[] args; /** * 通过反射创建该实例对象的,构造方法中的参数要加上如下参数 */ public ApplicatonEnvironDemoListener(SpringApplication application,String[] args){ this.application = application; this.args = args; } /** * 在准备环境之间调用 * SpringApplication#run -> listeners.starting(); */ @Override public void starting() { System.out.println("starting-----"); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { Properties properties = new Properties(); try { //demo.properties就是我们自定义的配置文件,extension是自定义目录 properties.load(this.getClass().getClassLoader(). getResourceAsStream("extension/demo.properties")); PropertySource propertySource =new PropertiesPropertySource("demo",properties); //PropertySource是资源加载的核心 MutablePropertySources propertySources = environment.getPropertySources(); //这里添加最后 propertySources.addLast(propertySource); } catch (IOException e) { e.printStackTrace(); } } //省略其他方法 ... /** * 这里可以设置该配置文件加载的顺序,在application.yml之前还是之后 * EventPublishingRunListener#getOrder方法返回 “0”,按照需求这里我们这是比0大, * 即在application.yml之后加载,这样在application.yml配置时,可以“覆盖”my.yml * 这里用“覆盖”可能不合适,意思到了就好 */ @Override public int getOrder() { return 1; } }转载于:https://www.cnblogs.com/liruiloveparents/p/9492797.html
相关资源:Spring Boot 中application.yml与bootstrap.yml的区别