spring美文共赏

Java面试题:Spring IOC容器启动流程附源码

2021-12-10  本文已影响0人  程序员驴子酱

1. IOC容器概述

IOC和AOP是Spring框架的核心功能,而IOC又是AOP实现的基础,因而可以说IOC是整个Spring框架的基石。那么什么是IOC?IOC即控制反转,通俗的说就是让Spring框架来帮助我们完成对象的依赖管理和生命周期控制等等工作。从面向对象的角度来说,具有这种行为,完成这种工作的主体就可以形象的称之为IOC容器。从代码角度来看,IOC容器不过是Spring中定义的具有IOC基本功能的一些类的统称,这些类都遵循一些共同的接口规范,所以我们可以说实现某些接口的具体的实现类就是IOC容器。而IOC容器的启动流程,就是创建并初始化一个该实现类的实例的过程,在这个过程中要进行诸如配置文件的加载解析,核心组件的注册,bean 实例的创建等一系列繁琐复杂的操作,因而整个过程显得相对漫长,逻辑也相对复杂。

2. BeanFactory和ApplicationContext的区别

前面说到Spring中为容器类定义了一些接口规范,如下图所示


11.png

具体而言,Spring中的容器类可以分为两大类。

3. 解读IOC容器启动流程的意义

4. 初探IOC容器启动源码

启动Spring容器,本质上是创建并初始化一个具体的容器类的过程,以常见的容器类ClassPathXmlApplicationContext为例,启动一个Spring容器可以用以下代码表示

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

尽管只有短短的一行代码,但已经创建并启动了一个Spring的IOC容器。为了后面更好的理解,先来看下ClassPathXmlApplicationContext的类继承结构


12.jpg

关键的几个类已经用红色箭头标注了出来。

下面就正式开始容器启动流程的源码阅读
进入ClassPathXmlApplicationContext的构造方法,首先调用了重载构造函数

/**
 * Create a new ClassPathXmlApplicationContext, loading the definitions
 * from the given XML file and automatically refreshing the context.
 * @param configLocation resource location
 * @throws BeansException if context creation failed
 */
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
   this(new String[] {configLocation}, true, null);
}

这里有两点需要注意下:

/** Parent context */
private ApplicationContext parent;

重载函数的第三个参数即表示要创建的ClassPathXmlApplicationContext的父容器,不过这里只需要设置为null。关于Spring的父子容器,还有一些独特的访问规则,子容器可以访问父容器中的Bean,父容器不可以访问子容器中的Bean。不知道这个规则在使用Spring做web开发时可能会碰到一些匪夷所思的问题。

继续跟进源码

//设置父容器
super(parent);
//设置xml文件的路径参数
setConfigLocations(configLocations);
if (refresh) { //默认为true
    //启动Spring容器
    refresh();
}

设置完父容器和xml文件的路径信息后,终于看到了refresh()方法,正如前面提到的,这是真正启动Spring容器的方法,想要知道Spring IOC容器的启动流程,就要知道该方法内部都做了什么。

4.1 容器启动流程的不同阶段

为了更好的进行讲解,可以将容器启动的整个流程划分为以下五个阶段


33.png

4.2 前期准备

主程序

 public static void main(String[] args) {   
        System.out.println("现在开始初始化容器");
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        System.out.println("容器初始化成功");
       Person person = (Person)ac.getBean("person");
        System.out.println(person);
 }

xml

    <bean id="person" class="com.atguigu.pojo.Person" init-method="myInit" destroy-method="myDestory">
        <property name="name" value="尚硅谷"/>
        <property name="address" value="武汉"/>
        <property name="age" value="22"/>
    </bean>

结果运行

容器初始化成功
Person{name='尚硅谷', address='武汉', age=22, beanFactory=org.springframework.beans.factory.support.DefaultListableBeanFactory@694e1548: defining beans [person]; root of factory hierarchy, beanName='person'}

(1) 配置的<bean>是怎么转换成Person对象的呢?

(2) 配置的value值是怎样设置到person中的呢?

(3) spring容器怎样管理该对象的呢?

请带着这些问题继续往下看

4.3 基础组件

spring底层容器,定义了最基本的容器功能,注意区分FactoryBean


191.jpg

扩展于BeanFactory,拥有更丰富的功能。例如:添加事件发布机制、父子级容器,一般都是直接使用ApplicationContext。


12.jpg

bean配置文件,一般为xml文件。可以理解为保存bean信息的文件。


13.jpg

beandifinition定义了bean的基本信息,根据它来创造bean


14.jpg

4.4 容器启动过程

(1)资源定位:找到配置文件Resource

(2)BeanDefinition载入和解析: 将配置文件解析成BeanDefinition

(3)BeanDefinition注册:将BeanDefinition向Map中注册 Map<name,beandefinition>

(4)bean的实例化和依赖注入

 此过程由getBean()方法触发
15.jpg 16.jpg

创造 bean


17.jpg

实现依赖注入


18.jpg 19.jpg

此过程根据上述的BeanDefition,

(1)通过反射或者Cglib的方式创造bean

(2)根据配置的依赖将所需要的bean注入进来,此过程会递归调用getBean()方法。

(3)根据bean的scope决定是否缓存该Bean,一般情况为单例。容器会缓存该对象。

这个过程大概可以理解为

将原材料进行加工,创造可以直接利用的产品。

到此,spring容器就可以对外提供服务了。

5. 总结

容器启动的过程可以分为2大步:

(1)获取、解析、注册配置信息,将配置的文件信息转换Map<name,beanDefinition>

(2)根据上述的Map<name,beanDefinition>去实例化bean,并完成以来注入


44.jpg

以上是根据传统的xml形式配置Bean,现在很少用,现在用的比较多的是注解和javaConfig的形式配置,但换汤不换药,只是容器获取Map<name,beanDefition>的过程变了而已。这也是容器容器初始化步骤细化的一个好处。易于扩展。

spring容器的启动过程由spring框架封装好了,并不需要我们手动编程,但理解其启动原理,更有利于我们对spring的使用和扩展。
上一篇 下一篇

猜你喜欢

热点阅读