spring可以管理单例bean的生命周期,知道何时被创建,核实初始化,何时销毁,也可以进行某些通用资源申请,销毁前资源回收。对于prototype,容器只负责创建,之后就撒手不管自生自灭。容器不知道一共创建了多少prototype,也不知道他们什么时候会被销毁,所以容器没法管理prototype。
管理bean的生命周期有如下两个时机:
- 注入依赖关系后
- 即将销毁bean前
依赖关系注入后的行为
通过设定init-method属性指定某个方法或者实现InitializingBean接口指定需要实现的方法,我们就可以在全部属性设定之后通过那些方法执行特定行为。
bean销毁之前的行为
同样两个方法实现,一个是destroy-method,另一个是DisposableBean接口。
singleton的bean会随容器的关闭而销毁,但容器什么时候关闭呢?web中能够自动地在关闭web应用时关闭容器,而在非web下,为了完美地关闭容器,并在singleton销毁前回调方法,我们需要在jvm里注册一个关闭钩子(shutdown hook)。只需使用AbstractApplicationContext中提供的registerShutdownHook方法即可。
AbstractApplicationContext act = new ClassPathXmlApplicationContext("beans.xml"); act.registerShutdownHook();
为了简介,也可以为beans配置default-destroy-method属性和default-init-method属性统一配置两个方法名。
当属性与接口都有时,优先执行接口的方法。
协调作用域不同的bean
如果一个单例依赖一个prototype,那么prototype会先被创建然后注入到单例,之后再创建单例,而由于单例已经有了一个prototype,就再也不会有其他的prototype了,所以每次访问prototype都是同一个,相当于它也变成了单例。这就带来了不同步的问题。有连个解决思路
- 重新请求一个singleton,这样就得到新的prototype了。
- 使用lookup方法可以让容器重写bean的抽象或者具体方法,返回查找容器中其他bean的结果。
第一种没人用,因为太垃圾了,会造成耦合。下面来理解一下第二种什么意思。
比如说单例的Chinese中的hunt方法想要每次都使用不同的Dog实例,显然Dod需要是prototype的,此时就不能使用简单的依赖注入,而要使用lookup。
首先将调用者bean的实现类定义为抽象类,并定义一个抽象方法来获取bean。
public abstract class Chinese implements Person { private Dog dog; public abstract Dog getDog(); public void hunt() { System.out.println(getDog()); System.out.println(getDog().run()); } }
在它的bean中添加lookup-method子元素,告诉容器需要实现哪个抽象方法。容器会按照配置的参数自动创建一个Bean实例。
<bean id="chinese" class="com.cm.Chinese"> <lookup-method name="getDog" bean="gunDog"/> </bean> <bean id="gunDog" class="com.cm.GunDOg" scope="prototype"> <property name="name" value="旺财" </bean>
这段代码意义如下:lookup表明了getDog这个方法由Spring负责实现,怎么实现呢?通过gunDog这个bean。gunDog是一个实现了Dog接口的类,getDog的返回值也是接口类型Dog。Spring是怎么实现这个方法的呢?通过一个getDog方法,得到容器中的gunDog实例,然后返回这个实例。
这么折腾了一圈(由之前简单的注入变为,先写一个抽象方法,Spring去完成这个抽象方法,再利用方法的返回值实现依赖),就是为了每次都得到一个不同的prototype(因为依赖关系不是写死的,而是每次得到那同一个Chinese,都会跑一边getDog方法,自然也就得到一个新的prototype。注意目标bean,也就是Dog,必须是prototype的!