Spring|Spring IoC容器源码分析(Spring技术内幕读书笔记)

1. 容器简介 在Spring容器设计中,BeanFactory实现容器的基本功能,ApplicationContext作为容器高级形态存在,在简单容器的基础上,增加了许多面向框架的特性。
Spring通过BeanDefinition来管理基于Spring应用中的各种对象以及它们之间的关系, 对IoC容器来说,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕BeanDefinition的处理来完成的。
在独立的程序中,一般使用ClassPathXmlApplicationContextFileSystemXmlApplicationContext启动Spring容器,这2个是ApplicationContext的具体实现类。
ApplicationContext表示IoC容器,负责实例化、配置以及组装Bean,通过配置数据获取(configuration metadata), 配置数据包括XML, Java注解, Java代码, 利用Java的反射功能实例化Bean并建立Bean与Bean之间的依赖关系
BeanFactory和ApplicationContext的区别
BeanFactory接口类是IoC设计的最基本的功能规范, ApplicationContext是复杂功能的衍生扩展
ApplicatonContext通过继承MessageSource、ResourceLoader、ApplicationEventPublisher接口,在BeanFactory简单容器的基础上添加了许多对高级容器的特性支持
所以我们一般是通过ApplicationContext启动IoC容器, ApplicationContext的getBean()方法就是BeanFactory接口方法
2. IoC容器的设计原理 2.1 接口
IoC容器是由一系列的接口组成的,以BeanFactoryApplicationContext为核心,BeanFactory是最基本的接口, 在ApplicationContext的设计中,它集成了BeanFactory接口体系中的ListableBeanFactoryAutowireCapableBeanFactoryHierarchicalBeanFactory等BeanFactory接口,具备BeanFactory IoC容器的基本功能,并且,通过集成MessageSourceResourceLoaderApplicationEventPublisher等接口赋予了IoC容器更高级的特性。
Spring|Spring IoC容器源码分析(Spring技术内幕读书笔记)
文章图片
Spring IoC容器的设计接口图 BeanFactory接口线 从接口BeanFactoryHierarchicalBeanFactory,再到ConfigurableBeanFactory是一条主要的BeanFactory设计路径,定义了IoC容器的基本规范。而HierarchicalBeanFactory增加了getParentBeanFactory(),使BeanFactory具备了双亲IoC容器的管理功能,在接下来的ConfigurableBeanFactory接口中,定义了一些对BeanFactory的配置功能
ApplicationContext接口线 第二条接口设计主线,以ApplicationContext接口上下文为设计核心,从 BeanFactoryListableBeanFactory, 再到ApplicationContext, 再到WebApplicationContext或者ConfigurableApplicationContext. 在这个体系中, ListableBeanFactory接口和HierarchicalBeanFactory两个接口, 连接BeanFactoryApplicationContext
ListableBeanFactory接口中,细化了BeanFactory的接口功能,对于ApplicationContext接口,通过继承MessageSourceResourceLoaderApplicationEventPublisher等接口,在BeanFactory的基础上添加了许多对高级容器的特性支持。
2.2 BeanFactory容器的设计原理
DefaultListableBeanFactory 是 IoC容器最常用的具体实现类,而XmlBeanFactory继承了DefaultListableBeanFactory, XmlBeanFactory扩展了读取Xml文件的功能,也就是说XmlBeanFactory是一个可以读取XML文件方式定义的BeanDefinition的IoC容器
XMLBeanFactory中,初始化了一个XmlBeanDefinitionReader对象,有了Reader对象,那些以XML方式定义的BeanDefinition就有了处理的地方, 实际上Xml信息的处理是由XmlBeanDefinitionReader完成的。
XMLBeanFactory构造函数中传入Resource对象,Resource是Spring用来封装I/O操作的类。比如XML文件形式的Resource可以使用ClassPathResource实现
所以,在整个BeanFactory容器的实现过程中,DefaultListableBeanFactory是一个很重要的IoC实现,在ApplicationContext的实现中,原理和XMLBeanFactory一样,也是通过持有或者扩展DefaultListableBeanFactory来获得基本的IoC容器的功能。
【Spring|Spring IoC容器源码分析(Spring技术内幕读书笔记)】编程实现

