关于java:java-对象创建和-Spring-Bean-的生命周期吧

6次阅读

共计 6231 个字符,预计需要花费 16 分钟才能阅读完成。

了解对象和 Bean 的关系

java 是一种面向对象的语言,简而言之,所有皆对象。Bean 天然也是对象,只不过它是托管给 Bean 工厂治理着的对象。

java 对象如何被创立

在写代码时,咱们通常用上面的语句来创立一个对象:

 A a=new A();

复制代码 

那么在创建对象的过程中,到底产生了什么呢。其实下面简略的一句话,在程序中产生了很多很多的事件。首先,一个对象是须要内存去寄存的。所以会有一个分配内存的过程。调配了内存之后,jvm 便会开始创建对象,并将它赋值给 a 变量。而后再去初始化 A 中的一些属性,并执行 A 的构造方法。在初始化的过程中,会先执行 static 代码块, 再执行构造方法。除此之外,如果有父类,会优先父类的进行执行。大抵如下图(图一)所示。

如何验证对象初始化的过程呢?用上面一段代码验证。这段代码很简略,有动态变量的初始化,有构造方法,有继承。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class InitTest {private static final Logger logger = LoggerFactory.getLogger(InitTest.class);
    // 1. 动态变量初始化
    static String staticWord = "hello";
    // 2. 动态代码块
    static  {logger.info("staticWord ="+staticWord);
    }
    public InitTest(){logger.info("father construct method invoke...");
    }
    public static void main(String[] args) {new Son();
    }
    static class Son extends InitTest{
        static  {logger.info("son staticWord init in static");
        }
        public Son(){logger.info("son construct method invoke...");
        }
    }
}

复制代码 

运行打印的日志如下,通过剖析日志,咱们能够得出,动态代码块先于构造方法,父类先于子类的执行程序。

00:55:18.869 [main] INFO com.fc.study.InitTest - staticWord = hello
00:55:18.877 [main] INFO com.fc.study.InitTest - son staticWord init in static
00:55:18.877 [main] INFO com.fc.study.InitTest - father construct method invoke...
00:55:18.877 [main] INFO com.fc.study.InitTest - son construct method invoke...

复制代码 

Spring Bean 的生命周期

好的,有了对象的初始化程序,咱们就能够持续剖析 bean 的生命周期了。咱们能够先回顾一下本人平时是怎么定义一个 bean 的。

@Component
public class TestBean{
}

复制代码 
@Bean
public Object myObject(){}

复制代码 

罕用的是下面这两种:第一种是通过 Component 注解标注类;第二中形式是在办法上做 @Bean 的注解。咱们都晓得,注解标注的办法或者类,便会被 spring 扫描,并最终生成一个 bean。本文不具体探讨 bean 扫描的过程,只剖析 bean 初始化过程中的一些接口。那么,Spring 创立 Bean 就能够分为两大步骤,第一步是由 Springboot 扫描并获取 BeanDefinition;第二部,是初始化 Bean。spring 在 bean 的初始化过程为咱们提供了很多的接口,咱们能够用它们在 bean 的生成过程中做一些事件。这些接口均采纳回调的形式,以下是局部接口的介绍和回调机会。

接口

阐明

回调机会

BeanNameAware

如果你的 bean 实现了该接口的 setName 办法,则能够通过这个办法获取到 bean 名

产生在 bean 生命周期初期,早于构造方法

ApplicationContextAware

如果一个 bean 实现了该接口的 setApplicationContext 办法,则能够通过此办法获取到 ApplicationContext

调用于生命周期初期,在 BeanNameAware 和构造方法之间

InitializingBean

此接口的办法为 afterPropertiesSet

在 bean 工厂设置完 bean 的所有属性之后,会回调此办法。回调机会在构造方法之后

BeanPostProcessor

此接口有 postProcessBeforeInitialization、postProcessAfterInitialization 两个办法,别离对应了 Bean 生命周期的两个回调

