Bean的生命周期

x33g5p2x  于2022-02-21 转载在 其他  
字(6.8k)|赞(0)|评价(0)|浏览(493)

1、Bean的简介

在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象,除此之外,bean就与应用程序中的其他对象没有什么区别了。而bean的定义以及bean相互间的依赖关系将通过配置元数据来描述。

Spring中bean对象默认都是单例的,同一个beanname获取的bean对象都单例的。对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框架之后,每个Action都是单例的,那么对于Spring托管的单例Service Bean,Spring的单例是基于BeanFactory也就是Spring容器的,单例Bean在此容器内只有一个,Java的单例是基于JVM,每个JVM内只有一个实例。

2、Bean的实例化过程

①Aware接口及相关子接口

Aware接口从字面上翻译过来是感知捕获的含义。单纯的bean(未实现Aware系列接口)是没有知觉的;实现了Aware系列接口的bean可以访问Spring容器。这些Aware系列接口增强了Spring bean的功能,但是也会造成对Spring框架的绑定,增大了与Spring框架的耦合度。(Aware是“意识到的,察觉到的”的意思,实现了Aware系列接口表明:可以意识到、可以察觉到)

接口源码:

  1. public interface Aware{
  2. }

可以发现该接口并没有定义任何方法,所以这只是一个标识接口,该接口的子接口如下:

可以发现子接口,都以Aware结尾,那这些子接口有什么作用呢?

先进入子接口一探究竟

  1. public interface ApplicationEventPublisherAware extends Aware {
  2. void setApplicationEventPublisher(ApplicationEventPublisher var1);
  3. }
  1. public interface MessageSourceAware extends Aware {
  2. void setMessageSource(MessageSource var1);
  3. }

我们发现每个子接口都定义了set方法。而方法中的形参类别是接口Aware前面的内容,也就是当前Bean需要感知的内容。所以我们需要在Bean中声明相关的成员变量来接收。

②举例说明
  1. /**
  2. * 实现了
  3. * ApplicationContextAware
  4. * BeanClassLoaderAware
  5. * BeanFactoryAware
  6. * BeanNameAware
  7. * 接口
  8. */
  9. public class User implements ApplicationContextAware,BeanClassLoaderAware,BeanFactoryAware,BeanNameAware{
  10. private int id;
  11. private String name;
  12. // 保存感知的信息
  13. private String beanName;
  14. // 保存感知的信息
  15. private BeanFactory beanFactory;
  16. // 保存感知的信息
  17. private ApplicationContext ac;
  18. // 保存感知的信息
  19. private ClassLoader classLoader;
  20. public BeanFactory getBeanFactory() {
  21. return beanFactory;
  22. }
  23. public ApplicationContext getAc() {
  24. return ac;
  25. }
  26. public ClassLoader getClassLoader() {
  27. return classLoader;
  28. }
  29. public User(){
  30. System.out.println("User 被实例化");
  31. }
  32. public int getId() {
  33. return id;
  34. }
  35. public void setId(int id) {
  36. this.id = id;
  37. }
  38. public String getName() {
  39. return name;
  40. }
  41. public void setName(String name) {
  42. this.name = name;
  43. }
  44. public String getBeanName() {
  45. return beanName;
  46. }
  47. /**
  48. * 自定义的初始化方法
  49. */
  50. public void start(){
  51. System.out.println("User 中自定义的初始化方法");
  52. }
  53. @Override
  54. public String toString() {
  55. return "User [id=" + id + ", name=" + name + ", beanName=" + beanName + "]";
  56. }
  57. @Override
  58. public void setBeanClassLoader(ClassLoader classLoader) {
  59. System.out.println(">>> setBeanClassLoader");
  60. this.classLoader = classLoader;
  61. }
  62. @Override
  63. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  64. System.out.println(">>> setApplicationContext");
  65. this.ac = applicationContext;
  66. }
  67. @Override
  68. public void setBeanName(String name) {
  69. System.out.println(">>> setBeanName");
  70. this.beanName = name;
  71. }
  72. @Override
  73. public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
  74. System.out.println(">>> setBeanFactory");
  75. this.beanFactory = beanFactory;
  76. }
  77. }

测试类

  1. @Test
  2. public void test1() {
  3. ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
  4. User user = ac.getBean(User.class);
  5. System.out.println("beanFactory:"+user.getBeanFactory());
  6. System.out.println("beanName:"+user.getBeanName());
  7. System.out.println("applicationContext:"+user.getAc());
  8. System.out.println("classLoader:"+user.getClassLoader());
  9. System.out.println(user);
  10. }

3、Bean的生命周期

