springboot2.1.7启动分析(二)SpringApplication run

目录

 

SpringApplicationRunListeners listeners = getRunListeners(args); 

 listeners.starting()

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args)

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments)


 

SpringApplicationRunListeners listeners = 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));
	}
	SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<>(listeners);
	}

 SpringApplicationRunListeners保存了SpringApplicationRunListener类型的集合,SpringApplicationRunListener也是从META-INF/spring.factories文件中获取到并反射实例化出来的。反射的时候调用的是有参数的构造函数,目前就一个值EventPublishingRunListener,如下所示。可以看出EventPublishingRunListener中的多播器保存了SpringApplication 实例化时的ApplicationListener实例。

	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}

 listeners.starting()

	public void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}
//EventPublishingRunListener
	@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}

多播器广播了一个ApplicationStartingEvent事件,所有支持ApplicationStartingEven事件的listener执行onApplicationEvent事件处理方法

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args)

封装了启动参数,例如启动时指定的applicaton文件(dev、prod)

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments)

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
        //广播environmentPrepared事件
		listeners.environmentPrepared(environment);
        //将环境绑定到SpringApplication
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
        //环境转换器(当前不需要转换,所以还是标准的servlet环境)
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
        //将ConfigurationPropertySource支持附加到指定的Environment 。 使环境管理的每个PropertySource适应ConfigurationPropertySource并允许经典的PropertySourcesPropertyResolver调用使用configuration property names进行解析
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
  • environment :StandardServletEnvironment。在实例化StandardServletEnvironment时会定制属性源PropertySources。
public AbstractEnvironment() {
   customizePropertySources(this.propertySources);
}

StandardServletEnvironment定制属性都将优先于StandardEnvironment超类提供的系统属性和环境变量。且与Servlet相关的属性源在此阶段作为stubs添加,并且在实际的ServletContext对象可用时将被完全初始化。所以此处servletConfigInitParams和servletContextInitParams对应的source都是空对象,起到占位符作用,而父类StandardEnvironment定制属性源systemProperties(System.getProperties())和systemEnvironment(System.getenv())对应的source都有值。

  •  configureEnvironment:配置环境。方法注释翻译:模板方法以该顺序委派给configurePropertySources(ConfigurableEnvironment, String[])和configureProfiles(ConfigurableEnvironment, String[]) 。 重写此方法以完全控制环境自定义,或者重写上述方法之一以分别对属性源或配置文件进行细粒度控制。因为SpringApplication是可以重写的,所以可以重写某些方法。
    	protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    		if (this.addConversionService) {
    			ConversionService conversionService = ApplicationConversionService.getSharedInstance();
    			environment.setConversionService((ConfigurableConversionService) conversionService);
    		}
    		configurePropertySources(environment, args);
    		configureProfiles(environment, args);
    	}
    ConversionService:用于类型转换的服务接口。 这是转换系统的入口。 调用convert(Object, Class)使用此系统执行线程安全的类型转换。并将此实例设置到environment属性propertyResolver中。
    configurePropertySources:配置属性源。主要是通过启动参数添加或替换属性源,在启动时指定defaultProperties添加defaultProperties属性源(例如SpringApplicationBuilder的properties(String... defaultProperties)方法)
    configureProfiles:配置该应用程序环境中哪些配置文件处于活动状态。在配置文件处理期间,可以通过{@code spring.profiles.active}属性激活其他配置文件。主要是通过查找StandardServletEnvironment中propertySources中是否有对应的key。
  • listeners.environmentPrepared(environment):广播environmentPrepared事件。这里调试源码发现主要关注ConfigFileApplicationListener的onApplicationEvent。onApplicationEvent会调用onApplicationEnvironmentPreparedEvent方法,该方法会首先通过SpringFactoriesLoader.loadFactories(META-INF/spring.factories)查找EnvironmentPostProcessor所有实例(环境的后置处理器),依次执行每个实例的postProcessEnvironment方法。这里依旧只关注ConfigFileApplicationListener这个EnvironmentPostProcessor。关键代码如下:将配置文件属性源添加到指定的环境。
	protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
		RandomValuePropertySource.addToEnvironment(environment);
		new Loader(environment, resourceLoader).load();
	}

 通过Loader构造函数发现了解析properties和yaml文件的PropertySourceLoader,这些PropertySourceLoader也是同通过SpringFactoriesLoader.loadFactories实例化出来的。

load方法。   

1、initializeProfiles():初始化一个null和default的profile.null是为了读取application.yml/applicaton.properties。

2、循环处理profiles 。先处理值为null的profile,读取spring.profiles.active值(例如 dev),然后profiles添加dev的profile且移除default的profile.接着循环处理dev的profile。因为该profile不为空且不是默认的,所以会将该profile的name设置为环境的profile(下方代码1处)

3、addLoadedPropertySources:StandardServletEnvironment添加已经加装的PropertySources(如下图5、6所示)

                                                                                                                       application.yml对应的PropertySource部分内容截图: 

		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();
                //1
				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();
		}

	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方法分析:其中DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/",也就是springboot查找配置文件的路径 ,可以看到asResolvedSet方法先将该字符串解析为list集合,然后进行了反转,所以springboot查找配置文件的顺序为:

		
		private Set<String> getSearchLocations() {
			if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
				return getSearchLocations(CONFIG_LOCATION_PROPERTY);
			}
			Set<String> locations = getSearchLocations(CONFIG_ADDITIONAL_LOCATION_PROPERTY);
			locations.addAll(
					asResolvedSet(ConfigFileApplicationListener.this.searchLocations, DEFAULT_SEARCH_LOCATIONS));
			return locations;
		}

private Set<String> asResolvedSet(String value, String fallback) {
			List<String> list = Arrays.asList(StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(
					(value != null) ? this.environment.resolvePlaceholders(value) : fallback)));
			Collections.reverse(list);
			return new LinkedHashSet<>(list);
		}

猜你喜欢

转载自blog.csdn.net/sinat_33472737/article/details/112762032