乐趣区

关于spring:详解Spring中Bean的作用域与生命周期

摘要:在利用 Spring 进行 IOC 配置时,对于 bean 的配置和应用始终都是比拟重要的一部分,同时如何正当的应用和创立 bean 对象,也是小伙伴们在学习和应用 Spring 时须要留神的局部,所以这一篇文章我就来和大家讲一下无关 Spring 中 bean 的作用域和其生命周期。

本文分享自华为云社区《详解 Spring 中 Bean 的作用域与生命周期》,原文作者:灰小猿。

在利用 Spring 进行 IOC 配置时,对于 bean 的配置和应用始终都是比拟重要的一部分,同时如何正当的应用和创立 bean 对象,也是小伙伴们在学习和应用 Spring 时须要留神的局部,所以这一篇文章我就来和大家讲一下无关 Spring 中 bean 的作用域和其生命周期。

一、Bean 的作用域

首先咱们来讲一下有对于 bean 的作用域,

个别状况下,咱们书写在 IOC 容器中的配置信息,会在咱们的 IOC 容器运行时被创立,这就导致咱们通过 IOC 容器获取到 bean 对象的时候,往往都是获取到了单实例的 Bean 对象,

这样就意味着无论咱们应用多少个 getBean()办法,获取到的同一个 JavaBean 都是同一个对象,这就是单实例 Bean,整个我的项目都会共享这一个 bean 对象。

在 Spring 中,能够在 <bean> 元素的 scope 属性里设置 bean 的作用域,以决定这个 bean 是单实例的还是多实例的。Scope 属性有四个参数,具体的应用能够看下图:

1、单实例 Bean 申明

默认状况下,Spring 只为每个在 IOC 容器里申明的 bean 创立惟一一个实例,整个 IOC 容器范畴内都能共享该实例:所有后续的 getBean()调用和 bean 援用都将返回这个惟一的 bean 实例。该作用域被称为 singleton,它是所有 bean 的默认作用域。也就是单实例。

为了验证这一说法,咱们在 IOC 中创立一个单实例的 bean,并且获取该 bean 对象进行比照:

<!-- singleton 单实例 bean
  1、在容器创立时被创立
  2、只有一个实例
  -->
<bean id="book02" class="com.spring.beans.Book" scope="singleton"></bean>

测试获取到的单实例 bean 是否是同一个:

@Test
public void test09() {
    // 单实例创立时创立的两个 bean 相等
    Book book03 = (Book)iocContext3.getBean("book02");
    Book book04 = (Book)iocContext3.getBean("book02");
    System.out.println(book03==book04);
}

失去的后果是 true;

2、多实例 Bean 申明

而既然存在单实例,那么就肯定存在多实例。咱们能够为 bean 对象的 scope 属性设置 prototype 参数,以示意该实例是多实例的,同时获取 IOC 容器中的多实例 bean,再将获取到的多实例 bean 进行比照,

<!-- prototype 多实例 bean
1、在容器创立时不会被创立,2、只有在被调用的时候才会被创立
3、能够存在多个实例
 -->
<bean id="book01" class="com.spring.beans.Book" scope="prototype"></bean>

测试获取到的多实例 bean 是否是同一个:

@Test
public void test09() {
    // 多实例创立时,创立的两个 bean 对象不相等
    Book book01 = (Book)iocContext3.getBean("book01");
    Book book02 = (Book)iocContext3.getBean("book01");
    System.out.println(book01==book02);
}

失去的后果是 false

这就阐明了,通过多实例创立的 bean 对象是各不相同的。

在这里须要留神:

同时对于单实例和多实例 bean 的创立也有不同,当 bean 的作用域为单例时,Spring 会在 IOC 容器对象创立时就创立 bean 的对象实例。而当 bean 的作用域为 prototype 时,IOC 容器在获取 bean 的实例时创立 bean 的实例对象。

二、Bean 的生命周期

1、bean 的初始和销毁

其实咱们在 IOC 中创立的每一个 bean 对象都是有其特定的生命周期的,在 Spring 的 IOC 容器中能够治理 bean 的生命周期,Spring 容许在 bean 生命周期内特定的工夫点执行指定的工作。如在 bean 初始化时执行的办法和 bean 被销毁时执行的办法。

Spring IOC 容器对 bean 的生命周期进行治理的过程能够分为六步:

  1. 通过结构器或工厂办法创立 bean 实例
  2. 为 bean 的属性设置值和对其余 bean 的援用
  3. 调用 bean 的初始化办法
  4. bean 能够失常应用
  5. 当容器敞开时,调用 bean 的销毁办法

那么对于 bean 的初始和销毁时执行的办法又该如何申明呢?

