Spring IOC 之个性化定制the nature of a bean

1.生命周期回调

为了影响容器管理的bean的生命周期,你可以实现Spring的InitializingBean和DisposableBean接口。容器首先调用afterPropertiesSet()方法最后调用destroy()方法就可以运行bean在初始化和销毁的时候来进行特定的行为动作。

注意:@PostConstruct和@PreDestroy注解本认为是在现在的Spring引用中最好的接受生命周期回调的方法。使用这些注解意味着你的bean不能喝特定的Spring接口耦合。如果你不想使用JSR-250注解,但是你仍然寻找移除这些偶像,可以考虑在定义元数据的时候使用初始化方法和销毁方法。(init-method、destory-method)。

在内部Spring框架使用BeanPostProcessor的实现通过它找到的并且调用合适的方法去处理任何的回调接口。如果你需要自定义功能或者其他的生命周期行为,Spring没有提供直接使用的方法,你可以实现一个BeanPostProcessor。

1.1处理话回调

org.springframework.beans.factory.InitializingBean接口允许一个bean在bean的所有必要属性被容器设置完成之后可以进行初始化的工作。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() {
 // do some initialization work
 }
}

这个效果和下面一样:

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
 public void afterPropertiesSet() {
 // do some initialization work
 }
}

但是没有和Spring的代码耦合在一块。

1.2销毁回调

实现org.springframework.beans.factory.DisposableBean接口允许一个bean在容器销毁的时候进行回调。DisposableBean接口也指明了唯一的一个方法:

void destroy() throws Exception;

建议你不要使用DisposableBean回调接口因为它没必要和Spring的代码耦合到了一起。作为可选项,你可以使用@PreDestroy注解或者指明一个bean定义支持的泛型方法。在基于XML配置文件中,你可以使用destory-method属性。例如:

<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)
 }
}

但是没有和Spring的代码耦合在一起。

1.3默认的初始化和销毁方法

当你没有使用Spring规范中的InitilaizingBean和DisoposableBean回调接口写初始化和销毁方法回调的时候,你一般会写这样的名字的方法,比如:init(),initialize(),dispose()等等。理想情况下,在整个项目中这样的生命周期回调方法的名字是标准化的以便所有的开发者使用相同的方法名字并且鞥能够复用一致性。

你可以配置Spring容器在每一个bean中去寻找命名的初始化和销毁毁掉方法名字。这意味着,作为一个应用程序开发者,你可以写的应用类,而且使用一个叫做init()的初始化回调而不用配置必须配置一个init-method="init"属性在每一个bean定义中。在一个bean被创建的时候Spring IOC容器会调用这个方法。这个功能强迫在初始化和销毁方法对调的命名上保持一致的习惯。

加入你的初始化回调方法是init(),而回调方法是destory()。你的类是下面这样的:

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.");
 }
 }
}
<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被创建和组装的时候,如果bean有一个这样的方法,那么它会在合适的时候被调用。

你可以类似的在元素中使用default-destory-method方法去配置销毁回调方法。

当存在的bean类已经有一个回调方法和约定的存在差异,那么你可以使用元素的init-method和destory-method方法来指明方法的名字。

Spring容器会确保在一个bean的所有依赖被设置后就会立即调用配置的初始化回调。这说明初始化回调被调用的时候bean还未被引用之前,这意味着AOP拦截器还未在bean上作用。一个目标bean首先被完全创建然后AOP代理的拦截器链才会被应用。如果目标bean和代理被分别定义,你的代码甚至可以为成为目标bean的产生作用通过传递代理。因此,讲拦截器应用在init方法中是前后矛盾的么因为在你代码和原始的目标bean产生交互的时候,会通过它的代理或者拦截器与目标bean的生命周期耦合到一起,造成语义生的奇异。

1.4联合生命周期机制

对于Spring 2.5,你可以有三个选择来控制bean的生命周期行为:初始化InitializingBean和销毁DisposableBean 回调接口;定制init()和destory()方法;@PostConstruct 和 @PreDestroy annotations注解。你可以将这些机制联合在一起来控制一个给定的bean。

