3、Springboot之ApplicationContext&Listener&Config

1、SpringApplication

public ConfigurableApplicationContext run(String... args) {
    
    
	...
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();
	ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
	//StandardServletEnvironment#1.1.1
	ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
	ConfigurableApplicationContext context = createApplicationContext();
	prepareContext(context, environment, listeners, applicationArguments, printedBanner);
	refreshContext(context);
	afterRefresh(context, applicationArguments);
	listeners.started(context);
	callRunners(context, applicationArguments);
	listeners.running(context);
	return context;
}
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
    
    
		ConfigurableEnvironment environment = getOrCreateEnvironment();//实例化StandardServletEnvironment
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
		...
		return environment;
	}

参考文章得知针对ApplicationEnvironmentPreparedEvent事件触发核心监听器ConfigFileApplicationListener执行。

1.1、解析应用参数Program Arguments & 环境属性Environment Variable

由核心类StandardServletEnvironment及其父类AbstractEnvironment完成解析&持有 Program Arguments、Environment Variable配置值。

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    
    
	configurePropertySources(environment, args);
	configureProfiles(environment, args);
}
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
    
    
	MutablePropertySources sources = environment.getPropertySources();
	...
	sources.addFirst(new SimpleCommandLinePropertySource(args));// #3
}

StandardServletEnvironment父类AbstractEnvironment属性MutablePropertySources持有应用参数|运行参数args【CommandLineArgs】

protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
    
    
	environment.getActiveProfiles(); //AbstractEnvironment#getActiveProfiles
	Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
	profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
	environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}

2、AbstractEnvironment

public abstract class AbstractEnvironment implements ConfigurableEnvironment {
    
    
	private final MutablePropertySources propertySources = new MutablePropertySources();
	private final ConfigurablePropertyResolver propertyResolver =
															new PropertySourcesPropertyResolver(this.propertySources);
	public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";
	public AbstractEnvironment() {
    
    
		customizePropertySources(this.propertySources);//StandardServletEnvironment#customizePropertySources
	}
	
	public String[] getActiveProfiles() {
    
    
		return StringUtils.toStringArray(doGetActiveProfiles());
	}

	protected Set<String> doGetActiveProfiles() {
    
    
		synchronized (this.activeProfiles) {
    
    
			if (this.activeProfiles.isEmpty()) {
    
    
				String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
				if (StringUtils.hasText(profiles)) {
    
    
					setActiveProfiles(StringUtils.commaDelimitedListToStringArray(
							StringUtils.trimAllWhitespace(profiles)));
				}
			}
			return this.activeProfiles;
		}
	}
	
	public String getProperty(String key) {
    
    
		return this.propertyResolver.getProperty(key);
	}
}

主要作用是创建MutablePropertySourcesPropertySourcesPropertyResolver

2.1、StandardServletEnvironment

Spring中的StandardServletEnvironment将由基于servlet的web应用程序使用,所有基于servlet的web相关ApplicationContext类将会初始化一个StandardServletEnvironment实例。
在这里插入图片描述
StandardServletEnvironment实例化过程:

  • MutablePropertySources实例化。
  • PropertySourcesPropertyResolver实例化。
  • 获取到应用全部的系统属性以及环境变量属性

2.1.1、StandardServletEnvironment实例化

private ConfigurableEnvironment getOrCreateEnvironment() {
    
    
	if (this.environment != null) {
    
    
		return this.environment;
	}
	switch (this.webApplicationType) {
    
    
	case SERVLET:
		return new StandardServletEnvironment();
	case REACTIVE:
		return new StandardReactiveWebEnvironment();
	default:
		return new StandardEnvironment();
	}
}

StandardServletEnvironment的实例化会调用其抽象类AbstractEnvironment的构造方法,在AbstractEnvironment构造方法中调用子类StandardServletEnvironment#customizePropertySources方法。

2.1.2、StandardServletEnvironment类信息

