通常,应用程序开发人员不需要对实现类进行子类化。相反,Spring IoC 容器可以通过插入来扩展 特殊集成接口的实现。接下来的几节将介绍这些 集成接口。ApplicationContextspring-doc.cn

使用BeanPostProcessor

该接口定义了您可以实现的回调方法 提供您自己的(或覆盖容器的默认)实例化逻辑、依赖项 resolution logic,依此类推。如果你想在 Spring 容器完成实例化、配置和初始化 bean 后,您可以 插入一个或多个自定义实现。BeanPostProcessorBeanPostProcessorspring-doc.cn

您可以配置多个实例,并且可以控制顺序 其中这些实例通过设置属性 仅当 实现接口时,才能设置此属性。如果您编写自己的 ,您应该考虑实现 界面也是如此。有关更多详细信息,请参阅 BeanPostProcessorOrdered 接口的 javadoc。另请参见有关以编程方式注册 BeanPostProcessor 实例的注释。BeanPostProcessorBeanPostProcessororderBeanPostProcessorOrderedBeanPostProcessorOrderedspring-doc.cn

BeanPostProcessor实例在 Bean(或对象)实例上运行。那是 Spring IoC 容器实例化一个 bean 实例,然后实例执行它们的工作。BeanPostProcessorspring-doc.cn

BeanPostProcessor实例的范围是按容器划分的。这仅在您 使用容器层次结构。如果您在一个容器中定义 它仅对该容器中的 bean 进行后处理。换句话说,是 定义的 API 不会由 另一个容器,即使两个容器都属于同一层次结构。BeanPostProcessorBeanPostProcessorspring-doc.cn

要更改实际的 Bean 定义(即定义 Bean 的 Blueprint),请执行以下操作: 相反,你需要使用一个 ,如使用BeanFactoryPostProcessor自定义配置元数据中所述。BeanFactoryPostProcessorspring-doc.cn

该接口包括 恰好是两个回调方法。当此类注册为后处理器时,使用 容器中,对于容器创建的每个 bean 实例, 后处理器在容器之前从容器获取回调 初始化方法(例如 OR declared method),并在任何 bean 初始化回调之后调用。 后处理器可以对 bean 实例执行任何操作,包括忽略 callback 的 Quin 函数。bean 后处理器通常会检查回调接口, 或者它可能用代理包装 bean。一些 Spring AOP 基础设施类是 作为 Bean 后处理器实现,以提供代理包装逻辑。org.springframework.beans.factory.config.BeanPostProcessorInitializingBean.afterPropertiesSet()initspring-doc.cn

An 会自动检测在 实现接口的配置元数据。将这些 bean 注册为后处理器,以便可以调用它们 稍后,在 bean 创建时。Bean 后处理器可以部署在容器中的 与任何其他Beans一样时尚。ApplicationContextBeanPostProcessorApplicationContextspring-doc.cn

请注意,当在 configuration 类,则工厂方法的返回类型应为 implementation 类本身或至少是接口,清楚地表明该 bean 的后处理器性质。否则,在完全创建它之前,无法按类型自动检测它。 由于 a 需要提前实例化才能应用于 初始化上下文中的其他 bean,这种早期类型检测至关重要。BeanPostProcessor@Beanorg.springframework.beans.factory.config.BeanPostProcessorApplicationContextBeanPostProcessorspring-doc.cn

以编程方式注册实例BeanPostProcessor
虽然推荐的注册方法是通过自动检测(如前所述),但您可以注册它们 使用 该方法以编程方式对 a 进行攻击。当您需要在 注册,甚至用于跨层次结构中的上下文复制 Bean 后处理器。 但请注意,以编程方式添加的实例不遵循 界面。在这里,是注册顺序决定了顺序 的执行。另请注意,实例以编程方式注册 始终在通过自动检测注册的 URL 之前处理,无论 显式排序。BeanPostProcessorApplicationContextConfigurableBeanFactoryaddBeanPostProcessorBeanPostProcessorOrderedBeanPostProcessor
BeanPostProcessor实例和 AOP 自动代理

