bean定义能够包含许多配置信息,包括构造函数参数,属性值和特定于容器的信息,例如初始化方法,静态工厂方法名称等。子bean定义从父定义继承配置数据。子定义能够覆盖某些值或根据须要添加其余值。使用父bean和子bean定义能够节省大量的输入。实际上,这是一种模板形式。php
若是以ApplicationContext
编程方式使用接口,则子bean定义由ChildBeanDefinition
类表示。大多数用户不在此级别上使用它们。相反,它们在类中以声明方式配置bean定义ClassPathXmlApplicationContext
。使用基于XML的配置元数据时,可使用该parent
属性指定子bean定义,并将父bean指定为此属性的值。如下示例显示了如何执行此操做:html
<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
属性。java
若是没有指定,则bean bean定义使用父定义中的bean类,但也能够覆盖它。在后一种状况下,子bean类必须与父类兼容(即,它必须接受父类的属性值)。mysql
子bean定义从父级继承范围,构造函数参数值,属性值和方法覆盖,并带有添加新值的选项。static
您指定的任何范围,初始化方法,销毁方法或工厂方法设置都会覆盖相应的父设置。spring
其他设置始终取自子定义:取决于,autowire模式,依赖性检查,单例和惰性初始化。sql
前面的示例经过使用该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定义。apache
ApplicationContext
默认状况下预先实例化全部单例。所以,重要的是(至少对于单例bean),若是你有一个(父)bean定义,你只打算用做模板,而且这个定义指定了一个类,你必须确保将abstract属性设置为true不然应用程序上下文将实际(尝试)预先实例化abstract
bean。编程
一般,应用程序开发人员不须要子类化ApplicationContext
实现类。相反,能够经过插入特殊集成接口的实现来扩展Spring IoC容器。接下来的几节将介绍这些集成接口。api
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
。
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引用aBean(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
(0):从不检查系统属性。fallback
(1):若是在指定的属性文件中没法解析,则检查系统属性。这是默认值。override
(2):在尝试指定的属性文件以前,首先检查系统属性。这使系统属性能够覆盖任何其余属性源。有关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