此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10spring-doc.cadn.net.cn

容器扩展点

通常,应用程序开发人员不需要将ApplicationContext实现类。相反,可以通过插入 特殊集成接口的实现。接下来的几节将介绍这些 集成接口。spring-doc.cadn.net.cn

使用BeanPostProcessor

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

您可以配置多个BeanPostProcessor实例,您可以控制顺序 其中这些BeanPostProcessor实例通过设置order财产。 仅当BeanPostProcessor实现Ordered接口。如果您编写自己的BeanPostProcessor,您应该考虑实现 这Ordered界面也是如此。有关更多详细信息,请参阅BeanPostProcessorOrdered接口。另请参阅有关程序化注册BeanPostProcessor实例.spring-doc.cadn.net.cn

BeanPostProcessor实例对 bean(或对象)实例进行作。那是 Spring IoC 容器实例化一个 bean 实例,然后BeanPostProcessor实例完成他们的工作。spring-doc.cadn.net.cn

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

要更改实际的 Bean 定义(即定义 Bean 的蓝图), 您需要改用BeanFactoryPostProcessor,如使用BeanFactoryPostProcessor.spring-doc.cadn.net.cn

org.springframework.beans.factory.config.BeanPostProcessor接口由以下内容组成 恰好有两个回调方法。当此类类注册为后处理器时,具有 容器,对于容器创建的每个 Bean 实例, 后处理器在容器之前从容器获取回调 初始化方法(例如InitializingBean.afterPropertiesSet()或任何 宣布init方法),并在任何 bean 初始化回调之后调用。 后处理器可以对 bean 实例执行任何作,包括忽略 回调。bean 后处理器通常检查回调接口, 或者它可以用代理包装 bean。一些 Spring AOP 基础设施类是 作为 bean 后处理器实现,以提供代理包装逻辑。spring-doc.cadn.net.cn

ApplicationContext自动检测在 实现BeanPostProcessor接口。这ApplicationContext将这些 bean 注册为后处理器,以便可以调用它们 后来,在 bean 创建时。Bean 后处理器可以部署在容器中的 与任何其他豆子一样。spring-doc.cadn.net.cn

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

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

实现BeanPostProcessor接口是特殊的,并且经过处理 不同于容器。都BeanPostProcessor实例和 bean,它们 直接引用在启动时实例化,作为特殊启动阶段的一部分 的ApplicationContext.接下来,所有BeanPostProcessor实例已注册 以分类的方式应用于容器中的所有其他豆子。因为 AOP 自动代理实现为BeanPostProcessor本身,也不是BeanPostProcessor实例和它们直接引用的 bean 都没有资格进行自动代理,并且, 因此,不要将方面交织到其中。spring-doc.cadn.net.cn

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

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

以下示例演示如何编写、注册和使用BeanPostProcessor实例 在ApplicationContext.spring-doc.cadn.net.cn

示例:Hello World,BeanPostProcessor-风格

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

以下列表显示了自定义BeanPostProcessor实现类定义:spring-doc.cadn.net.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
	}
}

以下内容beans元素使用InstantiationTracingBeanPostProcessor:spring-doc.cadn.net.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>