当使用不同的初始化方法的多个生命周期回调机制作用在同一个bean的时候,她们的调用如下:

  • @PostConstruct注解标识的方法
  • 通过InitializingBean回调接口定义的afterPropertiesSet()方法
  • 一个自定义的init() 方法

销毁方法会按照相同的顺序来被调用:

  • @PreDestory注解标识的方法
  • 通过DisposableBean回调接口方法定义的destory()方法
  • 自定义配置的destory()方法

1.4回调方法的开启和关闭

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接口的一个扩展,它也增加了两个其他方法来在上下文被刷新和关闭的时候来起作用的。

开启和关闭的调用顺序是很重要的。如果一个depends-on关系存在于两个对象之间,依赖的一方会在它的依赖之后开始,在它的依赖之前停止。但是,直接的依赖是位置的。你可能知道特定类型的对象应该在其他类型的对象之前启动。在这里例子中,SmartLifecycle接口定义了其他的选项,getPhase() 方法在它的父类接口中被定义:

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

当开启的时候,拥有最小相位的bean首先启动,在停止的时候,顺序就是反的了。因为,实现了SmartLifecycle接口的对象,它的getPhase()方法返回了Integer.MIN_VALUE 的对象将会首先启动而最后停止。在范围的另一边,拥有最大的相位将会表明它是最后一个被启动而首先被停止。当考虑这些相位值的时候,知道任何normal对象的没有实现SmartLifecycle接口的 默认相位值是0是很重要的。因此任何负的相位值将会表明这个对象首先被启动,对于正的相位值的对象反之亦然。

当年你看到通过SmartLifecycle定义的停止方法接受一个对调的。任何实现了的方法必须调用回调的run()方法在关闭流程完成的时候。这个可以异步的关闭那些需要的因为缺省的LifecycleProcessor接口实现DefaultLifecycleProcessor将会等待这组对象的每个相位去调用那个回调的超时的值。缺省的per-phase超时是30s,,你可以通过在上下午中定义的名字为lifecycleProcessor的bean覆盖默认的生命周期处理实例。如果你指向修改超时,下面的定义将会满足你的需求:

<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
 <!-- timeout value in milliseconds -->
 <property name="timeoutPerShutdownPhase" value="10000"/>
</bean>

正如前面提到的一样,LifecycleProcessor接口定义用于回到方法和refresh和close上下午一样。下面将会简单的演示关闭过程好像stop()被显示的调用一样。,但是当上下午关闭的时候就会发生。在另一方面refresh回调是SmartLifecycle其他功能可用。当上下午被刷新的时候,回调会被调用,而且同时默认的生命周期过程就会检查每一个SmartLifecycle对象isAutoStartup()方法返回的Boolean值。如果true,那个对象将会在那一刻开始而不是等待上下午显示的调用或者自己的start()方法。phase的值depends-on的关系一样将会以同样的方式来绝对启动的顺序。

1.5在非web应用中关闭Spring IOC容器

如果你在一个非web应用环境中使用Spring IOC容器;例如,在一个富客户端桌面环境中,你注册了一个关闭绑定 到JVM。这样做确保一个优雅的关闭而且调用了关联的销毁方法在你的单例bean中一般所有的资源被释放。当然,你必须配置和实现这些销毁回调。

