spring源码分析系列 (5) spring BeanFactoryPostProcessor拓展类PropertyPlaceholderConfigurer、PropertySourcesPlaceholderConfigurer解析

更多文章点击--spring源码分析系列

主要分析内容:

1、拓展类简述: 拓展类使用demo和自定义替换符号

2、继承图UML解析和源码分析

(源码基于spring 5.1.3.RELEASE分析)

 

1、拓展类简述

PropertyPlaceholderConfigurer和PropertySourcesPlaceholderConfigurer都是个bean工厂后置处理器接口BeanFactoryPostProcessor的实现类。可以将上下文(配置文件)中的属性值放在另一个单独的标准java Properties文件中去。在XML文件中用${key}替换指定的properties文件中的值或者可以自定义前缀和后缀。此时只需要对properties文件进 行修改,而不用对xml配置文件进行修改。spring 3.1之后更推荐使用PropertySourcesPlaceholderConfigurer, 因为其更加灵活。PropertyPlaceholderConfigurer通常使用的方式:

Bean.java

 public class Bean {

     public Bean(){

     }

     public Bean(String name){
System.out.println("构造函数被调用啦");
this.name = name ;
} private String name ; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "Bean{" +
"name='" + name + '\'' +
'}';
}
}

Bean.java

ioc-PlaceholderConfigurer.xml :

 <?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>bean.properties</value>
</list>
</property>
</bean>
<bean id="bean2" class="com.nancy.ioc.Bean">
<property name="name" value="${bean.field.name}"/>
</bean>
</beans>

bean.properties

bean.field.name=hello world

PlaceholderTest.java

 public class PlaceholderTest {
private ApplicationContext applicationContext ; @Before
public void beforeApplicationContext(){
/**
* ApplicationContext 自动注册 BeanPostProcessor、InstantiationAwareBeanPostProcessor、BeanFactoryPostProcessor
* 不需要手动注册
* */
applicationContext = new ClassPathXmlApplicationContext("ioc-PlaceholderConfigurer.xml") ;
} @Test
public void test(){
Bean bean = applicationContext.getBean("bean2", Bean.class) ;
System.out.println(bean);
} @After
public void after(){
((ClassPathXmlApplicationContext)applicationContext).close();
}
}

运行结果: name属性被配置文件对于的值所覆盖

Bean{name='hello world'}

修改ioc-PlaceholderConfigurer.xml 配置占位符前后缀, 结果跟上述保持一致

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>bean.properties</value>
</list>
</property>
<!-- 修改前缀-->
<property name="placeholderPrefix" value="**{"/>
<property name="placeholderSuffix" value="}**"/>
</bean>
<bean id="bean2" class="com.nancy.ioc.Bean">
<!--<property name="name" value="**{bean.field.name}**:**{bean.field.name2}**"/>-->
<!--<property name="name" value="${bean.field.name}"/>-->
<property name="name" value="**{bean.field.name}**"/>
</bean> </beans>

ioc-PlaceholderConfigurer.xml

运行结果

Bean{name='hello world'}

更多示例demo参考: https://gitee.com/zhouxiaoxing91/learning-src/tree/master/spring-src/src/test/java/com/nancy/ioc

2、继承图UML解析和源码分析

2.1、PropertyPlaceholderConfigurer继承图:

spring源码分析系列 (5) spring BeanFactoryPostProcessor拓展类PropertyPlaceholderConfigurer、PropertySourcesPlaceholderConfigurer解析

