bean定义可以包含许多配置信息,包括构造函数参数,属性值和特定于容器的信息,例如初始化方法,静态工厂方法名称等。子bean定义从父定义继承配置数据。子定义可以覆盖某些值或根据需要添加其他值。使用父bean和子bean定义可以节省大量的输入。实际上,这是一种模板形式。
如果以 ApplicationContext
编程方式使用接口,则子bean定义由 ChildBeanDefinition
类表示。大多数用户不在此级别上使用它们。相反,它们在类中以声明方式配置bean定义 ClassPathXmlApplicationContext
。使用基于XML的配置元数据时,可以使用该 parent
属性指定子bean定义,并将父bean指定为此属性的值。以下示例显示了如何执行此操作:
<bean id="inheritedTestBean" abstract="true" class="org.springframework.beans.TestBean"> <property name="name" value="parent"/> <property name="age" value="1"/> </bean> <bean id="inheritsWithDifferentClass" class="org.springframework.beans.DerivedTestBean" parent="inheritedTestBean" init-method="initialize"> <property name="name" value="override"/> <!-- the age property value of 1 will be inherited from parent --> </bean> 复制代码
注意 parent
属性。
如果没有指定,则bean bean定义使用父定义中的bean类,但也可以覆盖它。在后一种情况下,子bean类必须与父类兼容(即,它必须接受父类的属性值)。
子bean定义从父级继承范围,构造函数参数值,属性值和方法覆盖,并带有添加新值的选项。 static
您指定的任何范围,初始化方法,销毁方法或工厂方法设置都会覆盖相应的父设置。
其余设置始终取自子定义:取决于,autowire模式,依赖性检查,单例和惰性初始化。
前面的示例通过使用该 abstract
属性将父bean定义显式标记为abstract 。如果父定义未指定类, abstract
则根据需要显式标记父bean定义,如以下示例所示:
<bean id="inheritedTestBeanWithoutClass" abstract="true"> <property name="name" value="parent"/> <property name="age" value="1"/> </bean> <bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean" parent="inheritedTestBeanWithoutClass" init-method="initialize"> <property name="name" value="override"/> <!-- age will inherit the value of 1 from the parent bean definition--> </bean> 复制代码
父bean不能单独实例化,因为它不完整,并且也明确标记为 abstract
。定义时 abstract
,它仅可用作纯模板bean定义,用作子定义的父定义。尝试使用这样的 abstract
父bean,通过将其称为另一个bean的ref属性或 getBean()
使用父bean ID 进行显式调用,将返回错误。类似地,容器的内部 preInstantiateSingletons()
方法忽略定义为abstract的bean定义。
ApplicationContext
默认情况下预先实例化所有单例。因此,重要的是(至少对于单例bean),如果你有一个(父)bean定义,你只打算用作模板,并且这个定义指定了一个类,你必须确保将 abstract
属性设置为 true
否则应用程序上下文将实际(尝试)预先实例化 abstract
bean。
通常,应用程序开发人员不需要子类化 ApplicationContext
实现类。相反,可以通过插入特殊集成接口的实现来扩展Spring IoC容器。接下来的几节将介绍这些集成接口。
BeanPostProcessor
该 BeanPostProcessor
接口定义了您可以实现的回调方法,以提供您自己的(或覆盖容器的默认)实例化逻辑,依赖关系解析逻辑等。如果要在Spring容器完成实例化,配置和初始化bean之后实现某些自定义逻辑,则可以插入一个或多个自定义 BeanPostProcessor
实现。
您可以配置多个 BeanPostProcessor
实例,并且可以 BeanPostProcessor
通过设置 order
属性来控制这些实例的执行顺序。只有在 BeanPostProcessor
实现 Ordered
接口时才能设置此属性。如果你自己编写 BeanPostProcessor
,你也应该考虑实现这个 Ordered
接口。有关更多详细信息,请参阅
BeanPostProcessor
和
Ordered
接口的javadoc 。另见关于实例的
程序化登记 BeanPostProcessor
的说明。
BeanPostProcessor 实例在bean(或对象)实例上运行。也就是说,Spring IoC容器实例化一个bean实例,然后
BeanPostProcessor 实例执行它们的工作。
BeanPostProcessor 实例的范围是每个容器。仅当您使用容器层次结构时,这才是相关的。如果
BeanPostProcessor 在一个容器中定义一个容器,它只会对该容器中的bean进行后处理。换句话说,
BeanPostProcessor 即使两个容器都是同一层次结构的一部分,在一个容器中定义的bean也不会被另一个容器中定义的bean进行后处理。要更改实际的bean定义(即定义bean的蓝图),您需要使用a
BeanFactoryPostProcessor ,如 使用。[
定制配置元数据中所述`BeanFactoryPostProcessor]( docs.spring.io/spring/docs…
)
该 org.springframework.beans.factory.config.BeanPostProcessor
接口由两个回调方法组成。当这样的类被注册为具有容器的后处理器时,对于由容器创建的每个bean实例,后处理器在容器初始化方法(例如 InitializingBean.afterPropertiesSet()
或任何声明的 init
方法)之前都从容器获得回调。调用,并在任何bean初始化后回调。后处理器可以对bean实例执行任何操作,包括完全忽略回调。bean后处理器通常检查回调接口,或者它可以用代理包装bean。一些Spring AOP基础结构类实现为bean后处理器,以便提供代理包装逻辑。
的 ApplicationContext
自动检测中的实现的配置的元数据中定义的任何豆 BeanPostProcessor
接口。将 ApplicationContext
这些bean注册为后处理器,以便在创建bean时可以稍后调用它们。Bean后处理器可以以与任何其他bean相同的方式部署在容器中。
请注意,在配置类上 BeanPostProcessor
使用 @Bean
工厂方法声明a时,工厂方法的返回类型应该是实现类本身或至少是 org.springframework.beans.factory.config.BeanPostProcessor
接口,清楚地表明该bean的后处理器性质。否则, ApplicationContext
在完全创建之前, 无法按类型自动检测它。由于 BeanPostProcessor
需要尽早实例化以便应用于上下文中其他bean的初始化,因此这种早期类型检测至关重要。
以编程方式注册 BeanPostProcessor
实例虽然推荐的 BeanPostProcessor
注册方法是通过 ApplicationContext
自动检测(如前所述),但您可以 ConfigurableBeanFactory
使用该 addBeanPostProcessor
方法以编程方式对其进行注册。当您需要在注册前评估条件逻辑或甚至跨层次结构中的上下文复制Bean post处理器时,这非常有用。但请注意,以 BeanPostProcessor
编程方式添加的实例不尊重 Ordered
接口。这里,注册的顺序决定了执行的顺序。另请注意,以 BeanPostProcessor
编程方式注册的实例始终在通过自动检测注册的实例之前处理,而不管任何显式排序。
BeanPostProcessor 实例和AOP自动代理实现
BeanPostProcessor 接口的类是特殊的,容器会对它们进行不同的处理。
BeanPostProcessor 他们直接引用的所有实例和bean都会在启动时实例化,作为特殊启动阶段的一部分
ApplicationContext 。接下来,所有
BeanPostProcessor 实例都以排序方式注册,并应用于容器中的所有其他bean。因为AOP自动代理是作为一个
BeanPostProcessor 自身实现的,所以
BeanPostProcessor 实例和它们直接引用的bean都不符合自动代理的条件,因此没有编织方面。对于任何此类bean,您应该看到一条信息性日志消息:
Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying) 。如果您
BeanPostProcessor 通过使用自动装配或
@Resource (可能回退到自动装配)将bean连接到您的 ,则Spring可能会在搜索类型匹配依赖项候选项时访问意外的bean,因此,使它们不符合自动代理或其他类型的bean post -处理。例如,如果您有一个依赖项,
@Resource`其中字段或setter名称与bean的声明名称没有直接对应,并且没有使用name属性,则Spring会访问其他bean以按类型匹配它们。
以下示例显示如何在中编写,注册和使用 BeanPostProcessor
实例 ApplicationContext
。
示例:Hello World, BeanPostProcessor
-style
第一个例子说明了基本用法。该示例显示了一个自定义 BeanPostProcessor
实现,该实现调用 toString()
容器创建的每个bean 的方法,并将生成的字符串输出到系统控制台。
以下清单显示了自定义 BeanPostProcessor
实现类定义:
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; } } 复制代码
以下 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应用程序运行上述代码和配置:
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 = (Messenger) ctx.getBean("messenger"); System.out.println(messenger); } } 复制代码
上述应用程序的输出类似于以下内容:
Bean'sensenger'创建:org.springframework.scripting.groovy.GroovyMessenger@272961 org.springframework.scripting.groovy.GroovyMessenger@272961 复制代码
示例: RequiredAnnotationBeanPostProcessor
将回调接口或注释与自定义 BeanPostProcessor
实现结合使用 是扩展Spring IoC容器的常用方法。一个例子是Spring RequiredAnnotationBeanPostProcessor
- 一个 BeanPostProcessor
随Spring发行版一起提供的实现,它确保标记有(任意)注释的bean上的JavaBean属性实际上(配置为)依赖注入值。
BeanFactoryPostProcessor
我们看到的下一个扩展点是 org.springframework.beans.factory.config.BeanFactoryPostProcessor
。这个接口的语义类似于 BeanPostProcessor
它的一个主要区别: BeanFactoryPostProcessor
操作bean配置元数据。也就是说,Spring IoC容器允许 BeanFactoryPostProcessor
读取配置元数据,并可能在容器实例化除实例之外的任何bean 之前
更改它 BeanFactoryPostProcessor
。
您可以配置多个 BeanFactoryPostProcessor
实例,并且可以 BeanFactoryPostProcessor
通过设置 order
属性来控制这些实例的运行顺序。但是,如果 BeanFactoryPostProcessor
实现 Ordered
接口,则只能设置此属性。如果你自己编写 BeanFactoryPostProcessor
,你也应该考虑实现这个 Ordered
接口。有关更多详细信息,请参阅
BeanFactoryPostProcessor
和
Ordered
接口的javadoc 。
如果要更改实际的bean实例(即,从配置元数据创建的对象),则需要使用a BeanPostProcessor
(前面在
使用a定制Bean中进行了描述 BeanPostProcessor
)。虽然技术上可以在a中使用bean实例 BeanFactoryPostProcessor
(例如,通过使用 BeanFactory.getBean()
),但这样做会导致过早的bean实例化,从而违反标准的容器生命周期。这可能会导致负面影响,例如绕过bean后期处理。此外, BeanFactoryPostProcessor
实例的范围是每个容器的范围。仅当您使用容器层次结构时,这才有意义。如果 BeanFactoryPostProcessor
在一个容器中定义一个容器,则它仅应用于该容器中的bean定义。 BeanFactoryPostProcessor
即使两个容器都是同一层次结构的一部分,一个容器中的Bean定义也不会被另一个容器中的实例进行后处理。
Bean工厂后处理器在其内部声明时会自动执行 ApplicationContext
,以便将更改应用于定义容器的配置元数据。Spring包含许多预定义的bean工厂后处理器,例如 PropertyOverrideConfigurer
和 PropertyPlaceholderConfigurer
。您还可以使用自定义 BeanFactoryPostProcessor
- 例如,注册自定义属性编辑器。
一个 ApplicationContext
自动检测部署在它实现了任何豆 BeanFactoryPostProcessor
接口。它在适当的时候使用这些bean作为bean工厂后处理器。您可以像处理任何其他bean一样部署这些后处理器bean。
与 BeanPostProcessor
s一样,您通常不希望 BeanFactoryPostProcessor
为延迟初始化配置 s。如果没有其他bean引用a Bean(Factory)PostProcessor
,则该后处理器根本不会被实例化。因此,将其标记为延迟初始化将被忽略, Bean(Factory)PostProcessor
会急切地实例化,即使你设定的 default-lazy-init
属性 true
对你的声明 <beans />
元素。
示例:类名替换 PropertyPlaceholderConfigurer
您可以使用 PropertyPlaceholderConfigurer
标准Java Properties
格式在单独的文件中使用bean定义中的外部化属性值。这样做可以使部署应用程序的人员自定义特定于环境的属性,例如数据库URL和密码,而不会出现修改主XML定义文件或容器文件的复杂性或风险。
请考虑以下基于XML的配置元数据片段,其中 DataSource
定义了占位符值:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations" value="classpath:com/something/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> 复制代码
该示例显示了从外部 Properties
文件配置的属性。在运行时,a PropertyPlaceholderConfigurer
将应用于替换DataSource的某些属性的元数据。要替换的值被指定为表单的占位符 ${property-name}
,它遵循Ant和log4j以及JSP EL样式。
实际值来自标准Java Properties
格式的另一个文件:
jdbc.driverClassName = org.hsqldb.jdbcDriver jdbc.url = JDBC:HSQLDB:HSQL://localhost:9002 jdbc.username = root jdbc.password =root 复制代码
因此, ${jdbc.username}
在运行时使用值“sa”替换字符串,这同样适用于与属性文件中的键匹配的其他占位符值。在 PropertyPlaceholderConfigurer
为大多数属性和bean定义的属性占位符检查。此外,您可以自定义占位符前缀和后缀。
使用 context
Spring 2.5中引入的命名空间,您可以使用专用配置元素配置属性占位符。您可以在 location
属性中提供一个或多个位置作为逗号分隔列表,如以下示例所示:
<context:property-placeholder location="classpath:com/something/jdbc.properties"/> 复制代码
在 PropertyPlaceholderConfigurer
不仅将查找在属性 Properties
指定的文件。默认情况下,如果它在指定的属性文件中找不到属性,它还会检查Java System
属性。您可以通过 systemPropertiesMode
使用以下三个受支持的整数值之一设置configurer 的属性来自定义此行为:
never fallback override
有关
PropertyPlaceholderConfigurer
更多信息,请参阅javadoc。
您可以使用 PropertyPlaceholderConfigurer
替换类名称,这在您必须在运行时选择特定实现类时有时很有用。以下示例显示了如何执行此操作:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <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 class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <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}"/> 复制代码
如果类不能在分辨率,当它即将被创造,这是在失败 preInstantiateSingletons()
的阶段 ApplicationContext
对非延迟实例化的bean。
示例: PropertyOverrideConfigurer
在 PropertyOverrideConfigurer
另一个bean工厂后置处理器,类似 PropertyPlaceholderConfigurer
,但不同的是后者,原来的定义可以有缺省值或者根本没有值的bean属性。如果覆盖 Properties
文件没有某个bean属性的条目,则使用默认上下文定义。
请注意,bean定义不知道被覆盖,因此从XML定义文件中可以立即看出正在使用覆盖配置器。如果多个 PropertyOverrideConfigurer
实例为同一个bean属性定义了不同的值,则由于覆盖机制,最后一个实例会获胜。
属性文件配置行采用以下格式:
beanName.property=value 复制代码
以下清单显示了格式的示例:
dataSource.driverClassName=com.mysql.jdbc.Driver dataSource.url=jdbc:mysql:mydb 复制代码
此示例文件可以与包含名为 dataSource
has has driver
和 url
properties 的bean的容器定义一起使用 。
也支持复合属性名称,只要路径的每个组件(重写的最终属性除外)都已经非空(可能由构造函数初始化)。在下面的例子中, sammy
所述的属性 bob
的财产 fred
的财产 tom
豆被设置为标量值 123
:
tom.fred.bob.sammy = 123 复制代码
指定的覆盖值始终是文字值。它们不会被翻译成bean引用。当XML bean定义中的原始值指定bean引用时,此约定也适用。
使用 context
Spring 2.5中引入的命名空间,可以使用专用配置元素配置属性覆盖,如以下示例所示:
<context:property-override location="classpath:override.properties"/> 复制代码
FactoryBean
您可以 org.springframework.beans.factory.FactoryBean
为自己工厂的对象实现接口。
该 FactoryBean
接口是Spring IoC容器实例化逻辑的可插拔点。如果你有一个复杂的初始化代码,用Java表示,而不是(可能)冗长的XML,你可以创建自己的 FactoryBean
,在该类中编写复杂的初始化,然后将自定义 FactoryBean
插入容器。
该 FactoryBean
接口提供了三种方法:
Object getObject()
:返回此工厂创建的对象的实例。可以共享实例,具体取决于此工厂是返回单例还是原型。 boolean isSingleton()
: true
如果 FactoryBean
返回单例或 false
其他方式返回 。 Class getObjectType()
:返回 getObject()
方法返回的对象类型,或者 null
如果事先不知道类型。
该 FactoryBean
概念和接口被一些Spring框架内的场所。超过50个 FactoryBean
接口的实现随Spring一起提供。
当你需要向一个容器询问一个实际的 FactoryBean
实例本身而不是它生成的bean 时,在调用the的方法时 id
,用strersand符号( &
)作为前缀。因此,对于给定 与的,调用在容器上返回的产品,而调用返回的 实例本身。 getBean()``ApplicationContext``FactoryBean``id``myBean``getBean("myBean")``FactoryBean``getBean("&myBean")``FactoryBean