容器扩展点
通常,应用程序开发人员不需要子类化ApplicationContextimplementation 类。相反,Spring IoC 容器可以通过插入来扩展
特殊集成接口的实现。接下来的几节将介绍这些
集成接口。
使用BeanPostProcessor
这BeanPostProcessorinterface 定义您可以实现的回调方法
提供您自己的(或覆盖容器的默认)实例化逻辑、依赖项
resolution logic,依此类推。如果你想在
Spring 容器完成实例化、配置和初始化 bean 后,您可以
插入一个或多个自定义BeanPostProcessor实现。
您可以配置多个BeanPostProcessor实例,您可以控制 Order
其中这些BeanPostProcessor实例运行,方法是将order财产。
只有在BeanPostProcessor实现Ordered接口。如果您编写自己的BeanPostProcessor,您应该考虑实现
这Ordered界面也是如此。有关更多详细信息,请参阅BeanPostProcessor和Ordered接口。另请参见程序化注册BeanPostProcessor实例.
|
要更改实际的 Bean 定义(即定义 Bean 的 Blueprint),请执行以下作:
您需要改用 |
这org.springframework.beans.factory.config.BeanPostProcessor接口包括
恰好有两个回调方法。当此类注册为后处理器时,使用
容器中,对于容器创建的每个 bean 实例,
后处理器在容器之前从容器获取回调
初始化方法(如InitializingBean.afterPropertiesSet()或任何
宣布init方法)调用,并在任何 bean 初始化回调之后调用。
后处理器可以对 bean 实例执行任何作,包括忽略
callback 的 Quin 函数。bean 后处理器通常会检查回调接口,
或者它可能用代理包装 bean。一些 Spring AOP 基础设施类是
作为 Bean 后处理器实现,以提供代理包装逻辑。
一ApplicationContext自动检测在
配置元数据实现BeanPostProcessor接口。这ApplicationContext将这些 bean 注册为后处理器,以便可以调用它们
稍后,在 bean 创建时。Bean 后处理器可以部署在容器中的
与任何其他Beans一样时尚。
请注意,在声明BeanPostProcessor通过使用@Beanfactory 方法在
configuration 类,则工厂方法的返回类型应为 implementation
类本身或至少org.springframework.beans.factory.config.BeanPostProcessor接口,清楚地指示该 Bean 的后处理器性质。否则,ApplicationContext在完全创建之前无法按类型自动检测它。
由于BeanPostProcessor需要提前实例化,以便应用于
初始化上下文中的其他 bean,这种早期类型检测至关重要。
|
以编程方式注册 虽然BeanPostProcessor实例BeanPostProcessor注册通过ApplicationContextauto-detection (如前所述),您可以注册它们
以编程方式针对ConfigurableBeanFactory通过使用addBeanPostProcessor方法。当您需要在
注册,甚至用于跨层次结构中的上下文复制 Bean 后处理器。
但请注意,BeanPostProcessor以编程方式添加的实例不遵循
这Ordered接口。在这里,是注册顺序决定了顺序
的执行。另请注意,BeanPostProcessor以编程方式注册的实例
始终在通过自动检测注册的 URL 之前处理,无论
显式排序。 |
BeanPostProcessor实例和 AOP 自动代理实现 对于任何此类 bean,您应该会看到一条信息性日志消息: 如果你有 bean 连接到你的 |
以下示例演示如何编写、注册和使用BeanPostProcessor实例
在ApplicationContext.
示例:Hello World、BeanPostProcessor-风格
第一个示例说明了基本用法。该示例显示了自定义BeanPostProcessor调用toString()方法作为
它由容器创建,并将生成的字符串打印到系统控制台。
以下清单显示了自定义BeanPostProcessorimplementation 类定义:
-
Java
-
Kotlin
package scripting;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
// simply return the instantiated bean as-is
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean; // we could potentially return any object reference here...
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
return bean;
}
}
package scripting
import org.springframework.beans.factory.config.BeanPostProcessor
class InstantiationTracingBeanPostProcessor : BeanPostProcessor {
// simply return the instantiated bean as-is
override fun postProcessBeforeInitialization(bean: Any, beanName: String): Any? {
return bean // we could potentially return any object reference here...
}
override fun postProcessAfterInitialization(bean: Any, beanName: String): Any? {
println("Bean '$beanName' created : $bean")
return bean
}
}
以下内容beans元素使用InstantiationTracingBeanPostProcessor:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/lang
https://www.springframework.org/schema/lang/spring-lang.xsd">
<lang:groovy id="messenger"
script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
<lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>
</lang:groovy>
<!--
when the above bean (messenger) is instantiated, this custom
BeanPostProcessor implementation will output the fact to the system console
-->
<bean class="scripting.InstantiationTracingBeanPostProcessor"/>
</beans>
请注意InstantiationTracingBeanPostProcessor只是定义。它没有
甚至有一个名称,并且,因为它是一个 bean,所以它可以像任何 bean 一样被依赖注入
其他豆子。(前面的配置还定义了一个由 Groovy 支持的 bean
脚本。Spring 动态语言支持在标题为“动态语言支持”的章节中进行了详细介绍。
以下 Java 应用程序运行上述代码和配置:
-
Java
-
Kotlin
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;
public final class Boot {
public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");
Messenger messenger = ctx.getBean("messenger", Messenger.class);
System.out.println(messenger);
}
}
import org.springframework.beans.factory.getBean
fun main() {
val ctx = ClassPathXmlApplicationContext("scripting/beans.xml")
val messenger = ctx.getBean<Messenger>("messenger")
println(messenger)
}
上述应用程序的输出类似于以下内容:
Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961 org.springframework.scripting.groovy.GroovyMessenger@272961
使用BeanFactoryPostProcessor
我们要看的下一个扩展点是org.springframework.beans.factory.config.BeanFactoryPostProcessor.的语义
此接口类似于BeanPostProcessor,具有一个 Major
差异:BeanFactoryPostProcessor对 Bean 配置元数据进行作。
也就是说,Spring IoC 容器允许BeanFactoryPostProcessor阅读
配置元数据,并可能在容器实例化之前对其进行更改
除BeanFactoryPostProcessor实例。
您可以配置多个BeanFactoryPostProcessor实例,您可以在
这些BeanFactoryPostProcessor实例运行,方法是将order财产。
但是,只有在BeanFactoryPostProcessor实现Ordered接口。如果您编写自己的BeanFactoryPostProcessor,您应该
考虑实施Ordered界面也是如此。请参阅BeanFactoryPostProcessor和Orderedinterfaces 了解更多详情。
|
如果要更改实际的 Bean 实例(即创建的对象
),那么您需要改用 也 |
当 bean 工厂后处理器在ApplicationContext,以便将更改应用于
定义容器。Spring 包含许多预定义的 bean factory
后处理器,例如PropertyOverrideConfigurer和PropertySourcesPlaceholderConfigurer.您还可以使用自定义BeanFactoryPostProcessor— 例如,注册自定义属性编辑器。
一ApplicationContext自动检测部署到其中的任何 bean
实现BeanFactoryPostProcessor接口。它将这些 bean 用作 bean factory
后处理器。您可以将这些后处理器 bean 部署为
你会喜欢任何其他豆子。
与 一样BeanPostProcessors ,您通常不想配置BeanFactoryPostProcessors 进行延迟初始化。如果没有其他 bean 引用Bean(Factory)PostProcessor,该后处理器根本不会被实例化。
因此,将其标记为延迟初始化将被忽略,并且Bean(Factory)PostProcessor将预先实例化,即使您将default-lazy-init属性设置为true在您的<beans />元素。 |
示例:属性占位符替换为PropertySourcesPlaceholderConfigurer
您可以使用PropertySourcesPlaceholderConfigurer外部化属性值
使用标准 Java 从单独文件中的 bean 定义Properties格式。
这样做使部署应用程序的人员能够自定义特定于环境的
属性(例如数据库 URL 和密码),而不会产生
修改容器的一个或多个主 XML 定义文件。
请考虑以下基于 XML 的配置元数据片段,其中DataSourcewith placeholder values 定义:
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations" value="classpath:com/something/jdbc.properties"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<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>
该示例显示了从外部Properties文件。在运行时,
一个PropertySourcesPlaceholderConfigurer应用于元数据,该元数据将替换某些
的属性DataSource.要替换的值指定为
形式${property-name},它遵循 Ant、log4j 和 JSP EL 样式。
实际值来自标准 Java 中的另一个文件Properties格式:
jdbc.driverClassName=org.hsqldb.jdbcDriver jdbc.url=jdbc:hsqldb:hsql://production:9002 jdbc.username=sa jdbc.password=root
因此,${jdbc.username}string 在运行时替换为值 'sa' 和
这同样适用于与 Properties 文件中的键匹配的其他占位符值。这PropertySourcesPlaceholderConfigurer检查大多数属性中的占位符,以及
bean 定义的属性。此外,您还可以自定义占位符前缀
suffix、default value separator 和 escape 字符。此外,默认的转义
字符可以通过设置spring.placeholder.escapeCharacter.default属性(或通过 JVM 系统属性
这SpringProperties机制)。
使用contextnamespace 中,您可以配置属性占位符
替换为专用的配置元素。您可以将一个或多个位置作为
逗号分隔的列表location属性,如下例所示:
<context:property-placeholder location="classpath:com/something/jdbc.properties"/>
这PropertySourcesPlaceholderConfigurer不仅在Properties文件。默认情况下,如果在指定的属性文件中找不到属性,则
它检查 SpringEnvironmentproperties 和常规 JavaSystem性能。
|
对于具有
它需要。可以配置多个属性占位符,只要它们具有不同的
placeholder 语法 ( 如果需要模块化用于替换的属性源,则应
不创建多个属性占位符。相反,您应该创建自己的 |
|
您可以使用
如果该类在运行时无法解析为有效类,则 Bean 的解析
在即将创建时失败,即在 |
示例:PropertyOverrideConfigurer
这PropertyOverrideConfigurer,另一个 Bean Factory 后处理器,类似于PropertySourcesPlaceholderConfigurer,但与后者不同的是,原始定义
可以为 Bean 属性设置默认值或根本没有值。如果 overoverridePropertiesfile 没有某个 Bean 属性的条目,则默认的
上下文定义。
请注意,bean 定义不知道被覆盖,因此它不是
从 XML 定义文件中可以立即明显看出 override configurer 正在
使用。如果有多个PropertyOverrideConfigurer定义不同
值,由于覆盖机制,最后一个 Bean 属性优先。
Properties 文件配置行采用以下格式:
beanName.property=value
下面的清单显示了格式的示例:
dataSource.driverClassName=com.mysql.jdbc.Driver dataSource.url=jdbc:mysql:mydb
此示例文件可以与容器定义一起使用,该容器定义包含名为dataSource该driverClassName和url性能。
还支持复合属性名称,只要路径的每个组件
除了被覆盖的最终属性已经是非 null 的(大概是初始化的
由构造函数)。在以下示例中,sammy属性的bob属性的fred属性的tombean 设置为 Scalar 值123:
tom.fred.bob.sammy=123
| 指定的覆盖值始终是文本值。它们不会被翻译成 bean 引用。当 XML Bean 中的原始值 definition 指定 bean 引用。 |
使用contextnamespace 中引入的,则可以配置
属性替换为专用的 configuration 元素,如下例所示:
<context:property-override location="classpath:override.properties"/>
使用FactoryBean
您可以实现org.springframework.beans.factory.FactoryBean对象的接口
本身就是工厂。
这FactoryBeaninterface 是 Spring IoC 容器的
实例化逻辑。如果你有复杂的初始化代码,最好用
与 Java 相比,您可以创建自己的 XMLFactoryBean,在该类中编写复杂的初始化,然后将
习惯FactoryBean放入容器中。
这FactoryBean<T>interface 提供了三种方法:
-
T getObject():返回此工厂创建的对象的实例。这 实例可以共享,具体取决于此工厂是否返回单例 或原型。 -
boolean isSingleton():返回true如果此FactoryBean返回单例或false否则。此方法的默认实现返回true. -
Class<?> getObjectType():返回getObject()方法 或null如果事先不知道类型。
这FactoryBeanconcept 和 interface 在 Spring 中的许多地方使用
框架。超过 50 种FactoryBean与 Spring 的接口
本身。
当您需要向容器询问实际FactoryBean实例本身而不是
它生成的 bean 会作为 bean 的id当
调用&getBean()方法ApplicationContext.因此,对于给定的FactoryBean替换为id之myBean调用getBean("myBean")在容器上返回
的产品FactoryBean,而调用getBean("&myBean")返回FactoryBean实例本身。