springboot(三)——application.properties和application.yml是什麼時候解析的

前言

用過的springboot的小夥伴都知道springboot不須要再像springmvc引入那麼多的配置文件,只須要加入application.properties或者application.yml便可,好比在上一篇文章講到數據庫的配置,只須要在文件引入以下的配置便可:java

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/zplxjj?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=@ZPLxjj12345
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
複製代碼

下面簡單介紹下springboot在啓動的時候是在什麼時候讀取的properties和yml文件的內容的mysql

實現一個簡單的自定義監聽器

第一步:定義一個event,繼承ApplicationEventspring

public class CustomerApplicationEvent extends ApplicationEvent {
    public CustomerApplicationEvent(Object source) {
        super(source);
        System.out.println("CustomerApplicationEvent constructor...");
    }
}
複製代碼

第二步:定義一個listenersql

@Component
public class CustomerApplicationListener implements ApplicationListener<CustomerApplicationEvent> {

    @Override
    public void onApplicationEvent(CustomerApplicationEvent customerApplicationEvent) {
        System.out.println("customerApplicationEvent:"+customerApplicationEvent.getClass().getName());
    }
}
複製代碼

第三步:註冊監聽器數據庫

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        // 註冊 CustomerApplicationListener 事件監聽器
        context.addApplicationListener(new CustomerApplicationListener());
        // 發佈 CustomerApplicationEvent 事件
        context.publishEvent(new CustomerApplicationEvent(new Object()));
    }
}
複製代碼

啓動項目後,會發現控制檯輸出了:springboot

CustomerApplicationEvent constructor...
customerApplicationEvent:com.stone.zplxjj.event.CustomerApplicationEvent
複製代碼

springboot自帶的事件

  • ApplicationStartingEvent:應用啓動事件,在調用 SpringApplication.run() 方法以前,能夠從中獲取到 SpringApplication 對象,進行一些啓動前設置。
  • ApplicationEnvironmentPreparedEvent:Environment準備完成事件,此時能夠從中獲取到 Environment 對象並對其中的配置項進行查看或者修改
  • ApplicationPreparedEvent:ApplicationContext準備完成事件,接下來 Spring 就可以向容器中加載 Bean 了 。
  • ApplicationReadyEvent:應用準備完成事件,預示着應用能夠接收和處理請求了。
  • ApplicationFailedEvent:應用啓動失敗事件,能夠從中捕獲到啓動失敗的異常信息進行相應處理,例如:添加虛擬機對應的鉤子進行資源的回收與釋放。

讀取配置代碼入口:ApplicationEnvironmentPreparedEvent和ConfigFileApplicationListener

加載配置文件須要用到ConfigFileApplicationListener,其代碼以下:bash

@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent(
					(ApplicationEnvironmentPreparedEvent) event);
		}
		if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent(event);
		}
	}
複製代碼

進入方法:onApplicationEnvironmentPreparedEvent微信

private void onApplicationEnvironmentPreparedEvent( ApplicationEnvironmentPreparedEvent event) {
		List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
		postProcessors.add(this);
		AnnotationAwareOrderComparator.sort(postProcessors);
		for (EnvironmentPostProcessor postProcessor : postProcessors) {
			postProcessor.postProcessEnvironment(event.getEnvironment(),
					event.getSpringApplication());
		}
	}
複製代碼

進入postProcessor.postProcessEnvironment:mvc

//類:ConfigFileApplicationListener
	@Override
	public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
		addPropertySources(environment, application.getResourceLoader());
	}
複製代碼

進入addPropertySourcesapp

protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
        //將隨機方法放入到PropertySources中
		RandomValuePropertySource.addToEnvironment(environment);
        //load加載
		new Loader(environment, resourceLoader).load();
	}
複製代碼

進入load方法:

public void load() {
			this.profiles = new LinkedList<>();
			this.processedProfiles = new LinkedList<>();
			this.activatedProfiles = false;
			this.loaded = new LinkedHashMap<>();
			initializeProfiles();
			while (!this.profiles.isEmpty()) {
				Profile profile = this.profiles.poll();
				if (profile != null && !profile.isDefaultProfile()) {
					addProfileToEnvironment(profile.getName());
				}
				load(profile, this::getPositiveProfileFilter,
						addToLoaded(MutablePropertySources::addLast, false));
				this.processedProfiles.add(profile);
			}
			resetEnvironmentProfiles(this.processedProfiles);
			load(null, this::getNegativeProfileFilter,
					addToLoaded(MutablePropertySources::addFirst, true));
			addLoadedPropertySources();
		}
複製代碼

進入字方法load

private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
			getSearchLocations().forEach((location) -> {
				boolean isFolder = location.endsWith("/");
				Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
				names.forEach(
						(name) -> load(location, name, profile, filterFactory, consumer));
			});
		}
複製代碼
  • getSearchLocations():首先看CONFIG_LOCATION_PROPERTY,是否存在配置,無則走默認配置路徑DEFAULT_SEARCH_LOCATIONS
/** * The "config location" property name. */
	public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";

	// Note the order is from least to most specific (last one wins)
	private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
複製代碼
  • getSearchNames():首先看CONFIG_NAME_PROPERTY(spring.config.name)配置,不然走DEFAULT_NAMES(application)

**spring.config.name說明:**假如你不喜歡「application.properties」這個默認文件名,你能夠從新設定:spring.config.name屬性直接指定屬性文件名稱,spring.config.location屬性指定明確路徑,可是要注意不能寫在application.properties文件裏,這樣會不起做用,能夠寫在java -jar xxx.jar --spring.config.name=custom.properties,還能夠經過環境變量等方式,yml文件也能夠這樣

真正加載配置文件的方法:

private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
			if (!StringUtils.hasText(name)) {
				for (PropertySourceLoader loader : this.propertySourceLoaders) {
					if (canLoadFileExtension(loader, location)) {
						load(loader, location, profile,
								filterFactory.getDocumentFilter(profile), consumer);
						return;
					}
				}
			}
			Set<String> processed = new HashSet<>();
			for (PropertySourceLoader loader : this.propertySourceLoaders) {
				for (String fileExtension : loader.getFileExtensions()) {
					if (processed.add(fileExtension)) {
						loadForFileExtension(loader, location + name, "." + fileExtension,
								profile, filterFactory, consumer);
					}
				}
			}
		}
複製代碼

loader.getFileExtensions():獲取全部支持的文件後綴,loader初始化以下:

Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
			this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
					PropertySourceLoader.class, getClass().getClassLoader());
		}
複製代碼

經過加載jar:spring-boot-2.1.4.RELEASE.jar:META-INF/spring.factories文件下對應內容:

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
複製代碼

從這裏咱們能夠看到,經過PropertiesPropertySourceLoader和YamlPropertySourceLoader 加載配置文件,具體源碼沒有細看了,有興趣自行閱讀吧

加載完配置文件,調用方法:addLoadedPropertySources()

結語

至此,springboot加載properties和yml的入口就分析到這裏了,細節上確定不能面面俱到,可是入口知道了,後面就好分析了

本人也開通了微信公衆號:stonezplxjj和我的博客:www.zplxjj.com,更多文章歡迎關注公衆號:

相關文章
相關標籤/搜索