实现该接口的类是特殊的,并被视为 容器不同。它们 直接引用在启动时实例化,作为特殊启动阶段的一部分 的 .接下来,注册所有实例 以排序的方式,并应用于容器中的所有其他 bean。因为 AOP 自动代理是作为自身实现的,实例和它们直接引用的 bean 都没有资格进行自动代理,并且, 因此,不要将 aspects 编织到其中。BeanPostProcessorBeanPostProcessorApplicationContextBeanPostProcessorBeanPostProcessorBeanPostProcessorspring-doc.cn

对于任何此类 Bean,您应该会看到一条信息性日志消息:。Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying)spring-doc.cn

如果你通过使用自动装配或(可能会回退到自动装配)将 bean 连接到你的 bean 中,Spring 可能会访问意外的 bean 在搜索类型匹配的依赖项候选项时,因此,请将其 不符合自动代理或其他类型的 bean 后处理的条件。例如,如果你 具有 Comments 的依赖项,其中字段或 setter 名称没有 直接对应 bean 的声明名称,不使用 name 属性, Spring 访问其他 bean 以按类型匹配它们。BeanPostProcessor@Resource@Resourcespring-doc.cn

以下示例演示如何编写、注册和使用实例 在 .BeanPostProcessorApplicationContextspring-doc.cn

示例:Hello World、-styleBeanPostProcessor

第一个示例说明了基本用法。该示例显示了一个自定义实现,该实现将每个 bean 的方法作为 它由容器创建,并将生成的字符串打印到系统控制台。BeanPostProcessortoString()spring-doc.cn

下面的清单显示了自定义实现类定义:BeanPostProcessorspring-doc.cn

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
	}
}

以下元素使用 :beansInstantiationTracingBeanPostProcessorspring-doc.cn

<?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>

请注意 the 是如何被定义的。它没有 甚至有一个名称,并且,因为它是一个 bean,所以它可以像任何 bean 一样被依赖注入 其他豆子。(前面的配置还定义了一个由 Groovy 支持的 bean 脚本。Spring 动态语言支持在标题为“动态语言支持”的章节中进行了详细介绍。InstantiationTracingBeanPostProcessorspring-doc.cn

以下 Java 应用程序运行上述代码和配置:spring-doc.cn

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)
}

上述应用程序的输出类似于以下内容:spring-doc.cn

Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961

示例:AutowiredAnnotationBeanPostProcessor

将回调接口或 Comments 与自定义实现结合使用是扩展 Spring IoC 容器的常用方法。一个例子是 Spring 的 — 一个实现 它附带了 Spring 发行版和自动装配的注释字段、setter 方法、 和任意配置方法。BeanPostProcessorAutowiredAnnotationBeanPostProcessorBeanPostProcessorspring-doc.cn

BeanPostProcessor实例在 Bean(或对象)实例上运行。那是 Spring IoC 容器实例化一个 bean 实例,然后实例执行它们的工作。BeanPostProcessorspring-doc.cn

BeanPostProcessor实例的范围是按容器划分的。这仅在您 使用容器层次结构。如果您在一个容器中定义 它仅对该容器中的 bean 进行后处理。换句话说,是 定义的 API 不会由 另一个容器,即使两个容器都属于同一层次结构。BeanPostProcessorBeanPostProcessorspring-doc.cn

要更改实际的 Bean 定义(即定义 Bean 的 Blueprint),请执行以下操作: 相反,你需要使用一个 ,如使用BeanFactoryPostProcessor自定义配置元数据中所述。BeanFactoryPostProcessorspring-doc.cn

以编程方式注册实例BeanPostProcessor
虽然推荐的注册方法是通过自动检测(如前所述),但您可以注册它们 使用 该方法以编程方式对 a 进行攻击。当您需要在 注册,甚至用于跨层次结构中的上下文复制 Bean 后处理器。 但请注意,以编程方式添加的实例不遵循 界面。在这里,是注册顺序决定了顺序 的执行。另请注意,实例以编程方式注册 始终在通过自动检测注册的 URL 之前处理,无论 显式排序。BeanPostProcessorApplicationContextConfigurableBeanFactoryaddBeanPostProcessorBeanPostProcessorOrderedBeanPostProcessor
BeanPostProcessor实例和 AOP 自动代理

