spring技术内幕读书笔记之ioc容器的学习

     2022-03-21     167

关键词:

第二篇:IoC容器的初始化

在介绍FileSystemXmlApplicationContext 的例子时有说到IoC容器的初始化由refresh()方法开始启动,此方法标志着IoC容器的启动[构造器中有此方法的容器]。细分的话,这个启动过程具体的可分为三部分:

第一部分:BeanDefinition的Resource定位

是指BeanDefinition(容器内部定义的bean的数据结构),它由ResourceLoader通过统一的Resource接口来完成,Resource对各种形式的BeanDefinition的使用都提供了统一的接口,比如文件系统中的Bean定义信息可以通过FileSystemResource进行抽象,类路径中的Bean定信息可以通过ClassPathResource来获取。

在介绍XmlBeanFactory时可以看到,使用DefaultListableBeanFactory时,首先要定义一个Resource(即ClassPathResourceres = new ClassPathResource("SpringBeans.xml");)这里定义的Resource不能由DefaultListableBeanFactory直接使用,而是通过初始化一个XmlBeanDefinitionReader,靠reader对象来处理,完成定位的。

再拿FileSystemXmlApplicationContext的例子与之比较,不难发现ApplicationContext相对于直接使用DefaultListBeanFactory有一个好处----在ApplicationContext中Spring已经为我们提供了一系列加载不同Resource的读取器的实现。具体的读取器是怎样实现的正是此部分研究的重点。

还是那句话IoC容器的初始化由refresh()方法开始启动,FileSystemXmlApplicationContext在构造器中调用了refresh方法,在refresh方法中会调用obtainFreshBeanFactory获取beanFactory,在obtainFreshBeanFactory中会调用refreshBeanFactory这一抽象方法,在此方法的实现过程中构造了DefaultListableBeanFactory,通过XmlBeanFactory的例子我们知道DefaultListableBeanFactory初始化一个XmlBeanDefinitionReader,靠reader对象来处理,完成定位的。

reader对象有了之后,该怎样读入呢?

XmlBeanFactory是这样

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
    super(parentBeanFactory);
    this.reader.loadBeanDefinitions(resource);
}

这里同样需要调用loadBeanDefinitions方法,但resource是通过调用重写getResourceByPath获得的。子类FileSystemXmlApplicationContext重写了此方法会返回FileSystemResource对象,其他类型的ApplicationContext会对应生成其他类型的Resource。

技术分享

这个FileSystemXmlApplicationContext的Resource定位用一句话概括就是:refresh()之后,构造出XmlBeanDefinitionReader对象,在loadBeanDefinitions时根据FileSystemXmlApplicationContext重写getResourceByPath返回的以FileSystem方式存在的resource对象进行BeanDefinition的载入,在定位完成之后知识为载入提供了I/O操作条件,并未开始读入。

第二部分:BeanDefinition的载入和解析

这个过程就是把用户定义好的Bean表示成IoC容器内部的数据结构即BeanDefinition。首先需要知道IoC容器对Bean的管理和依赖注入功能的实现,是通过对其持有的BeanDefinition进行各种相关操作来完成的。这些BeanDefinition数据在IoC容器中通过一个HashMap来保持和维护。当然这只是一种比较简单的维护方式,如果需要提高IoC容器的性能和容量,完全可以自己做一些扩展。

下面,从DefaultListableBeanFactory的设计入手,看看IoC容器是怎样完成BeanDefinition载入的。

首先是初始化的入口—refresh方法,该方法在FileSystemXmlApplicationContext的构造方法中被调用,在FileSystemXmlApplicationContext的基类AbstractApplicationContext中实现,它详细地描述了整个ApplicationContext的初始化过程,比如BeanFactory的更新,MessageSource和PostProcessor的注册等。这里看起来更像是对ApplicationContext进行初始化的模块或执行提纲,这个执行过程为Bean的生命周期管理提供了条件。

refresh实现源码:

