java 新建对象的生命周期_面试官:小伙子,你说一下java 对象建立和 Spring Bean 的生命周期吧...

理解对象和Bean的关系

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

java 对象如何被建立

在写代码时,咱们一般用下面的语句来建立一个对象:spring

A a=new A();

那么在建立对象的过程当中,究竟发生了什么呢。其实上面简单的一句话,在程序中发生了不少不少的事情。

首先,一个对象是须要内存去存放的。因此会有一个分配内存的过程。分配了内存以后,jvm便会开始建立对象,并将它赋值给 a 变量。而后再去初始化A中的一些属性,并执行A的构造方法。在初始化的过程当中,会先执行 static 代码块,再执行构造方法。除此以外,若是有父类,会优先父类的进行执行。大体以下图(图一)所示。

53061fa3586cfafe83ada9e35969859d.pngspringboot

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

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...");

}

}

}

运行打印的日志以下,经过分析日志,咱们能够得出,静态代码块先于构造方法,父类先于子类的执行顺序。jvm

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的。post

@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 生命周期大体以下图(图二):

a28999fce51b7e470b3c9593fef69434.pngthis

一样,咱们用代码来验证一下这个回调顺序。用来测试的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 以下:code

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 在帮咱们初始化对象管理对象的过程当中额外作了一些事情。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值