实现该接口的类是特殊的,并被视为 容器不同。它们 直接引用在启动时实例化,作为特殊启动阶段的一部分 的 .接下来,注册所有实例 以排序的方式,并应用于容器中的所有其他 bean。因为 AOP 自动代理是作为自身实现的,实例和它们直接引用的 bean 都没有资格进行自动代理,并且, 因此,不要将 aspects 编织到其中。BeanPostProcessorBeanPostProcessorApplicationContextBeanPostProcessorBeanPostProcessorBeanPostProcessorspring-doc.cn

对于任何此类 Bean,您应该会看到一条信息性日志消息:。Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying)spring-doc.cn

如果你通过使用自动装配或(可能会回退到自动装配)将 bean 连接到你的 bean 中,Spring 可能会访问意外的 bean 在搜索类型匹配的依赖项候选项时,因此,请将其 不符合自动代理或其他类型的 bean 后处理的条件。例如,如果你 具有 Comments 的依赖项,其中字段或 setter 名称没有 直接对应 bean 的声明名称,不使用 name 属性, Spring 访问其他 bean 以按类型匹配它们。BeanPostProcessor@Resource@Resourcespring-doc.cn

使用BeanFactoryPostProcessor

我们要看的下一个扩展点是 .的语义 此接口类似于 的 ,具有一个 Major 差异:对 Bean 配置元数据进行操作。 也就是说,Spring IoC 容器允许读取 配置元数据,并可能在容器实例化之前对其进行更改 除实例以外的任何 bean。org.springframework.beans.factory.config.BeanFactoryPostProcessorBeanPostProcessorBeanFactoryPostProcessorBeanFactoryPostProcessorBeanFactoryPostProcessorspring-doc.cn

您可以配置多个实例,并且可以控制 这些实例通过设置属性来运行。 但是,只有在 实现接口时,才能设置此属性。如果你自己写 ,你应该 也请考虑实现接口。有关更多详细信息,请参见BeanFactoryPostProcessorOrdered接口的javadoc。BeanFactoryPostProcessorBeanFactoryPostProcessororderBeanFactoryPostProcessorOrderedBeanFactoryPostProcessorOrderedspring-doc.cn

如果要更改实际的 Bean 实例(即创建的对象 ),那么你需要改用一个(在前面的使用 BeanPostProcessor 定制 Bean 中描述)。 虽然从技术上讲,可以在 a 中使用 bean 实例(例如,通过使用),但这样做会导致 bean 过早实例化, 违反标准容器生命周期。这可能会导致负面的副作用,例如 绕过 bean post 处理。BeanPostProcessorBeanFactoryPostProcessorBeanFactory.getBean()spring-doc.cn

此外,实例的范围是按容器划分的。这仅相关 如果您使用容器层次结构。如果您定义 a 合一 container,则它仅适用于该容器中的 bean 定义。Bean 定义 不被另一个容器中的实例进行后处理 容器,即使两个容器属于同一层次结构。BeanFactoryPostProcessorBeanFactoryPostProcessorBeanFactoryPostProcessorspring-doc.cn

当 bean 工厂后处理器在 ,当它在 中声明时,它会自动运行,以便将更改应用于配置元数据 定义容器。Spring 包含许多预定义的 bean factory 后处理器,例如 和 .您还可以使用自定义 — 例如,注册自定义属性编辑器。ApplicationContextPropertyOverrideConfigurerPropertySourcesPlaceholderConfigurerBeanFactoryPostProcessorspring-doc.cn

An 会自动检测部署到其中的任何 bean 实现接口。它将这些 bean 用作 bean factory 后处理器。您可以将这些后处理器 bean 部署为 你会喜欢任何其他豆子。ApplicationContextBeanFactoryPostProcessorspring-doc.cn

与 s 一样,您通常不希望为 s 延迟初始化配置 s。如果没有其他 bean 引用 a ,则该后处理器将根本不被实例化。 因此,将其标记为延迟初始化将被忽略,并且即使您在元素的声明上将属性设置为 ,也会急切地实例化 。BeanPostProcessorBeanFactoryPostProcessorBean(Factory)PostProcessorBean(Factory)PostProcessordefault-lazy-inittrue<beans />

示例:类名替换PropertySourcesPlaceholderConfigurer

您可以使用 to externalize 属性值 使用标准 Java 格式从单独文件中的 bean 定义。 这样做使部署应用程序的人员能够自定义特定于环境的 属性(例如数据库 URL 和密码),而不会产生 修改容器的一个或多个主 XML 定义文件。PropertySourcesPlaceholderConfigurerPropertiesspring-doc.cn