public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {
    
    

	/** servletContext初始化参数属性源名 */
	public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";
	/** ServletConfig初始化参数属性源名 */
	public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";
	/** JNDI属性源名 */
	public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";
	
	@Override
	protected void customizePropertySources(MutablePropertySources propertySources) {
    
    
		propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
		propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
		if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
    
    
			propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
		}
		super.customizePropertySources(propertySources);//StandardEnvironment#customizePropertySources
	}

	@Override
	public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
    
    
		WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
	}

}

2.2、StandardEnvironment

public class StandardEnvironment extends AbstractEnvironment {
    
    
	public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
	public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
	@Override
	protected void customizePropertySources(MutablePropertySources propertySources) {
    
    
		propertySources.addLast(
				new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
		propertySources.addLast(
				new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
	}

}
  • getSystemProperties():其实就是通过(Map) System.getProperties()获取系统属性包括用户自定义的JVM的参数【-D】。
  • getSystemEnvironment():通过(Map) System.getenv()获取应用环境变量属性,例如spring.profiles.active=dev等。

此时并没有解析配置文件的配置信息

3、ConfigurableApplicationContext

protected ConfigurableApplicationContext createApplicationContext() {
    
    
	Class<?> contextClass = this.applicationContextClass;
	switch (this.webApplicationType) {
    
    
	case SERVLET://AnnotationConfigServletWebServerApplicationContext
		contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
		break;
	case REACTIVE://AnnotationConfigReactiveWebServerApplicationContext
		contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
		break;
	default://AnnotationConfigApplicationContext
		contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
	}
	return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

ConfigurableApplicationContext的子类同时继承了GenericApplicationContext。在其实例化过程中触发其父类的实例化,此时存在IOC顶级容器DefaultListableBeanFactory的实例化。

4、SimpleCommandLinePropertySource

public SimpleCommandLinePropertySource(String... args) {
    
    
	super(new SimpleCommandLineArgsParser().parse(args));
}

4.1、SimpleCommandLineArgsParser

public CommandLineArgs parse(String... args) {
    
    
	CommandLineArgs commandLineArgs = new CommandLineArgs();
	for (String arg : args) {
    
    
		if (arg.startsWith("--")) {
    
    
			String optionText = arg.substring(2, arg.length());
			String optionName;
			String optionValue = null;
			if (optionText.contains("=")) {
    
    
				optionName = optionText.substring(0, optionText.indexOf('='));
				optionValue = optionText.substring(optionText.indexOf('=')+1, optionText.length());
			}
			else {
    
    
				optionName = optionText;
			}
			commandLineArgs.addOptionArg(optionName, optionValue);
		}
		else {
    
    
			commandLineArgs.addNonOptionArg(arg);
		}
	}
	return commandLineArgs;
}
  • CommandLineArgs#addOptionArg:Map集合类型的存储结构。
  • CommandLineArgs#addNonOptionArg:List集合类型的存储结构。
    如果Program Arguments项中配置项是通过"–"符号设置,则以Map集合类型的结构存储,否则List集合类型的结构存储。

Program Arguments: --mq.client=huawei --mq.name=lisi mq.age=10 mq.addr=北京

5、PropertySourcesPropertyResolver

public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
    
    
	private final PropertySources propertySources;//MutablePropertySources
	
	protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
    
    
		if (this.propertySources != null) {
    
    
			for (PropertySource<?> propertySource : this.propertySources) {
    
    
				Object value = propertySource.getProperty(key);
				if (value != null) {
    
    
					if (resolveNestedPlaceholders && value instanceof String) {
    
    
						value = resolveNestedPlaceholders((String) value);
					}
					logKeyFound(key, propertySource, value);
					return convertValueIfNecessary(value, targetValueType);
				}
			}
		}
		return null;
	}
}

PropertySourcesPropertyResolver中PropertySources即为MutablePropertySources,其包含servletContextInitParams、servletConfigInitParams、jndiProperties、systemEnvironment、systemProperties五部分。最终通过systemEnvironment获取到spring.profiles.active的环境配置实际值。

6、ConfigFileApplicationListener

public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
    
    
	public void onApplicationEvent(ApplicationEvent event) {
    
    
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
    
    
			onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
		}
		if (event instanceof ApplicationPreparedEvent) {
    
    
			onApplicationPreparedEvent(event);
		}
	}
	
	private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
    
    
		List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();//通过SPI机制加载全部EnvironmentPostProcessor相关的后置处理器
		postProcessors.add(this);
		AnnotationAwareOrderComparator.sort(postProcessors);
		for (EnvironmentPostProcessor postProcessor : postProcessors) {
    
    
			postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
		}
	}
	
	public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
    
    
		addPropertySources(environment, application.getResourceLoader());
	}
	
	protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
    
    
		RandomValuePropertySource.addToEnvironment(environment);
		new Loader(environment, resourceLoader).load();
	}
}

