Spring IOC容器初始化之refresh()

前言

知其然知其所以然,是我们做技术所追求的,但在繁忙的工作中也会难于顾及与此。从本文开始,抽出时间对Spring 5.xIOC容器初始化的过程进行追踪梳理,探究学习Spring这件艺术品。

关于Spring的优势,每人都有一份见地:

  • 解耦
    我们把对象的创建、配置、生命周期控制权交给Spring来管理,由IOC容器来管理对象的创建、配置、和生命周期,所以称作控制反转(Inversion of Control)。IOC也称为DI(Dependancy Injection),是指在IOC容器创建时,通过构造函数参数、工厂方法参数或从工厂方法构造或返回对象实例后,设置该对象依赖其它对象的过程称作依赖注入
  • AOP
    它是OOP的补充,称作面向切面编程,通过它我们再传统业务项目上可以对日志、权限、异常处理等等。
  • 声明式事务
    通过使用@Transactional即可灵活的进行事务控制,让我们从传统事务管理代码中脱离出来。
  • 主流框架的集成支持
  • 各种接口封装
  • 等等

版本

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.4</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.18</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

1. IOC容器

org.springframework.context.ApplicationContext代表着Spring IOC容器,它负责实例化、配置及Bean的组装。容器通过读取配置元数据来获取需要实例化、配置和组装对象的各种指令。配置元数据通常使用XML、Java注解、Java代码来表示。它定义了组成应用程序的对象及对象间的依赖关系。

配置元数据

1.1 ApplicationContext

BeanFactory是IOC容器的顶层接口,ApplicitonContext在此基础上比BeanFactory拥有更多的支持,如:加载资源文件、国际化、事件发布等。

ApplicationContext类图

ApplicationContext的常用的实现类有:

  1. ClassPathXmlApplicationContext:加载类路径的配置文件创建容器。
  2. FileSystemXmlApplicationContext:加载磁盘任意路径下的配置文件创建容器。
  3. AnnotationConfigApplicationContext:用于读取注解创建容器。

1.1.1 示例:实例化IOC容器

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@ToString
@Getter
@Setter
public class Car {
    private String brand;
    private double price;
    private String color;

    public Car() {
    }

    public Car(String brand, double price, String color) {
        this.brand = brand;
        this.price = price;
        this.color = color;
    }
}

构建基于XML的配置元数据。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="constructorCar" class="com.zt.bean.Car">
        <constructor-arg name="brand" value="Benz"/>
        <constructor-arg name="price" value="200" />
        <constructor-arg name="color" value="white" />
    </bean>
</beans>

测试类:

import com.zt.bean.Car;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class XmlTest {

    static ApplicationContext context;

    static {
        context = new ClassPathXmlApplicationContext("classpath*:config/*Config.xml");
    }

    @Test
    public void baseOnConstructorInstanceBeanByXml() {
        Car car = (Car) context.getBean("constructorCar");
        System.out.println(car.toString());
    }
}

输出信息:

Car(brand=Benz, price=200.0, color=white)

工程目录结构

2. IOC容器的实例化过程

示例通过new ClassPathXmlApplicationContext("x")实例化容器,那就以它的构造方法作为切入点。

开始之前
源码阅读过程中若涉及到代码片段,会在代码片段之前添加// 代码片段所在类#方法名称(非标准定义)。
例:// AbstractApplicationContext#refresh()

2.1 new ClassPathXmlApplicationContext(“x”)

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {

	// 部分省略

	/**
	 * 根据给定的XML文件路径数组,加载定义并刷新上下文。
	 * @param configLocation 配置文件地址数组
	 * @throws BeansException 上下文创建失败异常
	 */
	public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}

	// 部分省略

	/**
	 * 根据给定的XML文件路径数组,用来加载定义
	 * 
	 * @param configLocations 资源地址列表
	 * @param refresh 是否刷新上下文加载所有的bean定义并创建单例
	 * @param parent 父级上下文
	 * @throws BeansException 上下文创建失败异常
	 * @see #refresh()
	 */
	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		super(parent);
		// 设置配置文件路径
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}
	// 部分省略
}

基本流程如下:

  1. 设置配置文件路径数组;
  2. refresh()销毁容器并重新进行容器初始化。

2.2 refresh()

Spring IOC容器初始化的核心方法:refresh(),本章初探refresh()全貌,后续章节将对refresh()核心方法一一展开阅读。

// AbstractApplicationContext#refresh()
@Override
public void refresh() throws BeansException, IllegalStateException {
	// 在多个线程持有对象监视器为同一个对象的前提下,
	// 保证同一时间只有一个线程可以执行synchronized(非this对象)同步代码块中的代码
	synchronized (this.startupShutdownMonitor) {
		// StartupStep主要用于记录Applicaition在启动过程中的进度。
		StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

		// 设置启动开始时间、活动标志位、
		// 初始化并校验环境配置,初始化应用监听与事件			
		prepareRefresh();

		// 解析资源配置文件,将文件中关于bean定义加载到beanFactory并返回beanFactory 
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
		
		// 配置beanFactory 特性
		prepareBeanFactory(beanFactory);

		try {
			// 空方法,通过重写postProcessBeanFactory(),在所有bean未被加载实例化前做一些前置处理或其他动作进行额外扩展。
			postProcessBeanFactory(beanFactory);

			StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
			
			// 调用beanFactory处理器
			invokeBeanFactoryPostProcessors(beanFactory);
			
			// 注册BeanPostProcessor
			registerBeanPostProcessors(beanFactory);
			
			beanPostProcess.end();
			
			// 初始化MessageSource
			initMessageSource();
			
			// 初始化事件Multicaster
			initApplicationEventMulticaster();

			// 可以重写该方法以添加特定于容器的刷新工作,
			// 在初始化特殊bean时,在实例化单例对象之前调用。 
			onRefresh();
			
			// 注册ApplicationListener
			registerListeners();
			
			// 初始化bean
			finishBeanFactoryInitialization(beanFactory);
			
			// 完成IOC容器创建
			finishRefresh();
		}

		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}
			// 销毁容器中已创建的bean
			destroyBeans();
			
			// 取消刷新
			cancelRefresh(ex);

			throw ex;
		}

		finally {
			resetCommonCaches();
			contextRefresh.end();
		}
	}
}

总结

本文简单了解了IOC容器的相关概念、如何通过代码实例化容器以及容器初始化的核心方法refresh(),下一章我们将对refresh()中的核心方法进行探索!

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

人生逆旅我亦行人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值