(1)、PropertiesLoaderSupport:抽象类,提供从properties文件中读取配置信息的能力,该类的属性locations指定需要加载的文件所在的路径。mergeProperties()中加载和合并所有的Properties属性, 可动态控制配置文件和BeanDefinition(默认配置)加载顺序.

 public abstract class PropertiesLoaderSupport {

     /** Logger available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass()); /**
* 本地资源例如从xml加载的配置信息等
*/
@Nullable
protected Properties[] localProperties; /**
* 为true则localProperties会覆盖从locations获取的属性值
*/
protected boolean localOverride = false; /**
* 配置文件Properties地址
*/
@Nullable
private Resource[] locations; /**
* 当解析不到对应的配置文件是否报错
*/
private boolean ignoreResourceNotFound = false; @Nullable
private String fileEncoding; /**
* Properties文件加载策略,可以配置加载编码形式等. 默认为DefaultPropertiesPersister,实质委托Properties.load 或者 Properties.store
*/
private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister(); /**
* Set local properties, e.g. via the "props" tag in XML bean definitions.
* These can be considered defaults, to be overridden by properties
* loaded from files.
*/
public void setProperties(Properties properties) {
this.localProperties = new Properties[] {properties};
} /**
* Set local properties, e.g. via the "props" tag in XML bean definitions,
* allowing for merging multiple properties sets into one.
*/
public void setPropertiesArray(Properties... propertiesArray) {
this.localProperties = propertiesArray;
} /**
* Set a location of a properties file to be loaded.
* <p>Can point to a classic properties file or to an XML file
* that follows JDK 1.5's properties XML format.
*/
public void setLocation(Resource location) {
this.locations = new Resource[] {location};
} /**
* Set locations of properties files to be loaded.
* <p>Can point to classic properties files or to XML files
* that follow JDK 1.5's properties XML format.
* <p>Note: Properties defined in later files will override
* properties defined earlier files, in case of overlapping keys.
* Hence, make sure that the most specific files are the last
* ones in the given list of locations.
*/
public void setLocations(Resource... locations) {
this.locations = locations;
} /**
* Set whether local properties override properties from files.
* <p>Default is "false": Properties from files override local defaults.
* Can be switched to "true" to let local properties override defaults
* from files.
*/
public void setLocalOverride(boolean localOverride) {
this.localOverride = localOverride;
} /**
* Set if failure to find the property resource should be ignored.
* <p>"true" is appropriate if the properties file is completely optional.
* Default is "false".
*/
public void setIgnoreResourceNotFound(boolean ignoreResourceNotFound) {
this.ignoreResourceNotFound = ignoreResourceNotFound;
} /**
* Set the encoding to use for parsing properties files.
* <p>Default is none, using the {@code java.util.Properties}
* default encoding.
* <p>Only applies to classic properties files, not to XML files.
* @see org.springframework.util.PropertiesPersister#load
*/
public void setFileEncoding(String encoding) {
this.fileEncoding = encoding;
} /**
* Set the PropertiesPersister to use for parsing properties files.
* The default is DefaultPropertiesPersister.
* @see org.springframework.util.DefaultPropertiesPersister
*/
public void setPropertiesPersister(@Nullable PropertiesPersister propertiesPersister) {
this.propertiesPersister =
(propertiesPersister != null ? propertiesPersister : new DefaultPropertiesPersister());
} /**
* 加载和合并所有的Properties属性, 可动态控制 配置文件属性 和 BeanDefinition属性(默认配置) 加载顺序
*/
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;
} /**
* 将配置文件信息,加载进入Properties实例
*/
protected void loadProperties(Properties props) throws IOException {
if (this.locations != null) {
for (Resource location : this.locations) {
if (logger.isTraceEnabled()) {
logger.trace("Loading properties file from " + location);
}
try {
PropertiesLoaderUtils.fillProperties(
props, new EncodedResource(location, this.fileEncoding), this.propertiesPersister);
}
catch (FileNotFoundException | UnknownHostException ex) {
if (this.ignoreResourceNotFound) {
if (logger.isDebugEnabled()) {
logger.debug("Properties resource not found: " + ex.getMessage());
}
}
else {
throw ex;
}
}
}
}
} }

PropertiesLoaderSupport.java

(2)、PropertyResourceConfigurer:抽象类, 继承PropertiesLoaderSupport并实现BeanFactoryPostProcessor接口,资源加载和占位符替换入口。在postProcessBeanFactory()方法从配置文件中读取了配置项,并调用抽象方法processProperties()由子类决定处理逻辑。除此之外,提供了convertProperty()方法,该方法是个扩展点,其实里面什么都没做,它可以用来子类在处理这些配置信息前,对配置信息进行一些转换,例如配置属性的解密。

 public abstract class PropertyResourceConfigurer extends PropertiesLoaderSupport
implements BeanFactoryPostProcessor, PriorityOrdered { private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered /**
* BeanFactoryPostProcessor拓展接口, 资源加载和占位符替换入口
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
// 继承自PropertiesLoaderSupport, 加载配置文件
Properties mergedProps = mergeProperties(); // 对已加载属性配置进行处理, spring一个拓展点. 例如从配置文件获取密文, 此时可以做解密
convertProperties(mergedProps); // 抽象方法, 由子类实现占位符替换逻辑
processProperties(beanFactory, mergedProps);
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
} protected void convertProperties(Properties props) {
Enumeration<?> propertyNames = props.propertyNames();
while (propertyNames.hasMoreElements()) {
String propertyName = (String) propertyNames.nextElement();
String propertyValue = props.getProperty(propertyName);
String convertedValue = convertProperty(propertyName, propertyValue);
if (!ObjectUtils.nullSafeEquals(propertyValue, convertedValue)) {
props.setProperty(propertyName, convertedValue);
}
}
} protected String convertProperty(String propertyName, String propertyValue) {
return convertPropertyValue(propertyValue);
} /**
* 默认返回原始值, 子类重写实现定制功能, 如:配置文件获取密文, 此时可以做解密
*/
protected String convertPropertyValue(String originalValue) {
return originalValue;
} /**
* 抽象方法, 由子类实现占位符替换逻辑
*/
protected abstract void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
throws BeansException; }