public void refresh() throwsBeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
  // Prepare this context forrefreshing.
  prepareRefresh();
  // Tell the subclass to refresh the internal bean factory.
  // 这里是在子类中启动refreshBeanFactory()的地方
  ConfigurableListableBeanFactory beanF actory= obtainFreshBeanFactory();
  // Prepare the bean factory for use in this context.
  prepareBeanFactory(beanFactory);
  try {
      // 设置BeanFactory的后置处理
    // Allows post-processing ofthe bean factory in context subclasses.
    postProcessBeanFactory(beanFactory);
    // 调用BeanFactory的后处理器,这些后处理器是在Bean定义中向容器注册的
    // Invoke factory processorsregistered as beans in the context.
    invokeBeanFactoryPostProcessors(beanFactory);
    // 注册Bean的后处理器,在Bean创建过程中调用
    // Register bean processorsthat intercept bean creation.
    registerBeanPostProcessors(beanFactory);
    // 对上下文中的消息源进行初始化
    // Initialize message sourcefor this context.
    initMessageSource();
    // 初始化上下文中的事件机制
    // Initialize event multicasterfor this context.
    initApplicationEventMulticaster();
    // 初始化其他的特殊Bean
    // Initialize other specialbeans in specific context subclasses.
    onRefresh();
    // 检查监听Bean并且将这些Bean向容器注册
    // Check for listener beans andregister them.
    registerListeners();
    // 实例化所有的(non-lazy-init)单件
    // Instantiate all remaining(non-lazy-init) singletons.
    finishBeanFactoryInitialization(beanFactory);
    // 发布容器事件,结束Refresh过程
    // Last step: publish correspondingevent.
    finishRefresh();
   }catch (BeansException ex) {
    logger.warn("Exceptionencountered during context initialization - cancelling refresh attempt",ex);
    // 为防止Bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件Bean
    // Destroy already createdsingletons to avoid dangling resources.
    destroyBeans();
    // 重置 ‘active‘标志
    // Reset ‘active‘ flag.
    cancelRefresh(ex);
    // Propagate exception tocaller.
    throw ex;
   }
 }
}

在ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory();中会调用AbstractRefreshableApplicationContext中的refreshBeanFactory方法,在这个方法中创建了BeanFactory。在创建IoC容器前,如果已经有容器存在,那么需要把已有的容器销毁和关闭,保证在refresh以后使用的是新建立起来的IoC容器。在建好容器后开始BeanDefinition的载入工作。

具体的refreshBeanFactory实现:

protected final void refreshBeanFactory()throws BeansException {
    // 这里判断,如果已经建立了BeanFactory,则销毁并关闭该BeanFactory
  if (hasBeanFactory()) {
    destroyBeans();
    closeBeanFactory();
  }
  // 这里是创建并设置持有的DefaultListableBeanFactory的地方
  // 同时调用loadBeanDefinitions载入BeanDefinition的信息
  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 beandefinition source for " + getDisplayName(), ex);
  }
 }

BeanDefinition的载入交互过程:

技术分享

这里调用的loadBeanDefinitions实际上是一个抽象方法,那么实际的载入过程发生在哪里呢?我们看看前面提到的loadBeanDefinitions在AbstractRefreshableApplicationContext的子类AbstractXmlApplicationContext中的实现,在这个loadBeanDefinitions中,初始化了读取器XmlBeanDefinitionReader,然后把这个读取器在IoC容器中设置好(过程和编程式使用XmlBeanFactory是类似的),最后是启动读取器来完成BeanDefinition在IoC容器的载入。

// 这里是实现loadBeanDefinitions的地方
protected void loadBeanDefinitions(DefaultListableBeanFactorybeanFactory) throws BeansException, IOException {
    // Create a new XmlBeanDefinitionReader for the given BeanFactory.
  // 创建XmlBeanDefinitionReader,并通过回调设置到BeanFactory中去,创建BeanFactory
  // 的过程可以参考上文对编程式使用IoC容器的相关分析,这里和前面一样,使用的也是
  // DefaultListableBeanFactory
  XmlBeanDefinitionReader beanDefinitionReader = newXmlBeanDefinitionReader(beanFactory);
  // Configure the bean definition reader with this context‘s
  // resource loading environment.
  // 这里设置XmlBeanDefinitionReader,为XmlBeanDefinitionReader配
  // ResourceLoader,因为DefaultResourceLoader是父类,所以this可以直接被使用
  beanDefinitionReader.setEnvironment(this.getEnvironment());
  beanDefinitionReader.setResourceLoader(this);
  beanDefinitionReader.setEntityResolver(newResourceEntityResolver(this));
  // Allow a subclass to provide custom initialization of the reader,
  // then proceed with actually loading the bean definitions.
  // 这是启动Bean定义信息载入的过程
  initBeanDefinitionReader(beanDefinitionReader);
  loadBeanDefinitions(beanDefinitionReader);
}
protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader){
       reader.setValidating(this.validating);
}

接着就是loadBeanDefinitions调用的地方,首先得到BeanDefinition信息的Resource定位,然后直接调用XmlBeanDefinitionReader来读取,具体的载入过程是委托给BeanDefinitionReader完成的。因为这里的BeanDefinition是通过XML文件定义的,所以这里使用XmlBeanDefinitionReader来载入BeanDefinition到容器中。

