Open xinrong2019 opened 5 years ago
Spring Framework提供了许多可用于自定义bean特性的接口。本节将它们分组如下:
Lifecycle Callbacks
ApplicationContextAware and BeanNameAware
Other Aware Interfaces
要与容器的bean生命周期管理进行交互,可以实现Spring InitializingBean和DisposableBean接口。容器为前者调用afterPropertiesSet(),为后者调用destroy(),让bean在初始化和销毁bean时执行某些操作。
JSR-250 @PostConstruct和@PreDestroy注释通常被认为是在现代Spring应用程序中接收生命周期回调的最佳实践。使用这些注释意味着您的bean不会耦合到特定于Spring的接口。有关详细信息,请参阅使用@PostConstruct和@PreDestroy。
如果您不想使用JSR-250注释但仍想删除耦合,请考虑init-method和destroy-method bean定义元数据。
在内部,Spring Framework使用BeanPostProcessor实现来处理它可以找到的任何回调接口并调用适当的方法。如果您需要Spring默认提供的自定义功能或其他生命周期行为,您可以自己实现BeanPostProcessor。有关更多信息,请参阅容器扩展点。
除了初始化和销毁回调之外,Spring管理的对象还可以实现Lifecycle接口,以便这些对象可以参与启动和关闭过程,这是由容器自身的生命周期驱动的。
本节描述了生命周期回调接口。
org.springframework.beans.factory.InitializingBean接口允许bean在容器为bean设置所有必需属性后执行初始化工作。 InitializingBean接口指定一个方法:
void afterPropertiesSet() throws Exception;
我们建议您不要使用InitializingBean接口,因为它会不必要地将代码耦合到Spring。或者,我们建议使用@PostConstruct
注释或指定POJO初始化方法。对于基于XML的配置元数据,可以使用init-method属性指定具有void无参数签名的方法的名称。使用Java配置,您可以使用@Bean的initMethod
属性。请参阅接收生命周期回调。请考虑以下示例:
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
// do some initialization work
}
}
前面的示例与以下示例几乎完全相同(包含两个列表):
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}
实现org.springframework.beans.factory.DisposableBean接口允许bean在包含它的容器被销毁时获得回调。 DisposableBean接口指定一个方法:
void destroy() throws Exception;
我们建议您不要使用DisposableBean回调接口,因为它会不必要地将代码耦合到Spring。或者,我们建议使用@PreDestroy
注释或指定bean定义支持的泛型方法。使用基于XML的配置元数据,您可以在<bean />
上使用destroy-method属性。使用Java配置,您可以使用@Bean的destroyMethod
属性。请参阅接收生命周期回调。考虑以下定义:
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
前面的定义与以下定义几乎完全相同:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
您可以为
<bean>
元素的destroy-method属性分配一个特殊(推断)值,该值指示Spring自动检测特定bean类的公共关闭或关闭方法。 (因此,任何实现java.lang.AutoCloseable或java.io.Closeable的类都将匹配。)您还可以在<beans>
元素的default-destroy-method属性上设置此特殊(推断)值,以将此行为应用于一整套bean(参见默认初始化和销毁方法)。请注意,这是Java配置的默认行为。
当您编写初始化和销毁不使用特定于Spring的InitializingBean和DisposableBean回调接口的方法回调时,通常会编写名称为init(),initialize(),dispose()等的方法。理想情况下,此类生命周期回调方法的名称在项目中是标准化的,以便所有开发人员使用相同的方法名称并确保一致性。
您可以将Spring容器配置为“查找”命名初始化并销毁每个bean上的回调方法名称。这意味着,作为应用程序开发人员,您可以编写应用程序类并使用名为init()的初始化回调,而无需为每个bean定义配置init-method =“init”属性。 Spring IoC容器在创建bean时调用该方法(并且符合前面描述的标准生命周期回调协定)。此功能还强制执行初始化和销毁方法回调的一致命名约定。
假设您的初始化回调方法名为init(),而您的destroy回调方法名为destroy()。然后,您的类类似于以下示例中的类:
public class DefaultBlogService implements BlogService {
private BlogDao blogDao;
public void setBlogDao(BlogDao blogDao) {
this.blogDao = blogDao;
}
// this is (unsurprisingly) the initialization callback method
public void init() {
if (this.blogDao == null) {
throw new IllegalStateException("The [blogDao] property must be set.");
}
}
}
然后,您可以在类似于以下内容的bean中使用该类:
<beans default-init-method="init">
<bean id="blogService" class="com.something.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
顶级<beans />
元素属性上存在default-init-method属性会导致Spring IoC容器将bean类上的init方法识别为初始化方法回调。当bean被创建和组装时,如果bean类具有这样的方法,则在适当的时候调用它。
您可以使用顶级<beans />
元素上的default-destroy-method属性,以类似方式(在XML中)配置destroy方法回调。
Spring容器保证在为bean提供所有依赖项后立即调用已配置的初始化回调。因此,在原始bean引用上调用初始化回调,这意味着AOP拦截器等尚未应用于bean。首先完全创建目标bean,然后应用带有拦截器链的AOP代理(例如)。如果目标bean和代理是分开定义的,那么您的代码甚至可以绕过代理与原始目标bean交互。因此,将拦截器应用于init方法是不一致的,因为这样做会将目标bean的生命周期耦合到其代理或拦截器,并在代码直接与原始目标bean交互时留下奇怪的语义。
从Spring 2.5开始,您有三个控制bean生命周期行为的选项:
The InitializingBean and DisposableBean callback interfaces
Custom init() and destroy() methods
@PostConstruct和@PreDestroy注释。您可以组合这些机制来控制给定的bean。
如果为bean配置了多个生命周期机制,并且每个机制都配置了不同的方法名称,则每个配置的方法都按照此注释后列出的顺序执行。但是,如果为初始化方法配置了相同的方法名称(例如,init() - 对于多个这些生命周期机制,该方法将执行一次,如上一节中所述。
为同一个bean配置的多个生命周期机制具有不同的初始化方法,如下所示:
Methods annotated with @PostConstruct
afterPropertiesSet() as defined by the InitializingBean callback interface
A custom configured init() method
Destroy方法以相同的顺序调用:
Methods annotated with @PreDestroy
destroy() as defined by the DisposableBean callback interface
A custom configured destroy() method
Lifecycle接口为任何具有自己的生命周期要求的对象(例如启动和停止某些后台进程)定义了基本方法:
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
任何Spring管理的对象都可以实现Lifecycle接口。然后,当ApplicationContext本身接收到启动和停止信号时(例如,对于运行时的停止/重启场景),它会将这些调用级联到该上下文中定义的所有生命周期实现。它通过委托LifecycleProcessor完成此操作,如下面的清单所示:
public interface LifecycleProcessor extends Lifecycle {
void onRefresh();
void onClose();
}
请注意,LifecycleProcessor本身是Lifecycle接口的扩展。它还添加了另外两种方法来响应刷新和关闭的上下文。
请注意,常规org.springframework.context.Lifecycle接口是显式启动和停止通知的简单合约,并不意味着在上下文刷新时自动启动。要对特定bean的自动启动(包括启动阶段)进行细粒度控制,请考虑实现org.springframework.context.SmartLifecycle。
此外,请注意,在销毁之前不保证停止通知。在常规关闭时,所有Lifecycle bean首先会在传播常规销毁回调之前收到停止通知。但是,在上下文生命周期中的热刷新或中止刷新尝试时,仅调用destroy方法。
启动和关闭调用的顺序非常重要。如果任何两个对象之间存在“依赖”关系,则依赖方在其依赖之后开始,并且在其依赖之前停止。但是,有时,直接依赖性是未知的。您可能只知道某种类型的对象应该在另一种类型的对象之前开始。在这些情况下,SmartLifecycle接口定义了另一个选项,即在其超级接口Phased上定义的getPhase()方法。以下清单显示了Phased接口的定义:
public interface Phased {
int getPhase();
}
以下清单显示了SmartLifecycle接口的定义:
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
启动时,具有最低相位的对象首先开始。停止时,遵循相反的顺序。因此,实现SmartLifecycle并且其getPhase()方法返回Integer.MIN_VALUE的对象将是第一个开始和最后一个停止的对象。在频谱的另一端,Integer.MAX_VALUE的阶段值将指示对象应该最后启动并首先停止(可能是因为它依赖于正在运行的其他进程)。在考虑阶段值时,同样重要的是要知道未实现SmartLifecycle的任何“正常”Lifecycle对象的默认阶段为0。因此,任何负相位值都表示对象应该在这些标准组件之前启动(并在它们之后停止)。任何正相值都是相反的。
SmartLifecycle定义的stop方法接受回调。在该实现的关闭过程完成之后,任何实现都必须调用该回调的run()方法。这样可以在必要时启用异步关闭,因为LifecycleProcessor接口的默认实现DefaultLifecycleProcessor等待每个阶段内的对象组的超时值来调用该回调。默认的每阶段超时为30秒。您可以通过在上下文中定义名为lifecycleProcessor的bean来覆盖缺省生命周期处理器实例。如果您只想修改超时,则定义以下内容就足够了:
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
<!-- timeout value in milliseconds -->
<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>
如前所述,LifecycleProcessor接口还定义了用于刷新和关闭上下文的回调方法。后者驱动关闭过程,就像显式调用了stop()一样,但是当上下文关闭时会发生。另一方面,'refresh'回调启用了SmartLifecycle bean的另一个功能。刷新上下文时(在实例化并初始化所有对象之后),将调用该回调。此时,默认生命周期处理器检查每个SmartLifecycle对象的isAutoStartup()方法返回的布尔值。如果为true,则在该点启动该对象,而不是等待显式调用上下文或其自己的start()方法(与上下文刷新不同,上下文启动不会自动发生在标准上下文实现中)。如前所述,阶段值和任何“依赖”关系确定启动顺序。
本节仅适用于非Web应用程序。 Spring的基于Web的ApplicationContext实现已经具有代码,可以在相关Web应用程序关闭时正常关闭Spring IoC容器。
如果在非Web应用程序环境中使用Spring的IoC容器(例如,在富客户机桌面环境中),请使用JVM注册关闭挂钩。这样做可确保正常关闭并在单例bean上调用相关的destroy方法,以便释放所有资源。您仍然必须正确配置和实现这些destroy回调。
要注册关闭挂钩,请调用ConfigurableApplicationContext接口上声明的registerShutdownHook()方法,如以下示例所示:
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Boot {
public static void main(final String[] args) throws Exception {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// add a shutdown hook for the above context...
ctx.registerShutdownHook();
// app runs here...
// main method exits, hook is called prior to the app shutting down...
}
}
当ApplicationContext创建实现org.springframework.context.ApplicationContextAware接口的对象实例时,将为该实例提供对该ApplicationContext的引用。以下清单显示了ApplicationContextAware接口的定义:
public interface ApplicationContextAware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
因此,bean可以通过ApplicationContext接口以编程方式操作创建它们的ApplicationContext,或者通过将引用转换为此接口的已知子类(例如ConfigurableApplicationContext,它公开其他功能)。一种用途是对其他bean进行编程检索。有时这种能力很有用。但是,一般情况下,您应该避免使用它,因为它将代码耦合到Spring并且不遵循Inversion of Control样式,其中协作者作为属性提供给bean。ApplicationContext的其他方法提供对文件资源的访问,发布应用程序事件和访问MessageSource。这些附加功能在ApplicationContext的附加功能中描述。
从Spring 2.5开始,自动装配是另一种获取ApplicationContext引用的替代方法。 “传统”构造函数和byType自动装配模式(如自动装配协作者中所述)可以分别为构造函数参数或setter方法参数提供ApplicationContext类型的依赖关系。为了获得更大的灵活性,包括自动装配字段和多参数方法的能力,请使用基于注释的新自动装配功能。如果这样做,ApplicationContext将自动装入一个字段,构造函数参数或方法参数,如果相关的字段,构造函数或方法带有@Autowired批注,则该参数需要ApplicationContext类型。有关更多信息,请参阅使用@Autowired。
当ApplicationContext创建实现org.springframework.beans.factory.BeanNameAware接口的类时,将为该类提供对其关联对象定义中定义的名称的引用。以下清单显示了BeanNameAware接口的定义:
public interface BeanNameAware {
void setBeanName(String name) throws BeansException;
}
The callback is invoked after population of normal bean properties but before an initialization callback such as InitializingBean, afterPropertiesSet, or a custom init-method.
除了ApplicationContextAware和BeanNameAware(前面讨论过)之外,Spring还提供了大量的Aware回调接口,让bean向容器指示它们需要某种基础结构依赖性。作为一般规则,名称表示依赖关系类型。下表总结了最重要的Aware接口:
Table 4. Aware interfaces
Name | Injected Dependency | Explained in… |
---|---|---|
ApplicationContextAware | Declaring ApplicationContext. | ApplicationContextAware and BeanNameAware |
ApplicationEventPublisherAware | Event publisher of the enclosing ApplicationContext. | Additional Capabilities of the ApplicationContext |
BeanClassLoaderAware | Class loader used to load the bean classes. | Instantiating Beans |
BeanFactoryAware | Declaring BeanFactory. | ApplicationContextAware and BeanNameAware |
BeanNameAware | Name of the declaring bean. | ApplicationContextAware and BeanNameAware |
BootstrapContextAware | Resource adapter BootstrapContext the container runs in. Typically available only in JCA-aware ApplicationContext instances. | JCA CCI |
LoadTimeWeaverAware | Defined weaver for processing class definition at load time. | Load-time Weaving with AspectJ in the Spring Framework |
MessageSourceAware | Configured strategy for resolving messages (with support for parametrization and internationalization). | Additional Capabilities of the ApplicationContext |
NotificationPublisherAware | Spring JMX notification publisher. | Notifications |
ResourceLoaderAware | Configured loader for low-level access to resources. | Resources |
ServletConfigAware | Current ServletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext. | Spring MVC |
ServletContextAware | Current ServletContext the container runs in. Valid only in a web-aware Spring ApplicationContext. | Spring MVC |
请再次注意,使用这些接口会将您的代码绑定到Spring API,而不会遵循Inversion of Control样式。因此,我们建议将它们用于需要以编程方式访问容器的基础架构bean。
bean定义可以包含许多配置信息,包括构造函数参数,属性值和特定于容器的信息,例如初始化方法,静态工厂方法名称等。子bean定义从父定义继承配置数据。子定义可以覆盖某些值或根据需要添加其他值。使用父bean和子bean定义可以节省大量的输入。实际上,这是一种模板形式。
如果以编程方式使用ApplicationContext接口,则子bean定义由ChildBeanDefinition类表示。大多数用户不在此级别上使用它们。相反,它们在类(如ClassPathXmlApplicationContext)中以声明方式配置bean定义。使用基于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>
如果没有指定,则bean bean定义使用父定义中的bean类,但也可以覆盖它。在后一种情况下,子bean类必须与父类兼容(即,它必须接受父类的属性值)。
子bean定义从父级继承范围,构造函数参数值,属性值和方法覆盖,并带有添加新值的选项。您指定的任何范围,初始化方法,销毁方法或静态工厂方法设置都会覆盖相应的父设置。
其余设置始终取自子定义:取决于,autowire模式,依赖性检查,单例和惰性初始化。
前面的示例通过使用abstract属性将父bean定义显式标记为abstract。如果父定义未指定类,则需要将父bean定义显式标记为abstract,如以下示例所示:
<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不能单独实例化,因为它不完整,并且也明确标记为抽象。当定义是抽象的时,它仅可用作纯模板bean定义,用作子定义的父定义。尝试使用这样一个抽象的父bean,通过将它作为另一个bean的ref属性引用或者使用父bean ID进行显式getBean()调用会返回错误。类似地,容器的内部preInstantiateSingletons()方法忽略定义为abstract的bean定义。
ApplicationContext默认情况下预先实例化所有单例。因此,重要的是(至少对于单例bean),如果你有一个(父)bean定义,你只打算用作模板,并且这个定义指定了一个类,你必须确保将abstract属性设置为true否则应用程序上下文将实际(尝试)预先实例化抽象bean。
通常,应用程序开发人员不需要继承ApplicationContext实现类。相反,可以通过插入特殊集成接口的实现来扩展Spring IoC容器。接下来的几节将介绍这些集成接口。
BeanPostProcessor接口定义了可以实现的回调方法,以提供您自己的(或覆盖容器的默认)实例化逻辑,依赖关系解析逻辑等。如果要在Spring容器完成实例化,配置和初始化bean之后实现一些自定义逻辑,可以插入一个或多个自定义BeanPostProcessor实现
您可以配置多个BeanPostProcessor实例,并且可以通过设置order属性来控制这些BeanPostProcessor实例的执行顺序。仅当BeanPostProcessor实现Ordered接口时,才能设置此属性。如果编写自己的BeanPostProcessor,则应考虑实现Ordered接口。有关更多详细信息,请参阅BeanPostProcessor和Ordered接口的javadoc。另请参阅有关BeanPostProcessor实例的编程注册的说明。
BeanPostProcessor实例在bean(或对象)实例上运行。也就是说,Spring IoC容器实例化一个bean实例,然后BeanPostProcessor实例完成它们的工作。
BeanPostProcessor实例的范围是每个容器的范围。仅当您使用容器层次结构时,这才是相关的。如果在一个容器中定义BeanPostProcessor,它只对该容器中的bean进行后处理。换句话说,在一个容器中定义的bean不会被另一个容器中定义的BeanPostProcessor进行后处理,即使两个容器都是同一层次结构的一部分。
要更改实际的bean定义(即定义bean的蓝图),您需要使用BeanFactoryPostProcessor,如使用BeanFactoryPostProcessor定制配置元数据中所述。
org.springframework.beans.factory.config.BeanPostProcessor接口由两个回调方法组成。当这样的类被注册为容器的后处理器时,对于容器创建的每个bean实例,后处理器在容器初始化方法之前从容器中获取回调(例如InitializingBean.afterPropertiesSet()或在任何bean初始化回调之后调用任何声明的init方法)。后处理器可以对bean实例执行任何操作,包括完全忽略回调。 bean后处理器通常检查回调接口,或者它可以用代理包装bean。一些Spring AOP基础结构类实现为bean后处理器,以便提供代理包装逻辑。
ApplicationContext自动检测在实现BeanPostProcessor接口的配置元数据中定义的任何bean。 ApplicationContext将这些bean注册为后处理器,以便在创建bean时可以稍后调用它们。 Bean后处理器可以以与任何其他bean相同的方式部署在容器中。
注意,在配置类上使用@Bean工厂方法声明BeanPostProcessor时,工厂方法的返回类型应该是实现类本身,或者至少是org.springframework.beans.factory.config.BeanPostProcessor接口,指示该bean的后处理器性质。否则,ApplicationContext无法在完全创建之前按类型自动检测它。由于BeanPostProcessor需要尽早实例化以便应用于上下文中其他bean的初始化,因此这种早期类型检测至关重要。
以编程方式注册BeanPostProcessor实例
虽然BeanPostProcessor注册的推荐方法是通过ApplicationContext自动检测(如前所述),但您可以使用addBeanPostProcessor方法以编程方式对ConfigurableBeanFactory注册它们。当您需要在注册前评估条件逻辑或甚至跨层次结构中的上下文复制Bean post处理器时,这非常有用。但请注意,以编程方式添加的BeanPostProcessor实例不遵循Ordered接口。这里,注册的顺序决定了执行的顺序。另请注意,以编程方式注册的BeanPostProcessor实例始终在通过自动检测注册的实例之前处理,而不管任何显式排序。
BeanPostProcessor实例和AOP自动代理
实现BeanPostProcessor接口的类是特殊的,容器会对它们进行不同的处理。作为ApplicationContext的特殊启动阶段的一部分,它们直接引用的所有BeanPostProcessor实例和bean都在启动时实例化。接下来,所有BeanPostProcessor实例都以排序方式注册,并应用于容器中的所有其他bean。因为AOP自动代理是作为BeanPostProcessor本身实现的,所以BeanPostProcessor实例和它们直接引用的bean都不符合自动代理的条件,因此没有编入方法。
For any such bean, you should see an informational log message: Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying).
如果您通过使用自动装配或@Resource(可能会回退到自动装配)将Bean连接到BeanPostProcessor,则Spring可能会在搜索类型匹配依赖项候选项时访问意外的bean,因此使它们不符合自动代理或其他类型的条件豆后处理。例如,如果您有一个使用@Resource注释的依赖项,其中字段或setter名称不直接对应于bean的声明名称而且没有使用name属性,则Spring会访问其他bean以按类型匹配它们。
以下示例显示如何在ApplicationContext中编写,注册和使用BeanPostProcessor实例。
第一个例子说明了基本用法。该示例显示了一个自定义BeanPostProcessor实现,该实现在容器创建时调用每个bean的toString()方法,并将生成的字符串输出到系统控制台。
以下清单显示了自定义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 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961
将回调接口或注释与自定义BeanPostProcessor实现结合使用是扩展Spring IoC容器的常用方法。一个例子是Spring的RequiredAnnotationBeanPostProcessor - 一个随Spring发行版一起提供的BeanPostProcessor实现,它确保用(任意)注释标记的bean上的JavaBean属性实际上(配置为)依赖注入值。
我们看到的下一个扩展点是org.springframework.beans.factory.config.BeanFactoryPostProcessor。此接口的语义类似于BeanPostProcessor的语义,但有一个主要区别:BeanFactoryPostProcessor对bean配置元数据进行操作。也就是说,Spring IoC容器允许BeanFactoryPostProcessor读取配置元数据,并可能在容器实例化除BeanFactoryPostProcessor实例之外的任何bean之前更改它。
您可以配置多个BeanFactoryPostProcessor实例,并且可以通过设置order属性来控制这些BeanFactoryPostProcessor实例的运行顺序。但是,如果BeanFactoryPostProcessor实现Ordered接口,则只能设置此属性。如果编写自己的BeanFactoryPostProcessor,则应考虑实现Ordered接口。有关更多详细信息,请参阅BeanFactoryPostProcessor和Ordered接口的javadoc。
如果要更改实际的bean实例(即,从配置元数据创建的对象),则需要使用BeanPostProcessor(前面在使用BeanPostProcessor定制Bean中进行了描述)。虽然技术上可以在BeanFactoryPostProcessor中使用bean实例(例如,通过使用BeanFactory.getBean()),但这样做会导致过早的bean实例化,从而违反标准的容器生命周期。这可能会导致负面影响,例如绕过bean后期处理。
此外,BeanFactoryPostProcessor实例的范围是每个容器的范围。仅当您使用容器层次结构时,这才有意义。如果在一个容器中定义BeanFactoryPostProcessor,则它仅应用于该容器中的bean定义。一个容器中的Bean定义不会被另一个容器中的BeanFactoryPostProcessor实例后处理,即使两个容器都是同一层次结构的一部分。
bean工厂后处理器在ApplicationContext中声明时自动执行,以便将更改应用于定义容器的配置元数据。 Spring包含许多预定义的bean工厂后处理器,例如PropertyOverrideConfigurer和PropertyPlaceholderConfigurer。您还可以使用自定义BeanFactoryPostProcessor - 例如,注册自定义属性编辑器。
ApplicationContext自动检测部署到其中的任何实现BeanFactoryPostProcessor接口的bean。它在适当的时候使用这些bean作为bean工厂后处理器。您可以像处理任何其他bean一样部署这些后处理器bean。
与BeanPostProcessors一样,您通常不希望为延迟初始化配置BeanFactoryPostProcessors。如果没有其他bean引用Bean(Factory)PostProcessor,则该后处理器根本不会被实例化。因此,将忽略将其标记为延迟初始化,即使在
<beans />
元素的声明中将default-lazy-init属性设置为true,也会急切地实例化Bean(Factory)PostProcessor。
您可以使用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>
该示例显示了从外部属性文件配置的属性。在运行时,PropertyPlaceholderConfigurer应用于替换DataSource的某些属性的元数据。要替换的值被指定为$ {property-name}
形式的占位符,它遵循Ant和log4j以及JSP EL样式。
实际值来自标准Java Properties格式的另一个文件:
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
因此,$ {jdbc.username}字符串在运行时将替换为值'sa',并且同样适用于与属性文件中的键匹配的其他占位符值。 PropertyPlaceholderConfigurer检查bean定义的大多数属性和属性中的占位符。此外,您可以自定义占位符前缀和后缀。
使用Spring 2.5中引入的上下文命名空间,您可以使用专用配置元素配置属性占位符。您可以在location属性中以逗号分隔列表的形式提供一个或多个位置,如以下示例所示:
<context:property-placeholder location="classpath:com/something/jdbc.properties"/>
PropertyPlaceholderConfigurer不仅在您指定的属性文件中查找属性。默认情况下,如果它在指定的属性文件中找不到属性,它还会检查Java System属性。您可以通过使用以下三个受支持的整数值之一设置configurer的systemPropertiesMode属性来自定义此行为:
never (0): Never check system properties.
fallback (1): Check system properties if not resolvable in the specified properties files. This is the default.
override (2): Check system properties first, before trying the specified properties files. This lets system properties override any other property source.
See the PropertyPlaceholderConfigurer javadoc for more information.
您可以使用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时,bean的解析将失败,这是在非lazy-init bean的ApplicationContext的preInstantiateSingletons()阶段期间。
PropertyOverrideConfigurer是另一个bean工厂后处理器,类似于PropertyPlaceholderConfigurer,但与后者不同,原始定义可以具有默认值,或者根本不具有bean属性的值。如果重写的Properties文件没有某个bean属性的条目,则使用默认的上下文定义。
请注意,bean定义不知道被覆盖,因此从XML定义文件中可以立即看出正在使用覆盖配置器。如果多个PropertyOverrideConfigurer实例为同一个bean属性定义了不同的值,则由于覆盖机制,最后一个实例将获胜。
属性文件配置行采用以下格式:
beanName.property=value
The following listing shows an example of the format:
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb
此示例文件可以与容器定义一起使用,该容器定义包含名为dataSource的bean,该bean具有driver和url属性。
也支持复合属性名称,只要路径的每个组件(重写的最终属性除外)都已经非空(可能由构造函数初始化)。在下面的示例中,tom bean的fred属性的bob属性的sammy属性设置为标量值123:
tom.fred.bob.sammy=123
指定的覆盖值始终是文字值。它们不会被翻译成bean引用。当XML bean定义中的原始值指定bean引用时,此约定也适用。
使用Spring 2.5中引入的上下文命名空间,可以使用专用配置元素配置属性覆盖,如以下示例所示:
<context:property-override location="classpath:override.properties"/>
FactoryBean接口是Spring IoC容器实例化逻辑的可插拔点。如果您有更复杂的初始化代码,这些代码在Java中更好地表达,而不是(可能)冗长的XML,您可以创建自己的FactoryBean,在该类中编写复杂的初始化,然后将自定义FactoryBean插入容器中。
FactoryBean接口提供了三种方法:
Object getObject():返回此工厂创建的对象的实例。可以共享实例,具体取决于此工厂是返回单例还是原型。
boolean isSingleton():如果此FactoryBean返回单例,则返回true,否则返回false。
Class getObjectType():返回getObject()方法返回的对象类型,如果事先不知道类型,则返回null。
FactoryBean概念和接口用于Spring Framework中的许多位置。 FactoryBean接口的50多个实现随Spring一起提供。
当您需要向容器询问实际的FactoryBean实例本身而不是它生成的bean时,在调用ApplicationContext的getBean()方法时,使用&符号(&)作为bean的id前缀。因此,对于id为myBean的给定FactoryBean,在容器上调用getBean(“myBean”)将返回FactoryBean的产品,而调用getBean(“&myBean”)则返回FactoryBean实例本身。
1.5. Bean Scopes
singleton
(默认)将单个bean定义范围限定为每个Spring IoC容器的单个对象实例。
prototype
将单个bean定义范围限定为任意数量的对象实例。
request
将单个bean定义范围限定为单个HTTP请求的生命周期。也就是说,每个HTTP请求都有自己的bean实例,它是在单个bean定义的后面创建的。仅在Web感知Spring ApplicationContext的上下文中有效。
session
将单个bean定义范围限定为HTTP会话的生命周期。仅在Web感知Spring ApplicationContext的上下文中有效。
application
将单个bean定义范围限定为ServletContext的生命周期。仅在Web感知Spring ApplicationContext的上下文中有效。
websocket
将单个bean定义范围限定为WebSocket的生命周期。仅在Web感知Spring ApplicationContext的上下文中有效。