PropertyResourceConfigurer.java

(3)、PlaceholderConfigurerSupport:抽象类,该类持有占位符符号的前缀、后缀、分隔符,在doProcessProperties()方法实现对BeanDefinition实例中的占位符进行替换。

 public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfigurer
implements BeanNameAware, BeanFactoryAware { /** Default placeholder prefix: {@value}. */
public static final String DEFAULT_PLACEHOLDER_PREFIX = "${"; /** Default placeholder suffix: {@value}. */
public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}"; /** Default value separator: {@value}. */
public static final String DEFAULT_VALUE_SEPARATOR = ":"; /** Defaults to {@value #DEFAULT_PLACEHOLDER_PREFIX}. */
protected String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX; /** Defaults to {@value #DEFAULT_PLACEHOLDER_SUFFIX}. */
protected String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX; /** Defaults to {@value #DEFAULT_VALUE_SEPARATOR}. */
@Nullable
protected String valueSeparator = DEFAULT_VALUE_SEPARATOR; protected boolean trimValues = false; @Nullable
protected String nullValue; /**
* 当解析不到对应的key是否忽略, 默认为false. 在配置多个解析类的时候, 可设置为ture防止属性加载报错.
*/
protected boolean ignoreUnresolvablePlaceholders = false; @Nullable
private String beanName; @Nullable
private BeanFactory beanFactory; // 省略getter 和 setter ........ /**
* 实现对BeanDefinition实例中的占位符进行替换
*/
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
StringValueResolver valueResolver) { /**
* 循环获得所有BeanDefinition, 依据模版类StringValueResolver替换对应BeanDefinition的占位符
*/
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.
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.
beanFactoryToProcess.resolveAliases(valueResolver); // New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
}

PlaceholderConfigurerSupport.java