ClassPathResource res = new ClasspathResource("beans.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(res);

  1. 创建IoC配置文件的抽象资源,包含了BeanDefinition的定义信息
  2. 创建一个BeanFactory,这里使用DefaultListableBeanFactory
  3. 创建一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来加载XML文件形式的BeanDefinition,通过一个回调配置给BeanFactory
  4. 加载定义好的资源,解析BeanDefinition,完成整个载入和注册Bean定义之后,需要的IoC容器就建立起来了
XmlBeanFactory在Spring 3.1之后被废弃,一般的,我们使用ApplicationContext启动IoC容器
2.3 ApplicationContext容器的设计原理
ApplicationContextBeanFactory的基础上添加了附加的功能,使得IoC容器的功能更加丰富,所以一般建议在开发时使用ApplicationContext作为IoC容器的基本形式,以常见的FileSystemXmlApplicationContext进行说明。
FileSystemXmlApplicationContext作为具体的应用上下文,实现和它自身设计相关的两个功能。
一个是直接使用FileSystemXmlApplicationContext,支持实例化的这个应用上下文,同时启动IoC容器的refresh()过程,这个refresh()过程涉及一系列复杂的操作,将在IoC容器的初始化过程中说明
另一个是与FileSystemXmlApplicationContext设计具体相关的功能,与从文件系统中加载XML的Bean定位资源相关,相关代码:
protected Resource getResourceByPath(String path) { if (path.startsWith("/")) { path = path.substring(1); } return new FileSystemResource(path); }

3. IoC容器的初始化过程
ApplicationContext context = new FileSystemXmlApplicationContext("beans.xml");

我们一般通过上面代码启动Spring IoC容器, 下面我们根据这行代码,进行分析IoC容器的初始化过程, Spring IoC容器的启动包括BeanDefinition的Resource定位、载入和注册三个基本过程。
Spring|Spring IoC容器源码分析(Spring技术内幕读书笔记)
文章图片
继承关系 3.1 Resource定位 Resource定位指的是BeanDefinition的定位过程,这里用的IoC容器是ListableBeanFactory,DefaultListableBeanFactory并不能直接使用Resource, 必须通过BeanDefinitionReader来处理,在ApplicationContext中,Spring为我们提供了一系列加载不同Resource的Reader实现,DefaultListableBeanFactory是纯粹的容器,我们需要为其配置特定的Reader才能完成这些功能
Spring|Spring IoC容器源码分析(Spring技术内幕读书笔记)
文章图片
image-20181228210454211-6002294.png FileSystemXmlApplicationContext类的继承关系 AbstractXmlApplicationContext
专门针对XML文件进行处理,重写了loadBeanDefinitions()方法,实例化了XmlBeanDefinitionReader,赋予读取了Xml配置文件的能力
AbstractRefreshableConfigApplicationContext
为ClassPathXmlApplicationContext、FileSystemXmlApplicationContext以及XmlWebApplicationContext提供通用的配置路径处理能力
AbstractRefreshableApplicationContext
最最重要的ApplicationContext的实现类,创建DefaultListableBeanFactory容器,并且提供了loadBeanDefinitions方法, 赋予了其子类AbstractXmlApplicationContext 等实现载入BeanDefinition的能力
AbstractApplicationContext
ApplicationContext接口的抽象实现类,是ApplicationContext的超级大打手,使用了模板方法设计模式,自动注册了BeanPostProcessors、ApplicationListeners以及MessageSource等高级功能,其中的refresh()方法就是FileSystemXmlApplicationContext等具体ApplicationContext实现类构造函数的启动入口, 老大终于现真身了
DefaultResourceLoader
ResourceLoader接口的实现类,会根据不同的配置文件类型,提供不同的Resource,比如UrlResource和ClassPathResource等,该类的getResource()方法就是真正调用FileSystemXmlApplicationContext的getResourceByPath()的地方。
源码剖析 通过上面几个类层级关系,大致清楚了FileSystemXmlApplicationContext的实现关系,下面通过分析源代码,来剖析实现过程。
new FileSystemXmlApplicationContext(String configLocation)
public FileSystemXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }

refresh()方法是整个容器的触发点
@Override protected Resource getResourceByPath(String path) { if (path.startsWith("/")) { path = path.substring(1); } return new FileSystemResource(path); }

在这个方法中,阐明了用何种Resource进行定位,这里通过FileSystemResource方式载入资源.
getResourceByPath()是在DefaultResourceLoader中定义的,通过上述继承图可以发现,DefaultResourceLoader是AbstractApplicationContext的基类, 而FileSystemXmlApplicationContext继承AbstractApplicationContext, 所以FileSystemXmlApplicationContext本质是通过DefaultResourceLoader进行资源定位的
最终该方法会被XmlBeanDefinitionReader的loadBeanDefinitions方法触发 InputStream inputStream = encodedResource.getResource().getInputStream(); , 在DefaultResourceLoader的getResource()方法中被调用
在XmlWebApplicationContext和ClassPathXmlApplicationContext等其他ApplicationContext中,都会定义自己的Resource, 并且最终由基类DefaultResourceLoader的getResource()方法执行
AbstractApplicationContext.refresh()
refresh是一个很重要的方法,是IoC容器的入口,refresh详细的描述了整个ApplicationContext初始化的过程,比如BeanFactory的更新,MessageSource和PostProcessor的注册等等,这个执行过程为Bean的生命周期提供了条件。
refresh中的执行步骤如下:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); }catch (BeansException ex) { ... }finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }

AbstractApplicationContext.obtainFreshBeanFactory()
这里执行AbstractRefreshableApplicationContext的refreshBeanFactory方法,准备刷新容器
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory(); return getBeanFactory(); }

AbstractRefreshableApplicationContext.refreshBeanFactory()
在创建容器前,如果容器已经存在,则需要先销毁和关闭,保证在refresh之后的容器的新的容器,所以refresh更像是重启的重启. 这里真正的创建了容器DefaultListableBeanFactory,然后当容器建立之后,就开始进行最重要的BeanDefinitions信息的载入
protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }

AbstractXmlApplicationContext.loadBeanDefinitions()
在AbstractRefreshableApplicationContext中调用的loadBeanDefinitions是抽象方法,真正执行的地方在这里,在AbstractXmlApplicationContext的loadBeanDefinitions中,初始化了读取器XmlBeanDefinitionReader,配置基本信息,使其具备了读取XML配置的能力
// Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

当XmlBeanDefinitionReader组装完毕后,就准备好大显身手了
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }

AbstractBeanDefinitionReader.loadBeanDefinitions(String location)
AbstractBeanDefinitionReader是XmlBeanDefinitionReader的父类,这里为载入BeanDefinitions做好了准备
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int count = 0; for (String location : locations) { count += loadBeanDefinitions(location); } return count; }

XmlBeanDefinitionReader.loadBeanDefinitions(Resource resource)
XMLBeanDefinitionReader是真正实现解析XML配置的地方,在loadBeanDefinitions方法中,拿到代表XML文件的Resource,真正打开了XML文件流,拿到Document对象,进行了BeanDefinitions的注册.
这里实际是通过DefaultResourceLoader调用了FileSystemApplicationContext的getResourceByPath方法拿到FileSystemResource进行XML资源的获取。
... InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); }...