请考虑以下基于 XML 的配置元数据片段,其中定义了 a with placeholder 值:DataSourcespring-doc.cn

<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>

该示例显示了从外部文件配置的属性。在运行时, a 应用于元数据,该元数据将替换某些 属性。要替换的值指定为 form ,它遵循 Ant 和 log4j 以及 JSP EL 样式。PropertiesPropertySourcesPlaceholderConfigurer${property-name}spring-doc.cn

实际值来自标准 Java 格式的另一个文件:Propertiesspring-doc.cn

jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

因此,字符串在运行时被替换为值 'sa' 和 这同样适用于与 Properties 文件中的键匹配的其他占位符值。 检查大多数属性中的占位符,以及 bean 定义的属性。此外,您还可以自定义占位符前缀和后缀。${jdbc.username}PropertySourcesPlaceholderConfigurerspring-doc.cn

使用 Spring 2.5 中引入的名称空间,您可以配置属性占位符 替换为专用的配置元素。您可以将一个或多个位置作为 逗号分隔的列表,如下例所示:contextlocationspring-doc.cn

<context:property-placeholder location="classpath:com/something/jdbc.properties"/>

不仅在指定的文件中查找属性。默认情况下,如果在指定的属性文件中找不到属性,则 它检查 Spring 属性和常规 Java 属性。PropertySourcesPlaceholderConfigurerPropertiesEnvironmentSystemspring-doc.cn

对于具有 它需要。可以配置多个属性占位符,只要它们具有不同的 占位符语法 ()。${…​}spring-doc.cn

如果需要模块化用于替换的属性源,则应 不创建多个属性占位符。相反,您应该创建自己的 bean 来收集要使用的属性。PropertySourcesPlaceholderConfigurerspring-doc.cn

您可以使用 to 替换类名,该类名 当您必须在运行时选择特定的实现类时,有时很有用。 以下示例显示了如何执行此操作:PropertySourcesPlaceholderConfigurerspring-doc.cn

<bean class="org.springframework.beans.factory.config.PropertySourcesPlaceholderConfigurer">
	<property name="locations">
		<value>classpath:com/something/strategy.properties</value>
	</property>
	<property name="properties">
		<value>custom.strategy.class=com.something.DefaultStrategy</value>
	</property>
</bean>

<bean id="serviceStrategy" class="${custom.strategy.class}"/>

如果该类在运行时无法解析为有效类,则 Bean 的解析 在即将创建时失败,这是在非惰性初始化 bean 的阶段。preInstantiateSingletons()ApplicationContextspring-doc.cn

示例:PropertyOverrideConfigurer

的 ,另一个 bean factory 后处理器类似于 ,但与后者不同的是,原始定义 可以为 Bean 属性设置默认值或根本没有值。如果覆盖文件没有某个 Bean 属性的条目,则默认的 上下文定义。PropertyOverrideConfigurerPropertySourcesPlaceholderConfigurerPropertiesspring-doc.cn

请注意,bean 定义不知道被覆盖,因此它不是 从 XML 定义文件中可以立即明显看出 override configurer 正在 使用。如果多个实例定义了不同的 值,由于覆盖机制,最后一个 Bean 属性优先。PropertyOverrideConfigurerspring-doc.cn

Properties 文件配置行采用以下格式:spring-doc.cn

beanName.property=value

下面的清单显示了格式的示例:spring-doc.cn

dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb

此示例文件可以与容器定义一起使用,该容器定义包含名为 has 和 properties 的 bean。dataSourcedriverClassNameurlspring-doc.cn

还支持复合属性名称,只要路径的每个组件 除了被覆盖的最终属性已经是非 null 的(大概是初始化的 由构造函数)。在以下示例中,bean 的 property 的 property 的 property 设置为 scalar 值:sammybobfredtom123spring-doc.cn

tom.fred.bob.sammy=123
指定的覆盖值始终是文本值。它们不会被翻译成 bean 引用。当 XML Bean 中的原始值 definition 指定 bean 引用。

使用 Spring 2.5 中引入的名称空间,可以配置 属性替换为专用的 configuration 元素,如下例所示:contextspring-doc.cn