(4)、PropertyPlaceholderConfigurer:继承自PlaceholderConfigurerSupport,并实现抽象方法processProperties()。该方法会创建模版类PlaceholderResolvingStringValueResolver,该类提供解析字符串的方法resolveStringValue。创建了StringValueResolver实现类后,交由它的父类PlaceholderConfigurerSupport的doProcessProperties()处理。

  • BeanDefinition:在spring容器初始化时,扫描并获取每个bean的声明(例如在xml中声明、通过注解声明等),然后组装成BeanDefinition,它描述了一个bean实例,拥有属性值,构造参数值和具体实现提供的其他信息。
  • BeanDefinitionVisitor:负责访问BeanDefinition,包括(1)从beanDefinition实例中,获取spring约定的可以替换的参数;(2)使用占位符解析器解析占位符,并从properties中获取它对应的值,最后把值设置到BeanDefinition中。
  • PropertyPlaceholderHelper:持有占位符的前缀、后缀、多值的分隔符,负责把占位符的字符串去除前缀、后缀. 调用PropertyPlaceholderConfigurerResolver进行字符串替换.
  • PropertyPlaceholderConfigurerResolver:从properties中将传入占位符替换为对应的值.
 public class PropertyPlaceholderConfigurer extends PlaceholderConfigurerSupport {

     /** Never check system properties. */
public static final int SYSTEM_PROPERTIES_MODE_NEVER = 0; /**
* Check system properties if not resolvable in the specified properties.
* This is the default.
*/
public static final int SYSTEM_PROPERTIES_MODE_FALLBACK = 1; /**
* Check system properties first, before trying the specified properties.
* This allows system properties to override any other property source.
*/
public static final int SYSTEM_PROPERTIES_MODE_OVERRIDE = 2; /**
* spring提供工具类, 利用jdk反射机制, 对类常量(public static final)进行映射, 可通过常量名称进行访问.
*/
private static final Constants constants = new Constants(PropertyPlaceholderConfigurer.class); /**
* 三种模式, 默认为 1:
* SYSTEM_PROPERTIES_MODE_NEVER-0: 此时以配置文件为准,不会加载 JVM系统变量和系统环境变量
* SYSTEM_PROPERTIES_MODE_FALLBACK-1: 此时以配置文件优先, 加载不到会再次load JVM系统变量和系统环境变量
* SYSTEM_PROPERTIES_MODE_OVERRIDE-2: 此时以JVM系统变量和系统环境变量, 加载不到会再次load 配置文件变量
*/
private int systemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK; /**
* 控制是否会加载系统环境变量
*/
private boolean searchSystemEnvironment =
!SpringProperties.getFlag(AbstractEnvironment.IGNORE_GETENV_PROPERTY_NAME); /**
* Set the system property mode by the name of the corresponding constant,
* e.g. "SYSTEM_PROPERTIES_MODE_OVERRIDE".
* @param constantName name of the constant
* @throws java.lang.IllegalArgumentException if an invalid constant was specified
* @see #setSystemPropertiesMode
*/
public void setSystemPropertiesModeName(String constantName) throws IllegalArgumentException {
this.systemPropertiesMode = constants.asNumber(constantName).intValue();
} public void setSystemPropertiesMode(int systemPropertiesMode) {
this.systemPropertiesMode = systemPropertiesMode;
} public void setSearchSystemEnvironment(boolean searchSystemEnvironment) {
this.searchSystemEnvironment = searchSystemEnvironment;
} /**
* 依据systemPropertiesMode配置的策略, 根据占位符名称换取对应的值
*/
@Nullable
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;
} @Nullable
protected String resolvePlaceholder(String placeholder, Properties props) {
return props.getProperty(placeholder);
} @Nullable
protected String resolveSystemProperty(String key) {
try {
String value = System.getProperty(key);
// 依据searchSystemEnvironment 判断是否搜索环境变量
if (value == null && this.searchSystemEnvironment) {
value = System.getenv(key);
}
return value;
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not access system property '" + key + "': " + ex);
}
return null;
}
} /**
* 重写PlaceholderConfigurerSupport抽象方法processProperties, 实现替换逻辑
* Visit each bean definition in the given bean factory and attempt to replace ${...} property
* placeholders with values from the given properties.
*/
@Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
throws BeansException { StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
doProcessProperties(beanFactoryToProcess, valueResolver);
} /**
* 实际替换占位符的模版类
*/
private class PlaceholderResolvingStringValueResolver implements StringValueResolver { private final PropertyPlaceholderHelper helper; private final PlaceholderResolver resolver; public PlaceholderResolvingStringValueResolver(Properties props) {
this.helper = new PropertyPlaceholderHelper(
placeholderPrefix, placeholderSuffix, valueSeparator, ignoreUnresolvablePlaceholders);
this.resolver = new PropertyPlaceholderConfigurerResolver(props);
} @Override
@Nullable
public String resolveStringValue(String strVal) throws BeansException {
String resolved = this.helper.replacePlaceholders(strVal, this.resolver);
if (trimValues) {
resolved = resolved.trim();
}
return (resolved.equals(nullValue) ? null : resolved);
}
} private final class PropertyPlaceholderConfigurerResolver implements PlaceholderResolver { private final Properties props; private PropertyPlaceholderConfigurerResolver(Properties props) {
this.props = props;
} @Override
@Nullable
public String resolvePlaceholder(String placeholderName) {
return PropertyPlaceholderConfigurer.this.resolvePlaceholder(placeholderName,
this.props, systemPropertiesMode);
}
} }

PropertyPlaceholderConfigurer.java

2.2、PropertySourcesPlaceholderConfigurer继承图:

spring源码分析系列 (5) spring BeanFactoryPostProcessor拓展类PropertyPlaceholderConfigurer、PropertySourcesPlaceholderConfigurer解析

PropertySourcesPlaceholderConfigurer继承体系和PropertyPlaceholderConfigurer类似, 这里只总结变化的部分:

  • PropertySourcesPlaceholderConfigurer会将Properties转换为属性集合PropertySources,以解析替换所有占位符.而PropertyPlaceholderConfigurer使用原始JDK Properties
  • 解析的来源包含: mergeProperties获取配置文件中的properties 和 Environment环境变量中的properties
  • 通过localOverride 控制加载的先后顺序
  • 一旦setPropertySources设置了propertySources属性资源,其他的资源将会被忽略.以此达到用户更细粒度控制资源加载