doLoadBeanDefinitions方法
Document doc = doLoadDocument(inputSource, resource); int count = registerBeanDefinitions(doc, resource);

registerBeanDefinitions方法
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore;

DefaultBeanDefinitionDocumentReader.registerBeanDefinitions()
DefaultBeanDefinitionDocumentReader是BeanDefinitionDocumentReader的实现类,真正的Document文档语义解析是委托给BeanDefinitionParserDelegate进行解析,该类包含了对各种Spring Bean定义规则的处理, 其处理的结果由BeanDefinitionHolder持有,BeanDefinitionHolder除了持有BeanDefinition对象外,还持有其他与BeanDefinition使用相关的信息,比如Bean的名字,别名名称等
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }

这样经过逐层的解析,在XML文件中定义的BeanDefinition就被载入到了IoC容器中,并在容器中建立了数据映射。这些数据结构以AbstractBeanDefinition为入口,让IoC容器执行索引、查询和操作,但重要的依赖注入此时还未发生,现在IoC容器中还只是存着静态的配置信息。
3.2 BeanDefinition在IoC容器中的注册 此时IoC容器还不能直接使用,需要在IoC容器中对这些BeanDefinition进行注册,具体是指在DefaultListableBeanFactory中的HashMap存储这些BeanDefinition信息
/** Map of bean definition objects, keyed by bean name. */ private final Map beanDefinitionMap = new ConcurrentHashMap<>(256);

BeanDefinition注册的动作是在前文的DefaultBeanDefinitionDocumentReader.processBeanDefinition中执行,具体实现在BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry), 其调用了DefaultListableBeanFactory的registerBeanDefinition方法.
BeanDefinition注册完成后,就已经可以被IoC容器所使用了,他们都被存在DefaultListableBeanFactory的beanDefinitionMap中,可以被检索和使用。
3.3 IoC容器的依赖注入 DefaultListableBeanFactory.getBean()和AbstractBeanFactory.getBean()
依赖注入的入口,其具体实现是在DefaultListableBeanFactory的基类AbstractBeanFactory的getBean方法中具体的实现的,之后会调用createBean()
AbstractAutowireCapableBeanFactory.createBean()
createBean不但生成了需要的Bean,还对Bean初始化进行了处理,比如实现了在BeanDefinition中的init-method属性定义,Bean后置处理器等
AbstractAutowireCapableBeanFactory.createBeanInstance()
在createBeanInstance中生成了Bean所包含的Java对象,这个对象的生成有很多种不同的方式,可以通过工厂方法生成,也可以通过容器的autowire特性生成,这些都由相关的BeanDefinition来指定的。
如果未指定生成策略,默认有2种方式,一种是JVM的反射功能,一种是通过cglib来生成,SimpleInstantiationStrategy是生成对象的默认处理类
AbstractAutowireCapableBeanFactory.populateBean()
实例化后,会进行property的配置,通过使用BeanDefinitionResolver来对BeanDefinition进行解析,然后注入到property中. 在BeanDefinitionValueResolver类进行具体的解析。 在这里会触发相关依赖Bean的递归实现,在resolveValueIfNecessary方法中进行解析处理。
依赖注入的发生是在BeanWrapper的setPropertyValues中实现的,具体的完成却是在BeanWrapper的子类BeanWrapperImpl中实现的。
一个递归是在上下文体系中查找需要的 Bean和创建Bean的递归调用, 另一个递归是在依赖注入时, 通过递归调用容器的getBean方法, 得到当前Bean的依赖Bean,同时也触发对依赖Bean的创建和注入 。在对Bean的属性进行依赖注入时 ,解析的过程也是一个递归的过程 。这样 ,根据依赖关系 ,一层一层地完成Bean的创建和注入 ,直到最后完成当前Bean的创建 。有了这个顶层Bean的创建和对它的属性依赖注入的完成 ,意味着和当前Bean相关的整个依赖链的注入也完成了 。

    推荐阅读