SpringBoot2.x基础篇—Bean的生命周期方法(与容器耦合)

JAVA && Spring && SpringBoot2.x — 学习目录

与容器的生命周期交互,即Spring容器启动后和容器销毁前对每个bean执行操作。

下文所说的声明周期方法,均是在创建bean时被调用。

创建bean的源码

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
        throws BeanCreationException {
      ....
    Object exposedObject = bean;
    try {
        //依赖注入,填充属性
        populateBean(beanName, mbd, instanceWrapper);
        //初始化的方法(在创建bean的时候被执行)
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    ...
    
    return exposedObject;
}
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
      ...
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        //CommandAnnotationBeanPostProcessor会回调@PostConstrut方法
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        //回调方法
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    ...
    if (mbd == null || !mbd.isSynthetic()) {
           //创建代理对象
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }
    return wrappedBean;
}
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
            throws Throwable {
    //判断是否实现InitializingBean接口,调用afterPropertiesSet()方法
    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
        ...
        else {
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }
    //判断是否含有Init方法,通过反射回调。
    if (mbd != null && bean.getClass() != NullBean.class) {
        String initMethodName = mbd.getInitMethodName();
        if (StringUtils.hasLength(initMethodName) &&
            !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}

1. 初始化方法

初始化方法.png

1.1 CommandLineRunner、ApplicationRunner接口

CommandLineRunner、ApplicationRunner接口是在容器成功启动后的最后一步回调。

在官方doc中:

在一个spring上下文中,多个CommandLineRunner可以被同时执行,并且执行顺序按照@Order注解的参数保持一致。

若是需要访问ApplicationArguments去代替掉字符串数组,可以考虑使用ApplicationRunner类。

//@Order(2)
@Component
public class ServerStartedReport implements CommandLineRunner{
    @Override
    public void run(String... args) throws Exception {
        System.out.println("===========ServerStartedReport启动====="+ LocalDateTime.now());
    }
}

1.2 InitializingBean接口

可以使用spring的InitializingBean接口的afterPropertiesSet来完成初始化。这个方法将在所有的属性被初始化后被调用。但是会在init方法前调用。

但如果是延迟加载的话,即使用@Lazy注解,可以让用户在首次使用的时候进行初始化。afterPropertiesSet也会在首次使用的时候进行初始化。

@Component
@Lazy
public class EventImpl implements InitializingBean {


    private String id;
    private  String name;


    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("来了!!!=========================================================");
    }

}

1.3 @PostConstruct注解

如果想在生成对象之后完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么无法在构造函数中实现,为此,可以使用@PostConstruct注解一个方法来完成初始化操作。

被@PostConstruct修饰的方法会在服务器加载Servlet(bean)的时候运行,并且只会被服务器调用一次,但是注意的是,该方法会在构造方法、属性依赖注入之后,init()方法之前运行

1.4 init()方法

在Spring中,我们将对象放入容器的方法一般有两种。

  1. 注解法,使用@Component注解等;
  2. 配置法,将bean注册到spring.xml中;

在SpringBoot中,我们可以使用@Configuration@Bean注解,将Bean放入到容器中。我们也可以指定init方法。初始化对象的时候,便可自动调用init方法执行。

@Configuration
public class DemoBeanConfiguration {

    @Bean(initMethod = "init",destroyMethod = "destroy")
    public DemoBean getBean(){
        DemoBean demoBean = new DemoBean();
        demoBean.setId("001");
        demoBean.setName("002");
        return demoBean;
    }
}
public class DemoBean implements InitializingBean {

    private String id;
    private  String name;

    public DemoBean() {
        System.out.println("初始化——构造方法!");
    }

    private void init(){
        System.out.println("初始化——init方法!");
    }
    private void destroy() {
        System.out.println("初始化——destroy方法!");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("初始化——InitializingBean接口");
    }
    @PostConstruct
    private void postConstruct(){
        System.out.println("初始化——@PostConstruct方法");
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

在容器初始化过程中便会按照如下的顺序执行初始化对象的操作。

初始化——构造方法!
初始化——@PostConstruct方法
初始化——InitializingBean接口
初始化——init方法!
---容器启动完毕后...
容器启动—CommandLineRunner接口方法!

1.4 SmartInitializingSingleton接口

实现SmartInitializingSingleton的接口后,当所有单例 bean 都初始化完成以后, Spring的IOC容器会回调该接口的 afterSingletonsInstantiated()方法。

使用方式:

import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.stereotype.Component;
 
@Component
public class MyRegister implements SmartInitializingSingleton {
 
    private ListableBeanFactory beanFactory;
 
    public MyRegister(ListableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }
 
    @Override
    public void afterSingletonsInstantiated() {
        String[] beanNames = beanFactory.getBeanNamesForType(IPerson.class);
        for (String s : beanNames) {
            System.out.println(s);
        }
    }

2. 销毁方法

和初始化方法对应,Spring也是提供了如下销毁对象的回调方法。

销毁方法.png

文章参考:

CommandLineRunner或者ApplicationRunner接口

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。