此篇解读自定义bean的功能。
Spring框架提供了许多接口,用于自定义bean的一些功能。
生命周期回调
为了容器与bean生命周期的交互,可以实现InitializingBean和DisposableBean接口。容器回调 afterPropertiesSet()和destroy()使bean在初始化和销毁bean时执行某些操作。
在内部,Spring Framework使用BeanPostProcessor实现来处理它可以找到的任何回调接口并调用适当的方法。如果需要自定义功能或其他生命周期行为,Spring默认不提供,可以自己实现BeanPostProcessor。
除了初始化和销毁?回调,Spring管理的对象还可以实现Lifecycle接口,以便这些对象可以在容器自身生命周期的驱动下参与启动和关闭过程。
- 初始化回调
InitializingBean接口,在容器设置好bean所必须的属性后才初始化,这个接口含有一个方法:
void afterPropertiesSet() throws Exception;
这个接口不建议使用,因为会将不必要的代码耦合到spring中,另外建议使用@PostConstruct注释或指定POJO初始化方法。对于基于XML的配置元数据,可以使用init-method属性指定具有无参数签名的方法的名称,或通过Java配置,您可以使用@Bean的initMethod属性 。
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
// 初始化工作,需要配置其初始化属性指定,可以自由选择是否放在spring中
}
}
另一种写法是
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
@Override
public void afterPropertiesSet() {
// 初始化工作,此代码必然存在于spring中
}
}
- 销毁回调
org.springframework.beans.factory.DisposableBean接口,当包含该接口的容器被销毁时,实现该接口可使Bean获得回调。该 DisposableBean接口指定一个方法:
void destroy() throws Exception;
同上面一样,不建议使用,会将不必要的条码耦合到spring中,建议使用@PreDestroy注释或指定bean定义支持的通用方法。XML中可以使用属性destroy-method来指定回调方法,也可以使用注解@Bean的属性destroyMethod。
销毁回调使用与初始化回调相似,这里不作例了。
注:可以在destroy-method属性上指定一个特殊的值:inferred。此值可以使spring自动检测特定bean类(实现了AutoCloseable或Closeable接口)上的公共close或者shutdown方法。还可以在default-destroy-method属性上设置此值,这样这个自动检测会作用在整个<beans>集上。
- 默认初始化和销毁??方法
当不使用提供的InitializingBean和DisposableBean接口所提供的方法,自定义方法名通常来说一般是init(),initialize(),dispose()...,在理想情况下回调方法名称应该在整个项目中标准化,以便协同工作中使用相同的方法名来保持一致。
可以让spring配置让容器去查找这些标准化的回调方法名称,因此在程序中使用init()方法即可,而不用指定init-method="init"属性,同时关于销毁方法使用destroy()即可不指定destroy-method属性。
在顶级<beans/>元素属性上使用default-init-method(或default-destroy-method)属性导致容器在其初始回调和销毁回调使用指定的方法名称。此方法可以在一些约定方法命名和spring官方推荐的不一致的情况下,覆盖默认的官方命名法。
<beans default-init-method="init">
<bean id="blogService" class="com.something.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
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.");
}
}
}
Spring容器保证在其为所有bean配置所有依赖项后立即调用初始化回调,因此回调针对的是原始bean,也就是说此时AOP拦截器尚未应用于原始bean,目标Bean创建后,然后拦截链应用AOP代理,如果目标bean和代理分别定义,代码甚至可以绕过代理与原始bean交互,因此将init方法应用在拦截器中是矛盾的,因为这样做会将目标Bean的生命周期耦合到其代理或拦截器,并在代码中直接与原始目标Bean交互时留下奇怪的语句。
- 组合生命周期机制
从Spring 2.5开始,您可以使用三个选项来控制Bean生命周期行为:
- 在InitializingBean和 DisposableBean接口中回调;
- 习惯上使用init()和destroy()方法;
- 在@PostConstruct和@PreDestroy注释。
可以结合使用这些机制来控制给定的bean,以上也是其回调方法执行顺序(接口、默认方法、注解,运行三次),但是假如三个顺序中使用的都是init()方法的话,则该方法只运行一次。
- 开启和关闭回调
Lifecycle接口定义了自定义生命周期类的基本方法(比如启动、停止某些后台进程):
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
任何Spring管理的对象都可以实现该Lifecycle接口。然后当 ApplicationContext容器接收到启动和停止信号时(例如运行时的停止/重新启动场景),它将这些调用级联到Lifecycle在该容器定义的所有实现,通过委派给LifecycleProcessor:
//注意其继承Lifecycle接口,增加了两个方法刷新和关闭
public interface LifecycleProcessor extends Lifecycle {
void onRefresh();
void onClose();
}
Lifecycle接口是用于显式启动和停止通知的普通协议,并不意味着在容器刷新时自动启动。为了对特定bean的自动启动进行精细控制(包括启动阶段),可以使用SmartLifecycle接口。
bean之间存在依赖关系的话,关于其启动关闭顺序,注意依赖方后启动且先关闭。
但是有时候不存在直接依赖,比如数据库驱动程序的加载,static静态块中依赖的对象是没有直接依赖在属性上,SmartLifecycle接口的上级接口Phased中有一个方法getPhase():
public interface Phased {
int getPhase();
}
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
在启动时,最低相位的对象先启动,停止时这个对象反而是最后一个停止。
因此,实现了上面接口,其中的getPhase方法返回Integer.MIN_VALUE的话,则是first start and last stop。同理Integer.MAX_VALUE为last start and first stop。
getPhase()方法默认值没有0这个值,负数表示对象在标准组件之前开始,并在标准组件停止之后才停止。
SmartLifecycle定义的stop方法接受回调,任何其实现在其关闭完成后必须调用Runnable的回调方法run()。由于LifecycleProcessor接口的默认实现DefaultLifecycleProcessor会在每个阶段内的对象组等待其超时,以调用该stop回调,因此可以在必要时启用异步关闭。默认的每阶段超时为30秒。可以通过定义lifecycleProcessor这个的bean来覆盖默认的生命周期处理器实例 。
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
<!-- timeout value in milliseconds -->
<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>
LifecycleProcessor接口还定义了用于刷新和关闭容器的回调方法。后者驱动关闭过程,就好像显式调用stop()一样,但是它在容器关闭时发生。
“刷新”回调启用了SmartLifecycle bean的另一个功能 。在容器刷新时(在所有bean初始化和实例化后)回调,默认的生命周期处理器都将检查每个SmartLifecycle对象的isAutoStartup()方法的返回值,如果为true,则在该点启动对象,而不是等待容器或其自身的显示调用start()方法(与容器刷新不同,对于标准容器,在容器启动时不会发生)。
- 在非Web应用程序中正常关闭Spring IoC容器
如果在非Web应用程序环境中(例如客户端桌面环境中)使用Spring的IoC容器,请向JVM注册一个关闭钩子。这样做可以确保正常关机,并在您的单例 bean上调用相关的destroy方法,以便释放所有资源,必须正确配置和实现这些destroy回调。
要注册关闭勾子,请调用ConfigurableApplicationContext接口上声明的registerShutdownHook()方法,如以下示例所示:
public final class Boot {
public static void main(final String[] args) throws Exception {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// 注册关闭勾子
ctx.registerShutdownHook();.
}
}
ApplicationContextAware和BeanNameAware
当一个容器创建了一个实现了ApplicationContextAware接口的对象实例时,这个对象实例获取了一个对容器的引用,ApplicationContextAware接口定义:
public interface ApplicationContextAware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
因此,可以编程来操纵ApplicationContext创建的beans,通过ApplicationContext接口或已知接口的子类转为的引用(比如ConfigurableApplicationContext)。一种用途是通过编程方式获取其他bean,虽然很有用,但是它将代码耦合到了spring中并且不遵循控制反转,因此应该避免使用。ApplicationContext提供了其他方法如文件访问,发布应用程序事件和访问MessageSource这些附加功能。
自动装配是另一种获取ApplicationContext引用的替代,传统的构造器和byType自动装配模式是提供构造器参数或者setter方法参数,如果要获得更大的灵活性包括能够自动注入属性或者多参数方法请使用基于注解的自动装配(@Autowired),这样容器会自动注入其所需要的属性字段于构造函数参数或者方法参数中。
实现BeanNameAware接口,使得该类提供一个在其相关对象中定义的名称的引用。
public interface BeanNameAware {
void setBeanName(String name) throws BeansException;
}
这个接口方法在常用bean配置完成之后,初始化回调之前调用。
其他 Aware 接口
其他的Aware接口,可以使bean向容器提示其需要的其他基础依赖,一般在接口名称上可以看出其依赖类型。
- ApplicationContextAware: ApplicationContext的声明
- ApplicationEventPublisherAware:容器中封闭的事件上发布
- BeanClassLoaderAware:类加载器用于加载beanClass
- BeanFactoryAware:BeanFactory的声明
- BeanNameAware:声明bean的名称
- LoadTimeWeaverAware:定义的weaver ,用于在加载时处理类定义
- MessageSourceAware:配置消息处理策略,支持参数化和国际化
- NotificationPublisherAware:Spring JMX通知发布者
- ResourceLoaderAware:配置加载低级资源访问的策略
- ServletConfigAware:配置容器运行的ServletConfig,仅在网络中有效。
- ServletContextAware:配置容器运行的ServletContext,仅在网络中有效。
注意使用这些接口会将代码与Spring API绑定在一起,并且不遵循“控制反转”模式。
建议通过容器编程的方式使用这些接口来访问底层bean。
本文暂时没有评论,来添加一个吧(●'◡'●)