Springboot默認加載application.yml原理以及擴展


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.ApplicatonEnvironDemoListener

2、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;
		}
	}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM