上一篇 Spring Boot IoC(四)依赖注入DI

五、Bean生命周期

简介

之前的Ioc学习中,我们只是关心如何正确的将Bean装配到Ioc容器中,而没有关心IoC容器如何装配和销毁bean的过程。有时候我们也需要自定义初始化或者销毁Bean的过程,以满足一些Bean的特殊初始化和销毁的要求。为了解决这些问题,我们有必要了解Spring Ioc初始化和销毁Bean的过程,这边是Bean的生命周期的过程。它大致分为Bean定义,bean初始化,bean的生存期和bean的销毁4个部分。

延迟依赖注入

spring的bean初始化如下图
在这里插入图片描述

先看前3步:

  • spring通过我们的配置,如@ComponentScan定义的扫描路径去找到带有@Component的类,这个过程就是一个资源定位的过程。
  • 一旦找到资源,那么它就开始解析,并将定义的信息保存起来,注意,此时还没有初始化bean,也就没有bean的实例,它有的仅仅是bean的定义。
  • 然后就会把bean定义发布到Spring IoC容器中。此时,IoC容器也只有bean的定义,还是没有bean的实例生成。

完成这3步只是一个资源定位并将Bean的定义发布到Ioc容器的过程。还没有Bean实例的成成,更没有依赖注入。在默认情况下,spring会如图所示去完成bean的实例化和依赖注入,这样从Ioc容器中就可以得到一个依赖注入完成的bean。但是,有些bean会受到变化因素的影响,这时候我们到希望是取出bean的时候完成初始化和依赖注入,换句话说就是让那些bean只是将定义发布到ioc容器而不做实例化和依赖注入,当我们取出来的时候才做初始化和依赖注入操作。

@Component中有一个配置像lazyInit,默认为false,不进行延迟初始化。因此在默认的情况下spring会对bean进行实例化和依赖注入对应的属性值。

我们首先定义两个接口和实现类

Shoes接口(需要注入的接口)

package com.lay.ioc.pojo.definiion;

public interface Shoes {
	
	public void put();
}

Person接口

package com.lay.ioc.pojo.definiion;

public interface Person {
	
	public void activity();

	public void setShoes(Shoes shoes);
}

Shoes接口实现类BasketShoes

package com.lay.ioc.pojo;

import org.springframework.stereotype.Component;

import com.lay.ioc.pojo.definiion.Shoes;
@Component
public class BasketShoes implements Shoes{

	@Override
	public void put() {
		System.out.println("穿篮球鞋【"+BasketShoes.class.getSimpleName()+"】去打球");
	}

}

Person实现类Programmer

@Component
public class Programmer implements Person, BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {
    private Shoes shoes = null;
    
    @Override
    public void activity() {
        this.shoes.put();
    }
    
    @Override
    @Autowired
    @Qualifier("basketShoes")
    public void setShoes(Shoes shoes) {
        System.out.println("延迟依赖注入");
        this.shoes = shoes;
    }
}

这里我们在setShoes方法里打印一句话

AppConfig配置类

package com.lay.ioc.config;
/**
 * @ComponentScan 标明会进行扫描
 * @author Lay
 */

@ComponentScan(basePackages= {"com.lay.ioc.*"})
@EnableAutoConfiguration
@Configuration
public class AppConfig {
}

测试类

package com.lay.ioc.config;

public class IoCTest {
    private static Logger log = LoggerFactory.getLogger(IocApplication.class);
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext(AppConfig.class);
        Person person=ctx.getBean(Programmer.class);//在这里打断点进行调试查看
        person.activity();
        ctx.close();
    }
   
}

我们在测试类的getBean方法上打断点,启动调试,会发现我们还并没有获取bean的实例,而日志就已经打印出了我们打印的语句,可见它是在ioc容器初始化时就执行了实例化和依赖注入。

为了改变这个情况,我们在配置类AppConfig加入lazyInit配置

package com.lay.ioc.config;
/**
 * @ComponentScan 标明会进行扫描
 * @author Lay
 */

@ComponentScan(basePackages= {"com.lay.ioc.*"},lazyInit=true)
@EnableAutoConfiguration
@Configuration
public class AppConfig {
}

然后进行测试,就会发现我们输出的语句“延迟依赖注入”并没有出现在控制台日志中。只有运行过断点处才会出现。这就是我我们把它修改为了延迟初始化。spring并不会在发布bean定义后马上为我们完成实例化和依赖注入。

Bean的生命周期

如果仅仅是实例化和依赖注入还是比较简单的,还不能完成进行自定义的要求。为了完成依赖注入的功能,spring在依赖注入之后,还提供了一系列的接口和配置来完成bean的初始化的过程。

图中描述的是整个ioc容器初始化bean的过程。

在这里插入图片描述

  • 注意这些接口都是针对生命而言的。在没有注释的情况下的流程节点都是针对单个Bean而言的,但是BeanProcessor是针对所有Bean而言的。
  • 即使你定义了ApplicationContextAware接口,但是有时候并不会调用,这要根据你的Ioc容器来决定,我们知道,Spring ioc容器最低额要求是实现BeanFactory接口。而不是实现ApplicationContext接口。对于那些没有实现ApplicationContext接口的容器,在生命周期对应的ApplicationContextAware定义的方法也是不会被调用的,只有实现了ApplicationContext接口的容器,才会在生命周期调用ApplicationAware所定义的setApplicationContext方法。

我们对Programmer进行改造模拟Bean的生命周期

Programmer

package com.lay.ioc.pojo;
@Component
public class Programmer implements Person, BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {
    //@Autowired
    private Shoes shoes = null;
    
    @Override
    public void activity() {
        this.shoes.put();
    }
    
    @Override
    @Autowired
    @Qualifier("basketShoes")
    public void setShoes(Shoes shoes) {
        System.out.println("延迟依赖注入");
        this.shoes = shoes;
    }
    
    @Override
    public void setBeanName(String name) {
        System.out.println("【"+this.getClass().getSimpleName()+"】调用BeanNameAware的setBeanName()方法");
    }
    
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("【"+this.getClass().getSimpleName()+"】调用BeanFactoryAware的setBeanFactory()方法");
    }
    

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("【"+this.getClass().getSimpleName()+"】调用ApplicationContextAware的setApplicationContext()方法");
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("【"+this.getClass().getSimpleName()+"】调用InitializingBean的afterPropertiesSet()方法");
    }
    
    @PostConstruct
    public void init() {
        System.out.println("【"+this.getClass().getSimpleName()+"】调用注解@PostConstruct定义的自定义初始化方法");
    }
    
    @PreDestroy
    public void destroy1() {
        System.out.println("【"+this.getClass().getSimpleName()+"】调用 注解@PreDestroy定义的自定义销毁方法");
    }
    
    @Override
    public void destroy() throws Exception {
        System.out.println("【"+this.getClass().getSimpleName()+"】调用DisposableBean的destroy()方法");
    }

}

这样,这个Bean就实现了生命周期中单个Bean可以实现的所有接口,并且通过注解@PostConstruct定义了初始化方法,通过注解@PreDestroy定义了自定义销毁方法。为了测试Bean的后置处理器,我们创建一个类BeanPostProcessorDemo来实现BeanPostProcessor,方法是针对所有Bean都生效的。

package com.lay.ioc.life;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class BeanPostProcessDemo implements BeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeanPostProcess调用postProcessBeforeInitialization()方法,参数【"+bean.getClass().getSimpleName()+"】 【"+beanName+"】");
        return bean;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeanPostProcess调用postProcessAfterInitialization()方法,参数【"+bean.getClass().getSimpleName()+"】 【"+beanName+"】");
        return bean;
    }
}

注意,这个Bean后置处理器将对所有的Bean生效。

测试

package com.lay.ioc.config;


public class IoCTest {
    private static Logger log = LoggerFactory.getLogger(IocApplication.class);
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext(AppConfig.class);
        Person person=ctx.getBean(Programmer.class);
        //关闭IoC容器
        ctx.close();
    }
}

观察日志

BeanPostProcess调用postProcessBeforeInitialization()方法,参数【BasketShoes】 【basketShoes】
BeanPostProcess调用postProcessAfterInitialization()方法,参数【BasketShoes】 【basketShoes】
18:08:31.354 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'basketShoes'
18:08:31.354 [main] DEBUG org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor - Autowiring by type from bean name 'programmer' to bean named 'basketShoes'
延迟依赖注入
【Programmer】调用BeanNameAware的setBeanName()方法
【Programmer】调用BeanFactoryAware的setBeanFactory()方法
【Programmer】调用ApplicationContextAware的setApplicationContext()方法
BeanPostProcess调用postProcessBeforeInitialization()方法,参数【Programmer】 【programmer】
18:08:31.354 [main] DEBUG org.springframework.context.annotation.CommonAnnotationBeanPostProcessor - Invoking init method on bean 'programmer': public void com.lay.ioc.pojo.Programmer.init()
【Programmer】调用注解@PostConstruct定义的自定义初始化方法
18:08:31.354 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Invoking afterPropertiesSet() on bean with name 'programmer'
【Programmer】调用InitializingBean的afterPropertiesSet()方法
BeanPostProcess调用postProcessAfterInitialization()方法,参数【Programmer】 【programmer】
18:08:31.354 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'programmer'
18:08:31.355 [main] INFO org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@490d6c15: startup date [Mon Oct 22 18:08:30 CST 2018]; root of context hierarchy
18:08:31.355 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'
18:08:31.356 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@57855c9a: defining beans org.springframework.boot.autoconfigure.info.ProjectInfoProperties,org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,restTemplateBuilder]; root of factory hierarchy
18:08:31.356 [main] DEBUG org.springframework.context.annotation.CommonAnnotationBeanPostProcessor - Invoking destroy method on bean 'programmer': public void com.lay.ioc.pojo.Programmer.destroy1()
【Programmer】调用 注解@PreDestroy定义的自定义销毁方法
18:08:31.356 [main] DEBUG org.springframework.beans.factory.support.DisposableBeanAdapter - Invoking destroy() on bean with name 'programmer'
【Programmer】调用DisposableBean的destroy()方法

从日志中可以看出,对于Bean后置处理器(BeanPostProcessor)而言,它对所有的Bean都起作用。而其他的接口则是对于单个Bean起作用。有时候Bean的定义可能使用的是第三方的类,此时可以使用注解@Bean来配置自定义初始化和销毁方法,如下

@Bean(initMethod="init",destroyMethod="destroy")

最后补一张Bean生命周期最全的图
在这里插入图片描述

下一篇 Spring Boot IoC(六)使用properties配置文件

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