首页 > 技术文章 > Spring Bean的生命周期

chy18883701161 2019-07-03 21:07 原文

 Spring容器可以管理singleton作用域的Bean的生命周期,可以调用创建、初始化、销毁等生命周期的方法。

对于prototype作用域的Bean,Spring容器只负责创建,创建后Bean的实例就交给客户端代码来管理,Spring容器不再跟踪其生命周期。

 


 

 

Bean的生命周期

1、检查此Bean是否有依赖的Bean,如果所依赖的Bean尚未实例化,则先实例化依赖的bean;

   如果依赖的bean都已实例化,则调用此Bean的构造方法或工厂方法创建此Bean的实例

 

2、利用依赖注入注入所需依赖(给成员变量赋值)

 

3、如果Bean实现了BeanNameAware接口,则调用setBeanName()方法传入当前Bean的name。

4、如果Bean实现了BeanFactoryAware接口,则调用setBeanFactory()传入当前工厂实例的引用

5、如果Bean实现了ApplicationContextAware接口,则调用setApplicationContext()方法传入当前ApplicationContext实例的引用

 

6、如果BeanPostProcessor和Bean关联,则调用预初始化方法postProcessBeforeInitialzation()进行加工操作,Spring AOP即利用此实现。

 

7、容器前处理(可以实现一些自定义的操作)

 

8、如果BeanPostProcessor和Bean关联,则调用初始化方法postProcessAfterInitialization()。此时,Bean已经可以被正常使用了。

 

9、如果指定了作用域为singleton,则将实例放在Spring IoC的缓存池中,并触发Spring容器对该Bean的生命周期管理,如果指定作用域为prototype,则将该Bean交给调用者,由调用者管理该Bean的生命周期。

 

10、容器后处理(销毁bean的实例时要做的操作)

 

 

 

 

说明:

以上接口中,均只有一个方法。

并不建议让Bean实现多个接口,因为继承多个接口会使代码耦合较高。

 

 


 

 

容器前处理

将bean的实例放到spring容器之前做一些处理。

有3种方式可以实现容器前处理:

  • 使用@PostConstruct标注要调用的方法
  • 实现InitializingBean接口,在afterPropertiesSet()中写代码
  • 在xml中使用init-method属性指定要调用的方法

 

 

容器后处理

在spring容器销毁bean的实例之前做一些处理。

同样有3种方式可以实现容器后处理:

  • 使用@PreDestroy标注要调用的方法
  • 实现DisposableBean接口,在destory()中写代码
  • 在xml中使用destory-method属性指定要调用的方法

 

 

不管是容器前处理,还是容器后处理,这三种方式都可以一起使用。

一起使用时,都是先执行注解标注的方法,再执行接口中的方法,最后执行xml中指定的方法。

 

有的教程说 init-method <=> @PostConstruct , destroy-method  <=>  @PreDestroy,

如果只使用三种中的一种,这三种的效果是一样的,这句话是对的;

如果同时使用2种或3种,则执行有先后之分,这句话是错的。

  

 


 

 

 

lazy-init

<bean name="" class="" lazy-init="true" />

默认scope="singleton",spring容器启动时就实例化scope="singleton"的bean。

 

lazy-init配置是否延迟加载bean:

  • true   延迟加载,需要时才创建
  • false   默认值。不延迟加载,spring容器启动时就实例化scope="singleton"的bean

 

虽然所有<bean>都能配置lazy-init,但lazy-init属性只对scope="singleton"的<bean>起作用。

 

 


 

 

示例

依赖:

@Component
@Scope("prototype")
public class B {
    public B(){
        System.out.println("正在创建B类实例...");
    }
}

为避免B类在spring容器启动时就创建实例,将其作用域设置为prototype。

 

 

主调类:

@Component
public class A implements InitializingBean, DisposableBean {
    private B b;

    @Autowired
    public A(B b) {
        System.out.println("正在执行A的构造函数...");
        this.b = b;
    }

    @PostConstruct
    public void postConstruct(){
        System.out.println("正在执行@PostConstruct注解指定的方法...");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("正在执行InitializingBean接口中的afterPropertiesSet()方法...");
    }

    public void xmlInit(){
        System.out.println("正在执行xml中init-method指定的方法...");
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("正在执行@PreDestroy注解指定的方法...");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("正在执行DisposableBean接口中的destroy()方法...");
    }

    public void xmlDestroy(){
        System.out.println("正在执行xml中destroy-method指定的方法...");
    }
}

 

 

xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.chy.bean"/>
    <bean name="a" class="com.chy.bean.A" init-method="xmlInit" destroy-method="xmlDestroy" />
</beans>

 

 

 

测试类:

public class Test {
    public static void main(String[] args) {
        //创建spring容器的实例(会实例化所有scope="singleton"的bean)
        ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-config.xml");

        //关闭spring容器(会销毁所有spring容器中的bean实例)
        applicationContext.close();
    }
}

在基于Web的Spring容器中,WEB服务器(比如Tomcat)已经提供了对应的设置,在Web应用关闭时,会自动关闭Spring容器。

在非Web的Spring容器中,需要手动调用close()方法关闭spring容器。

 

ApplicationContext即Spring容器,是一个接口,没有声明close()方法,如果把spring容器声明为ApplicatioContext,则不能使用close()方法。

ApplicationContext的实现类提供了close()方法,要声明为实现类(比如ClassPathXmlApplicationContext)才能使用close()方法。

 

 

运行程序,控制台输出如下:

正在创建B类实例...
正在执行A的构造函数...
正在执行@PostConstruct注解指定的方法...
正在执行InitializingBean接口中的afterPropertiesSet()方法...
正在执行xml中init-method指定的方法...
正在执行@PreDestroy注解指定的方法...
正在执行DisposableBean接口中的destroy()方法...
正在执行xml中destroy-method指定的方法...

 

 

执行过程分析:

A依赖B,B是prototype,需要时才创建实例,A是singleton,spring容器启动时就创建实例。

(1)A所依赖的B尚未实例化,先创建B的实例

(2)创建A的实例,注入B的实例

(3)执行容器前处理(先注解,再接口,最后xml)

(4)A的作用域是singleton,将创建的A的实例放到spring容器中(如果是prototype,则不放到容器中)

(5)spring容器关闭,销毁spring容器中所有的bean,执行容器后处理,销毁A的实例。

推荐阅读