<context:property-override location="classpath:override.properties"/>

如果要更改实际的 Bean 实例(即创建的对象 ),那么你需要改用一个(在前面的使用 BeanPostProcessor 定制 Bean 中描述)。 虽然从技术上讲,可以在 a 中使用 bean 实例(例如,通过使用),但这样做会导致 bean 过早实例化, 违反标准容器生命周期。这可能会导致负面的副作用,例如 绕过 bean post 处理。BeanPostProcessorBeanFactoryPostProcessorBeanFactory.getBean()spring-doc.cn

此外,实例的范围是按容器划分的。这仅相关 如果您使用容器层次结构。如果您定义 a 合一 container,则它仅适用于该容器中的 bean 定义。Bean 定义 不被另一个容器中的实例进行后处理 容器,即使两个容器属于同一层次结构。BeanFactoryPostProcessorBeanFactoryPostProcessorBeanFactoryPostProcessorspring-doc.cn

与 s 一样,您通常不希望为 s 延迟初始化配置 s。如果没有其他 bean 引用 a ,则该后处理器将根本不被实例化。 因此,将其标记为延迟初始化将被忽略,并且即使您在元素的声明上将属性设置为 ,也会急切地实例化 。BeanPostProcessorBeanFactoryPostProcessorBean(Factory)PostProcessorBean(Factory)PostProcessordefault-lazy-inittrue<beans />

对于具有 它需要。可以配置多个属性占位符,只要它们具有不同的 占位符语法 ()。${…​}spring-doc.cn

如果需要模块化用于替换的属性源,则应 不创建多个属性占位符。相反,您应该创建自己的 bean 来收集要使用的属性。PropertySourcesPlaceholderConfigurerspring-doc.cn

您可以使用 to 替换类名,该类名 当您必须在运行时选择特定的实现类时,有时很有用。 以下示例显示了如何执行此操作:PropertySourcesPlaceholderConfigurerspring-doc.cn

<bean class="org.springframework.beans.factory.config.PropertySourcesPlaceholderConfigurer">
	<property name="locations">
		<value>classpath:com/something/strategy.properties</value>
	</property>
	<property name="properties">
		<value>custom.strategy.class=com.something.DefaultStrategy</value>
	</property>
</bean>

<bean id="serviceStrategy" class="${custom.strategy.class}"/>

如果该类在运行时无法解析为有效类,则 Bean 的解析 在即将创建时失败,这是在非惰性初始化 bean 的阶段。preInstantiateSingletons()ApplicationContextspring-doc.cn

指定的覆盖值始终是文本值。它们不会被翻译成 bean 引用。当 XML Bean 中的原始值 definition 指定 bean 引用。

使用FactoryBean

您可以为 本身就是工厂。org.springframework.beans.factory.FactoryBeanspring-doc.cn

该接口是 Spring IoC 容器的 实例化逻辑。如果你有复杂的初始化代码,最好用 Java 而不是(可能)冗长的 XML,您可以创建自己的 ,在该类中编写复杂的初始化,然后将 custom 添加到容器中。FactoryBeanFactoryBeanFactoryBeanspring-doc.cn

该接口提供了三种方法:FactoryBean<T>spring-doc.cn

  • T getObject():返回此工厂创建的对象的实例。这 实例可以共享,具体取决于此工厂是否返回单例 或原型。spring-doc.cn

  • boolean isSingleton(): 如果返回单例或其他,则返回。此方法的默认实现返回 。trueFactoryBeanfalsetruespring-doc.cn

  • Class<?> getObjectType():返回方法返回的对象类型 或者如果事先不知道类型。getObject()nullspring-doc.cn

概念和接口在 Spring 中的许多地方使用 框架。Spring 附带了 50 多个接口实现 本身。FactoryBeanFactoryBeanspring-doc.cn

当您需要向容器请求实际实例本身而不是 它生成的 bean 在 bean 前面加上 & 符号 () 时 调用 .因此,对于具有 of 的给定,在容器上调用将返回 product 的 ,而调用则返回实例本身。FactoryBeanid&getBean()ApplicationContextFactoryBeanidmyBeangetBean("myBean")FactoryBeangetBean("&myBean")FactoryBeanspring-doc.cn