zoukankan      html  css  js  c++  java
  • 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;
    		}
    	}
  • 相关阅读:
    POJ2481(树状数组:统计数字 出现个数)
    jenkins(2)-linux环境下jenkins启动/重启/停止命令
    jenkins(1)-部分插件由于缺少依赖无法加载。要恢复这些插件提供的功能,需要修复这些问题并重启Jenkins的解决办法
    linux(4)-rpm包安装
    charles(2)-charles如何打断点,修改Request数据
    charles(1)-charles如何打断点,修改Response数据
    linux(3)-普通用户如何切换到root
    jmeter(51) Groovy脚本高级实战
    jmeter(49)-jenkins+ant+jmeter持续集成接口自动化测试-(linux环境)
    Codeforces Round #545 (Div. 2)D(KMP,最长公共前后缀,贪心)
  • 原文地址:https://www.cnblogs.com/liruiloveparents/p/9492797.html
Copyright © 2011-2022 走看看