spring源码分析系列 (5) spring BeanFactoryPostProcessor拓展类PropertyPlaceholderConfigurer、PropertySourcesPlaceholderConfigurer解析
主要分析内容:
(源码基于spring 5.1.3.RELEASE分析)
PropertyPlaceholderConfigurer和PropertySourcesPlaceholderConfigurer都是个bean工厂后置处理器接口BeanFactoryPostProcessor的实现类。可以将上下文(配置文件)中的属性值放在另一个单独的标准java Properties文件中去。在XML文件中用${key}替换指定的properties文件中的值或者可以自定义前缀和后缀。此时只需要对properties文件进 行修改,而不用对xml配置文件进行修改。spring 3.1之后更推荐使用PropertySourcesPlaceholderConfigurer, 因为其更加灵活。PropertyPlaceholderConfigurer通常使用的方式:
Bean.java
1 public class Bean { 2 3 public Bean(){ 4 5 } 6 7 public Bean(String name){ 8 System.out.println("构造函数被调用啦"); 9 this.name = name ; 10 } 11 12 private String name ; 13 14 15 public String getName() { 16 return name; 17 } 18 19 public void setName(String name) { 20 this.name = name; 21 } 22 23 @Override 24 public String toString() { 25 return "Bean{" + 26 "name='" + name + '\'' + 27 '}'; 28 } 29 }
ioc-PlaceholderConfigurer.xml :
1 <?xml version="1.0" encoding="utf-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> 5 <bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 6 <property name="locations"> 7 <list> 8 <value>bean.properties</value> 9 </list> 10 </property> 11 </bean> 12 <bean id="bean2" class="com.nancy.ioc.Bean"> 13 <property name="name" value="${bean.field.name}"/> 14 </bean> 15 </beans>
bean.properties
bean.field.name=hello world
PlaceholderTest.java
1 public class PlaceholderTest { 2 private ApplicationContext applicationContext ; 3 4 @Before 5 public void beforeApplicationContext(){ 6 /** 7 * ApplicationContext 自动注册 BeanPostProcessor、InstantiationAwareBeanPostProcessor、BeanFactoryPostProcessor 8 * 不需要手动注册 9 * */ 10 applicationContext = new ClassPathXmlApplicationContext("ioc-PlaceholderConfigurer.xml") ; 11 } 12 13 @Test 14 public void test(){ 15 Bean bean = applicationContext.getBean("bean2", Bean.class) ; 16 System.out.println(bean); 17 } 18 19 @After 20 public void after(){ 21 ((ClassPathXmlApplicationContext)applicationContext).close(); 22 } 23 }
运行结果: 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>
运行结果
Bean{name='hello world'}
更多示例demo参考: https://gitee.com/zhouxiaoxing91/learning-src/tree/master/spring-src/src/test/java/com/nancy/ioc
2.1、PropertyPlaceholderConfigurer继承图:
(1)、PropertiesLoaderSupport:抽象类,提供从properties文件中读取配置信息的能力,该类的属性locations指定需要加载的文件所在的路径。mergeProperties()中加载和合并所有的Properties属性, 可动态控制配置文件和BeanDefinition(默认配置)加载顺序.
1 public abstract class PropertiesLoaderSupport { 2 3 /** Logger available to subclasses. */ 4 protected final Log logger = LogFactory.getLog(getClass()); 5 6 /** 7 * 本地资源例如从xml加载的配置信息等 8 */ 9 @Nullable 10 protected Properties[] localProperties; 11 12 /** 13 * 为true则localProperties会覆盖从locations获取的属性值 14 */ 15 protected boolean localOverride = false; 16 17 /** 18 * 配置文件Properties地址 19 */ 20 @Nullable 21 private Resource[] locations; 22 23 /** 24 * 当解析不到对应的配置文件是否报错 25 */ 26 private boolean ignoreResourceNotFound = false; 27 28 @Nullable 29 private String fileEncoding; 30 31 /** 32 * Properties文件加载策略,可以配置加载编码形式等. 默认为DefaultPropertiesPersister,实质委托Properties.load 或者 Properties.store 33 */ 34 private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister(); 35 36 /** 37 * Set local properties, e.g. via the "props" tag in XML bean definitions. 38 * These can be considered defaults, to be overridden by properties 39 * loaded from files. 40 */ 41 public void setProperties(Properties properties) { 42 this.localProperties = new Properties[] {properties}; 43 } 44 45 /** 46 * Set local properties, e.g. via the "props" tag in XML bean definitions, 47 * allowing for merging multiple properties sets into one. 48 */ 49 public void setPropertiesArray(Properties... propertiesArray) { 50 this.localProperties = propertiesArray; 51 } 52 53 /** 54 * Set a location of a properties file to be loaded. 55 * <p>Can point to a classic properties file or to an XML file 56 * that follows JDK 1.5's properties XML format. 57 */ 58 public void setLocation(Resource location) { 59 this.locations = new Resource[] {location}; 60 } 61 62 /** 63 * Set locations of properties files to be loaded. 64 * <p>Can point to classic properties files or to XML files 65 * that follow JDK 1.5's properties XML format. 66 * <p>Note: Properties defined in later files will override 67 * properties defined earlier files, in case of overlapping keys. 68 * Hence, make sure that the most specific files are the last 69 * ones in the given list of locations. 70 */ 71 public void setLocations(Resource... locations) { 72 this.locations = locations; 73 } 74 75 /** 76 * Set whether local properties override properties from files. 77 * <p>Default is "false": Properties from files override local defaults. 78 * Can be switched to "true" to let local properties override defaults 79 * from files. 80 */ 81 public void setLocalOverride(boolean localOverride) { 82 this.localOverride = localOverride; 83 } 84 85 /** 86 * Set if failure to find the property resource should be ignored. 87 * <p>"true" is appropriate if the properties file is completely optional. 88 * Default is "false". 89 */ 90 public void setIgnoreResourceNotFound(boolean ignoreResourceNotFound) { 91 this.ignoreResourceNotFound = ignoreResourceNotFound; 92 } 93 94 /** 95 * Set the encoding to use for parsing properties files. 96 * <p>Default is none, using the {@code java.util.Properties} 97 * default encoding. 98 * <p>Only applies to classic properties files, not to XML files. 99 * @see org.springframework.util.PropertiesPersister#load 100 */ 101 public void setFileEncoding(String encoding) { 102 this.fileEncoding = encoding; 103 } 104 105 /** 106 * Set the PropertiesPersister to use for parsing properties files. 107 * The default is DefaultPropertiesPersister. 108 * @see org.springframework.util.DefaultPropertiesPersister 109 */ 110 public void setPropertiesPersister(@Nullable PropertiesPersister propertiesPersister) { 111 this.propertiesPersister = 112 (propertiesPersister != null ? propertiesPersister : new DefaultPropertiesPersister()); 113 } 114 115 /** 116 * 加载和合并所有的Properties属性, 可动态控制 配置文件属性 和 BeanDefinition属性(默认配置) 加载顺序 117 */ 118 protected Properties mergeProperties() throws IOException { 119 Properties result = new Properties(); 120 121 if (this.localOverride) { 122 // Load properties from file upfront, to let local properties override. 123 loadProperties(result); 124 } 125 126 if (this.localProperties != null) { 127 for (Properties localProp : this.localProperties) { 128 CollectionUtils.mergePropertiesIntoMap(localProp, result); 129 } 130 } 131 132 if (!this.localOverride) { 133 // Load properties from file afterwards, to let those properties override. 134 loadProperties(result); 135 } 136 137 return result; 138 } 139 140 /** 141 * 将配置文件信息,加载进入Properties实例 142 */ 143 protected void loadProperties(Properties props) throws IOException { 144 if (this.locations != null) { 145 for (Resource location : this.locations) { 146 if (logger.isTraceEnabled()) { 147 logger.trace("Loading properties file from " + location); 148 } 149 try { 150 PropertiesLoaderUtils.fillProperties( 151 props, new EncodedResource(location, this.fileEncoding), this.propertiesPersister); 152 } 153 catch (FileNotFoundException | UnknownHostException ex) { 154 if (this.ignoreResourceNotFound) { 155 if (logger.isDebugEnabled()) { 156 logger.debug("Properties resource not found: " + ex.getMessage()); 157 } 158 } 159 else { 160 throw ex; 161 } 162 } 163 } 164 } 165 } 166 167 }
(2)、PropertyResourceConfigurer:抽象类, 继承PropertiesLoaderSupport并实现BeanFactoryPostProcessor接口,资源加载和占位符替换入口。在postProcessBeanFactory()方法从配置文件中读取了配置项,并调用抽象方法processProperties()由子类决定处理逻辑。除此之外,提供了convertProperty()方法,该方法是个扩展点,其实里面什么都没做,它可以用来子类在处理这些配置信息前,对配置信息进行一些转换,例如配置属性的解密。
1 public abstract class PropertyResourceConfigurer extends PropertiesLoaderSupport 2 implements BeanFactoryPostProcessor, PriorityOrdered { 3 4 private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered 5 6 /** 7 * BeanFactoryPostProcessor拓展接口, 资源加载和占位符替换入口 8 */ 9 @Override 10 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 11 try { 12 // 继承自PropertiesLoaderSupport, 加载配置文件 13 Properties mergedProps = mergeProperties(); 14 15 // 对已加载属性配置进行处理, spring一个拓展点. 例如从配置文件获取密文, 此时可以做解密 16 convertProperties(mergedProps); 17 18 // 抽象方法, 由子类实现占位符替换逻辑 19 processProperties(beanFactory, mergedProps); 20 } 21 catch (IOException ex) { 22 throw new BeanInitializationException("Could not load properties", ex); 23 } 24 } 25 26 protected void convertProperties(Properties props) { 27 Enumeration<?> propertyNames = props.propertyNames(); 28 while (propertyNames.hasMoreElements()) { 29 String propertyName = (String) propertyNames.nextElement(); 30 String propertyValue = props.getProperty(propertyName); 31 String convertedValue = convertProperty(propertyName, propertyValue); 32 if (!ObjectUtils.nullSafeEquals(propertyValue, convertedValue)) { 33 props.setProperty(propertyName, convertedValue); 34 } 35 } 36 } 37 38 protected String convertProperty(String propertyName, String propertyValue) { 39 return convertPropertyValue(propertyValue); 40 } 41 42 /** 43 * 默认返回原始值, 子类重写实现定制功能, 如:配置文件获取密文, 此时可以做解密 44 */ 45 protected String convertPropertyValue(String originalValue) { 46 return originalValue; 47 } 48 49 /** 50 * 抽象方法, 由子类实现占位符替换逻辑 51 */ 52 protected abstract void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) 53 throws BeansException; 54 55 }
(3)、PlaceholderConfigurerSupport:抽象类,该类持有占位符符号的前缀、后缀、分隔符,在doProcessProperties()方法实现对BeanDefinition实例中的占位符进行替换。
1 public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfigurer 2 implements BeanNameAware, BeanFactoryAware { 3 4 /** Default placeholder prefix: {@value}. */ 5 public static final String DEFAULT_PLACEHOLDER_PREFIX = "${"; 6 7 /** Default placeholder suffix: {@value}. */ 8 public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}"; 9 10 /** Default value separator: {@value}. */ 11 public static final String DEFAULT_VALUE_SEPARATOR = ":"; 12 13 /** Defaults to {@value #DEFAULT_PLACEHOLDER_PREFIX}. */ 14 protected String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX; 15 16 /** Defaults to {@value #DEFAULT_PLACEHOLDER_SUFFIX}. */ 17 protected String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX; 18 19 /** Defaults to {@value #DEFAULT_VALUE_SEPARATOR}. */ 20 @Nullable 21 protected String valueSeparator = DEFAULT_VALUE_SEPARATOR; 22 23 protected boolean trimValues = false; 24 25 @Nullable 26 protected String nullValue; 27 28 /** 29 * 当解析不到对应的key是否忽略, 默认为false. 在配置多个解析类的时候, 可设置为ture防止属性加载报错. 30 */ 31 protected boolean ignoreUnresolvablePlaceholders = false; 32 33 @Nullable 34 private String beanName; 35 36 @Nullable 37 private BeanFactory beanFactory; 38 39 // 省略getter 和 setter ........ 40 41 /** 42 * 实现对BeanDefinition实例中的占位符进行替换 43 */ 44 protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess, 45 StringValueResolver valueResolver) { 46 47 /** 48 * 循环获得所有BeanDefinition, 依据模版类StringValueResolver替换对应BeanDefinition的占位符 49 */ 50 BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver); 51 52 String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames(); 53 for (String curName : beanNames) { 54 // Check that we're not parsing our own bean definition, 55 // to avoid failing on unresolvable placeholders in properties file locations. 56 if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) { 57 BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName); 58 try { 59 visitor.visitBeanDefinition(bd); 60 } 61 catch (Exception ex) { 62 throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex); 63 } 64 } 65 } 66 67 // New in Spring 2.5: resolve placeholders in alias target names and aliases as well. 68 beanFactoryToProcess.resolveAliases(valueResolver); 69 70 // New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes. 71 beanFactoryToProcess.addEmbeddedValueResolver(valueResolver); 72 }
(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中将传入占位符替换为对应的值.
1 public class PropertyPlaceholderConfigurer extends PlaceholderConfigurerSupport { 2 3 /** Never check system properties. */ 4 public static final int SYSTEM_PROPERTIES_MODE_NEVER = 0; 5 6 /** 7 * Check system properties if not resolvable in the specified properties. 8 * This is the default. 9 */ 10 public static final int SYSTEM_PROPERTIES_MODE_FALLBACK = 1; 11 12 /** 13 * Check system properties first, before trying the specified properties. 14 * This allows system properties to override any other property source. 15 */ 16 public static final int SYSTEM_PROPERTIES_MODE_OVERRIDE = 2; 17 18 /** 19 * spring提供工具类, 利用jdk反射机制, 对类常量(public static final)进行映射, 可通过常量名称进行访问. 20 */ 21 private static final Constants constants = new Constants(PropertyPlaceholderConfigurer.class); 22 23 /** 24 * 三种模式, 默认为 1: 25 * SYSTEM_PROPERTIES_MODE_NEVER-0: 此时以配置文件为准,不会加载 JVM系统变量和系统环境变量 26 * SYSTEM_PROPERTIES_MODE_FALLBACK-1: 此时以配置文件优先, 加载不到会再次load JVM系统变量和系统环境变量 27 * SYSTEM_PROPERTIES_MODE_OVERRIDE-2: 此时以JVM系统变量和系统环境变量, 加载不到会再次load 配置文件变量 28 */ 29 private int systemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK; 30 31 /** 32 * 控制是否会加载系统环境变量 33 */ 34 private boolean searchSystemEnvironment = 35 !SpringProperties.getFlag(AbstractEnvironment.IGNORE_GETENV_PROPERTY_NAME); 36 37 38 /** 39 * Set the system property mode by the name of the corresponding constant, 40 * e.g. "SYSTEM_PROPERTIES_MODE_OVERRIDE". 41 * @param constantName name of the constant 42 * @throws java.lang.IllegalArgumentException if an invalid constant was specified 43 * @see #setSystemPropertiesMode 44 */ 45 public void setSystemPropertiesModeName(String constantName) throws IllegalArgumentException { 46 this.systemPropertiesMode = constants.asNumber(constantName).intValue(); 47 } 48 49 public void setSystemPropertiesMode(int systemPropertiesMode) { 50 this.systemPropertiesMode = systemPropertiesMode; 51 } 52 53 public void setSearchSystemEnvironment(boolean searchSystemEnvironment) { 54 this.searchSystemEnvironment = searchSystemEnvironment; 55 } 56 57 /** 58 * 依据systemPropertiesMode配置的策略, 根据占位符名称换取对应的值 59 */ 60 @Nullable 61 protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) { 62 String propVal = null; 63 if (systemPropertiesMode == SYSTEM_PROPERTIES_MODE_OVERRIDE) { 64 propVal = resolveSystemProperty(placeholder); 65 } 66 if (propVal == null) { 67 propVal = resolvePlaceholder(placeholder, props); 68 } 69 if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) { 70 propVal = resolveSystemProperty(placeholder); 71 } 72 return propVal; 73 } 74 75 @Nullable 76 protected String resolvePlaceholder(String placeholder, Properties props) { 77 return props.getProperty(placeholder); 78 } 79 80 @Nullable 81 protected String resolveSystemProperty(String key) { 82 try { 83 String value = System.getProperty(key); 84 // 依据searchSystemEnvironment 判断是否搜索环境变量 85 if (value == null && this.searchSystemEnvironment) { 86 value = System.getenv(key); 87 } 88 return value; 89 } 90 catch (Throwable ex) { 91 if (logger.isDebugEnabled()) { 92 logger.debug("Could not access system property '" + key + "': " + ex); 93 } 94 return null; 95 } 96 } 97 98 99 /** 100 * 重写PlaceholderConfigurerSupport抽象方法processProperties, 实现替换逻辑 101 * Visit each bean definition in the given bean factory and attempt to replace ${...} property 102 * placeholders with values from the given properties. 103 */ 104 @Override 105 protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) 106 throws BeansException { 107 108 StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props); 109 doProcessProperties(beanFactoryToProcess, valueResolver); 110 } 111 112 113 /** 114 * 实际替换占位符的模版类 115 */ 116 private class PlaceholderResolvingStringValueResolver implements StringValueResolver { 117 118 private final PropertyPlaceholderHelper helper; 119 120 private final PlaceholderResolver resolver; 121 122 public PlaceholderResolvingStringValueResolver(Properties props) { 123 this.helper = new PropertyPlaceholderHelper( 124 placeholderPrefix, placeholderSuffix, valueSeparator, ignoreUnresolvablePlaceholders); 125 this.resolver = new PropertyPlaceholderConfigurerResolver(props); 126 } 127 128 @Override 129 @Nullable 130 public String resolveStringValue(String strVal) throws BeansException { 131 String resolved = this.helper.replacePlaceholders(strVal, this.resolver); 132 if (trimValues) { 133 resolved = resolved.trim(); 134 } 135 return (resolved.equals(nullValue) ? null : resolved); 136 } 137 } 138 139 private final class PropertyPlaceholderConfigurerResolver implements PlaceholderResolver { 140 141 private final Properties props; 142 143 private PropertyPlaceholderConfigurerResolver(Properties props) { 144 this.props = props; 145 } 146 147 @Override 148 @Nullable 149 public String resolvePlaceholder(String placeholderName) { 150 return PropertyPlaceholderConfigurer.this.resolvePlaceholder(placeholderName, 151 this.props, systemPropertiesMode); 152 } 153 } 154 155 }
2.2、PropertySourcesPlaceholderConfigurer继承图:
PropertySourcesPlaceholderConfigurer继承体系和PropertyPlaceholderConfigurer类似, 这里只总结变化的部分:
- PropertySourcesPlaceholderConfigurer会将Properties转换为属性集合PropertySources,以解析替换所有占位符.而PropertyPlaceholderConfigurer使用原始JDK Properties
- 解析的来源包含: mergeProperties获取配置文件中的properties 和 Environment环境变量中的properties
- 通过localOverride 控制加载的先后顺序
- 一旦setPropertySources设置了propertySources属性资源,其他的资源将会被忽略.以此达到用户更细粒度控制资源加载
PropertySourcesPlaceholderConfigurer源码解析
1 public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware { 2 3 // 1、标识不同来源的属性配置, 从配置文件获取 或者 环境变量获取和系统变量(比如springboot中applications.properties定义属性) 4 /** 5 * {@value} is the name given to the {@link PropertySource} for the set of 6 * {@linkplain #mergeProperties() merged properties} supplied to this configurer. 7 */ 8 public static final String LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME = "localProperties"; 9 10 /** 11 * {@value} is the name given to the {@link PropertySource} that wraps the 12 * {@linkplain #setEnvironment environment} supplied to this configurer. 13 */ 14 public static final String ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME = "environmentProperties"; 15 16 @Nullable 17 private MutablePropertySources propertySources; 18 19 @Nullable 20 private PropertySources appliedPropertySources; 21 22 @Nullable 23 private Environment environment; 24 25 26 /** 27 * Customize the set of {@link PropertySources} to be used by this configurer. 28 * <p>Setting this property indicates that environment property sources and 29 * local properties should be ignored. 30 * @see #postProcessBeanFactory 31 */ 32 public void setPropertySources(PropertySources propertySources) { 33 this.propertySources = new MutablePropertySources(propertySources); 34 } 35 36 /** 37 * {@code PropertySources} from the given {@link Environment} 38 * will be searched when replacing ${...} placeholders. 39 * @see #setPropertySources 40 * @see #postProcessBeanFactory 41 */ 42 @Override 43 public void setEnvironment(Environment environment) { 44 this.environment = environment; 45 } 46 47 48 // PropertySourcesPlaceholderConfigurer会将Properties转换为属性集合PropertySources,以解析替换所有占位符.而PropertyPlaceholderConfigurer使用原始JDK Properties 49 // 解析的来源包含: mergeProperties获取配置文件中的properties 和 Environment环境变量中的properties 50 // 也是通过localOverride 控制加载的先后顺序 51 // 一旦setPropertySources设置了propertySources属性资源,其他的资源将会被忽略.以此达到用户更细粒度控制资源加载 52 /** 53 * Processing occurs by replacing ${...} placeholders in bean definitions by resolving each 54 * against this configurer's set of {@link PropertySources}, which includes: 55 * <ul> 56 * <li>all {@linkplain org.springframework.core.env.ConfigurableEnvironment#getPropertySources 57 * environment property sources}, if an {@code Environment} {@linkplain #setEnvironment is present} 58 * <li>{@linkplain #mergeProperties merged local properties}, if {@linkplain #setLocation any} 59 * {@linkplain #setLocations have} {@linkplain #setProperties been} 60 * {@linkplain #setPropertiesArray specified} 61 * <li>any property sources set by calling {@link #setPropertySources} 62 * </ul> 63 * <p>If {@link #setPropertySources} is called, <strong>environment and local properties will be 64 * ignored</strong>. This method is designed to give the user fine-grained control over property 65 * sources, and once set, the configurer makes no assumptions about adding additional sources. 66 */ 67 @Override 68 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 69 if (this.propertySources == null) { 70 this.propertySources = new MutablePropertySources(); 71 if (this.environment != null) { 72 this.propertySources.addLast( 73 new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) { 74 @Override 75 @Nullable 76 public String getProperty(String key) { 77 return this.source.getProperty(key); 78 } 79 } 80 ); 81 } 82 try { 83 PropertySource<?> localPropertySource = 84 new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties()); 85 if (this.localOverride) { 86 this.propertySources.addFirst(localPropertySource); 87 } 88 else { 89 this.propertySources.addLast(localPropertySource); 90 } 91 } 92 catch (IOException ex) { 93 throw new BeanInitializationException("Could not load properties", ex); 94 } 95 } 96 97 processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources)); 98 this.appliedPropertySources = this.propertySources; 99 } 100 101 /** 102 * Visit each bean definition in the given bean factory and attempt to replace ${...} property 103 * placeholders with values from the given properties. 104 */ 105 protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, 106 final ConfigurablePropertyResolver propertyResolver) throws BeansException { 107 108 // 区分于PropertyPlaceholderConfigurer, 使用ConfigurablePropertyResolver实例持有占位符的前缀、后缀、多值的分隔符 已经进行字符串替换 109 propertyResolver.setPlaceholderPrefix(this.placeholderPrefix); 110 propertyResolver.setPlaceholderSuffix(this.placeholderSuffix); 111 propertyResolver.setValueSeparator(this.valueSeparator); 112 113 StringValueResolver valueResolver = strVal -> { 114 String resolved = (this.ignoreUnresolvablePlaceholders ? 115 propertyResolver.resolvePlaceholders(strVal) : 116 propertyResolver.resolveRequiredPlaceholders(strVal)); 117 if (this.trimValues) { 118 resolved = resolved.trim(); 119 } 120 return (resolved.equals(this.nullValue) ? null : resolved); 121 }; 122 123 // 委托抽象类PlaceholderConfigurerSupport#doProcessProperties解析,同PropertyPlaceholderConfigurer一样 124 doProcessProperties(beanFactoryToProcess, valueResolver); 125 } 126 127 // 此时已废弃PlaceholderConfigurerSupport#processProperties 128 /** 129 * Implemented for compatibility with 130 * {@link org.springframework.beans.factory.config.PlaceholderConfigurerSupport}. 131 * @deprecated in favor of 132 * {@link #processProperties(ConfigurableListableBeanFactory, ConfigurablePropertyResolver)} 133 * @throws UnsupportedOperationException in this implementation 134 */ 135 @Override 136 @Deprecated 137 protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) { 138 throw new UnsupportedOperationException( 139 "Call processProperties(ConfigurableListableBeanFactory, ConfigurablePropertyResolver) instead"); 140 } 141 142 /** 143 * Return the property sources that were actually applied during 144 * {@link #postProcessBeanFactory(ConfigurableListableBeanFactory) post-processing}. 145 * @return the property sources that were applied 146 * @throws IllegalStateException if the property sources have not yet been applied 147 * @since 4.0 148 */ 149 public PropertySources getAppliedPropertySources() throws IllegalStateException { 150 Assert.state(this.appliedPropertySources != null, "PropertySources have not yet been applied"); 151 return this.appliedPropertySources; 152 } 153 }