探索spring源码实现,精华的设计模式,各类jdk提供的陌生api,还有那么点黑科技都是一直以来想作的一件事!可是读源码是一件很是痛苦的事情,须要有很大的耐心和扎实的基础。spring
在曾经读两次失败的基础上,此次但愿能一站到底!这个系列基于spring v4.3.20版本探索。编程
###Spring上下文启动加载过程的分段设计模式
spring上下文的实现很是多,其中基于Xml启动的有ClassPathXmlApplicationContext、FileSystemXmlApplicationContext等等。这些上下文都很是相似,基于解析Xml,获取配置的bean,最终完成上下文的启动加载过程。api
这里以ClassPathXmlApplicationContext为主分析Spring ApplicatonContext整个启动过程。这里将spring上下文的启动分为两个大步骤:函数
- 上下文对象自己的构造和初始化:建立ClassPathXmlApplicationContext对象,构造器中执行一些初始化操做,如:设置上下文的环境Enviroment、设置上下文的资源解析器等
- 上下文获取bean配置,解析实例化bean:构造BeanFactory,解析Xml,构造bean定义,依赖注入,实例化bean。这个过程很是复杂;
<div align="center"><image src="https://img2018.cnblogs.com/blog/1286175/201811/1286175-20181105095558497-955412634.jpg" width="300" high="20"></image> </div>this
这节主要分析第一个阶段:上下文对象自己的构造和初始化。spa
###上下文对象自己的构造和初始化 编写debug代码,构造ClassPathXmlApplicationContext对象:debug
ApplicationContext context = new ClassPathXmlApplicationContext("/beans.xml"); HelloWorldBean h = context.getBean("helloWorld", HelloWorldBean.class); h.printHelloWorld();
编写配置文件beans.xml,配置helloWorld的bean:设计
<bean id="helloWorld" class="com.learn.ioc.beans.HelloWorldBean"></bean>
下面主要分析ClassPathXmlApplicationContext new的过程。ClassPathXmlApplicationContext构造函数以下:code
// 使用beans.xml做为上下文的配置文件构造ApplicationContext对象 public ClassPathXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); } // 以配置文件、是否刷新上下文、父上下文做为参数构造ApplicationContext对象 public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }
super(parent)和setConfigLocations(configLocations)方法对应两阶段中的第一阶段:上下文对象自己的构造和初始化。refresh()方法完成第二阶段:上下文获取bean配置,解析实例化bean。
super(parent)以该上下文的父上下文做为参数调用父类的构造器,这里因为没有父上下文,因此为null:
public AbstractXmlApplicationContext(ApplicationContext parent) { super(parent); }
AbstractXmlApplicationContext是以Xml做为配置的Spring上下文,AbstractXmlApplicationContext又继续调用其父类的构造器:
public AbstractRefreshableConfigApplicationContext(ApplicationContext parent) { super(parent); }
AbstractRefreshableConfigApplicationContext也是以Xml文件做为基础配置的上下文,可是它具备能够刷新配置文件的能力,AbstractRefreshableConfigApplicationContext中提供了setConfigLocations方法能够用于设置配置文件。是Xml配置文件上下文的基石。 该上下文中又调用父类构造函数:
public AbstractRefreshableApplicationContext(ApplicationContext parent) { super(parent); }
Spring上下文能够称做为上下文家族,继承有多代。其中AbstractRefreshableApplicationContext是上下文中家族中的元老。它提供了两个很是重要的接口refreshBeanFactory()和loadBeanDefinitions(DefaultListableBeanFactory beanFactory)用于配置BeanFactory和定义接下载入Bean的接口。虽然构造函数中依然调用父类的构造函数,可是它的确很是重要:
public AbstractApplicationContext(ApplicationContext parent) { this(); // 设置该上下文的父上下文 setParent(parent); } public AbstractApplicationContext() { // 初始化资源解析器,用于解析获取Xml配置 this.resourcePatternResolver = getResourcePatternResolver(); }
上下文家族的鼻祖AbstractApplicationContext中定义了整个Bean的声明周期和处理过程。在构造器中初始化resourcePatternResolver资源解析器,赋予ApplicationContext具备解析资源的能力。且设置父上下文。
getResourcePatternResolver主要用来建立资源解析器:
protected ResourcePatternResolver getResourcePatternResolver() { return new PathMatchingResourcePatternResolver(this); }
PathMatchingResourcePatternResolver主要以路径匹配的模式进行解析获取资源,其主要能力和实现后续会详细介绍。
setParent(parent)主要用于设置该上下文的父上下文:
public void setParent(ApplicationContext parent) { // 设置父上下文 this.parent = parent; // 若是父上下文不空 if (parent != null) { // 获取父上下文的环境变量Environment Environment parentEnvironment = parent.getEnvironment(); if (parentEnvironment instanceof ConfigurableEnvironment) { // 将父上下文的环境变量Environment合并至该级上下文环境变量中 getEnvironment().merge((ConfigurableEnvironment) parentEnvironment); } } }
通过一系列的父类的构造器的调用,Spring上下文完成了多级上下文的载入过程。能够从下图看出其继承顺序关系:
<div align="center"><image src="https://img2018.cnblogs.com/blog/1286175/201811/1286175-20181105095630158-1281780770.jpg" width="450" high="20"></image> </div>
Tips
上下文的实现中设计模式很是强,秉着设计的六大原则进行实现:
单一职责原则:每种上下文都有本身的职责能力,使得上下文的扩展能力极强;
开闭原则:AbstractRefreshableApplicationContext提供了对loadBeanDefinitions的定义由其子类按照不一样加载Bean的逻辑各自实现;
依赖倒置原则:经过多级的抽象,提供了不一样的接口,达到针对接口编程;
ClassPathXmlApplicationContext中调用层级父类上下文对象的构造后,再执行AbstractRefreshableConfigApplicationContext中实现的setConfigLocations设置上下文的Xml配置:
// 设置配置文件路径 public void setConfigLocations(String... locations) { // 若是不为空,则构早配置文件路径,支持多个配置文件 if (locations != null) { Assert.noNullElements(locations, "Config locations must not be null"); this.configLocations = new String[locations.length]; // 循环解析多个配置文件路径 for (int i = 0; i < locations.length; i++) { this.configLocations[i] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; } }
关于resolvePath的具体过程涉及到Spring IOC容器的环境Enviroment组件,具体的解析路径的过程将在下章的探索Enviroment中详解。
###总结 Spring上下文类型很是繁多,其中有直接面向各类场景直接使用的FileSystemApplicationContext、ClasspathXmlApplicationContext等等,还有不少实现基础能力的上下文:
AbstractApplicationContext是上下文实现中的基石,其中定义了上下文Bean对象依赖注入的模板;
AbstractRefreshableApplicationContext也是很是重要的上下文,其中组合了BeanFactory和定义加载Bean的接口;