乐趣区

关于后端:去字节面试直接让人出门左拐Bean-生命周期都不知道

前言

Spring Bean 的生命周期,面试时非常容易问,这不,前段时间就有个粉丝去字节面试,因为不会答复这个问题,一面都没有过。

如果只讲基础知识,感觉和网上大多数文章没有区别,然而我又想写得略微深刻一点。

思考很多同学不喜爱看源码,我就把文章分为 2 大部分,后面是基础知识,次要不便大家面试和学习,前面是源码局部,对源码感兴趣的同学能够持续往后面看。

1. 基础知识

1.1 什么是 IoC?

IoC,管制反转,想必大家都晓得,所谓的管制反转,就是把 new 对象的权力交给容器,所有的对象都被容器管制,这就叫所谓的管制反转。

IoC 很好地体现了面向对象设计法令之一 —— 好莱坞法令:“别找咱们,咱们找你”,即由 IoC 容器帮对象找相应的依赖对象并注入,而不是由对象被动去找。

了解好 IoC 的要害是要明确“谁管制谁,管制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”。

谁管制谁,管制什么?

传统 Java SE 程序设计,咱们间接在对象外部通过 new 进行创建对象,是程序被动去创立依赖对象。而 IoC 是由专门一个容器来创立这些对象,即由 IoC 容器来管制对象的创立。

  • 谁管制谁?当然是 IoC 容器管制了对象;
  • 管制什么?次要管制了内部资源获取(不只是对象,比方包含文件等)。

为何是反转,哪些方面反转了?

有反转就有正转,传统应用程序是由咱们本人在对象中被动管制去间接获取依赖对象,也就是正转,而反转则是由容器来帮忙创立及注入依赖对象。

  • 为何是反转?因为由容器帮咱们查找及注入依赖对象,对象只是被动的承受依赖对象,所以是反转;
  • 哪些方面反转了?依赖对象的获取被反转了。

1.2 Bean 生命周期

对 Prototype Bean 来说,当用户 getBean 取得 Prototype Bean 的实例后,IOC 容器就不再对以后实例进行治理,而是把管理权交由用户,尔后再 getBean 生成的是新的实例。

所以咱们形容 Bean 的生命周期,都是指的 Singleton Bean。

Bean 生命周期过程:

  • 实例化:第 1 步,实例化一个 Bean 对象;
  • 属性赋值:第 2 步,为 Bean 设置相干属性和依赖;
  • 初始化:初始化的阶段的步骤比拟多,5、6 步是真正的初始化,第 3、4 步为在初始化前执行,第 7 步在初始化后执行,初始化实现之后,Bean 就能够被应用了;
  • 销毁:第 8~10 步,第 8 步其实也能够算到销毁阶段,但不是真正意义上的销毁,而是先在应用前注册了销毁的相干调用接口,为了前面第 9、10 步真正销毁 Bean 时再执行相应的办法。

整个执行流程略微有些形象,上面咱们通过代码,来演示执行流程。

1.3 执行流程

创立一个 LouzaiBean。

public class LouzaiBean implements InitializingBean, BeanFactoryAware, BeanNameAware, DisposableBean {

    /**
     * 姓名
     */
    private String name;

    public LouzaiBean() {System.out.println("1. 调用构造方法:我出世了!");
    }

    public String getName() {return name;}

    public void setName(String name) {
        this.name = name;
        System.out.println("2. 设置属性:我的名字叫"+name);
    }

    @Override
    public void setBeanName(String s) {System.out.println("3. 调用 BeanNameAware#setBeanName 办法: 我要上学了,起了个学名");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("4. 调用 BeanFactoryAware#setBeanFactory 办法:选好学校了");
    }

    @Override
    public void afterPropertiesSet() throws Exception {System.out.println("6.InitializingBean#afterPropertiesSet 办法:退学注销");
    }

    public void init() {System.out.println("7. 自定义 init 办法:致力上学 ing");
    }

    @Override
    public void destroy() throws Exception {System.out.println("9.DisposableBean#destroy 办法:平淡的毕生闭幕了");
    }

    public void destroyMethod() {System.out.println("10. 自定义 destroy 办法: 睡了,别想叫醒我");
    }

    public void work(){System.out.println("Bean 应用中:工作,只有对社会没有用的人才放假。。");
    }
}

自定义一个后处理器 MyBeanPostProcessor。

public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("5.BeanPostProcessor.postProcessBeforeInitialization 办法:到学校报名啦");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("8.BeanPostProcessor#postProcessAfterInitialization 办法:终于毕业,拿到毕业证啦!");
        return bean;
    }
}

applicationContext.xml 配置文件(局部)。

<bean name="myBeanPostProcessor" class="demo.MyBeanPostProcessor" />
<bean name="louzaiBean" class="demo.LouzaiBean"
      init-method="init" destroy-method="destroyMethod">
    <property name="name" value="楼仔" />
</bean>

测试入口:

public class MyTest {public static void main(String[] args) {ApplicationContext context =new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        LouzaiBean louzaiBean = (LouzaiBean) context.getBean("louzaiBean");
        louzaiBean.work();
        ((ClassPathXmlApplicationContext) context).destroy();}
}