这两个办法也在构造方法之后,不过别离在 InitializingBean 前后

如果将下面的接口退出,则 bean 生命周期大抵如下图(图二):

同样,咱们用代码来验证一下这个回调程序。用来测试的 Bean 代码如下,这个测试 bean 没有继承其余父类,仅用来验证 springboot 的接口在 bean 生命周期的调用机会:

package com.fc.study.beanLife;

import com.fc.study.InitTest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class TestBean implements BeanNameAware, InitializingBean, ApplicationContextAware {private static final Logger logger = LoggerFactory.getLogger(InitTest.class);
    private String beanName;
    static String staticWord;
    static  {logger.info("father staticWord init in static");
        staticWord="hi";
    }
    public TestBean(){logger.info("testBean construct method invoke...");
    }

    public void setBeanName(String name) {logger.info("setBeanName");
        this.beanName = name;
    }

    public void afterPropertiesSet() throws Exception {logger.info("afterProperties Set");
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {logger.info("applictionContextAware");
    }
}

复制代码 

同时,我定义了一个 BeanPostProcessor 如下:

package com.fc.study.beanLife;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class DefaultBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {private static Logger logger = LoggerFactory.getLogger(DefaultBeanPostProcessor.class);
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if(beanName.equals("testBean")) {logger.info(beanName + "postProcessBeforeInitialization 执行");
        }
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if(beanName.equals("testBean")) {logger.info(beanName + "postProcessAfterInitialization 执行");
        }
        return bean;
    }

    private ApplicationContext applicationContext;
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}
}

复制代码 

接下来是启动类:

package com.fc.study.beanLife;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan("com.fc.study")
public class SimpleSpringBoot {private static final Logger logger = LoggerFactory.getLogger(SimpleSpringBoot.class);

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(SimpleSpringBoot.class);

        logger.info("before get Bean");
        context.getBean(TestBean.class);
        logger.info("after get bean");
    }
}

复制代码 

运行启动类,打印出日志如下:

2021-01-23 02:18:09,764  INFO InitTest:29 - father staticWord init in static
2021-01-23 02:18:09,768  INFO InitTest:34 - testBean construct method invoke...
2021-01-23 02:18:09,768  INFO InitTest:38 - setBeanName
2021-01-23 02:18:09,768  INFO InitTest:48 - applictionContextAware
2021-01-23 02:18:09,768  INFO DefaultBeanPostProcessor:27 - testBean postProcessBeforeInitialization 执行
2021-01-23 02:18:09,768  INFO InitTest:44 - afterProperties Set
2021-01-23 02:18:09,768  INFO DefaultBeanPostProcessor:34 - testBean postProcessAfterInitialization 执行
2021-01-23 02:18:11,449  INFO SimpleSpringBoot:24 - before get Bean
2021-01-23 02:18:11,449  INFO SimpleSpringBoot:26 - after get bean

复制代码 

看看这个日志,印证了图二对各个接口调用机会论断。

总结

对象初始化,就是创建对象,并且初始化其属性的过程。首先是加载类文件,其次对象所须要的内存。而后动态代码块会被调用,最初是构造方法。Spring Bean 的初始化,除了创建对象这些步骤之外,还在其中交叉了一些生命周期的接口。首先在类加载实现后,会失去 BeanDefinition,而后通过这个定义来初始化,而不是间接通过加载后的类对象来生成对象。在动态代码块和构造方法两头,Spring 提供了几个 Aware 接口,如表格中的 BeanNameAware 和 ApplicationContextAware。在构造方法调用完结,并且 springboot 给 bean set 了所有属性之后,会调用 Initializing 接口和 BeanPostProcessor。以上,便是我了解的 spring bean 生命周期,它就是 spring 在帮咱们初始化对象治理对象的过程中额定做了一些事件。

参考:《2020 最新 Java 根底精讲视频教程和学习路线!》

链接:https://juejin.cn/post/692121…

正文完
 0