IOC
IOC设计模式是企业应用开发中,解耦组件之间的复杂关系的利器,Spring IOC模块就是这个模式的一种实现,Spring IOC提供了一个基本的JAVABean容器,通过IOC模式管理依赖关系,并通过依赖注入和AOP切面增强为JAVABean这样的POJO对象赋予事务管理、生命周期管理等基本功能。
前言:主要记录IOC初始化过程的几个步骤,参考:SPRING技术内幕:深入解析SPRING架构与设计原理,多年前翻过的一本Spring原理书,里面讲的细节大部分都忘了,回溯一下,顺便做个记录,也好将来翻阅,对书中的IOC,AOP,WEB等都做详细的记录,此章节为Spring系列,会不断上传,篇幅原因,一章就讲一部分。先简单介绍一下IOC的几个底层接口,以及核心容器,主要讲解IOC容器初始化的定位部分
涉及核心接口
Resource
数据源接口,封装了InputStream,作用是为容器提供用户数据源的接口。通俗的讲,容器启动需要的配置文件或配置类在哪?
ResourceLoader
加载资源的策略接口
BeanDefinition
BeanDefinition是IOC容器体系中非常重要的核心数据结构,可以看成是对bean定义的抽象
BeanDefinitionReader
用于将Resource装载入BeanDedintion中的解析器
BeanFactory
容器基类接口,所有IOC容器的父接口
ApplicationContext
应用上下文容器的父接口
红色线路从接口BeanFactory——HierarchicalBeanFactroy——ConfigurableBeanFactory主要设计路径
以ApplicationContext应用上下文接口为核心的接口设计,主要涉及几口
BeanFactory——ListableBeanFactory——ApplicationContext——WebApplicationContext / ConfigurableApplicationContext
常用的应用上下文基本上都是WebApplicationContext / ConfigurableApplicationContext实现在这个接口体系中
ListableBeanFactory和HierarchicalBeanFactory两个接口,连接BeanFactory接口定义和ApplicationContext应用上下文的接口定义。
ApplicationContext通过继承MessageSouree、ResourceLoader、ApplicationEvenPublisher接口,在BeanFactory简单容器的基础上添加了许多对高级容易的特性支持
IOC初始化过程
定位
Resource定位过程,这里指的是BeanDefinition的资源定位,它由ResourceLoader通过统一的Resource接口完成,这个Resource对各种形式的BeanDefinition的使用提供了统一接口,如FileSystemResource、ClassPathResource。定位过程类似于容器寻找数据的过程,就像水桶装水先要找水。
BeanDefinition的Resource定位步骤:
以编程的方式使用DefaultListableBeanFactory,先要定义Resource来定位容器使用的BeanDefinition.
ClassPathResource resource=new ClassPathResource("beans.xml"); (找水)创建IOC配置文件的资源,
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();(桶)创建一个BeanFactory。
(装水器)创建一个读取器,resource并不能直接被使用,必须通过BeanDefinitionReader来对这些信息进行处理。
XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinition(res);(装水)从定义好的资源位置读入配置信息
DefaultListableBeanFactory
是接口ConfigurableListableBeanFactory和BeanDefinitionRegistry的默认实现,是一个纯粹的容器,需要为它配置特定的读取器才能完成这些功能,适合用于定制IOC,可以理解为Spring容器中默认的对象工厂的实现,是一个比较全面的对象工厂。工厂中的Bean都是基于元数据定义的 bean 和通过post-processors扩展的bean。它的典型作用就是注册所有的bean通过读取bean的配置文件,这样就可以通过bean的命名方便快速访问bean,通过本地缓存表。
ApplicationContext
一般我们使用ApplicationContext容器,ApplicationContext通过继承MessageSouree、ResourceLoader、ApplicationEvenPublisher接口,在BeanFactory简单容器的基础上添加了许多对高级容易的特性支持,这个接口系统是以BeanFactory和ApplicationContext为核心的,而BeanFactory又是IOC容器的最基本接口,在ApplicationContext的设计中,一方面,可以看到它集成了BeanFactory接口体系中的ListableBeanFactory、autowireCapableBeanFactory\HierarchicalBeanFactory等BeanFactory接口,具备了BeanFactory IOC容器的基本功能,另一方面通过继承MessageSource\ResourceLoader\ApplicationEventPublisher这些几口,BeanFactory为ApplicationContext赋予了更高级的IOC容器特性,为了再Web环境中使用它,还涉及了WebApplicationContext接口,而这个接口通过继承ThemeSource接口来扩充功能
先来看看三个常用的ApplicationContext实现类:
FileSystemXmlApplicationContext:从XML文件中载入Resource(配置文件)
ClassPathXmlApplicationContext:从类路径载入Resource(配置类)
XmlWebApplicationContext:从Web容器载入Resource(web项目)
我们主要以FileSystemXmlApplicationContext为例,讲述一下IOC容器中ApplicationContext系列的定位过程
先来看看FileSystemXmlApplicationContext的继承体系
FileSystemXmlApplicationContext的具体实现,要走进源码,篇幅有限,只贴关键代码
//初始化过程中,调用refresh函数载入BeanDefinition,这个refresh启动了BeanDefinition的载入过程,它是容器启动的入口
public FileSystemXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
@Override
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
我们来找找BeanDefinition的完整定位过程,首先查看调用栈
清除看到BeanDefinition资源定位的过程,最初由refresh方法触发
那么,FileSystemXmlApplicationContext是在哪里BeanDefinitionReader的呢?
根据调用栈进入源码
AbstractApplicationContext——refresh()方法
详细描述了整个ApplicationContext的初始化过程,BeanFactory更新,MessageSource和PostProcessor注册,看起来更像是对ApplicationContext进行初始化的模板或执行提纲,为Bean的生命周期管理提供了条件
进入obtainFreshBeanFactory()——调用了模板方法refreshBeanFactory();该方法由子类去实现,这里的实现类是 AbstractRefreshableApplicationContext类
loadBeanDefinitions()方法是AbstractRefreshableApplicationContext类的抽象方法,由子类 AbstractXmlApplicationContext来实现
实例化了XmlBeanDefinitionReader
具体实现:loadBeanDefinitions(XmlBeanDefinitionReader reader)
看到具体的载入过程是委托给BeanDefinitionReader来完成的,因为这里的BranDefinition是通过XML文件定义,所以这里使用XmlBeanDefinitionReader 来载入BeanDefinition到容器中
我们得出部分结论
1: FileSystemXmlApplicationContext通过调用refresh()容器启动方法 其中实现了BeanDefinition的载入,
2:载入过程由XmlBeanDefinitionReader来完成,由于使用的是XML方式的定义,所以使用的是 XmlBeanDefinitionReader、如果使用了其他的BeanDefinition定义,则需要其他的BeanDefinitionReader实现类来完成
3:容器实际使用的IOC容器是标准容器DefultListableBeanFactory
而Resource载入在BeanDefinitionReader读入BeanDefinition时实现
进入reader.loadBeanDefinitions(configLocations);
loadBeanDefinitions(location)最后会通过resourceLoader.getResources(location)获得resource
再调用loadBeanDefinitions(resource);
其中getResources(location)为抽象方法,使用的类是DefaultResourceLoader()
DefaultResourceLoader默认使用ClassPathContextResource来获得resource
再看看FileSystemXmlApplicationContext的集成路线上层继承了DefaultResourceLoader,重写了getResourceByPath(String path)方法,使用FileSystemResource返回Resource,在参数Resource中封装了对XML文件的I/O操作,所以读取器可以在打开I/O流后得到XML的文件对象。有了这个文件对象以后,将其解析为Document对象,再按照Spring的Bean定义规则来对XML的文档树进行解析,这个解析交给了BeanDefinitionParserDelegate来完成。
到此,ApplicationContext的resource定位流程就结束了,而后面的解析,注册,注入将在下一篇中具体讲解