一、自定义bean的生命周期
通过实现spring的InitializingBean和DisposableBean接口,可以让容器来管理bean的生命周期。容器在调用afterPropertiesSet()方法后和调用destroy()方法前会允许bean在初始化和销毁bean时执行以下操作,但使用这些接口就与springAPI产生了耦合。
解耦合的处理方式有两种:
使用JSR-250的@PostConstruct和@PreDestroy注解是spring应用生命周期回调的最佳实践。
使用init-method和destroy-method的定义来解耦spring接口。
spring框架使用BeanPostProcessor接口的实现来处理接口的回调,BeanPostProcessor能找到并调用合适的方法。如果需要定制spring不直接提供的生命周期行为,可自行实现一个BeanPostProcessor。
除初始化回调和销毁回调外,spring管理的对象也实现了Lifecycle接口,让管理的对象在容器的生命周期内启动或关闭。
1、初始化回调
org.springframework.beans.factory.InitializingBean接口允许bean在所有必要依赖配置完成后执行初始化bean。
不建议使用InitializingBean接口,否则会将代码耦合到spring特定接口上。使用@PostConstruct注解或指定一个POJO的实现方法的方式更好。在基于XML的配置元数据时可以使用init-method属性来指定一个没有参数的方法。使用Java配置时也可以使用@Bean中的init-method属性初始化回调。示例如下:
public class InitBean {
public void init(){...}
}
以下方式等效于以上方式,但会耦合到spring中:
public class InitBean implements InitializingBean {
public void afterPropertiesSet(){...}
}
2、销毁回调
实现org.springframework.beans.factory.DisposableBean接口就可以让容器通过回调来销毁bean锁引用的资源。
与InitializingBean接口类似,同样不推荐使用。使用@PreDestroy注解或指定一个bean支持的配置方法或在基于XML的配置元数据中,在bean标签上指定destroy-method属性。在基于Java的配置中可以配置@Bean中的destroy-method属性实现销毁回调。示例如下:
public class DesBean {
public void cleanup(){...}
}
以下方式等效于以上方式,但会耦合到spring中:
public class DesBean implements DisposableBean {
public void destroy(){...}
}
3、结合生命周期机制
spring2.5之后,有3种方式来控制bean的生命周期行为:
InitializingBean和DisposableBean回调接口
自定义的init()和destroy()方法
使用@PostConstruct和@PreDestroy注解
如果一个bean配置了多个生命周期机制,并且含有不同的方法名,执行顺序如下:
初始化时:
1)包含@PostConstruct注解的方法
2)在InitializingBean接口中的afterPropertiesSet()方法
3)自定义的init()方法
销毁时:
1)包含@PreDestroy注解的方法
2)在DisposableBean接口中的destroy()方法
3)自定义的destroy()方法
4、启动和关闭回调
Lifecycle接口中为任何有生命周期需求的对象定义了一些基本方法,spring管理的任何对象都可以实现其接口。当ApplicationContext接收到了启动或停止信号时,它会通知所有上下文包含的生命周期对象,通过LifecycleProcessor接口来串联上下文中的Lifecycle来实现对象。
LifecycleProcessor是对Lifecycle的扩展,它增加了2个方法来对上下文的刷新和关闭作出反应。
如果不同bean之间存在depends-on的关系,被依赖的一方需要更早的启动或关闭,有时直接的依赖是未知的,开发者可能并不值得哪些类型需要更早的进行初始化。SmartLifecycle接口定义了另外一种选项,即其父接口Phased中的getPhase()方法。
当启动时,拥有最低phased的对象优先启动,当关闭时,按相反的顺序。如果一个对象实现了SmartLifecycle接口,其getPhase()返回了Integer.MIN_VALUE,就会让该对象最早启动,最晚销毁。如果getPhase()返回了Integer.MAX_VALUE,就会让该对象最晚启动,最早销毁。当使用phased的值时,需要知道正常没有实现SmartLifecycle的Lifecycle对象的默认值,此值为0。任何负值都会将标明对象在标准组件启动前启动,在标准组件销毁后销毁。
SmartLifecycle定义了一个stop()回调函数,任何实现了其接口的方法都必须在关闭流程完成后调用回调中的run()方法,这样可以使其异步关闭。LifecycleProcessor的默认实现DefaultLifecycleProcessor会等到配置的时间超时后再调用回调,每阶段的超时默认30S。可以通过定义一个名为lifecycleProcessor的bean来覆盖默认的生命周期处理器,若需要配置超时时间,可如下配置:
SmartLifecycle接口定义回调方法来刷新和关闭上下文,如果关闭则说明stop()已被调用,就会驱动关闭流程;如果上下文正在关闭,就不会发生这种情况。刷新的回调会使用SmartLifecycle的另一特性:当上下文刷新完毕就会调用回调,默认的生命周期处理器会检查每一个SmartLifecycle对象的isAutoStartup()返回值。若为true,对象将会自动启动而非等待明确的上下文调用,或调用自己的start()。phased的值及depends-on会决定对象启动和销毁的顺序。
5、在非web应用中关闭Spring IoC容器
在桌面客户端等非web环境下使用Spring IoC容器,需要在JVM上注册一个关闭的钩子,确保在关闭IoC容器时能够调用相关的销毁方法来释放引用的资源,同时也需要正确的配置和实现销毁回调。
在ConfigurableApplicationContext接口调用registerShutdownHook()方法来注册销毁的钩子。示例如下:
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
...
ctx.registerShutdownHook();
6、ApplicationContextAware和BeanNameAware
当ApplicationContext在创建实现了org.springframework.context.ApplicationContextAware的对象时,该对象的实例会包含一个到ApplicationContext的引用。这样bean可以通过编程的方式操作和创建ApplicationContext。
通过ApplicationContext接口或通过将引用转换为已知接口的子类,如ConfigurableApplicationContext,其中一个用法是可以通过编程的方式来获取其他bean。但不建议这样做,这样代码会耦合到spring。ApplicationContext的其他方法可以提供资源访问、发布应用事件、进入MessageSource等功能。
自动装载也是获得ApplicationContext的一种方式,构造函数和通用类型的装载方式,是指可以通过构造函数或setter方法注入,也可以通过注解注入的方式。
当ApplicationContext创建一个实现了org.springframework.beans.factory.BeanNameAware接口的类,这个类就可以针对其名称进行配置。这个回调的调用发生在属性配置完成后,初始化回调之前,如InitializingBean.afterPropertiesSet()及自定义的初始化方法等。
7、其他Aware接口
spring还提供了其他Aware接口来让bean通知容器,这些bean需要一些具体的注入的依赖信息,如下:
ApplicationContextAware:声明的ApplicationContext
ApplicationEventPublisherAware:ApplicationContext中的事件发布器
BeanClassLoaderAware:加载bean使用的类加载器
BeanFactoryAware:声明的BeanFactory
BeanNameAware:bean的名称
LoadTimeWeaverAware:加载期间处理类定义的Weaver
MessageSourceAware:解析消息的配置策略
NotificationPublisherAware:spring JMX通知发布器
ResourceLoaderAware:配置的资源加载器
上面这些接口的不建议使用,因为其会违反IOC原则。
二、定义继承
bean的定义可以包含构造方法参数、属性值、容器特定的信息等。子bean定义可以从父bean定义的配置元数据来继承,它可以覆盖或添加一些所需的值。父子bean是一种典型的模板形式。
如果编程式的使用ApplicationContext接口,子bean的定义可以通过ChildBeanDefinition类来表示,但使用更多的是在类似于ClassPathXmlApplicationContext中声明式的配置bean的定义。如果使用基于XML的配置时,可以在子bean中使用parent属性,用于标识父bean。
子bean如果没有指定class,它将使用父bean定义的class或进行重载。在重载情况下子bean必须与父bean兼容,即它必须接受父bean的属性值。
子bean定义可以从父bean继承作用域、构造器参数、属性值和可重写的方法,还可以增加新值。开发者指定的任何作用域、初始化方法、销毁方法、静态工厂方法设置,都会覆盖相应的父bean设置。其余的设置总是取自子bean的定义,如depends-on、autowire mode、dependency check、singleton、scope和lazy init等。
上例中使用abstract属性明确表明父bean是抽象的,如果父bean定义没有明确指出所属类,则需要标记父bean定义为abstract。
上例中只有一个bean定义为abstract,它只能作为一个纯粹的为子bean定义的bean模板。独立使用这样一个abstract的父bean,把它作为另一个bean的引用,或根据这个父bean的id显式调用getBean(),都将返回错误。
ApplicationContext默认会预实例化所有单例bean,如果把一个bean定义仅作为模板来用,同时给它指定了class属性,就必须设置abstract=true,否则ApplicationContext就会预实例化这个abstract bean。
--参考文献《Srping5开发大全》