3、Springboot之ApplicationContext&Listener&Config

SpringBoot中讲究的是约定大于配置。即使用方按照规范处理不符合默认约定的配置即可,大部分配置框架中存在默认值或者缺省值,不需要使用方参与设值。

SpringBoot中的配置文件分为两部分:提前加载配置文件的数据以及@Value注解对应属性的赋值操作。

本文着重分析ApplicationListener类型的监听器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();
		//ConfigFileApplicationListener自身也是EnvironmentPostProcessor类型的后置处理器
		postProcessors.add(this);
		AnnotationAwareOrderComparator.sort(postProcessors);
		for (EnvironmentPostProcessor postProcessor : postProcessors) {
			postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
		}
	}
	List<EnvironmentPostProcessor> loadPostProcessors() {
		return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());
	}
	
	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();
	}
}

加载EnvironmentPostProcessor类型的后置处理器,其中包括ConfigFileApplicationListener 以及Apollo涉及的ApolloApplicationContextInitializer

ConfigFileApplicationListener since 2.4.0 for removal in 2.6.0 in favor of ConfigDataEnvironmentPostProcessor

1.StandardServletEnvironment初始化配置信息

1.1.Loader

spring.config.name:确定配置文件前缀名,默认为application。可以在环境变量中配置该选项。

如果spring.profiles.active=dev,则默认配置文件 & dev对应的配置文件均生效,并且相同的key以dev对应的配置的value为终值。

private class Loader {
	// 贯穿应用上下文的StandardServletEnvironment
	private final ConfigurableEnvironment environment;
	private final PropertySourcesPlaceholdersResolver placeholdersResolver;
	//PropertiesPropertySourceLoader & YamlPropertySourceLoader
	private final List<PropertySourceLoader> propertySourceLoaders;
	// profiles 始终存在两个值 default && null 或者 profiles && null
	private Deque<Profile> profiles;

	private List<Profile> processedProfiles;

	private boolean activatedProfiles;

	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<>();
		// 获取用户配置的spring.profiles.active、spring.profiles.include值。
		initializeProfiles();
		while (!this.profiles.isEmpty()) {// 激活环境变量profiles
			// 不管是否配置环境变量profiles,profiles始终存在一个null值
			Profile profile = this.profiles.poll();
			// 非default && 非 null
			if (profile != null && !profile.isDefaultProfile()) {
				addProfileToEnvironment(profile.getName());
			}
			// 两个Lambda参数:DocumentFilterFactory && DocumentConsumer 
			load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false));// 1
			this.processedProfiles.add(profile);
		}
		...
		//加载默认的配置文件,例如application.properties
		load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
		addLoadedPropertySources();
	}

	private void addLoadedPropertySources() {
		// 从 StandardServletEnvironment 获取 MutablePropertySources
		MutablePropertySources destination = this.environment.getPropertySources();
		// 加载得到的不同profiles对应的配置文件信息
		List<MutablePropertySources> loaded = new ArrayList<>(this.loaded.values());
		Collections.reverse(loaded);
		String lastAdded = null;
		Set<String> added = new HashSet<>();
		for (MutablePropertySources sources : loaded) {//从局部属性 loaded 获取全部配置信息
			for (PropertySource<?> source : sources) {
				if (added.add(source.getName())) {
					// 添加配置信息到 StandardServletEnvironment之属性PropertySources
					addLoadedPropertySource(destination, lastAdded, source);
					lastAdded = source.getName();
				}
			}
		}
	}

	private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
		// getSearchLocations获取配置文件的路径包含 "file:./config/"、"file:./"、"classpath:/config/"、"classpath:/"
		getSearchLocations().forEach((location) -> {
			boolean isFolder = location.endsWith("/");
			//如果是文件优先获取 spring.config.name 配置文件名,否则就是默认值application
			Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
			names.forEach((name) -> load(location, name, profile, filterFactory, consumer));// 2
		});
	}

	private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
			DocumentConsumer consumer) {
		...
		Set<String> processed = new HashSet<>();
		//以.properties类型文件为研究对象
		for (PropertySourceLoader loader : this.propertySourceLoaders) {
			for (String fileExtension : loader.getFileExtensions()) {//获取文件后缀名
				if (processed.add(fileExtension)) {
					// location + name, "." + fileExtension = classpath:/application-dev.properties
					String pre = location + name;
					loadForFileExtension(loader, pre, "." + fileExtension, profile, filterFactory,consumer);//3
				}
			}
		}
	}
	...
	private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
									Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
		//调用Lambda表达式:获取得到Lambda表达式表示的DocumentFilter
		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);
			// Try profile specific sections in files we've already processed
			for (Profile processedProfile : this.processedProfiles) {
				if (processedProfile != null) {
					String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
					load(loader, previouslyLoaded, profile, profileFilter, consumer);
				}
			}
		}
		// 加载默认配置文件中配置信息
		load(loader, prefix + fileExtension, profile, profileFilter, consumer);
	}

	private void load(PropertySourceLoader loader,String location,Profile profile, DocumentFilter filter,
																					DocumentConsumer consumer) {
		Resource resource = this.resourceLoader.getResource(location);
		...
		String name = "applicationConfig: [" + location + "]";
		// 最终通过 OriginTrackedPropertiesLoader 加载配置信息至Map集合类型,最终转化为Document
		List<Document> documents = loadDocuments(loader, name, resource);
		if (CollectionUtils.isEmpty(documents)) {
			...
			return;
		}
		List<Document> loaded = new ArrayList<>();
		for (Document document : documents) {
			if (filter.match(document)) {// 判断:环境变量读取的profile 是否与 配置文件扩展名相等
				addActiveProfiles(document.getActiveProfiles());
				addIncludedProfiles(document.getIncludeProfiles());
				loaded.add(document);
			}
		}
		Collections.reverse(loaded);
		if (!loaded.isEmpty()) {
			//调用 DocumentConsumer#addToLoaded方法,将配置信息初始化到局部Map类型变量 loaded 中
			loaded.forEach((document) -> consumer.accept(profile, document));
		}
	}

