3.8.2 使用BeanFactoryPostProcessor定制配置元数据
我们将要看到的下一个扩展点是“org.springframework.beans.factory.config.BeanFactoryPostProcessor”。这个接口的语义类似于BeanPostProcessor
,其中一个主要区别是:BeanFactoryPostProcessor对 bean配置元数据操作;也就是说,Spring IoC容器允许BeanFactoryPostProcessor
读取配置元数据,并可能在容器实例化(除BeanFactoryPostProcessor之外的任何)bean之前改变它。
你可以配置多个BeanFactoryPostProcessor
s,你可以通过设置order
property来控制这些BeanFactoryPostProcessor的执行顺序。但是,如果BeanFactoryPostProcessor实现了 Ordered
接口,则只能设置此属性。如果你写自己的“BeanFactoryPostProcessor”,你应该考虑实现 Ordered
接口。有关更多详细信息,请参阅BeanFactoryPostProcessor
和 Ordered
接口的javadoc。
如果你想改变实际的bean 实例(即从配置元数据创建的对象),那么你需要使用一个BeanPostProcessor (如上所述第3.8.1节“ BeanPostProcessor“)。虽然在技术上可以使用BeanFactoryPostProcessor(例如,使用BeanFactory.getBean() )中的bean实例,这样做会导致提前的bean实例化,违反标准容器生命周期。这可能导致负面的副作用,如绕过bean后处理。此外,BeanFactoryPostProcessor s的作用域也是在各自的容器内 。这仅在使用容器层次结构时才相关。如果你在一个容器中定义一个BeanFactoryPostProcessor ,它只会应用于该容器中的bean定义。一个容器中的Bean定义不会被另一个容器中的BeanFactoryPostProcessor进行后置处理,即使这两个容器都是同一层次结构的一部分。 |
当在“ApplicationContext”中声明一个bean工厂后置处理器时,它会自动执行,以便将定义容器的配置元数据进行的更改应用。 Spring包括一些预定义的bean工厂后置处理器,例如PropertyOverrideConfigurer
和PropertyPlaceholderConfigurer
。 例如,定制的BeanFactoryPostProcessor也可以用来注册自定义属性编辑器。
一个ApplicationContext
自动检测到它里面实现BeanFactoryPostProcessor
接口的任何bean并部署。 它使用这些bean作为bean工厂后置处理器,在适当的时间。 你可以像部署其他bean一样部署这些后置处理器bean。(即ApplicationContext
自动探测BeanFactoryPostProcessor
接口的实现类。容器使用这些bean作为bean工厂post-processors
。可以像其他bean那样将post-processor
部署在容器内。)
和BeanPostProcessor s一样,你通常不想为延迟初始化配置BeanFactoryPostProcessor s。 如果没有其他bean引用Bean(Factory)PostProcessor ,后置处理器将不会被实例化。 因此,标记它的延迟初始化将被忽略,并且Bean(Factory)PostProcessor 将被及时实例化,即使你在<beans/> 元素的声明上将default-lazy-init 属性设置为 true 。也不行 |
Example: the Class name substitution PropertyPlaceholderConfigurer
你使用PropertyPlaceholderConfigurer
将bean的属性值使用标准的Java Properties格式定义在一个单独的文件中。这样可以将应用的自定义环境配置属性隔离出来,例如数据库URL和密码,而不需要修改容器的主XML定义文件或Java 代码文件的复杂性或风险。
考虑以下基于XML的配置元数据片段,其中定义了具有占位符值的“DataSource”。 该示例显示从外部Properties
文件配置的属性。 在运行时,将一个PropertyPlaceholderConfigurer
应用于将替换DataSource的某些属性的元数据。 要替换的值被指定为遵循Ant / log4j / JSP EL样式的${property-name}
形式的 placeholder 。
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean>
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
在标准java Properties格式文件中实际的值:
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
因此,字符串${jdbc.username}
在运行时被替换为值'sa',这同样适用于与属性文件中的键匹配的其他占位符值。 PropertyPlaceholderConfigurer
检查bean定义的大多数属性和属性中的占位符。 此外,可以自定义占位符前缀和后缀。
使用Spring 2.5中引入的context
命名空间,可以使用专用配置元素配置属性占位符。 一个或多个位置可以在location
属性中以逗号分隔的列表形式提供。
<context:property-placeholder location="classpath:com/foo/jdbc.properties"/>
PropertyPlaceholderConfigurer
不仅在你指定的Properties
文件中寻找属性。 默认情况下,如果在指定的属性文件中找不到属性,它还会检查Java“System”属性。 你可以通过将configurer的systemPropertiesMode
属性设置为以下三个支持的整数值之一来定制此行为:
- never * (0):不检查系统属性
- fallback * (1):如果在指定的属性文件中不可解析,请检查系统属性。 这是默认值。
- override * (2) :首先检查系统属性,然后尝试指定的属性文件。 这允许系统属性覆盖任何其他属性源。
有关更多信息,请参阅PropertyPlaceholderConfigurer
javadocs。
你可以使用
PropertyPlaceholderConfigurer
来替换类名,当你必须在运行时选择一个特定的实现类时,这是有用的。 例如:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>classpath:com/foo/strategy.properties</value>
</property>
<property name="properties">
<value>custom.strategy.class=com.foo.DefaultStrategy</value>
</property>
</bean>
<bean id="serviceStrategy" class="${custom.strategy.class}"/>
若类在运行时期间不能解析为合法类,
ApplicationContext
创建非延迟初始化bean的preInstantiateSingletons()
期间抛错误
Example: the PropertyOverrideConfigurer
“PropertyOverrideConfigurer”是另一个bean工厂后置处理器,类似于PropertyPlaceholderConfigurer,但是与后者不同,原始定义可以设置默认值或者根本不设置值。 如果重写的“Properties”文件没有特定bean属性的条目,那么就使用默认的上下文定义。
注意,bean定义并不知道它会被重写,所以使用了重写配置在XML配置中并不直观。 在多个PropertyOverrideConfigurer
实例为同一个bean属性定义不同的值的情况下,最后一个实例配置会生效。
Properties文件配置格式如下:
beanName.property=value
例如:
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb
此示例文件可用于包含名为 dataSource 的bean的容器定义,该bean具有 driver 和 url 属性。
还支持复合属性名称,只要路径(除了覆盖的最终属性)的每个组件都已为非空
(可能由构造函数初始化)。 在此示例中...
foo.fred.bob.sammy=123
- foo bean的
fred
属性的bob
属性的sammy
属性设置为标量值123
。
指定的重写值都是字面值; 它们不会转换为bean引用。 当XML bean定义中的原始值指定一个bean引用时,此约定也适用。 |
使用Spring 2.5中引入的context
命名空间,可以使用专用配置元素配置属性覆盖:
<context:property-override location="classpath:override.properties"/>