PropertySourcesPlaceholderConfigurer源码解析

 public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {

     // 1、标识不同来源的属性配置, 从配置文件获取 或者 环境变量获取和系统变量(比如springboot中applications.properties定义属性)
/**
* {@value} is the name given to the {@link PropertySource} for the set of
* {@linkplain #mergeProperties() merged properties} supplied to this configurer.
*/
public static final String LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME = "localProperties"; /**
* {@value} is the name given to the {@link PropertySource} that wraps the
* {@linkplain #setEnvironment environment} supplied to this configurer.
*/
public static final String ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME = "environmentProperties"; @Nullable
private MutablePropertySources propertySources; @Nullable
private PropertySources appliedPropertySources; @Nullable
private Environment environment; /**
* Customize the set of {@link PropertySources} to be used by this configurer.
* <p>Setting this property indicates that environment property sources and
* local properties should be ignored.
* @see #postProcessBeanFactory
*/
public void setPropertySources(PropertySources propertySources) {
this.propertySources = new MutablePropertySources(propertySources);
} /**
* {@code PropertySources} from the given {@link Environment}
* will be searched when replacing ${...} placeholders.
* @see #setPropertySources
* @see #postProcessBeanFactory
*/
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
} // PropertySourcesPlaceholderConfigurer会将Properties转换为属性集合PropertySources,以解析替换所有占位符.而PropertyPlaceholderConfigurer使用原始JDK Properties
// 解析的来源包含: mergeProperties获取配置文件中的properties 和 Environment环境变量中的properties
// 也是通过localOverride 控制加载的先后顺序
// 一旦setPropertySources设置了propertySources属性资源,其他的资源将会被忽略.以此达到用户更细粒度控制资源加载
/**
* Processing occurs by replacing ${...} placeholders in bean definitions by resolving each
* against this configurer's set of {@link PropertySources}, which includes:
* <ul>
* <li>all {@linkplain org.springframework.core.env.ConfigurableEnvironment#getPropertySources
* environment property sources}, if an {@code Environment} {@linkplain #setEnvironment is present}
* <li>{@linkplain #mergeProperties merged local properties}, if {@linkplain #setLocation any}
* {@linkplain #setLocations have} {@linkplain #setProperties been}
* {@linkplain #setPropertiesArray specified}
* <li>any property sources set by calling {@link #setPropertySources}
* </ul>
* <p>If {@link #setPropertySources} is called, <strong>environment and local properties will be
* ignored</strong>. This method is designed to give the user fine-grained control over property
* sources, and once set, the configurer makes no assumptions about adding additional sources.
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.propertySources == null) {
this.propertySources = new MutablePropertySources();
if (this.environment != null) {
this.propertySources.addLast(
new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
@Override
@Nullable
public String getProperty(String key) {
return this.source.getProperty(key);
}
}
);
}
try {
PropertySource<?> localPropertySource =
new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
if (this.localOverride) {
this.propertySources.addFirst(localPropertySource);
}
else {
this.propertySources.addLast(localPropertySource);
}
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
} processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
this.appliedPropertySources = this.propertySources;
} /**
* Visit each bean definition in the given bean factory and attempt to replace ${...} property
* placeholders with values from the given properties.
*/
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
final ConfigurablePropertyResolver propertyResolver) throws BeansException { // 区分于PropertyPlaceholderConfigurer, 使用ConfigurablePropertyResolver实例持有占位符的前缀、后缀、多值的分隔符 已经进行字符串替换
propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
propertyResolver.setValueSeparator(this.valueSeparator); StringValueResolver valueResolver = strVal -> {
String resolved = (this.ignoreUnresolvablePlaceholders ?
propertyResolver.resolvePlaceholders(strVal) :
propertyResolver.resolveRequiredPlaceholders(strVal));
if (this.trimValues) {
resolved = resolved.trim();
}
return (resolved.equals(this.nullValue) ? null : resolved);
}; // 委托抽象类PlaceholderConfigurerSupport#doProcessProperties解析,同PropertyPlaceholderConfigurer一样
doProcessProperties(beanFactoryToProcess, valueResolver);
} // 此时已废弃PlaceholderConfigurerSupport#processProperties
/**
* Implemented for compatibility with
* {@link org.springframework.beans.factory.config.PlaceholderConfigurerSupport}.
* @deprecated in favor of
* {@link #processProperties(ConfigurableListableBeanFactory, ConfigurablePropertyResolver)}
* @throws UnsupportedOperationException in this implementation
*/
@Override
@Deprecated
protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) {
throw new UnsupportedOperationException(
"Call processProperties(ConfigurableListableBeanFactory, ConfigurablePropertyResolver) instead");
} /**
* Return the property sources that were actually applied during
* {@link #postProcessBeanFactory(ConfigurableListableBeanFactory) post-processing}.
* @return the property sources that were applied
* @throws IllegalStateException if the property sources have not yet been applied
* @since 4.0
*/
public PropertySources getAppliedPropertySources() throws IllegalStateException {
Assert.state(this.appliedPropertySources != null, "PropertySources have not yet been applied");
return this.appliedPropertySources;
}
}

PropertySourcesPlaceholderConfigurer.java

上一篇:CoreData的使用入门到精通


下一篇:Testlink & Redmine组合拳演练