PropertySourceLoader:PropertiesPropertySourceLoader & YamlPropertySourceLoader。

如果是默认配置,profiles集合元素为default、null。如果profiles取值为dev,则profiles集合元素为dev、null。

1.2.DocumentFilterFactory

形参是指激活的环境变量。document#profile是指配置文件的扩展名。
作用:返回布尔值,发挥过滤作用。

@FunctionalInterface
private interface DocumentFilterFactory {
	DocumentFilter getDocumentFilter(Profile profile);
}

@FunctionalInterface
private interface DocumentFilter {
	boolean match(Document document);
}
private DocumentFilter getPositiveProfileFilter(Profile profile) {
	return (Document document) -> {
		if (profile == null) {// 没有激活的profile,同时配置文件也没有扩展名
			return ObjectUtils.isEmpty(document.getProfiles());
		}
		// 激活的环境变量profile 对应的配置文件也存在其扩展名
		return ObjectUtils.containsElement(document.getProfiles(), profile.getName())
				&& this.environment.acceptsProfiles(Profiles.of(document.getProfiles()));
	};
}
  • 环境没有显式激活的profile & 解析的当前配置文件正好是默认配置文件。
  • 解析的当前配置文件显式指定的profile 正好等于当前环境显式激活的profile。

总结:当前分析的环境profile正好与当前解析的配置文件制定的profile一致,则返回true。

private DocumentFilter getNegativeProfileFilter(Profile profile) {
	return (Document document) -> (profile == null && !ObjectUtils.isEmpty(document.getProfiles())
			&& this.environment.acceptsProfiles(Profiles.of(document.getProfiles())));
}
  • 环境没有显式激活的profile & 解析的当前配置文件显式指定了profile,则返回true。

profile:是指环境变量配置所激活的profile。
document.getProfiles():是指配置文件指定的profile。

帮助理解上述表达式:

public void loadForFileExtension(){
    //其实DocumentFilterFactory的实现类可以如此Lambda实现。但是这个实现方式又可以通过 JDK8新特性之方法引用 再次简化实现
    load(null,(Profile profile) -> {
        return getPositiveProfileFilter(profile);
    }, new DocumentConsumer() {
        @Override
        public void accept(Profile profile, Document document) {

        }
    });
}

1.3.DocumentConsumer

目的是将配置信息初始化到属性【Map<Profile, MutablePropertySources> loaded】中。

private DocumentConsumer addToLoaded(BiConsumer<MutablePropertySources, PropertySource<?>> addMethod,
		boolean checkForExisting) {
	return (profile, document) -> {
		...
		MutablePropertySources merged = this.loaded.computeIfAbsent(profile,
				(k) -> new MutablePropertySources());
		addMethod.accept(merged, document.getPropertySource());
	};
}

2.PropertySourcesPlaceholderConfigurer

在这里插入图片描述
从上述继承图关系得知,当前类实现了接口BeanFactoryPostProcessor。该类的特性是单例的,是被全部bean所共享的,每个bean可以通过应用上下文获取到该bean。