请注意InstantiationTracingBeanPostProcessor只是定义的。它不会 甚至有一个名字,而且,因为它是一个 bean,所以它可以像你一样注入依赖 其他豆子。(前面的配置还定义了一个由 Groovy 支持的 bean 脚本。Spring 动态语言支持在标题为 动态语言支持 的章节中进行了详细介绍。spring-doc.cadn.net.cn

以下 Java 应用程序运行上述代码和配置:spring-doc.cadn.net.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.cadn.net.cn

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

示例:该AutowiredAnnotationBeanPostProcessor

将回调接口或注解与自定义BeanPostProcessor实现是扩展 Spring IoC 容器的常用手段。一个例子是 Spring的AutowiredAnnotationBeanPostProcessor— 一个BeanPostProcessor实现 附带 Spring 分布和自动连接注释字段、setter 方法、 和任意配置方法。spring-doc.cadn.net.cn

使用BeanFactoryPostProcessor

我们看的下一个扩展点是org.springframework.beans.factory.config.BeanFactoryPostProcessor.的语义 此接口类似于BeanPostProcessor,有一个专业 差异:BeanFactoryPostProcessor对 Bean 配置元数据进行作。 也就是说,Spring IoC 容器允许BeanFactoryPostProcessor阅读 配置元数据,并可能在容器实例化之前对其进行更改 除BeanFactoryPostProcessor实例。spring-doc.cadn.net.cn

您可以配置多个BeanFactoryPostProcessor实例,您可以控制 这些BeanFactoryPostProcessor实例通过设置order财产。 但是,只有在BeanFactoryPostProcessor实现Ordered接口。如果您编写自己的BeanFactoryPostProcessor,你应该 考虑实现Ordered界面也是如此。请参阅BeanFactoryPostProcessorOrdered接口了解更多详细信息。spring-doc.cadn.net.cn

如果要更改实际的 bean 实例(即创建的对象 )中,则需要改为BeanPostProcessor(前面在使用BeanPostProcessor). 虽然从技术上讲可以在BeanFactoryPostProcessor(例如,通过使用BeanFactory.getBean()),这样做会导致 bean 实例化过早, 违反标准容器生命周期。这可能会导致负面副作用,例如 绕过 Bean 后处理。spring-doc.cadn.net.cn

BeanFactoryPostProcessor实例按容器限定范围。这仅与如果您使用容器层次结构。如果您定义了BeanFactoryPostProcessor在一个容器中,它仅应用于该容器中的 Bean 定义。Bean 定义在一个容器中不会由BeanFactoryPostProcessor实例,即使两个容器属于同一层次结构。spring-doc.cadn.net.cn

当 bean 工厂后处理器在ApplicationContext,以便将更改应用于 定义容器。Spring 包括一些预定义的 bean 工厂 后处理器,例如PropertyOverrideConfigurerPropertySourcesPlaceholderConfigurer.您还可以使用自定义BeanFactoryPostProcessor- 例如,注册自定义属性编辑器。spring-doc.cadn.net.cn

ApplicationContext自动检测部署到其中的任何 bean 实现BeanFactoryPostProcessor接口。它使用这些豆子作为豆子工厂 后处理器,在适当的时间。您可以将这些后处理器 Bean 部署为 你会喜欢任何其他豆子。spring-doc.cadn.net.cn

BeanPostProcessors 时,您通常不想配置BeanFactoryPostProcessors 用于延迟初始化。如果没有其他 bean 引用Bean(Factory)PostProcessor,则该后处理器根本不会被实例化。因此,将其标记为延迟初始化将被忽略,并且Bean(Factory)PostProcessor即使您将default-lazy-init属性设置为true在声明您的<beans />元素。

示例:属性占位符替换PropertySourcesPlaceholderConfigurer

您可以使用PropertySourcesPlaceholderConfigurer外部化属性值使用标准 Java 从单独文件中的 Bean 定义Properties格式。 这样做使部署应用程序的人员能够自定义特定于环境的属性,例如数据库 URL 和密码,而不会有修改容器的一个或多个主 XML 定义文件。spring-doc.cadn.net.cn

考虑以下基于 XML 的配置元数据片段,其中DataSourcewith 占位符值被定义:spring-doc.cadn.net.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>

该示例显示从外部Properties文件。 在运行时, 一个PropertySourcesPlaceholderConfigurer应用于替换某些属性的元数据DataSource. 要替换的值被指定为 形式${property-name},它遵循 Ant、log4j 和 JSP EL 风格。spring-doc.cadn.net.cn

实际值来自标准 Java 中的另一个文件Properties格式:spring-doc.cadn.net.cn

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

因此,${jdbc.username}string 在运行时替换为值 'sa',并且这同样适用于与属性文件中的键匹配的其他占位符值。 这PropertySourcesPlaceholderConfigurer检查大多数属性中的占位符和bean 定义的属性。此外,您可以自定义占位符前缀、suffix、默认值分隔符和转义字符。此外,默认转义字符可以通过设置spring.placeholder.escapeCharacter.default属性通过 JVM 系统属性(或通过 这SpringProperties机制)。spring-doc.cadn.net.cn

使用context命名空间,您可以配置属性占位符使用专用配置元素。您可以将一个或多个位置作为逗号分隔的列表location属性,如以下示例所示:spring-doc.cadn.net.cn

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

PropertySourcesPlaceholderConfigurer不仅在Properties您指定的文件。默认情况下,如果在指定的属性文件中找不到属性,它会检查 SpringEnvironment属性和常规 JavaSystem性能。spring-doc.cadn.net.cn

只需为给定应用程序定义一个这样的元素,其属性它需要的。可以配置多个属性占位符,只要它们具有不同的占位符语法 (${…​}).spring-doc.cadn.net.cn

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

您可以使用PropertySourcesPlaceholderConfigurer替换类名,这有时在运行时必须选择特定的实现类时很有用。以下示例显示了如何执行此作:spring-doc.cadn.net.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在即将创建时(即在preInstantiateSingletons()阶段ApplicationContext对于非 lazy-init bean。spring-doc.cadn.net.cn

示例:该PropertyOverrideConfigurer

PropertyOverrideConfigurer,另一个 bean 工厂后处理器,类似于PropertySourcesPlaceholderConfigurer,但与后者不同的是,原始定义可以具有 bean 属性的默认值或根本没有值。如果覆盖Propertiesfile 没有某个 bean 属性的条目,则使用默认的context 定义。spring-doc.cadn.net.cn

请注意,bean 定义不知道被覆盖,因此它不是从 XML 定义文件中可以立即看出覆盖配置器正在被覆盖 使用。 如果出现多个PropertyOverrideConfigurer定义不同值的实例,由于覆盖机制,最后一个获胜。spring-doc.cadn.net.cn

属性文件配置文件行采用以下格式:spring-doc.cadn.net.cn

beanName.property=value

以下列表显示了格式的示例:spring-doc.cadn.net.cn

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

此示例文件可以与包含名为dataSource那有driverClassNameurl性能。spring-doc.cadn.net.cn

还支持复合属性名称,只要路径的每个组件除了被覆盖的最终属性已经非 null(大概由构造函数初始化)。在下面的示例中,sammy属性的bob属性的fred属性的tombean 设置为标量值123:spring-doc.cadn.net.cn

tom.fred.bob.sammy=123
指定的覆盖值始终是文字值。它们不会转换为bean 引用。当 XML bean 中的原始值definition 指定 bean 引用时,此约定也适用。

使用context命名空间,可以在 Spring 2.5 中引入,可以配置属性,使用专用配置元素覆盖,如以下示例所示:spring-doc.cadn.net.cn

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

使用FactoryBean

您可以实现org.springframework.beans.factory.FactoryBean对象的接口本身就是工厂。spring-doc.cadn.net.cn

FactoryBean接口是 Spring IoC 容器的实例化逻辑的可插拔性点。如果您有复杂的初始化代码,可以更好地用Java 而不是(可能)冗长的 XML 数量,您可以创建自己的 XMLFactoryBean,在该类中编写复杂的初始化,然后将 习惯FactoryBean放入容器中。spring-doc.cadn.net.cn

FactoryBean<T>interface 提供了三种方法:spring-doc.cadn.net.cn

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

  • boolean isSingleton():返回true如果这FactoryBean返回单例或false否则。 此方法的默认实现返回true.spring-doc.cadn.net.cn

  • Class<?> getObjectType():返回getObject()方法 或null如果事先不知道类型。spring-doc.cadn.net.cn

FactoryBean概念和接口在 Spring 中的许多地方都使用了 框架。 超过 50 个实现FactoryBean与 Spring 一起发布的接口 本身。spring-doc.cadn.net.cn

当您需要向容器索取实际的FactoryBean实例本身,而不是它生成的 bean,为 bean 的id当调用 & 符号 () 时调用&getBean()方法ApplicationContext. 所以,就给定而言FactoryBean使用idmyBean调用getBean("myBean")返回的FactoryBean,而调用getBean("&myBean")返回FactoryBean实例本身。spring-doc.cadn.net.cn