spring-boot.jar#spring.factories文件:

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor

ConfigFileApplicationListener本身也是EnvironmentPostProcessor后置处理器。
同时存在三个不同扩展名的配置文件,配置信息均生效,如果文件中存在相同配置key则其优先级为:properties>yaml>yml。

6.1、静态内部类Loader

private Map<Profile, MutablePropertySources> loaded;
private Map<DocumentsCacheKey, List<Document>> loadDocumentsCache = new HashMap<>();

public void load() {
    
    
	this.profiles = new LinkedList<>();
	this.processedProfiles = new LinkedList<>();
	this.activatedProfiles = false;
	this.loaded = new LinkedHashMap<>();
	initializeProfiles();//获取环境变量spring.profiles.active
	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();
}

静态内部类Loader中的profiles包含两个一个是通过StandardServletEnvironment获取环境变量得到的。另一个profiles为null,其作用是保证读取到默认文件的配置文件信息【即使没有通过spring.profiles.active环境变量激活profiles】。

  1. profiles通常包括两个:代表默认配置文件的null以及通过spring.profiles.active激活的profiles。
  2. SearchLocations:获取不同路径下的全部配置文件。
  3. propertySourceLoaders:配置文件不同后缀扩展名类型【.properties、.yml】的解析器。

loadDocumentsCache的作用:对于相同路径下的配置文件避免重复加载。
loaded:配置文件抽象成Document后的存储容器。通常包含默认文件【profile = null】以及激活的配置文件。

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));
	});
}

getSearchNames:就是选择配置文件默认名DEFAULT_NAMES【application】。

6.1.1、获取项目配置文件所在的磁盘路径

private Set<String> getSearchLocations() {
    
    
	// CONFIG_LOCATION_PROPERTY:spring.config.location
	if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
    
    
		return getSearchLocations(CONFIG_LOCATION_PROPERTY);
	}
	// CONFIG_ADDITIONAL_LOCATION_PROPERTY : "spring.config.additional-location"
	Set<String> locations = getSearchLocations(CONFIG_ADDITIONAL_LOCATION_PROPERTY);
	locations.addAll(
			// DEFAULT_SEARCH_LOCATIONS : "classpath:/,classpath:/config/,file:./,file:./config/"
			asResolvedSet(ConfigFileApplicationListener.this.searchLocations, DEFAULT_SEARCH_LOCATIONS));
	return locations;
}

通常情况下会选择DEFAULT_SEARCH_LOCATIONS配置的文件分别读取应用项目中配置信息。

如果启动类增加运行参数–spring.config.location=/home/Desktop/application-dev.properties 则应用配置信息均由对应文件负责

6.1.2、抽象配置文件为Document

private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
		DocumentConsumer consumer) {
    
    
	...
	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);
			}
		}
	}
}

propertySourceLoaders:PropertiesPropertySourceLoader【扩展名properties】、YamlPropertySourceLoader【yml、yaml】。

private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
		Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
    
    
	DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
	DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
	if (profile != null) {
    
    
		String profileSpecificFile = prefix + "-" + profile + fileExtension;
		load(loader, profileSpecificFile, profile, defaultFilter, consumer);//加载默认配置文件
		load(loader, profileSpecificFile, profile, profileFilter, consumer);//加载profiles对应的配置文件
		for (Profile processedProfile : this.processedProfiles) {
    
    
			if (processedProfile != null) {
    
    
				String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
				load(loader, previouslyLoaded, profile, profileFilter, consumer);
			}
		}
	}
	//profiles没有激活
	load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}