public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {
	@Nullable
	private MutablePropertySources propertySources;

	@Nullable
	private PropertySources appliedPropertySources;

	@Nullable
	private Environment environment;//StandardServletEnvironment

	@Override//BeanFactoryPostProcessor后置处理器默认的回调方法
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		if (this.propertySources == null) {
			this.propertySources = new MutablePropertySources();// 初始化 propertySources
			if (this.environment != null) {// 条件成立
				this.propertySources.addLast(// 将 StandardServletEnvironment 与 propertySources 建立绑定关系
					new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
						@Override
						@Nullable
						public String getProperty(String key) {
							// 从 StandardServletEnvironment 中获取key配置的value值
							return this.source.getProperty(key);// source就是局部变量 environment
						}
					}
				);
			}
			...
		}
		
		processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
		this.appliedPropertySources = this.propertySources;
	}
	
	protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
			final ConfigurablePropertyResolver propertyResolver) throws BeansException {

		propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);//${
		propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);//}
		propertyResolver.setValueSeparator(this.valueSeparator);// : 

		StringValueResolver valueResolver = strVal -> {
			String resolved = (this.ignoreUnresolvablePlaceholders ?
					propertyResolver.resolvePlaceholders(strVal) :
					// 利用 PropertySourcesPropertyResolver获取strVal对应的value值,最终调用到上述PropertySource#getProperty
					propertyResolver.resolveRequiredPlaceholders(strVal));
			if (this.trimValues) {
				resolved = resolved.trim();
			}
			return (resolved.equals(this.nullValue) ? null : resolved);
		};
		// 将 StringValueResolver 与 DefaultListableBeanFactory 建立起绑定关系
		doProcessProperties(beanFactoryToProcess, valueResolver);
	}
}

StringValueResolver是一个Lambda表达式,在整个应用上下文中是单例的。与DefaultListableBeanFactory 绑定关系则任何一个bean都可以通过应用上下文获取到该Lambda表达式,通过回调利用PropertySourcesPropertyResolver获取到真实变量的值。

3.AutowiredAnnotationBeanPostProcessor

通过@Value注解获取配置文件的信息就是通过该后置处理器处理的。
在这里插入图片描述
了解SpringBoot启动原理都知道,在Bean实例化、初始化等过程中涉及到BeanPostProcessor类型的后置处理器对bean做一些额外的处理。
对于BeanPostProcessor类型的后置处理器存在核心回调方法postProcessBeforeInitialization、postProcessAfterInitialization。

对于MergedBeanDefinitionPostProcessor后置处理器其核心方法postProcessMergedBeanDefinition也是在bean实例化、初始化等过程中执行。针对当前AutowiredAnnotationBeanPostProcessor类其postProcessMergedBeanDefinition方法就是解析bean中存在的Autowired、Value注解信息。

public class AutowiredAnnotationBeanPostProcessor{
	public AutowiredAnnotationBeanPostProcessor() {// 从该类构造器得知该类主要处理注解Autowired、Value
		this.autowiredAnnotationTypes.add(Autowired.class);
		this.autowiredAnnotationTypes.add(Value.class);
		...
	}
	
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) 	
	{	
		InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
		metadata.checkConfigMembers(beanDefinition);
	}
	
	private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
		String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
		InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
		if (InjectionMetadata.needsRefresh(metadata, clazz)) {
			synchronized (this.injectionMetadataCache) {
				metadata = this.injectionMetadataCache.get(cacheKey);
				if (InjectionMetadata.needsRefresh(metadata, clazz)) {
					if (metadata != null) {
						metadata.clear(pvs);
					}
					// 解析beanName的局部变量存在的Autowired、Value注解,对于Value注解则会解析到局部变量值为${myConfigTets}
					metadata = buildAutowiringMetadata(clazz);
					this.injectionMetadataCache.put(cacheKey, metadata);
				}
			}
		}
		return metadata;
	}
	
	//该方法同样属于BeanPostProcessor类型的后置处理器
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
		// Metadata 指的是bean中某个field、Method属性
		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
		metadata.inject(bean, beanName, pvs);// 给 bean 中局部变量进行赋值,其中就包括@Value注解的变量
		return pvs;
	}
}

InjectionMetadata#inject:最终利用其持有的应用上下文获取到PropertySourcesPlaceholderConfigurer中Lambda表达式之StringValueResolver,最终调用Lambda表达式得到InjectionMetadata对应局部变量的真实value。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值