执行后果:

1. 调用构造方法:我出世了!2. 设置属性:我的名字叫楼仔
3. 调用 BeanNameAware#setBeanName 办法: 我要上学了,起了个学名
4. 调用 BeanFactoryAware#setBeanFactory 办法:选好学校了
5.BeanPostProcessor.postProcessBeforeInitialization 办法:到学校报名啦
6.InitializingBean#afterPropertiesSet 办法:退学注销
7. 自定义 init 办法:致力上学 ing
8.BeanPostProcessor#postProcessAfterInitialization 办法:终于毕业,拿到毕业证啦!Bean 应用中:工作,只有对社会没有用的人才放假。。9.DisposableBean#destroy 办法:平淡的毕生闭幕了
10. 自定义 destroy 办法: 睡了,别想叫醒我 

这个流程十分清晰,Bean 生命周期流程图能齐全对应起来。

1.4 扩大办法

咱们发现,整个生命周期有很多扩大过程,大抵能够分为 4 类:

  • Aware 接口:让 Bean 能拿到容器的一些资源,例如 BeanNameAware 的 setBeanName(),BeanFactoryAware 的 setBeanFactory();
  • 后处理器:进行一些前置和后置的解决,例如 BeanPostProcessor 的 postProcessBeforeInitialization() 和 postProcessAfterInitialization();
  • 生命周期接口:定义初始化办法和销毁办法的,例如 InitializingBean 的 afterPropertiesSet(),以及 DisposableBean 的 destroy();
  • 配置生命周期办法:能够通过配置文件,自定义初始化和销毁办法,例如配置文件配置的 init() 和 destroyMethod()。

2. 源码解读

留神:Spring 的版本是 5.2.15.RELEASE,否则和我的代码不一样!!!

下面的常识,网上其实都有,上面才是咱们的重头戏,让你跟着我走一遍代码流程。

2.1 代码入口

这里须要多跑几次,把后面的 beanName 跳过去,只看 louzaiBean。

进入 doGetBean(),从 getSingleton() 没有找到对象,进入创立 Bean 的逻辑。

2.2 实例化

进入 doCreateBean() 后,调用 createBeanInstance()。

进入 createBeanInstance() 后,调用 instantiateBean()。

走进示例 LouzaiBean 的办法,实例化 LouzaiBean。

2.3 属性赋值

再回到 doCreateBean(),持续往后走,进入 populateBean()。

这个办法十分重要,外面其实就是依赖注入的逻辑.

进入 populateBean() 后,执行 applyPropertyValues()

进入 applyPropertyValues(),执行 bw.setPropertyValues()

进入 processLocalProperty(),执行 ph.setValue()。

走进示例 LouzaiBean 的办法,给 LouzaiBean 赋值 name。

到这里,populateBean() 就执行结束,上面开始初始化 Bean。

2.4 初始化

咱们持续回到 doCreateBean(),往后执行 initializeBean()。

走进示例 LouzaiBean 的办法,给 LouzaiBean 设置 BeanName。

回到 invokeAwareMethods()。

走进示例 LouzaiBean 的办法,给 LouzaiBean 设置 BeanFactory。

第一次回到 initializeBean(),执行上面逻辑。

这里须要多循环几次,找到 MyBeanPostProcessor 的策略办法。

咱们本人定义的后置解决办法。

第二次回到 initializeBean(),执行上面逻辑。

走进示例 LouzaiBean 的办法,执行 afterPropertiesSet()。

返回 invokeInitMethods(),执行上面逻辑。

进入 invokeCustomInitMethod(),执行上面逻辑。

走进示例 LouzaiBean 的办法,执行 init()。

第三次回到 initializeBean(),执行上面逻辑。

咱们本人定义的后置解决办法。

到这里,初始化的流程全副完结,都是围绕 initializeBean() 开展。

2.4 销毁

当 louzaiBean 生成后,前面开始执行销毁操作,整个流程就比较简单。

走进示例 LouzaiBean 的办法,执行 destroy()。

回到 destroy(),执行上面逻辑。

走进示例 LouzaiBean 的办法,执行 destroyMethod()。

到这里,所有的流程全副完结,文章详细描述所有的代码逻辑流转,你能够齐全依据下面的逻辑,本人 debug 一遍。

3. 写在最初

咱们再回顾一下几个重要的办法:

  • doCreateBean():这个是入口;
  • createBeanInstance():用来初始化 Bean,外面会调用对象的构造方法;
  • populateBean():属性对象的依赖注入,以及成员变量初始化;
  • initializeBean():外面有 4 个办法,

先执行 aware 的 BeanNameAware、BeanFactoryAware 接口;

再执行 BeanPostProcessor 前置接口;

而后执行 InitializingBean 接口,以及配置的 init();

最初执行 BeanPostProcessor 的后置接口。

destory():先执行 DisposableBean 接口,再执行配置的 destroyMethod()。

对于 populateBean(),外面的外围其实是对象的依赖注入,这里也是常考的知识点,比方循环依赖

退出移动版