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接口

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355