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

使用BeanPostProcessor

该接口定义了可以实现的回调方法 提供你自己的(或覆盖容器的默认)实例化逻辑、依赖关系 解析逻辑等。如果要在 Spring 容器完成 Bean 的实例化、配置和初始化,可以 插入一个或多个自定义实现。BeanPostProcessorBeanPostProcessor

您可以配置多个实例,并且可以控制顺序 通过设置属性来运行这些实例。 仅当实现接口时,才能设置此属性。如果你自己编写,你应该考虑实现 界面也是如此。有关更多详细信息,请参阅 BeanPostProcessorOrdered 接口的 javadoc。另请参阅有关 BeanPostProcessor 实例的编程注册的说明。BeanPostProcessorBeanPostProcessororderBeanPostProcessorOrderedBeanPostProcessorOrdered

BeanPostProcessor实例在 Bean(或对象)实例上运行。那是 Spring IoC 容器实例化一个 bean 实例,然后实例执行其工作。BeanPostProcessor

BeanPostProcessor实例的作用域是按容器划分的。这仅在以下情况下才相关 使用容器层次结构。如果在一个容器中定义一个, 它仅对该容器中的 Bean 进行后处理。换句话说,豆子是 在一个容器中定义的容器不由定义的容器进行后处理 另一个容器,即使两个容器都属于同一层次结构。BeanPostProcessorBeanPostProcessor

要更改实际的 Bean 定义(即定义 Bean 的蓝图), 您需要改用 ,如使用 BeanFactoryPostProcessor 定制配置元数据中所述。BeanFactoryPostProcessor

该接口包括 正好是两个回调方法。当这样的类被注册为后处理器时 容器,对于容器创建的每个 Bean 实例, 后处理器在容器之前从容器获取回调 初始化方法(如 或任何 声明的方法)被调用,并在任何 Bean 初始化回调之后。 后处理器可以对 Bean 实例执行任何操作,包括忽略 完全回调。Bean 后处理器通常检查回调接口, 或者它可能用代理包装 bean。一些 Spring AOP 基础结构类是 作为 Bean 后处理器实现,以提供代理包装逻辑。org.springframework.beans.factory.config.BeanPostProcessorInitializingBean.afterPropertiesSet()init

一个自动检测 实现接口的配置元数据。将这些 Bean 注册为后处理器,以便可以调用它们 后来,在豆子创造时。Bean 后处理器可以部署在容器中的 与任何其他豆子相同的时尚。ApplicationContextBeanPostProcessorApplicationContext

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

以编程方式注册实例BeanPostProcessor
虽然推荐的注册方法是通过自动检测(如前所述),但您可以注册它们 使用该方法以编程方式针对 A。当您需要在之前评估条件逻辑时,这可能很有用 注册,甚至用于在层次结构中跨上下文复制 Bean 后处理器。 但请注意,以编程方式添加的实例不遵守 接口。在这里,注册顺序决定了顺序 的执行。另请注意,以编程方式注册的实例 始终在通过自动检测注册之前进行处理,无论是否有任何 显式排序。BeanPostProcessorApplicationContextConfigurableBeanFactoryaddBeanPostProcessorBeanPostProcessorOrderedBeanPostProcessor
BeanPostProcessor实例和 AOP 自动代理

实现接口的类是特殊的,并经过处理 因容器而异。所有实例和 bean 他们 直接引用在启动时实例化,作为特殊启动阶段的一部分 的 .接下来,注册所有实例 以分类方式应用于容器中的所有其他豆子。因为AOP 自动代理是作为自身实现的,实例和它们直接引用的 Bean 都不符合自动代理的条件,并且, 因此,不要将方面编织到其中。BeanPostProcessorBeanPostProcessorApplicationContextBeanPostProcessorBeanPostProcessorBeanPostProcessor

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

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

以下示例说明如何编写、注册和使用实例 在 .BeanPostProcessorApplicationContext

示例:Hello World、-styleBeanPostProcessor

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

下面的清单显示了自定义实现类定义:BeanPostProcessor

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

以下元素使用 :beansInstantiationTracingBeanPostProcessor

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

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

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

示例:AutowiredAnnotationBeanPostProcessor

将回调接口或注释与自定义实现结合使用是扩展 Spring IoC 容器的常用方法。一个例子是 春天的 — 实现 随 Spring 发行版和 autowires 带注释的字段、setter 方法、 和任意配置方法。BeanPostProcessorAutowiredAnnotationBeanPostProcessorBeanPostProcessor

使用BeanFactoryPostProcessor

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

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

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

