目錄
簡介
- 在項目中,我們一般都會用配置文件(properties文件)更優雅的實現基礎配置信息;在java中我們一般會用內置的工具包
java.util.Properties
去讀取properties
配置文件;往往有一些問題是,我們要自己寫代碼去讀取配置文件,還有就是如何管理已經讀取過的配置,這種配置文件的數據一般不會頻繁改變,不可能我需要一次就讀一下文件吧!那就太不優雅了,而且自己去寫讀取文件的代碼也麻煩。 - 而
Spring
剛好能解決這樣的痛點,Spring
有一個類PropertyPlaceholderConfigurer
,這個類是BeanFactoryPostProcessor
的實現類。其主要的原理在是。Spring容器初始化的時候,會讀取 xml 或者 annotation 對 Bean 進行初始化。Bean
初始化的時候會對配置的${xxxx}
進行替換,根據我們Properties文件中配置的進行替換。從而實現表達式的替換操作 。 - 如果不瞭解
BeanFactoryPostProcessor
的,可以參考BeanFactoryPostProcessor詳解
使用
我這裏有2中方式,二選一(可能不止)
- 1、xml方式配置
PropertyPlaceholderConfigurer
(我源碼演示使用的是第1種)
...... 省略 ............
<!-- 加載配置文件 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:prop-jdbc.properties</value>
</list>
</property>
<!-- PropertyPlaceholderConfigurer 一般需要把配置文件合併形成 一個 ,否則在AbstractBeanFactory#resolveEmbeddedValue裏面for循環時會有可能遍歷某個StringValueResolver找不到想要的配置而報錯 , 還有個辦法設置忽略ignoreUnresolvablePlaceholders修改爲true -->
<property name="ignoreUnresolvablePlaceholders" value="true"></property>
</bean>
...... 省略 ............
- 2、java config (看了源碼之後想到
直接創建PropertyPlaceholderConfigurer對象
);
package com.zzq.core.configuration;
import java.io.File;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
@Configuration
public class ConfigurationTest {
//PropertyPlaceholderConfigurer 一般需要把配置文件合併形成 一個 ,否則在AbstractBeanFactory#resolveEmbeddedValue裏面for循環時會有可能遍歷某個StringValueResolver可能找不到想要的配置而報錯 , 還有個辦法設置忽略ignoreUnresolvablePlaceholders修改爲true
@Bean
public PropertyPlaceholderConfigurer propertyPlaceholder(){
PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
File file = new File("D:/eclipse_workspace/CeShi2/test_web/src/main/resources/zookeeper.properties");
//File jdbc = new File("D:/eclipse_workspace/CeShi2/test_web/src/main/resources/prop-jdbc.properties");
org.springframework.core.io.Resource [] locations = {new FileSystemResource(file)
/*,new FileSystemResource(jdbc)*/};
//location.add(new FileSystemResource(file));
configurer.setLocations(locations);
configurer.setIgnoreUnresolvablePlaceholders(true);
return configurer;
}
}
要注意此處
Resource
接口的實現類有很多,我用的絕對路徑FileSystemResource
;還可以使用classPath
路徑,使用ClassPathResource
實現類;還有些其他的可以實現可以自己參考下源碼Resource
的其他實現。
來看下prop-jdbc.properties
的內容
獲取值可以使用@Value
註解,在controller
使用
@Value("${druid.url}")
private String druidUrl;
最後測試一波,能夠成功獲取到值
源碼分析
基本信息
- 先來看看
PropertyPlaceholderConfigurer
的繼承關係
解析配置文件
PropertyResourceConfigurer
是BeanFactoryPostProcessor
的實現類,在項目啓動時會調用到AbstractApplicationContext#invokeBeanFactoryPostProcessors
方法,裏面最終就是for
循環調用BeanFactoryPostProcessor
實現類的postProcessBeanFactory
方法;這段邏輯可以參考BeanFactoryPostProcessor詳解。
- 當調用到
PropertyResourceConfigurer#postProcessBeanFactory
//BeanFactoryPostProcessor的後處理
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
//合併配置文件的屬性值
Properties mergedProps = mergeProperties();
// Convert the merged properties, if necessary.
//如有必要, 轉換合併的屬性
convertProperties(mergedProps);
// Let the subclass process the properties.
//讓子類處理屬性
processProperties(beanFactory, mergedProps);
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}
- 進入
mergeProperties
方法
protected Properties mergeProperties() throws IOException {
Properties result = new Properties();
if (this.localOverride) {
// Load properties from file upfront, to let local properties override.
loadProperties(result);
}
if (this.localProperties != null) {
for (Properties localProp : this.localProperties) {
CollectionUtils.mergePropertiesIntoMap(localProp, result);
}
}
if (!this.localOverride) {
// Load properties from file afterwards, to let those properties override.
// 從文件加載屬性
loadProperties(result);
}
return result;
}
-
loadProperties
protected void loadProperties(Properties props) throws IOException { if (this.locations != null) { for (Resource location : this.locations) { if (logger.isInfoEnabled()) { logger.info("Loading properties file from " + location); } try { //查找配置文件的屬性 並且都合併到props PropertiesLoaderUtils.fillProperties( props, new EncodedResource(location, this.fileEncoding), this.propertiesPersister); } catch (IOException ex) { if (this.ignoreResourceNotFound) { if (logger.isWarnEnabled()) { logger.warn("Could not load properties from " + location + ": " + ex.getMessage()); } } else { throw ex; } } } } }
- 驗證我們上面的知識:此時我們知道
locations
是Resource數組
,並且實現類是ClassPathResource
- 驗證我們上面的知識:此時我們知道
-
來到
PropertiesLoaderUtils#fillProperties
這個方法就在讀取配置文件了,並把值設置到props
裏static void fillProperties(Properties props, EncodedResource resource, PropertiesPersister persister) throws IOException { InputStream stream = null; Reader reader = null; try { //得到文件名稱 String filename = resource.getResource().getFilename(); //判斷不爲空 並且後綴是.xml if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) { stream = resource.getInputStream(); persister.loadFromXml(props, stream); } else if (resource.requiresReader()) { // 需要 Reader reader = resource.getReader(); persister.load(props, reader); } else { // 普通的properties文件讀取 stream = resource.getInputStream(); persister.load(props, stream); } } finally { if (stream != null) { stream.close(); } if (reader != null) { reader.close(); } } }
- 看到這個方法我覺得以後非要自己讀取配置文件可以調用
PropertiesLoaderUtils#fillProperties
(前提是引入了相關spring jar包),具體操作可以參考從spring、spring boot中找到解析properties、xml、yml、yaml文件的方法
- 看到這個方法我覺得以後非要自己讀取配置文件可以調用
-
convertProperties
轉換屬性沒啥好說的,沒有覆蓋方法,就是返回原值。可以看看最後的convertProperty
方法
protected String convertPropertyValue(String originalValue) {
return originalValue;
}
-
重點是之類覆蓋的方法
processProperties
,來到子類PropertyPlaceholderConfigurer#processProperties
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) throws BeansException { // 創建字符串解析器 StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props); // 處理屬性 - 重點 doProcessProperties(beanFactoryToProcess, valueResolver); }
-
doProcessProperties
處理配置屬性protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess, StringValueResolver valueResolver) { BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver); String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames(); for (String curName : beanNames) { // Check that we're not parsing our own bean definition, // to avoid failing on unresolvable placeholders in properties file locations. //檢查我們沒有解析自己的bean定義, //以避免在屬性文件位置的不可解析佔位符上失敗。 if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) { BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName); try { visitor.visitBeanDefinition(bd); } catch (Exception ex) { throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex); } } } // New in Spring 2.5: resolve placeholders in alias target names and aliases as well. //Spring2.5中的新特性:解析別名目標名稱和別名中的佔位符。 beanFactoryToProcess.resolveAliases(valueResolver); // New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes. // Spring3.0中的新特性:解析嵌入值(如註釋屬性)中的佔位符。 放到 embeddedValueResolvers屬性裏面 便於註解獲取字符串的值 重點 beanFactoryToProcess.addEmbeddedValueResolver(valueResolver); }
-
AbstractBeanFactory#addEmbeddedValueResolver
public void addEmbeddedValueResolver(StringValueResolver valueResolver) {
Assert.notNull(valueResolver, "StringValueResolver must not be null");
// 把解析器添加進去
this.embeddedValueResolvers.add(valueResolver);
}
設置值
- 我們首先定位到創建bean的代碼
AbstractAutowireCapableBeanFactory#doCreateBean
;給屬性賦值,以上面的TestController
druidUrl
屬性爲例
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
.................省略代碼.....................
// 這一步也是非常關鍵的,這一步負責屬性裝配,因爲前面的實例只是實例化了,並沒有設值,這裏就是設值
populateBean(beanName, mbd, instanceWrapper);
.................省略代碼.....................
}
- 再來看看
AbstractAutowireCapableBeanFactory#populateBean
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
// bean 實例的所有屬性都在這裏了
PropertyValues pvs = mbd.getPropertyValues();
if (bw == null) {
if (!pvs.isEmpty()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// Skip property population phase for null instance.
return;
}
}
// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
// 到這步的時候,bean 實例化完成(通過工廠方法或構造方法),但是還沒開始屬性設值,
// InstantiationAwareBeanPostProcessor 的實現類可以在這裏對 bean 進行狀態修改,
// 我也沒找到有實際的使用,所以我們暫且忽略這塊吧
boolean continueWithPropertyPopulation = true;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 如果返回 false,代表不需要進行後續的屬性設值,也不需要再經過其他的 BeanPostProcessor 的處理
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
if (!continueWithPropertyPopulation) {
return;
}
//目前看到的上面autowireByName和autowireByType的屬性注入是定義bean(xml定義和代碼定義)的時候纔會進,下面postProcessPropertyValues是用來處理註解的注入 還有@Bean註解可以設置
//mbd.getResolvedAutowireMode()一般爲0 都不進 , 在源碼BeanDefinitionParserDelegate#parseBeanDefinitionAttributes方法發現xml方式可以設置autowireMode屬性值,java config方式目前不知
//byName 比如可以這樣設置 <bean id ="myTestBean" class= "com.zzq.core.test.entity.MyTestBean" autowire="byName">
//在mybatis 源碼 ClassPathMapperScanner.class#processBeanDefinitions發現這段代碼直接設置mapper爲byType if !explicitFactoryUsed definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
// 通過名字找到所有屬性值,如果是 bean 依賴,先初始化依賴的 bean。記錄依賴關係
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
// 通過類型裝配。複雜一些
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
//InstantiationAwareBeanPostProcessor接口的主要作用在於目標對象的實例化過程中需要處理的事情,包括實例化對象的前後過程以及實例的屬性設置
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
if (hasInstAwareBpps || needsDepCheck) {
//篩選屬性
PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
if (hasInstAwareBpps) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 此處重點
// 這裏有個非常有用的 BeanPostProcessor 進到這裏: AutowiredAnnotationBeanPostProcessor
// 對採用 @Autowired、@Value 註解的依賴進行設值,這裏的內容也是非常豐富的,不過本文不會展開說了,感興趣的讀者請自行研究
//@Resource註解一般使用這個後處理器org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
// @Autowired註解是AutowiredAnnotationBeanPostProcessor @Resource註解是CommonAnnotationBeanPostProcessor
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
}
if (needsDepCheck) {
checkDependencies(beanName, mbd, filteredPds, pvs);
}
}
// 設置 bean 實例的屬性值,不過pvs空元素的時候並不會進
applyPropertyValues(beanName, mbd, bw, pvs);
}
- 使用
@Value
註解依賴進行設值的後處理器是AutowiredAnnotationBeanPostProcessor
,先來debug看看執行完AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues
的效果。
- 此處的
pvs
是空元素,所以後面執行到applyPropertyValues
會直接return。 - 可以看到在執行完
AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues
後TestController
的druidUrl
屬性值已經被設置進去了,我們具體看看內部的實現。
- 此處的
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
// 查找需要依賴注入的元數據屬性
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 注入
metadata.inject(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
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);
}
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata
此處是從緩存裏找數據,injectionMetadataCache
的元素是在AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors
初始化的。buildAutowiringMetadata
方法就是封裝一個metadata
對象,供下面注入使用。
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
Class<?> targetClass = clazz;
do {
LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<InjectionMetadata.InjectedElement>();
for (Field field : targetClass.getDeclaredFields()) {
Annotation ann = findAutowiredAnnotation(field);
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static fields: " + field);
}
continue;
}
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
}
for (Method method : targetClass.getDeclaredMethods()) {
Annotation ann = null;
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
ann = findAutowiredAnnotation(bridgedMethod);
}
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static methods: " + method);
}
continue;
}
if (method.getParameterTypes().length == 0) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation should be used on methods with actual parameters: " + method);
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredMethodElement(method, required, pd));
}
}
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new InjectionMetadata(clazz, elements);
}
-
原理很簡單就是找到滿足條件的加入到
list
裏,最後構建出一個InjectionMetadata
對象返回 -
現在已經獲取到要注入那些屬性了,現在來看看注入
metadata.inject(bean, beanName, pvs);
public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable { Collection<InjectedElement> elementsToIterate = (this.checkedElements != null ? this.checkedElements : this.injectedElements); if (!elementsToIterate.isEmpty()) { boolean debug = logger.isDebugEnabled(); for (InjectedElement element : elementsToIterate) { if (debug) { logger.debug("Processing injected element of bean '" + beanName + "': " + element); } element.inject(target, beanName, pvs); } } }
element
包裝的就是要注入的屬性,當然element
也可能是方法,因爲我看到有兩個實現類;
-
現在這兒就看
AutowiredFieldElement#inject
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { Field field = (Field) this.member; try { Object value; if (this.cached) { value = resolvedCachedArgument(beanName, this.cachedFieldValue); } else { DependencyDescriptor desc = new DependencyDescriptor(field, this.required); Set<String> autowiredBeanNames = new LinkedHashSet<String>(1); TypeConverter typeConverter = beanFactory.getTypeConverter(); //這個beanName 是當前類文件的beanName value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); synchronized (this) { if (!this.cached) { if (value != null || this.required) { this.cachedFieldValue = desc; //保存依賴關係 registerDependentBeans(beanName, autowiredBeanNames); if (autowiredBeanNames.size() == 1) { String autowiredBeanName = autowiredBeanNames.iterator().next(); if (beanFactory.containsBean(autowiredBeanName)) { if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { this.cachedFieldValue = new RuntimeBeanReference(autowiredBeanName); } } } } else { this.cachedFieldValue = null; } this.cached = true; } } } if (value != null) { ReflectionUtils.makeAccessible(field); //通過反射對字段注入 field.set(bean, value); } } catch (Throwable ex) { throw new BeanCreationException("Could not autowire field: " + field, ex); } }
-
下一步就是獲取屬性值,定位到
DefaultListableBeanFactory#resolveDependency
public Object resolveDependency(DependencyDescriptor descriptor, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException { descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); if (descriptor.getDependencyType().equals(ObjectFactory.class)) { return new DependencyObjectFactory(descriptor, beanName); } else if (descriptor.getDependencyType().equals(javaxInjectProviderClass)) { return new DependencyProviderFactory().createDependencyProvider(descriptor, beanName); } else { // 一般都是走這裏 return doResolveDependency(descriptor, descriptor.getDependencyType(), beanName, autowiredBeanNames, typeConverter); } }
protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException { //得到註解上的值 一般是獲取value屬性的值 Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); if (value != null) { if (value instanceof String) { // 表達式註解獲取值 一般是獲取配置裏的值 重點 String strVal = resolveEmbeddedValue((String) value); BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); value = evaluateBeanDefinitionString(strVal, bd); } TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); return (descriptor.getField() != null ? converter.convertIfNecessary(value, type, descriptor.getField()) : converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); } ..........省略下面獲取屬性值用不到的代碼................ } }
- 還記得之前創建的的創建
字符串解析器
嗎?這裏就會用到它了resolveEmbeddedValue
public String resolveEmbeddedValue(String value) { String result = value; for (StringValueResolver resolver : this.embeddedValueResolvers) { if (result == null) { return null; } result = resolver.resolveStringValue(result); } return result; }
public String resolveStringValue(String strVal) throws BeansException { String value = this.helper.replacePlaceholders(strVal, this.resolver); return (value.equals(nullValue) ? null : value); }
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) { Assert.notNull(value, "'value' must not be null"); return parseStringValue(value, placeholderResolver, new HashSet<String>()); }
//解析出表達式的值 protected String parseStringValue( String strVal, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) { StringBuilder result = new StringBuilder(strVal); // placeholderPrefix 佔位符前綴 int startIndex = strVal.indexOf(this.placeholderPrefix); while (startIndex != -1) { //找到佔位符結束位置的下標 int endIndex = findPlaceholderEndIndex(result, startIndex); if (endIndex != -1) { //得到真實的key值 String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex); String originalPlaceholder = placeholder; if (!visitedPlaceholders.add(originalPlaceholder)) { throw new IllegalArgumentException( "Circular placeholder reference '" + originalPlaceholder + "' in property definitions"); } // Recursive invocation, parsing placeholders contained in the placeholder key. //遞歸調用,分析佔位符鍵中包含的佔位符。 placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders); // Now obtain the value for the fully resolved key... //獲取解析鍵的值 String propVal = placeholderResolver.resolvePlaceholder(placeholder); if (propVal == null && this.valueSeparator != null) { int separatorIndex = placeholder.indexOf(this.valueSeparator); if (separatorIndex != -1) { String actualPlaceholder = placeholder.substring(0, separatorIndex); String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length()); propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder); if (propVal == null) { propVal = defaultValue; } } } if (propVal != null) { // Recursive invocation, parsing placeholders contained in the // previously resolved placeholder value. propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders); //替換佔位符 result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal); if (logger.isTraceEnabled()) { logger.trace("Resolved placeholder '" + placeholder + "'"); } startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length()); } else if (this.ignoreUnresolvablePlaceholders) { //忽略不可解析的佔位符 ,佔位符的解析支持多個StringValueResolver , 因爲某個StringValueResolver可能找不到想要的配置,其他的StringValueResolver能找到,可以設置這個值爲true 避免拋出異常 // Proceed with unprocessed value. // 這裏的startIndex肯定是爲-1的 startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length()); } else { // PropertyPlaceholderConfigurer 一般需要把配置文件合併形成 一個 ,否則在AbstractBeanFactory#resolveEmbeddedValue裏面for循環時會有可能遍歷某個StringValueResolver找不到想要的配置而報錯 throw new IllegalArgumentException("Could not resolve placeholder '" + placeholder + "'" + " in string value \"" + strVal + "\""); } visitedPlaceholders.remove(originalPlaceholder); } else { startIndex = -1; } } return result.toString(); }
- 還記得之前創建的的創建
-
最後會調用到此處
PropertyPlaceholderConfigurer#resolvePlaceholder
//得到配置文件的值 protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) { String propVal = null; if (systemPropertiesMode == SYSTEM_PROPERTIES_MODE_OVERRIDE) { propVal = resolveSystemProperty(placeholder); } if (propVal == null) { //會進入這裏 propVal = resolvePlaceholder(placeholder, props); } if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) { propVal = resolveSystemProperty(placeholder); } return propVal; }
protected String resolvePlaceholder(String placeholder, Properties props) { return props.getProperty(placeholder); }
-
得到了屬性值,最後就是對屬性設值
-
本篇文章比較長,讀完確實需要比較大的耐心;最後本人水平有限,如果文章有誤的地方,希望批評指正,感謝您的觀看。