首先咱们应该在 bean 类外部增加初始和销毁时执行的办法。如上面这个 javabean:

package com.spring.beans;

public class Book {
    private String bookName;
    private String author;
    /**
     * 初始化办法
     * */
    public void myInit() {System.out.println("book bean 被创立");
    }
 
    /**
     * 销毁时办法
     * */
    public void myDestory() {System.out.println("book bean 被销毁");
    }
 
    public String getBookName() {return bookName;}
    public void setBookName(String bookName) {this.bookName = bookName;}
    public String getAuthor() {return author;}
    public void setAuthor(String author) {this.author = author;}
    @Override
    public String toString() {return "Book [bookName=" + bookName + ", author=" + author + "]";
    }
}

这时咱们在配置 bean 时,能够通过 init-method 和 destroy-method 属性为 bean 指定初始化和销毁办法,

<!-- 设置 bean 的生命周期
destory-method:完结调用的办法
init-method:起始时调用的办法
 -->
<bean id="book01" class="com.spring.beans.Book" destroy-method="myDestory" init-method="myInit"></bean>

这样当咱们在通过 IOC 容器创立和销毁 bean 对象时就会执行相应的办法,

然而这里还是有一点须要留神:

咱们下面说了,单实例的 bean 和多实例的 bean 的创立工夫是不同的,那么他们的初始办法和销毁办法的执行工夫就稍稍有不同。

  • 单实例下 bean 的生命周期

容器启动——> 初始化办法——>(容器敞开)销毁办法

  • 多实例下 bean 的生命周期

容器启动——> 调用 bean——> 初始化办法——> 容器敞开(销毁办法不执行)

2、bean 的后置处理器

什么是 bean 的后置处理器?bean 后置处理器容许在调用 初始化办法前后 对 bean 进行额定的解决

bean 后置处理器对 IOC 容器里的所有 bean 实例逐个解决,而非繁多实例。

其典型利用是:查看 bean 属性的正确性或依据特定的规范更改 bean 的属性。

bean 后置处理器应用时须要实现接口:

org.springframework.beans.factory.config.BeanPostProcessor。
在初始化办法被调用前后,Spring 将把每个 bean 实例别离传递给上述接口的以下两个办法:

postProcessBeforeInitialization(Object, String)调用前
postProcessAfterInitialization(Object, String) 调用后
如下是一个实现在该接口的后置处理器:

package com.spring.beans;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * 测试 bean 的后置处理器
 * 在这里要留神一点是为了呈现 bean 和 beanName,而不是 arg0、arg1,须要绑定相应的源码 jar 包
 * */
public class MyBeanPostProcessor implements BeanPostProcessor{

    /**
     * postProcessBeforeInitialization
     * 初始化办法执行前执行
     * Object bean
     * String beanName xml 容器中定义的 bean 名称
     * */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("【"+ beanName+"】初始化办法执行前...");
        return bean;
    }

    /**
     * postProcessAfterInitialization
     * 初始化办法执行后执行
     * Object bean
     * String beanName xml 容器中定义的 bean 名称
     * */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("【"+ beanName+"】初始化办法执行后...");
        return bean;
    }

}

将该后置处理器退出到 IOC 容器中:

<!-- 测试 bean 的后置处理器 -->
<bean id="beanPostProcessor" class="com.spring.beans.MyBeanPostProcessor"></bean>

因为当初咱们的 bean 对象是单实例的,所以容器运行时就会间接创立 bean 对象,同时也会执行该 bean 的后置处理器办法和初始化办法,在容器被销毁时又会执行销毁办法。咱们测试如下:

//*************************bean 生命周期 *****************
//    因为 ApplicationContext 是一个顶层接口,外面没有销毁办法 close,所以须要应用它的子接口进行接管
    ConfigurableApplicationContext iocContext01 = new ClassPathXmlApplicationContext("ioc1.xml");
 
    @Test
    public void test01() {iocContext01.getBean("book01");
        iocContext01.close();}

运行后果:

总结一下后置处理器的执行过程:

  1. 通过结构器或工厂办法 创立 bean 实例
  2. 为 bean 的 属性设置值 和对其余 bean 的援用
  3. 将 bean 实例传递给 bean 后置处理器的 postProcessBeforeInitialization() 办法
  4. 调用 bean 的 初始化 办法
  5. 将 bean 实例传递给 bean 后置处理器的 postProcessAfterInitialization() 办法
  6. bean 能够应用了
  7. 当容器敞开时调用 bean 的销毁办法

所以增加 bean 后置处理器后 bean 的生命周期为:

容器启动——后置处理器的 before…——> 初始化办法——> 后置处理器的 after…———>(容器敞开)销毁办法

点击关注,第一工夫理解华为云陈腐技术~

退出移动版