Spring源码解析
前言
Spring的启动流程
准备环境对象,事件监听者。 加载Spring配置文件。即读入并解析xml配置文件,构建初始Spring IOC容器。 、 对创建的Spring IOC容器进行一些初始化配置,设置忽略接口,注册环境上下文。
调用Spring IOC容器的postProcessBeanFactory,留给子类实现。
实例化并执行BeanFactoryPostProcessor相关接口,包括解析配置类。并将BeanFactoryPostProcessor相关类放入特定容器,@Configuration的扫描及配置在此。
实例化并注册所有BeanPostProcessor进容器类。
初始化MessageSource,用于支持国际化。
初始化事件广播器。
注册监听者。
实例化和初始化IOC容器中剩下的所有Bean。在初始化Bean时,Spring 根据Bean的定义以及配置信息,实现对Bean的实例化、属性赋值、以及初始化等操作。
完成IoC容器的准备工作。所有单例的Bean都已经被实例化、初始化并装配到容器中后,容器的准备工作就完成了,此时Spring框架已经可以对外提供服务。
执行定制化的后置处理器。Spring容器中可能会存在一些实现了BeanPostProcessor接口的定制化组件。这些组件会参与到IoC容器中Bean的生命周期过程,比如AOP、事务处理等。
执行自定义的初始化方法和销毁方法。容器中某些Bean可能需要在容器启动时执行自定义的初始化方法。这些方法在容器启动时就会被调用;同理,某些Bean在容器关闭时需要调用自定义的销毁方法,以清理资源。
容器启动后,整个应用将进入正常的工作状态。
一、启动Spring
创建一个简单的 Spring 启动例子
import com.king.entity.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
//配置文件方式
ApplicationContext beanFactory = new ClassPathXmlApplicationContext("spring-config.xml");
User user = beanFactory.getBean("user", User.class);
user.say();
}
}
<?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="user" class="com.king.entity.User"></bean>
</beans>
当我们传入一个 Spring 配置文件去实例化 ClassPathXmlApplicationContext时,可以看一下它的构造方法都做了什么
第一步:读取xml文件,并且将其加载到Spring容器中进行管理
第二步:从Spring容器中获取xml加载到该容器里面的bean
第三步:调用 say() 方法
OK。事实上,这个地方现在是5.0版本了,最原始的时候,其实他只是简单的通过BeanFactory进行操作的,这也是为什么很多地方都说BeanFactory和ApplicationContext的区别。
那我们用BeanFactory试一试。
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
public class Main {
public static void main(String[] args) {
// 5.0之前版本
Resource resource = new ClassPathResource("spring-config.xml");
BeanFactory beanFactory1 = new XmlBeanFactory(resource);
beanFactory1.getBean("user");
User user = beanFactory1.getBean("user", User.class);
user.say();
}
}
这里更加清楚的解释Spring是如何读取的 xml:
第一步 将xml文件转成Resource文件,可以简单认为这个地方就是将xml文件转成IO流(方便理解)
第二步,给这个IO流一个BeanFactory工程去读取他
第三步,从Bean工厂获取bean
第四步,调用 User.say() 方法
好了。在看完上面可能会有一些不清楚,但是我们已经知道如何简单的构建一个Spring启动程序,并且,ApplicationContext和BBeanFactory确实是实现了同一个功能。
ApplicationContext里面的功能会比BeanFactory要多,我们一步步来调试,看他是怎么读取xml文件并且放到Spring容器中的
二、读取流程
创建断点,开始 Debugger
我们多次下一步会跳转到如下方法
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
//读取 xml 文件,并将 Bean 注入
refresh();
}
}
在 ClassPathXmlApplicationContext 的位该构造方法实现了 xml 的读取以及Bean 的注入,具体是在refresh()
方法中实现的。 进入refresh()
方法
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
/**
* 【1】准备刷新
* (1) 设置容器启动时间
* (2) 设置活跃状态为true
* (3) 设置关闭状态为false
* (4) 获取Environment对象,校验配置文件
* (5) 准本监听器和事件的集合对象,默认为空set集合
*/
prepareRefresh();
/**
* 【2.初始化 新BeanFactory】 重点
* (1) 存在旧BeanFactory 则销毁
* (2) 创建新BeanFactory (DefaluListbaleBeanFactory)
* (3) 解析xml/加载Bean定义、注册Bean定义到BeanFactory
* (4) 返回新的BeanFactory
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/**
* 【3.bean工厂前置操作】
* 为BeanFactory配置容器特性
* 比如类加载器,表达式解析器,注册默认环境Bean、后置管理器BeanPostProcessor
*/
prepareBeanFactory(beanFactory);
try {
// 为容器的某些子类指定特殊的 BeanPost 事件处理器
postProcessBeanFactory(beanFactory);
//【5.调用bean工厂后置处理器】开始调用我们自己实现的接口
invokeBeanFactoryPostProcessors(beanFactory);
// 为 BeanFactory 注册 BeanPost 事件处理器.
// BeanPostProcessor 是 Bean 后置处理器,用于监听容器触发的事件
registerBeanPostProcessors(beanFactory);
// 初始化信息源(国际化相关)
initMessageSource();
// 初始化应用事件多播器,用于在容器内广播应用事件,比如当Bean被初始化或销毁时
initApplicationEventMulticaster();
//调用子类重写的方法,允许在容器刷新时执行特定的初始化操作
onRefresh();
//为事件传播器注册事件监听器.
registerListeners();
//完成BeanFactory的初始化,实例化所有非懒加载的单例Bean,并调用它们的初始化方法。
finishBeanFactoryInitialization(beanFactory);
//最后,初始化容器的生命周期处理器,发布容器刷新完成的事件,使得其他监听此事件的组件知道容器已经准备好使用。
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// 取消 refresh 操作,重置容器的同步标识.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
接下来我们对里面的方法进行逐一解析,重点方法我会标注
三、时序图
四、Spring流程源码分析
4.1 prepareRefresh()
protected void prepareRefresh() {
// 设置启动时间
this.startupDate = System.currentTimeMillis();
//设置关闭状态为false
this.closed.set(false);
//设置活跃表示为true
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
} else {
logger.debug("Refreshing " + getDisplayName());
}
}
// 初始化上下文环境中的任何占位符属性源,对于子类:默认情况下什么都不做,可以自定义ApplicationContext重写这个方法
initPropertySources();
// 获取Environment对象,进行环境属性校验
getEnvironment().validateRequiredProperties();
// 存储 预刷新监听器集合
if (this.earlyApplicationListeners == null) {
//将当前的应用程序监听器 this.applicationListeners 复制到 this.earlyApplicationListeners
this.earlyApplicationListeners = new LinkedHashSet < > (this.applicationListeners);
} else {
/**
* 如果存在则清空 this.applicationListeners并重新添加到 this.applicationListeners
* 目的是为了在刷新过程之前,将预刷新的应用程序监听器存储起来。
* 这些监听器在刷新过程中可能会被重新注册,所以将它们在刷新之前进行备份是为了后续重新注册时能保持一致性和正确性
* why do this?
* 比如说 在执行refresh方法时 有两个监听器 一个用于在每个请求到达时记录请求日志 另一个用于在异常发生时发送邮件通知管理员
* 在刷新方法的时候,假如移除了一个记录接口请求的监听器 但是出问题了 导致日志记录失败或者管理员未能及时收到异常通知
* 备份之前的所有的监听器 在错误后能够使用之前正确配置的监听器集合 避免应用程序出现不可预测的问题
*/
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
//准备事件的集合对象,事件默认为空set集合
this.earlyApplicationEvents = new LinkedHashSet < > ();
}
4.2 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()
在org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory
方法中,首先调用 refreshBeanFactory
方法来刷新内部的 bean 工厂,并最终返回这个刷新过的 bean 工厂。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
在org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
方法中,首先检查是否已存在 bean 工厂,如果存在则先销毁已有的 bean 并关闭 bean 工厂。接着,创建一个新的 DefaultListableBeanFactory
实例,设置其序列化 ID 为容器的 ID,然后调用 customizeBeanFactory
定制 bean 工厂。随后,通过 loadBeanDefinitions
加载 bean 的定义,包括读取配置文件、解析 bean 的定义等。
protected final void refreshBeanFactory() throws BeansException {
// 检查容器当前是否持有BeanFactory对象,如果存在Bean工厂,销毁已有的 Bean 并关闭 Bean 工厂
if (hasBeanFactory()) {
destroyBeans(); //销毁 bean
closeBeanFactory(); //关闭旧BeanFactory
}
try {
/**
* 创建一个新的 beanFactory
* DefaultListableBeanFactory 的构造方法中会设置一些忽略的bean类型如下:
* BeanNameAware、BeanFactoryAware、BeanClassLoaderAware
* 以上着三个类型维护在 beanFactory 的ignoredDependencyInterfaces参数中
* 这几个类会忽略 依赖检查 和 自动装配
*/
DefaultListableBeanFactory beanFactory = createBeanFactory();
//设置序列化id为容器的id
beanFactory.setSerializationId(getId());
//初始化循环依赖 和 依赖覆盖配置
customizeBeanFactory(beanFactory);
//加载bean定义到 beanFactory(从配置文件中),包括读取配置文件、解析 bean 的定义等
loadBeanDefinitions(beanFactory);
// 将创建好的 bean 工厂赋值给容器
this.beanFactory = beanFactory;
}
catch (IOException ex) {
// 如果在解析 bean 定义时发生 I/O 错误,则抛出 ApplicationContextException 异常
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
在org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(beanFactory)
方法中,首先创建了一个 XmlBeanDefinitionReader
对象,用于读取 XML 格式的 bean 定义。然后,配置了 bean 定义阅读器的环境、资源加载器和实体解析器等属性。接着,允许子类通过 initBeanDefinitionReader
提供自定义的初始化操作。最后,调用阅读器的加载方法,实际上读取配置文件,解析 bean 定义,将它们注册到 bean 工厂中。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 为给定的BeanFactory 创建一个 XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 使用该上下文的资源加载环境配置 bean 定义阅读器
beanDefinitionReader.setEnvironment(this.getEnvironment());
// 将资源加载器设置为当前应用程序上下文
beanDefinitionReader.setResourceLoader(this);
// 设置实体解析器为 ResourceEntityResolver,用于解析 bean 定义中的实体引用
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 允许子类提供对阅读器的自定义初始化,然后继续实际加载 bean 定义
initBeanDefinitionReader(beanDefinitionReader);
// 调用阅读器的加载方法,实际上会读取配置文件,解析 bean 定义,将它们注册到 bean 工厂中
loadBeanDefinitions(beanDefinitionReader);
}
参考
1.【【手动读Spring源码】图片+源码+调试(全程可以自己动手)】程序员温玉