对于Singleton做用域的Bean,Spring容器将会跟踪它们的生命周期,容器知道什么时候实例化结束、什么时候销毁。Spring能够管理Bean在实例化结束以后和Bean销毁以前的行为。
java
Bean依赖关系注入以后的行为:app
Spring提供了两种方式在Bean所有属性设置成功后执行特定的行为:
ide
在Spring配置文件中使用init-method属性:这个属性指定某个方法在Bean所有依赖关系设置结束后自动执行。这个方法写在Bean里面。使用这种方法不须要将代码与Spring耦合在一块儿,代码污染小,推荐使用。this
让Bean实现InitializingBean接口:该接口提供了一个afterPropertiesSet() throwsException方法,在Bean里面实现它。spa
Spring容器会在为该Bean注入依赖关系后,调用该Bean实现的afterPropertiesSet方法。先看例子:
设计
public interface Animal { public void eatFood(); }
public class Dog implements Animal, InitializingBean { private Food food; public Dog() { System.out.println("Spring实例化主调Bean...Dog实例"); } public void setFood(Food food) { System.out.println("Spring执行依赖关系注入..."); this.food = food; } //Animal的方法 @Override public void eatFood() { System.out.println(food.getName() + "真好吃"); } //自定义的用于在Spring中配置的当Bean初始化完成时调用的方法 public void init() { System.out.println("正在执行初始化:init方法..."); } //实现InitializingBean接口中的方法 @Override public void afterPropertiesSet() throws Exception { System.out.println("正在执行初始化:afterPropertiesSet方法..."); } }
public class Food { private String name; public void setName(String name) { this.name = name; } public String getName() { return name; } public Food() { System.out.println("Spring实例化依赖Bean..."); } }
上面的程序中定义了一个普通的init方法,实际上这个方法名是任意的,并不必定叫init,Spring也不会对这个init方法进行任何特别的处理。只是接下来会在Spring配置文件中使用init-method属性指定该方法是一个“生命周期”方法。
code
增长init-method="init" 来指定init方法应在Bean的所有属性设置结束后自动执行,若是它不实现InitializingBean接口,上面的Dog类没有实现任何Spring接口,只是增长了一个普通的init方法。它依然是一个普通的Java文件,代码没有污染。下面是Spring配置文件:xml
<!-- 使用init-method="init"来指定Bean的所有属性设置结束后执行的方法 --> <bean id="dog" class="com.abc.Dog" init-method="init"> <property name="food" ref="food" /> </bean> <bean id="food" class="com.abc.Food"> <property name="name" value="骨头" /> </bean>
使用主程序获取、并调用Dog类的eatFood方法:
接口
public class Test { public static void main(String args[]) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Dog d = context.getBean("dog", Dog.class); d.eatFood(); } }
输出结果:生命周期
Spring实例化依赖Bean...Food实例 Spring实例化主调Bean...Dog实例 Spring执行依赖关系注入... 正在执行初始化:afterPropertiesSet方法... 正在执行初始化:init方法... 骨头真好吃
经过上面的例子能够看出:当Spring将Food注入到Dog以后——也就是完成依赖以后,程序先调用afterPropertiesSet方法进行初始化,在调用init-method中指定的方法进行初始化。
对于实现InitializingBean接口的Bean,无需使用init-method属性来指定初始化方法,配置该Bean实例与普通Bean实例彻底同样,Spring容器会自动检测Bean实例是否实现了特定生命周期接口,并决定是否须要执行生命周期方法。Spring在为Bean完成注入全部依赖关系后,会自动调用该Bean实现的afterProperties方法。但InitializingBean接口污染了代码,是侵入式设计,所以不推荐使用。
另外,从上面的执行结果能够看出,若是既实现了InitializingBean接口又使用了init-method来指定初始化方法,那么两个初始化方法都会被执行,先执行接口中的afterProperties方法,再执行自定义的初始化方法。
Bean销毁以前的行为:
与定制初始化行为类似,Spring也提供了两种方式定制在Bean销毁以前的特定行为:
使用destroy-method属性:指定某个方法在Bean销毁以前被自动执行。使用这种方法,不须要将代码与Spring的接口耦合在一块儿,代码污染小,推荐使用。
实现DisposableBean接口:该接口提供了一个destroy() throws Exception的方法。在Bean里面实现它,这个方法将在Bean销毁以前被Spring调用。
例子与前文类似,这里不赘述。
singleton做用域的Bean一般会随着容器的关闭而销毁,但问题是:ApplicationContext容器在何时关闭呢?在基于Web的ApplicationContext实现中,系统已经提供了相应的代码保证关闭Web应用时恰当的关闭Spring容器。但对于一个非Web应用的环境下,为了让Spring容器优雅的关闭,并自动调用singleton上的相应回调方法,则须要在JVM里面注册一个关闭钩子(shutdown hook),这样就能够保证Spring容器被恰当关闭,并自动执行singleton的Bean里面的相应回调方法。例如:
public class Test { public static void main(String args[]) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Dog d = context.getBean("dog", Dog.class); d.eatFood(); //为Spring容器注册关闭钩子 context.registerShutdownHook(); } }
除此以外,若是Spring容器中不少Bean都须要指定特定的生命周期行为,则能够考虑使用<beans>的default-init-method属性和default-destroy-method属性,这两个属性的做用相似于<bean>的init-method和destroy-method属性的做用。但因为前两个属性是<beans>标签的,所以对标签中的全部Bean都有效。即:若是<beans>标签中的Bean配置了default-init-method="init",那么若是<beans>标签中的Bean配置了init方法,则该方法会被自动调用。
下图显示了Spring容器中Bean实例完整的生命周期行为:
须要指出的是,当Bean实现了ApplicationAware、BeanNameAware接口以后,Spring容器会在该Bean初始化完成以后——也就是init-method属性指定的方法(若是有)以后,再来回调setApplicationContext(ApplicationContext context)和setBeanName(String beanName)方法。