protected voidloadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException,IOException {
    // 以Resource的方式获得配置文件的资源位置
  Resource[] configResources = getConfigResources();
  if (configResources != null) {
      reader.loadBeanDefinitions(configResources);
  }
  // 以String的形式获得配置文件的定位
  String[] configLocations = getConfigLocations();
  if (configLocations != null) {
    reader.loadBeanDefinitions(configLocations);
  }
}
protected Resource[] getConfigResources() {
  return null;
}

通过以上对实现原理的分析,我们可以看到,在初始化FileSystemXmlApplicationContext的过程中是通过调用IoC容器的refresh来启动整个BeanDefinition的载入过程的,这个初始化是通过定义的XmlBeanDefinitionReader来完成的。同时,我们也知道实际使用的IoC容器是DefualtListableBeanFactory,具体的Resource载入在XmlBeanDefinitionReader读入BeanDefinition时实现

因为Spring可以对应不同形式的BeanDefinition。由于这里使用的是XML方式的定义,所以需要使用XmlBeanDefinitionReder。如果使用了其他的BeanDefinition方式,就需要使用其他种类的BeanDefinitionReder来完成数据的载入工作。

在XmlBeanDefinitionReader的实现中可以看到,是在reader.loadBeanDefinitions中开始进行BeanDefinition的载入的,而这时XmlBeanDefinitionReader的父类AbstractBean-Definition-Reader已经为BeanDefinition的载入做好了准备。

@Override
publicint loadBeanDefinitions(Resource... resources) throwsBeanDefinitionStoreException {
    Assert.notNull(resources,"Resource array must not be null");
  intcounter = 0;
  for(Resource resource : resources) {
    counter+= loadBeanDefinitions(resource);
  }
  returncounter;
}

这里调用的loadBeanDefinitions(resource);在AbstractBeanDefinitionReader中没有实现,它是一个接口方法,具体实现在XmlBeanDefinitionReader中。在拥有了文件对象后就可以按照spring的bean定义规则来对XML文档树进行解析了,如何得到的document对象,可以参考DefaultDocumentLoader。紧接着Spring的BeanDefinition如何按照bean语义进行解析转化成容器内部数据结构的,这部分在registerBeanDefinition(doc,resource)中完成。

loadBeanDefinitionsà doLoadBeanDefinitionsà registerBeanDefinitions

至于怎样详细转化的不再深入了。

第三部分:向IoC容器注册这些BeanDefinition

这个过程是通过BeanDefinitionRegistry接口把载入过程中解析得到的BeanDefinition项IoC容器注册,IoC容器内部将这些BeanDefinition注入到HashMap中,IoC容器就是通过这个hashmap来持有这些BeanDefinition数据的。可以看到HashMap定义在DefaultBeanFactory中。

private static final Map<String,Reference<DefaultListableBeanFactory>> serializableFactories =

new ConcurrentHashMap<String,Reference<DefaultListableBeanFactory>>(8);

具体的注册调用过程为:

技术分享

技术分享

从上图的调用关系可以看到registerBeanDefinition方法何时被调用。

注意:这个初始化过程不包括Bean依赖注入的实现,依赖注入一般发生在应用第一次通过getBean向容器索取Bean的时候,可以通过设置Bean的lazyinit属性使得bean的依赖注入在IoC容器初始化是就完成。


《spring技术内幕》笔记-第二章ioc容器的实现

简单介绍1,在Spring中,SpringIoC提供了一个主要的JavaBean容器。通过IoC模式管理依赖关系。并通过依赖注入和AOP切面增强了为JavaBean这样子的POJO提供事务管理,生命周期管理等功能。2,SpringIoC的设计中,主要包括两个基本的容器... 查看详情

spring学习笔记

Spring学习笔记(一)因为最近在看《Spring技术内幕-深入解析Spring架构与设计原理》,相当于做了个笔记,以及把自己理解的东西记录下来。Spring整体架构图1.Spring子项目1.1SpringFramework(Core)  Spring项目的核心,其中包含IOC容器,... 查看详情

spring源码解析之ioc容器(代码片段)

...在技术的道路上注定走不长远。最近,学习了一段时间的spring源码,现在整理出来,以便日后温故知新。  IOC容器是spring最核心的模块之一,是整个spring体系的基石,spring其他模块中,都需要用到IOC容器的功能。spring框架为我... 查看详情

spring揭秘读书笔记二beanfactory的对象注冊与依赖绑定

本文是王福强所著<<spring揭秘>>一书的读书笔记我们前面就说过,Spring的IoC容器时一个IoCServiceProvider,并且IoCServiceProvider提供两个功能对象的创建,依赖关系的管理。只是,IoC容器这个词中,我们还得关注容器二字。它还包括了... 查看详情

spring.net入门学习笔记

一、基本概念  Spring.Net是一个轻量级的控制反转(Ioc)和面向切面的(Aop)的容器框架;Ioc:控制反转:简单的说就是将创建对象的控制权转交给外部容器(IApplicationContext);DI:依赖注入:其实就是将通过容器创建对象的... 查看详情

