转载

深入理解Spring4框架(六)——自定义Bean属性

1 生命周期回调

容器对Bean生命周期进行管理,为了便于与之交互,可以实现InitializingBean和DisposableBean接口,容器会在Bean初始化之前调用前者的afterPropertiesSet(),销毁之前调用后者的destroy。

JSR-250的@PostConstruct和@PreDestroy注解被认为是现代Spring应用处理生命周期回调的最佳实践,使用这些注解,可以避免代码和Spring的特定接口耦合。

如果不想使用JSR-250注解,但又想与Spring框架解耦,也可以考虑使用init-method和destroy-method对象定义元数据。

在Spring中,Spring框架使用BeanPostProcessor实现来执行回调接口,然后调用合适的方法。如果想使用Spring所提供之外的自定义功能,可以自己实现BeanPostProcessor。

除了初始化和销毁回调之外,Spring所管理的对象也可以实现Lifecycle接口,这些对象就可以参与到容器自身生命周期所驱动的启动和关闭流程。

1.1 初始化回调

在一个Bean的所有必要属性都被容器设值之后,org.springframework.beans.factory.InitializingBean接口允许执行一些初始化工作。InitializingBean接口只指定了一个方法:

void afterPropertiesSet() throws Exception;

一般不推荐使用InitializingBean接口,因为它使得代码和Spring耦合了。推荐使用@PostConstruct注解,或者指定一个POJO初始化方法。在基于XML配置元数据的例子中,使用init-method属性来指定无参方法的名字。

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/> public class ExampleBean {     public void init() {         //做一些初始化工作     } }

和下面的一样

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {     public void afterPropertiesSet() {         //做一些初始化工作     } }

1.2 销毁回调

实现了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() {         //做一些销毁任务(比如释放连接池的连接)     } }

以上的配置等同于:

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {     public void destroy() {         //做一些销毁任务(比如释放连接池的连接)     } }

1.3 默认初始化和销毁方法

当不使用Spring指定的InitializingBean和DispposableBean回调接口时,通常都是自己编写的如nit(),initialize(), dispose()等方法。理想情况下,这种生命周期回调方法的命名是标准的,贯穿于整个工程,使得所有的开发者都使用相同的方法名,确保一致性。

可以配置Spring容器,让它查找每个Bean的初始化和销毁回调方法的名称。这就意味着,开发者可以在编写好类之后,直接使用一个称为init()的初始化回调,而不需为每个Bean配置init-method=”init”属性。当Bean被创建的时候,Spring的IoC容器就会调用init()。

假如初始化回调方法为init(),销毁回调方法为destroy,那么累的组织如下所示:

public class DefaultBlogService implements BlogService {     private BlogDao blogDao;     public void setBlogDao(BlogDao blogDao) {         this.blogDao = blogDao;     }     // 初始化回调方法     public void init() {         if (this.blogDao == null) {             throw new IllegalStateException("The [blogDao] property must be set.");         }     } }
<beans default-init-method="init">     <bean id="blogService" class="com.foo.DefaultBlogService">         <property name="blogDao" ref="blogDao" />     </bean> </beans>

default-init-method属性使得Spring IoC容器将init识别为初始化回调方法。

初始化回调在原生Bean引用上调用的,也就是说,这个时候AOP拦截器之类的东西还没有应用到Bean上面。比如,只有一个目标Bean被完全创建之后,一个AOP代理才会被应用到这个Bean上。因此,如果将拦截器应用到初始化方法中,将会出现不一致的问题,因为这样做会将目标Bean的生命周期与代理藕合起来。

1.4生命周期机制的组合应用

在Spring2.5中,可以通过三种方式来控制Bean的生命周期:InitializingBean和DisposableBean回调接口;自定义init()和destroy()方法;还有@PostConstruct和@PreDestroy注解。可以组合这些机制来控制给定的Bean。

如果为一个Bean配置了多个生命周期机制,那么每个配置的方法将按以下顺序执行:

(1)添加了@PostConstruct注解的方法。

(2)InitializingBean回调接口所定义的afterPropertiesSet()方法。

(3)自定义的init()方法。

销毁方法调用顺序也类似:

(1)添加了@PreDestroy注解的方法。

(2)DisposableBean回调接口所定义的destroy()方法。

(3)自定义的destroy()方法

然而,如果在不同的生命周期机制中配置了相同的方法(比如使用init()来作为初始化方法),超过1一个生命周期机制,那么init()只会被执行一次。

1.5 启动和停止回调

Lifecycle接口为拥有生命周期需求的对象定义了基本方法(比如启动和停止一些后台进程):

public interface Lifecycle {     void start();     void stop();     boolean isRunning(); }

任何Spring管理的对象可能都会实现那个接口。然后,当ApplicationContext接收到启动或停止信号时(比如运行时停止或重启场景),它会将这些调用一层一层传递给Lifecycle的实现类,这种做法是通过委托给LifecycleProcessor来完成的:

public interface LifecycleProcessor extends Lifecycle {     void onRefresh();     void onClose(); }

我们可以看出,LifecycleProcessor本身就是Lifecycle的一个扩展,它也为上下文被刷新和关闭添加了两个回应方法。

启动和关闭调用的顺序也很重要。如果两个对象存在依赖关系,依赖方将会晚于被依赖方启动,依赖方会早于被依赖方关闭。然而,有时直接引用是未知的,也许只知道确定类型的对象应该先于另一种类型的对象启动。在那些场景中,SmartLifecycle接口定义了另一个选项,也就是超类接口定义的getPhase()方法。

public interface Phased {     int getPhase(); } public interface SmartLifecycle extends Lifecycle, Phased {     boolean isAutoStartup();     void stop(Runnable callback); }

当启动的时候,phase值最小的对象最先启动,当停止的时候,顺序相反。因此,一个实现了SmartLifecycle接口的对象,若getPhase()方法返回Integer.MIN_VALUE,那么它会是最先启动,最后停止的。否则顺序相反。在考虑phase值的时候,需要知道,那些没有实现SmartLifecycle接口的Lifecycle对象phase默认值为0.

可见,SmartLifecycle定义的stop方法接受一个回调。这个接口的任何实现的关闭进程完成之后,都需要调用回调参数的run()方法。这就使得在必要的时候可以异步关闭,而LifecycleProcessor和DefaultLifecycleProcessor的默认实现将会等待一系列对象来调用那个回调参数,直到超时。默认的每个phase的超时时间是30秒。可以在上下文中重新定义一个名为”lifecycleProcessor”的Bean来覆盖默认的生命周期处理器。如果仅仅想修改超时时间,如下定义就足够了:

<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">     <!-- 超时值,毫秒 -->     <property name="timeoutPerShutdownPhase" value="10000"/> </bean>

LifecycleProcessor接口也为context的刷新和关闭定义了回调方法。若stop()被显式调用了,后者就仅会驱动关闭进程,但是在上下文关闭的时候也会有这样的行为。而refresh回调就会激活SmartLifecycle的另一个功能,当上下文被刷新后(所有的对象均被实例化和初始化之后),那个回调就会被调用。这时,默认的生命周期处理器将会检查每个SmartLifecycle对象的isAutoStartup()方法返回的boolean值。如果为true的话,然后那个对象将会立即启动,而不是等待一个显式的调用。

2 ApplicationContextAware和BeanNameAware

当一个ApplicationContext创建了一个实现了org.springframework.context.ApplicationContextAware接口的对象实例时,这个实例就会拥有一个对那个ApplicationContext的引用。

public interface ApplicationContextAware {     void setApplicationContext(ApplicationContext applicationContext) throws BeansException; }

通过ApplicationContext接口,Bean可以从程序上操作创建它的ApplicationContext。也可以将引用转换为它的子类,比如ConfigurableApplicationContext,它暴露了更多的功能。一种用法就是以程序的方式检索其它Bean。有时,这个功能很有用;然而,一般情况下应该避免使用它,因为它与Spring代码耦合了,并且它没有遵守控制反转风格。传统的constructor和byType自动注入模型可以为构造器参数或者setter方法参数提供一种ApplicationContext类型依赖。为了使得程序更灵活,请使用新的基于注解的功能@Autowired,更多详细信息请关注后续讲解。

当ApplicationContext创建了一个实现org.springframework.beans.factory.BeanNameAware接口的类时,这个类就拥有了这个对象名称的引用。

public interface BeanNameAware {     void setBeanName(string name) throws BeansException; }
原文  http://www.sanesee.com/article/thinking-in-spring-4-custom-bean-nature
正文到此结束
Loading...