Spring 工具类 PlaceholderConfigurerSupport (抽象基类)

概述

Spring PlaceholderConfigurerSupport是一个抽象基类,抽象了bean定义属性值中的占位符解析的功能。它继承自PropertyResourceConfigurer。基类已经定义了要在bean容器后置处理阶段对容器中所有bean定义属性进行处理,而PlaceholderConfigurerSupport则进一步约定了要进行的属性值处理是:解析属性值中的占位符,同时提供了进行处理所需的一些属性(占位符前后缀等),以及一些工具方法。实现子类会从一个属性文件或者其他org.springframework.core.env.PropertySource属性源"拉取(pull)"属性值,然后使用PlaceholderConfigurerSupport约定的属性和属性值占位符替换方法处理bean定义。

缺省的占位符格式为${...},遵从Ant/Log4J/JSP EL风格。PlaceholderConfigurerSupport允许设置不同的占位符前缀后缀。

如果是XML方式定义bean,占位符例子如下 :

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${driver}"/>
    <property name="url" value="jdbc:${dbname}"/>
</bean>

对应的属性文件内容如下 :

driver=com.mysql.jdbc.Driver
dbname=mysql:mydb

而如果是注解方式使用占位符,占位符例子如下 :

@Value("${person.age}")

另外支持嵌套使用占位符:

rootPath=myrootdir
subPath=${rootPath}/subdir

如果占位符无法被解析,可以抛出一个异常BeanDefinitionStoreException,也可以置之不理,这个机制通过属性ignoreUnresolvablePlaceholders来控制,ignoreUnresolvablePlaceholdersfalse表示无法解析占位符时抛出异常。这也是缺省值。

PlaceholderConfigurerSupport 所在包 : org.springframework.beans.factory.config

PlaceholderConfigurerSupport在类层级关系中的位置如下图所示:
Spring 工具类 PlaceholderConfigurerSupport (抽象基类)

源代码解析

基类

PropertyResourceConfigurer

所实现接口

1.BeanNameAware
2.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;

    // 是否要对值做 trim
	protected boolean trimValues = false;

    // 遇到占位符对应属性值为""或者null时的替代填充值
	@Nullable
	protected String nullValue;

    // 不能解析的占位符是否抛出异常, false 表示抛出异常, true 表示不抛出异常
	protected boolean ignoreUnresolvablePlaceholders = false;

    // 对应 BeanNameAware 接口方法setBeanName()
    // 用于记录当前bean的名称
	@Nullable
	private String beanName;

    // 对应 BeanFactoryAware 接口方法setBeanFactory()
    // 用于记录当前bean所在容器,也就是需要处理的bean定义所在的容器
	@Nullable
	private BeanFactory beanFactory;

对于上述实例成员属性,PlaceholderConfigurerSupport都提供了相应的set方法,所以上面带缺省值的属性都可以被覆盖。

主要功能方法

除了对上述实例成员的set方法之外,PlaceholderConfigurerSupport仅仅提供了一个功能方法doProcessProperties,使用指定的字符串值解析器处理容器中所有的bean定义的属性。

doProcessProperties()


// 参数 beanFactoryToProcess : 要处理的bean定义所属的容器
// 参数 valueResolver : 属性值解析器
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
			StringValueResolver valueResolver) {
        // 使用指定的字符串值解析器 valueResolver 定义一个bean定义访问器,
       // 该访问器的目的就是每次访问一个bean定义,将其中所有可能包含占位符的属性值,包括bean属性值,
       // bean构造函数参数值,双亲bean名称,bean类名,bean工厂bean名称,bean工厂方法名称,作用域
       // 等都遍历一遍,进行需要的占位符解析
		BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);

       // 获取容器中所有bean的名称
		String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
       // 逐个bean定义进行属性值占位符解析
		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))) {
            // 确保处理的容器是自己所在的容器,并且被处理的bean定义不是自己的bean定义
				BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
				try {
                  // 对bean定义bd进行属性值占位符解析
					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);
	}

总结

从上面分析可以看出:

  1. PlaceholderConfigurerSupport继承自PropertyResourceConfigurer,所以它具备PropertyResourceConfigurer定义的所有能力。
  2. PlaceholderConfigurerSupport实现了接口BeanNameAware,所以它的bean实例知道自己的bean名称。
  3. PlaceholderConfigurerSupport实现了接口BeanFactoryAware,所以它知道自己的容器是谁。
  4. PlaceholderConfigurerSupport提供了实现子类需要的一些属性的设置和缺省值,比如占位符前后缀,值分隔符等。
  5. PlaceholderConfigurerSupport实现了一个工具方法doProcessProperties(),该方法处理容器中除了自身bean定义之外的所有其他bean定义,对它们的属性值中的占位符进行解析。
  6. 不过,PlaceholderConfigurerSupport仍未对基类PropertyResourceConfigurer定义的抽象方法processProperties提供实现。换句话讲,如果想使用PlaceholderConfigurerSupport的能力的话,需要继续提供实现子类。

参考文章

Spring 工具类 PropertyResourceConfigurer(抽象基类)