springinaction读书笔记——容器和依赖注入(代码片段)

容器java程序员都知道IOC和AOP是Spring的两大核心概念。IOC是控制反转,也就是通过注入的方式把原本由类自己完成的内部依赖对象实例化这个行为转交给Spring的容器去完成。IOC之后的一个很大的好处就是:代码里消除了无... 查看详情

spring学习笔记--环境搭建和初步理解ioc

  Spring框架是一个轻量级的框架,不依赖容器就能够运行,像重量级的框架EJB框架就必须运行在JBoss等支持EJB的容器中,核心思想是IOC,AOP,Spring能够协同Struts,hibernate等其他众多的框架.  下面介绍环境的搭建,首先下载好spring的完... 查看详情

学习spring容器ioc之依赖和依赖注入

学习开涛跟我学spring3http://jinnianshilongnian.iteye.com/为什么要应用依赖注入,应用依赖注入能给我们带来哪些好处呢?1.动态替换Bean依赖对象,程序更灵活:替换Bean依赖对象,无需修改源文件:应用依赖注入后,由于可以采用配置... 查看详情

spring学习第0节--核心技术ioc容器ioc理解

耦合与内聚耦合是指模块间的依赖关系,内聚是指模块内组件的交互程度如果模块间依赖程度很高,那么在多人合作编程中,一个人的工作会很容易的受到另一个人工作的波及。 反射Java反射机制是指在运行状态下,完成对... 查看详情

学习笔记--spring框架

//概述轻量级,一站式,开发框架IoC,InversionofControl,控制反转DI,DependencyInjection,依赖注入AOP,Aspect-OrientedProgramming,面向切面编程:业务逻辑与非业务逻辑分离,如日志、安全...IoC容器:对象创建、装配对象生命周期管理上... 查看详情

《深入分析javaweb技术内幕》读书笔记——中文编码

为什么要编码在计算机中存储信息的最小单元是1个字节(8bit),所以能表示的字符范围是0-255个。人类要表达的字符太多,无法用1个字节完全表示。要解决这个问题需要使用新的数据结构char,从char到byte必须编码。 编码格... 查看详情

spring:笔记整理——ioc容器

IOC容器什么是IOC  IOC,全称Inversionofcontrol,即,控制反转,是一种设计思想。控制:在Java中,IOC意味着:你将设计好的对象交给容器控制,而不是传统的在你的对象内容直接控制。在传统程序设计中,我们直接在对象内容通过n... 查看详情

深入分析javaweb技术内幕读书笔记-第一章

第一章、深入web请求过程B/S架构带来了2个方面的好处。1、客户端使用统一的浏览器。2、服务端基于统一地http。 1.1B/S网络架构概述B/S网络架构从前端到后端都得到了简化,都基于统一地应用层协议HTTP来交互数据,与大多数... 查看详情

spring学习笔记

1.Spring框架的描述   1.Spring是一个开源框架   2.Spring为简化企业级应用开发而生   3.Spring是一个IoC/DI和AOP容器框架2.具体描述Spring   1.轻量级:Spring是非侵入式的-基于Spring开发的应用中的对象可以不依赖于Spring的API   2.... 查看详情

学习笔记——spring底层ioc实现;spring依赖注入数值问题;spring依赖注入方式

2023-01-14一、Spring底层IOC实现1、IOC:将对象的控制器反转给Spring2、BeanFactory与ApplicationContext(1)BeanFactory:IOC容器的基本实现,是Spring内部的使用接口,是面向Spring本身的,不是提供给开发人员使用的。(2)ApplicaitonContext:BeanFact... 查看详情

spring——ioc容器基本概念(代码片段)

       前一段时间在学习Spring,感觉Spring确实挺有魅力的,学习之后,对学过的知识简单的做一下总结,如果要总结IOC容器自然是少不了的,我前边有过一篇文章来介绍IOC的思想,但是没有涉及到Spring... 查看详情

spring核心思想之ioc总结

说到Spring的核心思想Ioc和aop不是Spring提出来的,在Spring之前就已经存在,只不过更偏向理论化,Spring在技术层次把两个思想做了非常好的实现(java)第一部分:什么是IOCIOCInversionofControl(控制反转/反转控制),注意它是?个技术思想... 查看详情

spring2——ioc之bean的装配

  spring容器对于bean的装配提供了两个接口容器分别是"ApplicationContext接口容器"和"BeanFactory接口容器",其中"BeanFactory接口容器"是spring的顶级接口容器,"ApplicationContext接口容器"继承了"BeanFactory接口容器",并且两个接口容器对于B... 查看详情