此外,实例的范围是按容器划分的。这是唯一的相关 如果使用容器层次结构。如果将 container,它仅应用于该容器中的 Bean 定义。Bean 定义 在一个容器中不会被另一个容器中的实例进行后处理 容器,即使两个容器都属于同一层次结构。BeanFactoryPostProcessorBeanFactoryPostProcessorBeanFactoryPostProcessor

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

一个自动检测部署到其中的任何 Bean 实现接口。它使用这些豆子作为豆子工厂 后处理器,在适当的时间。您可以将这些后处理器 Bean 部署为 你会任何其他豆子。ApplicationContextBeanFactoryPostProcessor

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

示例:类名替换PropertySourcesPlaceholderConfigurer

可以使用 将属性值外部化 使用标准 Java 格式从单独文件中的 Bean 定义。 这样做使部署应用程序的人员能够自定义特定于环境的 属性,例如数据库 URL 和密码,而没有复杂性或风险 修改容器的一个或多个主 XML 定义文件。PropertySourcesPlaceholderConfigurerProperties

请考虑以下基于 XML 的配置元数据片段,其中定义了具有占位符值的片段:DataSource

<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 应用于替换某些 DataSource 的属性。要替换的值被指定为 form ,它遵循 Ant 和 log4j 以及 JSP EL 样式。PropertiesPropertySourcesPlaceholderConfigurer${property-name}

实际值来自标准 Java 格式的另一个文件:Properties

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

因此,字符串在运行时将替换为值“sa”和 这同样适用于与属性文件中的键匹配的其他占位符值。 对大多数属性中的占位符的检查,以及 Bean 定义的属性。此外,您可以自定义占位符前缀和后缀。${jdbc.username}PropertySourcesPlaceholderConfigurer

使用 Spring 2.5 中引入的命名空间,您可以配置属性占位符 具有专用配置元素。您可以提供一个或多个位置作为 属性中的逗号分隔列表,如以下示例所示:contextlocation

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

不仅在指定的文件中查找属性。默认情况下,如果它在指定的属性文件中找不到属性, 它根据 Spring 属性和常规 Java 属性进行检查。PropertySourcesPlaceholderConfigurerPropertiesEnvironmentSystem

对于具有以下属性的给定应用程序,只应定义一个这样的元素 它需要的。可以配置多个属性占位符,只要它们具有不同的 占位符语法 ()。${…​}

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

您可以使用 来替换类名,这 当您必须在运行时选择特定的实现类时,有时很有用。 以下示例演示如何执行此操作:PropertySourcesPlaceholderConfigurer

<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()ApplicationContext

示例:PropertyOverrideConfigurer

另一个 bean factory 后处理器类似于 ,但与后者不同的是,原始定义 Bean 属性可以有缺省值,也可以根本没有值。如果覆盖文件没有某个 bean 属性的条目,那么默认 使用上下文定义。PropertyOverrideConfigurerPropertySourcesPlaceholderConfigurerProperties

请注意,bean 定义不知道被覆盖,因此它不会 从 XML 定义文件中可以立即明显看出覆盖配置程序正在 使用。如果多个实例定义不同 由于覆盖机制,同一 Bean 属性的值(最后一个属性获胜)的值。PropertyOverrideConfigurer

属性文件配置行采用以下格式:

beanName.property=value

以下列表显示了该格式的示例:

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

此示例文件可用于容器定义,该容器定义包含名为 has 和 properties 的 Bean。dataSourcedriverurl

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

tom.fred.bob.sammy=123
指定的替代值始终是文本值。它们没有被翻译成 Bean 引用。当 XML Bean 中的原始值 definition 指定 Bean 引用。

使用 Spring 2.5 中引入的命名空间,可以配置 使用专用配置元素重写属性,如以下示例所示:context

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

使用FactoryBean

您可以为以下对象实现接口 本身就是工厂。org.springframework.beans.factory.FactoryBean

该接口是可插入Spring IoC容器的一个点 实例化逻辑。如果您有复杂的初始化代码,最好用 Java 与(可能)冗长的 XML 相对,您可以创建自己的 XML,在该类中编写复杂的初始化,然后插入 自定义到容器中。FactoryBeanFactoryBeanFactoryBean

该接口提供三种方法:FactoryBean<T>

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

  • boolean isSingleton():如果返回单例或其他方式,则返回。此方法的默认实现返回 。trueFactoryBeanfalsetrue

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

这个概念和界面在Spring的许多地方都有使用 框架。Spring 附带了 50 多个接口实现 本身。FactoryBeanFactoryBean

当您需要向容器请求实际实例本身而不是 它生成的 Bean,当 调用 .因此,对于给定的 ,调用容器将返回 的乘积,而 invocationing 返回实例本身。FactoryBeanid&getBean()ApplicationContextFactoryBeanidmyBeangetBean("myBean")FactoryBeangetBean("&myBean")FactoryBean