①执行过程:
    • Spring对bean进行实例化,默认bean是单例;
  • Spring对bean进行依赖注入;
  • 如果bean实现了BeanNameAware接口,Spring将bean的名称传给setBeanName()方法;
  • 如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory实例传进来;
  • 如果bean实现了ApplicationContextAware接口,它的setApplicationContext()方法将被调用,将应用上下文的引用传入到bean中;
  • 如果bean实现了BeanPostProcessor接口,它的postProcessBeforeInitialization()方法将被调用;
  • 如果bean中有方法添加了@PostConstruct注解,那么该方法将被调用;
  • 如果bean实现了InitializingBean接口,spring将调用它的afterPropertiesSet()接口方法,类似的如果bean使用了init-method属性声明了初始化方法,该方法也会被调用;
  • 如果在xml文件中通过标签的init-method元素指定了初始化方法,那么该方法将被调用;
  • 如果bean实现了BeanPostProcessor接口,它的postProcessAfterInitialization()接口方法将被调用;
  • 此时bean已经准备就绪,可以被应用程序使用了,他们将一直驻留在应用上下文中,直到该应用上下文被销毁;
  • 如果bean中有方法添加了@PreDestroy注解,那么该方法将被调用;
  • 若bean实现了DisposableBean接口,spring将调用它的distroy()接口方法。同样的,如果bean使用了destroy-method属性声明了销毁方法,则该方法被调用;

有时候,我们并没有实现那些接口,我们可以除去哪些接口,针对Bean的单例和非单例来描述下bean的生命周期。

②单例Bean

当scope=“singleton”,即默认情况下,会在启动容器时(即实例化容器时)时实例化。但我们可以指定Bean节点的lazy-init="true"来延迟初始化bean,这时候,只有在第一次获取bean时才会初始化bean,即第一次请求该bean时才初始化。如下配置:

  1. <bean id="serviceImpl" class="cn.csdn.service.ServiceImpl" lazy-init="true"/>

如果想对所有的默认返利bean都应用延迟初始化,可以在根节点beans设置default-lazy-init属性为true,如下所示:

  1. <beans default-lazy-init="true">

默认情况下,Spring在读取xml文件的时候,就会创建对象。在创建对象的时候先调用构造器,然后调用init-method属性值中所指定的方法。对象在被销毁的时候,会调用destroy-method属性值中所指定的方法(例如调用Container.destroy()方法的时候)。写一个测试类,代码如下:

  1. public class LifeBean {
  2. private String name;
  3. public LifeBean(){
  4. System.out.println("LifeBean()构造函数");
  5. }
  6. public String getName() {
  7. return name;
  8. }
  9. public void setName(String name) {
  10. System.out.println("setName()");
  11. this.name = name;
  12. }
  13. public void init(){
  14. System.out.println("this is init of lifeBean");
  15. }
  16. public void destory(){
  17. System.out.println("this is destory of lifeBean " + this);
  18. }
  19. }

beans.xml配置如下:

  1. <bean id="life_singleton" class="com.bean.LifeBean" scope="singleton"
  2. init-method="init" destroy-method="destory" lazy-init="true"/>

测试代码:

  1. public class LifeTest {
  2. @Test
  3. public void test() {
  4. AbstractApplicationContext container =
  5. new ClassPathXmlApplicationContext("life.xml");
  6. LifeBean life1 = (LifeBean)container.getBean("life");
  7. System.out.println(life1);
  8. container.close();
  9. }
  10. }
③非单例管理的对象

Prototype作用域和singleton刚刚好相反,singleton只会返回同一个对象,Prototype则是返回一个bean对应多个对象实例,Prototype作用域的bean会导致在每次对这个bean请求(将它注入到另一个bean中,或者以程序的方式调用getBean()方法)时都会创建一个新的bean实例,相当于执行newXxxBean()。Prototypes 原型类型,它在我们创建容器时并没有实例化,只会在我们获取bean的时候才会去创建一个对象,而且我们每次创建的对象都不是同一个对象,根据需要,对有状态的bean应该使用Prototype作用域,对无状态的的bean则可以使用singleton作用域。
来看看Prototype作用域的差别,其余代码同上 ,在。beans.xml中把scope改成prototype

  1. <bean id="life_prototype" class="cn.hsd.mybatis.entity.LifeBean" scope="prototype"
  2. init-method="init" destroy-method="destroy" lazy-init="true"/>

测试代码:

  1. @Test
  2. public void test3(){
  3. AbstractApplicationContext container = new ClassPathXmlApplicationContext("beans.xml");
  4. LifeBean life1 = (LifeBean) container.getBean("life_singleton");
  5. LifeBean life2 = (LifeBean) container.getBean("life_singleton");
  6. System.out.println(life1==life2);//true
  7. LifeBean life3 = (LifeBean)container.getBean("life_prototype");
  8. LifeBean life4 = (LifeBean)container.getBean("life_prototype");
  9. System.out.println(life3==life4);//false
  10. container.close();
  11. }

可以发现,对于作用域为prototype的bean,其destroy方法并没有被调用。如果bean的scope设为prototype时,当容器关闭时,destroy方法不会被调用。对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。
ln(life3==life4);//false

  1. container.close();

}

  1. 可以发现,对于作用域为prototypebean,其destroy方法并没有被调用。如果beanscope设为prototype时,当容器关闭时,destroy方法不会被调用。对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。

相关文章