为了注册一个关闭操作,你调用了在AbstractApplicationContext类中声明的registerShutdownHook()方法:

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Boot {
 public static void main(final String[] args) throws Exception {
 AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(
 new String []{ "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...
 }
}

2.ApplicationContextAware和BeanNameAware

当一个ApplicationContext创建一个实现了org.springframework.context.ApplicationContextAware接口的实例,这个实例就是的一个指向ApplicationContext的引用。

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

尽管bean可以通过ApplicationContext接口来在程序中操纵创建他们的ApplicationContext,或者是通过强制转换已知的这个接口的子类的引用,比如ConfigureApplicationContext,它可以提供额外的功能。使用之一便是可以在程序中获取其他bean。有时候这是非常有用的;但是一般的话你不要使用这个,因为这个和Spring的代码耦合到一块了而且在协作类在提供给这个bean做属性的时候不遵循IoC规则。ApplicationContext的其他方法提供了访问文件资源、发布应用事件、而且访问MessageSource。

对于Spring 2.5,自动注入是另外的一个可选项去将操作一个引用到ApplicationContext。传统的constructor和byType自动注入模式能够提供一个ApplicationContext依赖使用一个构造器参数哦或者是setter方法参数。为了更加的灵活,也是为了自动注入域和多个参数的,使用一个新的机遇注解的自动注入功能更,如果你这样做了,ApplicationContext会被自动注入成一个域或者一个构造器参数译个方法参数如果被标记@Autowired注解:

当一个ApplicationContext创建了一个org.springframework.beans.factory.BeanNameAware接口的类,这个类就被提供了一个定义在相关对象定义的名字的引用。

public interface BeanNameAware {
 void setBeanName(string name) throws BeansException;
}

这个回调在正常是bean属性被设置后被调用,但是在想InitializingBean afterPropertiesSet或者是自定义的init-method的初始化对调之前。

3.其他Aware接口

除了上面讨论的ApplicationContextAware和BeanNameAware接口,Spring提供了多个Aware接口允许bean去告知容器他们需要特定的结构依赖。最重要的Aware接口总结在下面了,名字就是表示依赖类型不过的方式:

<table>
	<tr>
		<td>名字</td>
		<td>注入的依赖</td>
		<td></td>
	</tr>
	<tr>
		<td>ApplicationContextAware</td>
		<td>声明为ApplicationContext</td>
		<td></td>
	</tr>
	<tr>
		<td>ApplicationEventPublisherAwareEvent</td>
		<td>关闭事件发布ApplicationContext</td>
		<td></td>
	</tr>
	<tr>
		<td>BeanClassLoaderAware</td>
		<td>用于加载bean的类的类加载器</td>
		<td></td>
	</tr>
	<tr>
		<td>BeanFactoryAware</td>
		<td>声明为BeanFactory</td>
		<td></td>
	</tr>
	<tr>
		<td>BeanNameAware</td>
		<td>声明的bean的名字</td>
		<td></td>
	</tr>
	<tr>
		<td>BootstrapContextAware</td>
		<td>容器运行所在的资源适配器BootstrapContextAware。一般来讲只是在JCA aware ApplicationContexts中可用</td>
		<td></td>
	</tr>
	<tr>
		<td>LoadTimeWeaverAware</td>
		<td>加载时处理类定义被定义的weaver</td>
		<td></td>
	</tr>
	<tr>
		<td>MessageSourceAware</td>
		<td>解析信息时配置策略</td>
		<td></td>
	</tr>
	<tr>
		<td>NotificationPublisherAwareSpring</td>
		<td>Spring JMX消息发布</td>
		<td></td>
	</tr>
	<tr>
		<td>PortletConfigAware</td>
		<td>容器运行在当前的PortletConfigAware。只有在基于web的Spring ApplicationContext中中有效</td>
		<td></td>
	</tr>
	<tr>
		<td>PortletContextAware</td>
		<td>容器运行在当前的PortletContextAware。只有在基于web的Spring ApplicationContext中中有效</td>
		<td></td>
	</tr>
	<tr>
		<td>ResourceLoaderAware</td>
		<td>配置资源用低级别去访问资源</td>
		<td></td>
	</tr>
	<tr>
		<td>ServletConfigAware</td>
		<td>容器运行在当前的ServletConfigAware。只有在基于web的Spring ApplicationContext中中有效</td>
		<td></td>
	</tr>
	<tr>
		<td>ServletContextAware</td>
		<td>容器运行在当前的ServletContextAware。只有在基于web的Spring ApplicationContext中中有效</td>
		<td></td>
	</tr>
</table>

注意这些接口的使用将使你的代码和Spring API耦合在一块,而且不遵循IOC原则。因此,他们姿势被建议架构那个程序需要访问容器的bean。

posted on 2015-01-28 23:28  叼烟斗的纤夫  阅读(1350)  评论(1编辑  收藏  举报