仅记录本人学习,欢迎交流
序言
定义:IoC(Inversion of Controller,控制反转)就是将原本代码里需要实现的对象创建、依赖,反转给容器来帮忙实现。我们需要创建一个容器,同时需要一种描述来让容器知道要创建的对象与对象的关系。这个描述最具体的表现就是我们所看到的配置文件
基于XML的IOC容器初始化
_IoC初始化包括BeanDefinition的Resource的三个阶段:定位、加载、注册
- 定位:寻找入口、获取配置文件路径、容器启动
- 加载:解析配置文件路径、载入配置文件路径
- 注册:将配置文件解析为BeanDefinition存储bean定义配置信息
流程如下:
寻找入口、获取配置文件路径、开始启动、创建容器、载入配置路径、分析路径处理策略、解析配置文件路径、读取配置文件、准备文档对象、分配解析策略、将配置载入内存、载入Bean标签元素、载入property标签元素、载入priperties标签子元素、载入list标签元素、分配注册策略、向容器注册
Spring中Bean的创建是典型的工厂模式,BeanFactory定义了IoC容器的基本功能实现。在BeanFactory中只对IOC容器的基本行为做了定义,根本不关系Bean是如何定义以及怎样加载的。要知道工厂是如何生产对象的,我们需要看绝体的IoC容器实现。
本文以ClasspathXmlApplicationContext为例,ApplicationContext是Spring提供的一个高级的IoC容器
各种配置文件到内存中都会变成BeanDefinition
Srping IoC容器管理我们定义的各种Bean对象以及其相互关系,Bean对象在Spring实现中是以BeanDefinition来描述的
由BeanDefinitionReader来解析配置文件
简单说,就是读取配置文件,找到需要加载到IOC容器中的Bean
项目启动时加载DispatcherServlet,其调用父类的init()方法进行初始化。
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// Set bean properties from init parameters.
try {
// 获取servletConfig,定位资源文件
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
// 将dispatcherServlet对象封装到BeanWrapper类里,加载配置细腻些
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
// 获取servletContext
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
// 空的方法
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}
// Let subclasses do whatever initialization they like.
//是真正初始化容器动作的代码
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
其中initServletBean()是真正完成初始化webApplicationContext动作的,这里不进行详细讲解,会在SpringMVC时序图中详解。
继续跟进代码,会看到在configAndRefreshWebApplicationContext()方法中调用了**refresh()**方法进行bean初始化
梦开始的地方,通过构造方法启动
此类做了以下三项重要的工作
- 首先调用夫容器的构造方法为容器设置好Bean资源加载器
- 然后调用父类的setConfigLocations(configLocations)方法设置Bean配置信息的定位路径
- 等加做完以上动作后,开始调用父类AbstractApplicationContext的refresh()方法启动整个IoC容器对Bean定义的载入过程
public ClassPathXmlApplicationContext(
String[] configLocations,
boolean refresh,
@Nullable ApplicationContext parent)
throws BeansException {
//调用父容器的构造方法为容器设置好Bean资源加载器
super(parent);
//设置Bean定义资源文件、配置信息的路径
setConfigLocations(configLocations);
if (refresh) {
//重启、刷新、重置。对Bean配置资源的载入
refresh();
}
}
refrresh()方法主要作用是:在创建IOC容器前,如果已经有容器存在,需要把已有的容器销毁和关闭,以保证refresh()方法之后使用的是新创建的IOC容器,它类似于对IOC容器的重启,在新创建的容器中对容器进行初始化,对Bean配置资源进行载入。
主要为IoC容器Bean的生命周期管理提供条件,SpringIoC容器载入Bean配置信息从其子类容器的refresgBeanFactory()方法启动:
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//1、调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
prepareRefresh();
//2、告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从
//子类的refreshBeanFactory()方法启动
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//3、为BeanFactory配置容器特性,例如类加载器、事件处理器等
prepareBeanFactory(beanFactory);
try {
//4、为容器的某些子类指定特殊的BeanPost事件处理器
postProcessBeanFactory(beanFactory);
//5、调用所有注册的BeanFactoryPostProcessor的Bean
invokeBeanFactoryPostProcessors(beanFactory);
//6、为BeanFactory注册BeanPost事件处理器.
//BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件
registerBeanPostProcessors(beanFactory);
//7、初始化信息源,和国际化相关.
initMessageSource();
//8、初始化容器事件传播器.
initApplicationEventMulticaster();
//9、调用子类的某些特殊Bean初始化方法
onRefresh();
//10、为事件传播器注册事件监听器.
registerListeners();
//11、初始化所有剩余的单例Bean
finishBeanFactoryInitialization(beanFactory);
//12、初始化容器的生命周期事件处理器,并发布容器的生命周期事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
//13、销毁已创建的Bean
destroyBeans();
//14、取消refresh操作,重置容器的同步标识。
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
//15、重设公共缓存
resetCommonCaches();
}
}
}
obtainFreshBeanFactory()方法中,调用了子类的refreshBeanFactory()方法
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法,具体实现调用子类容器的refreshBeanFactory()方法
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
- 首先判断beanFactory是否存在,如果存在则先销毁Bean并关闭beanFactory
- 接着创建DefaultListableBeanFactory
- 并调用loadBeanDefinitions()方法装载Bean定义
@Override
protected final void refreshBeanFactory() throws BeansException {
//如果已经有容器,销毁容器中的bean,关闭容器
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建IOC容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
//对IOC容器进行定制化,如设置启动参数,开启注解的自动装配等
customizeBeanFactory(beanFactory);
//调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
到此为止定位阶段结束,开始进入加载阶段
本次源码之旅主要针对xml形式,故继续跟进AbstractXmlApplicationContext中的loadBeanDefinitions()方法。
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容器使用该读取器读取Bean定义资源
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//为Bean读取器设置Spring资源加载器,AbstractXmlApplicationContext的
//祖先父类AbstractApplicationContext继承DefaultResourceLoader,因此,容器本身也是一个资源加载器
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
//为Bean读取器设置SAX xml解析器
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
//当Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制
initBeanDefinitionReader(beanDefinitionReader);
//Bean读取器真正实现加载的方法
loadBeanDefinitions(beanDefinitionReader);
}