知其然知其所以然,是我们做技术所追求的,但在繁忙的工作中也会难于顾及与此。从本文开始,抽出时间对Spring 5.x
的IOC容器
初始化的过程进行追踪梳理,探究学习Spring
这件艺术品。
关于Spring
的优势,每人都有一份见地:
控制反转
(Inversion of Control)。IOC
也称为DI
(Dependancy Injection),是指在IOC容器创建时,通过构造函数参数、工厂方法参数或从工厂方法构造或返回对象实例后,设置该对象依赖其它对象的过程称作依赖注入
。@Transactional
即可灵活的进行事务控制,让我们从传统事务管理代码中脱离出来。<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.4version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.18version>
<scope>providedscope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
org.springframework.context.ApplicationContext
代表着Spring IOC容器,它负责实例化、配置及Bean的组装。容器通过读取配置元数据来获取需要实例化、配置和组装对象的各种指令。配置元数据通常使用XML、Java注解、Java代码来表示。它定义了组成应用程序的对象及对象间的依赖关系。
BeanFactory
是IOC容器的顶层接口,ApplicitonContext
在此基础上比BeanFactory
拥有更多的支持,如:加载资源文件、国际化、事件发布等。
ApplicationContext的常用的实现类有:
ClassPathXmlApplicationContext
:加载类路径的配置文件创建容器。FileSystemXmlApplicationContext
:加载磁盘任意路径下的配置文件创建容器。AnnotationConfigApplicationContext
:用于读取注解创建容器。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的配置元数据。
<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)
示例通过new ClassPathXmlApplicationContext("x")
实例化容器,那就以它的构造方法作为切入点。
开始之前
源码阅读过程中若涉及到代码片段,会在代码片段之前添加// 代码片段所在类#方法名称
(非标准定义)。
例:// AbstractApplicationContext#refresh()
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();
}
}
// 部分省略
}
基本流程如下:
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()
中的核心方法进行探索!