prefix + fileExtension:classpath:/application.properties。

  • 如果profiles没有激活【profiles = null】:加载默认配置文件即可。
  • 如果profiles已经激活,则同时加载默认配置文件以及profiles对应的配置文件。

defaultFilter & profileFilter:执行步骤#6.1.3时,参数profile的取值不同,过滤条件对应不同

private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
				DocumentConsumer consumer) {
    
    
	Resource resource = this.resourceLoader.getResource(location);
	if (resource == null || !resource.exists()) {
    
    //表明本地磁盘没有对应的配置文件
		return;
	}
	String name = "applicationConfig: [" + location + "]";
	//读取项目中本地文件中配置信息,将配置文件抽象成Document
	List<Document> documents = loadDocuments(loader, name, resource);
	List<Document> loaded = new ArrayList<>();
	for (Document document : documents) {
    
    
		if (filter.match(document)) {
    
    //true:表示读取到本地配置文件信息
			addActiveProfiles(document.getActiveProfiles());
			addIncludedProfiles(document.getIncludeProfiles());
			loaded.add(document);
		}
	}
	Collections.reverse(loaded);
	if (!loaded.isEmpty()) {
    
    
		// #6.1.4 将当前加载的配置信息添加到Map集合loaded中【Map<Profile, MutablePropertySources> loaded】
		loaded.forEach((document) -> consumer.accept(profile, document));
	}
}

将Document文件添加到缓存loadDocumentsCache中。

6.1.3、DocumentFilter

Document中Profiles属性表示在配置文件显式的spring.profiles.active属性激活。

private DocumentFilter getPositiveProfileFilter(Profile profile) {
    
    
	return (Document document) -> {
    
    
		if (profile == null) {
    
    
			return ObjectUtils.isEmpty(document.getProfiles());
		}
		return ObjectUtils.containsElement(document.getProfiles(), profile.getName())
				&& this.environment.acceptsProfiles(Profiles.of(document.getProfiles()));
	};
}

过滤当前激活的profile配置文件:

  1. 返回true则表示当前激活的profile配置文件有效。
  2. 如果profile & document#profiles 均为null,则返回true。
  3. document#profiles集合包含profile并且profile已经被激活,则返回true。
private DocumentFilter getNegativeProfileFilter(Profile profile) {
    
    
	return (Document document) -> (profile == null && !ObjectUtils.isEmpty(document.getProfiles())
			&& this.environment.acceptsProfiles(Profiles.of(document.getProfiles())));
}

6.1.4、DocumentConsumer

private DocumentConsumer addToLoaded(BiConsumer<MutablePropertySources, PropertySource<?>> addMethod,
		boolean checkForExisting) {
    
    
	return (profile, document) -> {
    
    
		if (checkForExisting) {
    
    
			for (MutablePropertySources merged : this.loaded.values()) {
    
    
				if (merged.contains(document.getPropertySource().getName())) {
    
    
					return;
				}
			}
		}
		MutablePropertySources merged = this.loaded.computeIfAbsent(profile,
				(k) -> new MutablePropertySources());
		// MutablePropertySources#addLast
		// document.getPropertySource():当前加载的配置文件信息
		addMethod.accept(merged, document.getPropertySource());
	};
}

作用:

6.1.5、MutablePropertySources

public class MutablePropertySources implements PropertySources {
    
    
	private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
	
	public void addLast(PropertySource<?> propertySource) {
    
    
		removeIfPresent(propertySource);
		this.propertySourceList.add(propertySource);
	}
}

PropertySource:

6.1.6、Document

作用:将配置文件中的信息抽象为Document对象中propertySource属性持有。

Document(PropertySource<?> propertySource, String[] profiles, Set<Profile> activeProfiles,
		Set<Profile> includeProfiles) {
    
    
	this.propertySource = propertySource;
	this.profiles = profiles;
	this.activeProfiles = activeProfiles;
	this.includeProfiles = includeProfiles;
}

profiles:配置文件显式配置spring.profiles
activeProfiles:配置文件显式配置spring.profiles.active

猜你喜欢

转载自blog.csdn.net/qq_36851469/article/details/128838425