spring-基础四
简介
通常情况下我们可以通过xml方式定义Bean,但是很多时候有特殊的要求我们需要自定义bean,在Spring体系下我们如何自定义Bean?
Spring 下的实体都是通过容器来管理其生命周期,实体的创建与销毁都与Spring容器管理有关,我们自定义实体的时候如何能够做到让我们的实体与Spring容器管理相关?
生命周期回调
我了将我们的实体交予Spring容器管理,Spring提供了InitializingBean 与 DisposableBean接口,容器为Spring提供了InitializingBean调用afterPropertiesSet()方法为DisposableBean调用destroy()方法,可以让实体在初始化和销毁时执行某些操作.
Bean的初始化回调
- 实现InitializingBean 方法
public class TestBean implements InitializingBean {
public void afterPropertiesSet() {
// 实体在初始化时需要做额外事情
}
}
- 通过@PostConstruct注解
public class TestBean {
@PostConstruct
public void init() {
// 实体在初始化时需要做额外事情
}
}
- 通过xml 配置init-method属性
<bean id="testBean" class="com.panic.TestBean" init-method="init"/>
public class TestBean {
public void init() {
// 实体在初始化时需要做额外事情
}
}
Bean的销毁回调
- 实现DisposableBean 接口
public class TestBean implements DisposableBean {
public void destroy() {
// 实体在销毁时需要做额外事情
}
}
- 指定xml配置 destroy-method属性
<bean id="testBean" class="com.panic.TestBean" destroy-method="cleanAll"/>
public class TestBean {
public void cleanAll() {
// 实体在初始化时需要做额外事情
}
}
- 通过@PreDestroy注解
public class TestBean {
@PreDestroy
public void cleanAll() {
// 实体在初始化时需要做额外事情
}
}
执行顺序
如果我们配置一个bean的时候及配置xml的init-method方法又继承了InitializingBean,并且还使用了@PostConstruct注解,三者同时存在的时候,执行的顺序是怎样的?
在实体初始化的时候先执行@PostConstruct所标记的方法再执行afterPropertiesSet()方法最后执行xml指定的初始化方法,所以xml指定的方法优先级是最高的.同理在实体销毁的时候执行顺序是先执行注解指定方法,再执行实现的回调destroy()方法,最后实现xml指定的销毁方法.
选择哪种形式定义生命周期回调?
Spring官网上说明:
不推荐我们使用继承方式,推荐我们使用注解的方式来完成,理由是继承的方式增加了代码与Spring框架的耦合程度.
启动与关闭回调
Lifecycle
Lifecycle接口定义任意bean说必须要要的生命周期方法,比如启动,停止与后台运行.
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
任何Spring管理的对象可能实现Lifecycle接口。然而,当ApplicationContext接收到启动和停止信号(例如:在运行时停止和重启场景),它将这些调用级联到在该上下文中定义的所有生命周期实现。通过代理到LifecycleProcessor处理,在下面清单显示:
public interface LifecycleProcessor extends Lifecycle {
void onRefresh();
void onClose();
}
LifecycleProcessor 是 Lifecycle 接口的拓展,增加了两个额外的方法接收上下文的刷新与关闭.当我们实现Lifecycle接口时,如果我们想要其start或者stop执行,必须显式的调用容器的start()或者stop()方法.stop方法不一定能保证在我们之前介绍的销毁方法之前执行.
SmartLifecycle
启动和关闭顺序调用是非常重要的。如果依赖关系存在任何对象之间,依赖侧开始在被依赖之后并且它的停止在它的依赖之前。然而,在运行时,这个直接依赖是未知的。我们可能只知道某种类型的对象应该先于另一种类型的对象开始。在这种情况下,SmartLifecycle接口定义其他可选,即getPhase()方法在它的父接口Phased被定义。
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的“正常”生命周期对象的默认阶段为0。因此,任何负的阶段值表示对象应该启动在这些标准的组件之前(停止在它们之后)。对于任何正阶段值,情况正好相反。
停止方法通过SmartLifecycle接受一个回调被定义。任何实现必须在实现的关闭处理完成后调用回调的run()方法。这将在必要时启用异步关闭,因为LifecycleProcessor接口的默认实现DefaultLifecycleProcessor会等待其超时值,以等待每个阶段内的对象组调用该回调。每个阶段默认超时时间30秒。我们可以通过定义一个bean 命名为lifecycleProcessor在上下文中覆盖这个默认生命周期处理实例。如果我们想仅仅修改超时时间,定义以下内容就足够了:
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
<!-- 设置超时时间 -->
<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>
像前面提到的,LifecycleProcessor接口定义回调方法为上下文更好的刷新和关闭。后者驱动关闭过程,就好像已经显式调用了stop()一样,但是它在上下文关闭时发生。另一方面,“refresh”回调启用了SmartLifecycle bean的另一个特性。当这个上下文被刷新(所有的bean被初始化和实例化),即回调被调用。在那个阶段,默认的生命周期处理器通过每个SmartLifecycle对象的isAutoStartup()方法检测返回boolean值。如果true,对象在那个阶段被启动而不是等待上下文或者自身的start()方法显示调用(与上下文刷新不同,对于标准上下文实现,上下文启动不会自动发生)。
ApplicationContextAware和BeanNameAware
当ApplicationContext创建一个对象实例同时实现了org.springframework.context.ApplicationContextAware接口,这个实例提供一个对ApplicationContext的引用。
ApplicationContextAware接口:
public interface ApplicationContextAware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
因此,这些bean可以编程地操作ApplicationContext创建它们,通过ApplicationContext接口或者转换引用为已知的这个接口的子类(例如:ConfigurableApplicationContext,它暴露了附加的功能)。一种用途是通过编程方式检索其他bean。有时候这个能力非常有用。然而,一般的,我们应该避免使用,因为它耦合Spring的代码并且不遵循控制反转格式,将协调者作为bean的属性。ApplicationContext其他方法提供获取资源文件、发布应用事件和访问MessageSource。这些额外的特性在ApplicationContext的附加能力中描述。
自动装配是获得对ApplicationContext的引用的另一种选择。传统的constructor和byType自动装配模式(在自动装配协作器中描述)能够为构造函数或者Setter方法参数提供一个ApplicationContext类型的依赖。为了获得更大的灵活性,包括自动装配字段和多个参数方法的能力,可以使用基于注解的自动装配特性。如果这样做,ApplicationContext自动装配到字段、构造参数或方法参数,如果字段、构造函数或方法带有@Autowired注解那么该参数期望ApplicationContext类型。
其他Aware接口
所有的Aware接口都是为了能让我们拿到容器中相关资源,比如BeanNameAware,可以让我们拿到Bean的名称,ApplicationContextAware 可以让我们拿到整个容器.
除了ApplicationContextAware和BeanNameAware之外,Spring提供了广泛的Aware回调接口,这些接口使Bean向容器指示它们需要一些基础结构依赖性。作为基本规则,这个名字指示依赖类型。下面总结最重要的Aware接口:
Spring 不推荐我们使用这些接口,原因是使用了这些接口会使我们代码与Spring容器发生了强耦合,违背IOC原则.