1.3浅谈Spring(IOC容器的实现)

这一节我们来讨论IOC容器到底做了什么。

还是借用之前的那段代码

ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
Car car =app.getBean(Car.class);
System.out.println(car.getBrand()+","+car.getDesc());

这里ClassPathXmlApplicationContext是如何加载beans.xml的呢?

它到底做了哪些事情?

1.资源定位 2.资源加载 3.资源注册

再次之前,补充一点:SpringIOC容器管理了我们定义的各种Bean对象及其相互的关系,Bean对象在Spring实现中是以BeanDefinition(Bean定义资源文件中配置的POJO对象在Spring IoC容器中的映射)来描述的。

IOC初始化

现在我们通过源码来分析并验证:

首先进入它的构造方法,这个构造方法调用其他的构造方法

没错,就是这个方法啦,参数跟上图一样。这里有三个方法:

super();

setConfigLocation();

refresh();

来看一下这三个方法:

1、资源定义

①super();//资源加载器的配置

执行的是AbstractApplicationContext的构造方法

这里的this,就是ClassPathXmlApplicationContext,它间接的继承自AbstractApplication Context,而AbstractApplicationContext又继承了DefaultResourceLoader,故它本身就是个ResourceLoader

②setConfigLocation()//资源定位

处理文件路径为一个字符串的情况

处理多个多个资源文件字符串数组

StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS)
//String CONFIG_LOCATION_DELIMITERS = ",; \t\n";

这个方法是用来解析我们给的location,因为在创建ClassPathXmlApplicationContext时,我们可以传入多个Xml的配置,并用分号隔开。如:

ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("beans.xml;spring.xml");

这时候就需要来解析这个String了,当然这个String有多种写法,不止用分号这么简单。所以需要来解析一下。

this.configLocations[i] = resolvePath(locations[i]).trim();

这个resovlePath是用来解析一些占位符的,由于这些文件可能是 classpath , filesystem ,或者是 URL 网络资源, servletContext 等。所以可能会存在占位符。

具体的解析过程在PropertyPlaceholderHelper的parseStringValue中。

2、资源加载

AbstractApplicationContext的refresh()是一个模版方法,它就是对IOC容器进行初始化并且对资源进行载入。其中obtainFreshBeanFactory()就是对"Bean"资源加载的关键。

startupShutdownMonitor用于刷新和销毁的同步标记

①prepareRefresh()//准备刷新

准备此上下文以进行刷新,设置其启动日期和活动标志以及执行属性源的任何初始化。

②obtainFreshBeanFactory()//资源加载

先看refreshBeanFactory();//刷新BeanFactory

判断是否存在BeanFacotry(即IOC容器),如果存在就销毁,因为IOC容器是单例的。只能存在一个。

这里createBeanFactory创建的是一个默认的DefaultListableBeanFactory

customizeBeanFactory();//初始化工厂参数

自定义此上下文使用的内部bean工厂。 为每次refresh()尝试调用。默认实现应用此上下文的“allowBeanDefinitionOverriding”和“allowCircularReferences”设置(如果已指定), 可以在子类中重写以自定义任何DefaultListableBeanFactory的设置。

loadBeanDefinitions();//加载BeanDefinitions

这里调用的是AbstractXmlApplicationContext的loadBeanDefinitions方法

这里传入了RourceLoader的资源加载器为ClassPathXmlApplicationContext

真正执行是重写的方法

继续执行重写方法

真正执行的重写方法:

图-X

这里首先获取getResourceLoader即是获取ClassPathXmlApplication,之前说过它继承自AbstractApplicationContext,而AbstractApplicationContext又继承了DefaultResource Loader

IOC容器是如何取到资源的呢?

这里的getResourceLoader();//取资源加载器

//获取资源
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);

这里locationPattern用来区分是以多资源文件的形式,还是单资源文件的形式(就像Spring跟SpringMVC同时使用时的情况).

String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

这里走else分支的else分支(获取单个资源)

return new Resource[] {getResourceLoader().getResource(locationPattern)};

getResourceLoader()即获取资源加载器,这里的就是ClassPathXmlApplicationContext,

getResource()//获取资源

这里仍然走的是else分支,执行getResourceByPath(location);区分是从 本地,还是URL,还是其他的方式来配置的。

最终执行ClassPathResource的构造方法

最终得到的resource如下图:

在获取到了Resource之后,我们继续回到图-X,看它接下来的操作

int loadCount = loadBeanDefinitions(resources);

最终执行XmlBeanDefinitionReader的loadBeanDefinitions方法:

/**
*从指定的XML文件加载bean定义。
* @param encodedResource XML文件的资源描述符,
*允许指定用于解析文件的编码
* @return找到的bean定义数
* @throws BeanDefinitionStoreException在加载或解析错误的情况下
*/

3、资源注册

在注册之前需要将读取到的resource转换成BeanDefinitions

这里获取资源的输入流,并执行真正执行的方法doLoadBeanDefinitions()

/**
*实际上从指定的XML文件加载bean定义。
* @param inputSource要读取的SAX InputSource
* @param资源XML文件的资源描述符
* @return找到的bean定义数
* @throws BeanDefinitionStoreException在加载或解析错误的情况下
* @see #doLoadDocument
* @see #registerBeanDefinitions
*/

这里doLoadDocument(inputSource,resource)将资源文件转换成DOM对象

具体转换过程这里就不进行细看了,主要使用JAXP。

转换成DOM之后需要做的就是将DOM转换成Spring能够识别的数据结构BeanDefinitions

这里的BeanDefinitionDocumentReader用于解析实际的DOM文档。

这里执行DefaultBeanDefinitionDocumentReader的registerBeanDefinitions

doRegisterBeanDefinitions();是真正的注册beanDefinitions的方法

这里有一个用来帮助解析的类:

BeanDefinitionParserDelegate

官方给的解释是:用于解析XML bean定义的有状态委托类。 旨在供主解析器和任何扩展BeanDefinitionParsers或BeanDefinitionDecorators使用。

之后是对Spring命名空间的一些检测。

最后才是真正的解析BeanDefinitions——parseBeanDefinitions

/**
*解析文档中根级别的元素:
*“import”,“alias”,“bean”。
* @param root文档的DOM根元素
*/

这里补充一点XML的知识

xml文档 —————-> Document对象 代表整个xml文档
节点 —————>Node对象 父类
标签节点 —————> Element对象 子类
属性节点 —————> Attribute对象 子类
文本节点 —————>Text对象 子类
NodeList ---------->节点列表集合(Node的集合)

先看这个默认命名空间的解析方法:

parseDefaultElement();

这里分别对<import> <alias> <bean><beans> 用各自的方法进行解析

这里的顺序也是比较讲究

先查看是否有<import>标签,如果有可以先引入其他资源文件到IOC容器中。

然后查看<alias>标签,如果有先引入别名到IOC容器中(后面会根据是否有id将别名赋值给bean)

然后查看<bean>标签

最后查看<beans>,可能<beans>里引入了其他<bean>,故在最后

这里我们查看对于 <bean>标签的解析:

processBeanDefinition();

/**
*处理给定的bean元素,解析beanDefinition
*并在注册表中注册。
*/

首先解析BeanDefinition的基本属性:

BeanDefinitionParserDelegate里的parseBeanDefinitionElement();

原文地址:https://www.cnblogs.com/Qiansion/p/9780663.html

时间: 2024-10-09 00:43:42

1.3浅谈Spring(IOC容器的实现)的相关文章

浅谈Spring IOC

Spring IOC 1.IOC是什么? IOC(Inversion of Control)---控制反转: IOC不是一种技术,是一种设计思想.在没有Spring IOC时,对象的创建和依赖关系是由我们自己来创建和管理,有了IOC之后,对象的创建和依赖关系由Sping IOC容器来创建和管理. 理解好IOC,需要明白: 谁控制谁,控制什么: 在以前我们是通过new的方式来创建对象,组装对象之间的依赖关系:有了IOC容器之后,由容器负责对象的创建和依赖关系. 谁控制谁:Spring IOC容器控

1.1浅谈Spring(一个叫春的框架)

如今各种Spring框架甚嚣尘上,但是终归还是属于spring的东西.所以在这里,个人谈一谈对spring的认识,笔者觉得掌握spring原理以及spring所涉及到的设计模式对我们具有极大的帮助.我们基于what ,why ,how来研究Spring. Spring是什么? Spring为什么? 如何使用Spring? 关于这三个问题可以先自行百度!!!针对这个3个问题提出以下几点. Spring有三大地方值得注意: 1.IOC容器 2.IOC控制反转和DI依赖注入 3.AOP面向切面编程 首

由openSession、getCurrentSession和HibernateDaoSupport浅谈Spring对事物的支持

由openSession.getCurrentSession和HibernateDaoSupport浅谈Spring对事物的支持 Spring和Hibernate的集成的一个要点就是对事务的支持,openSession.getCurrentSession都是编程式事务(手动设置事务的提交.回滚)中重要的对象,HibernateDaoSupport则提供了更方便的声明式事务支持. Hibernate中最重要的就是Session对象的引入,它是对jdbc的深度封装,包括对事务的处理,Session对

详解Spring IoC容器

一.Spring IoC容器概述 1.依赖反转(依赖注入):依赖对象的获得被反转了. 如果合作对象的引用或依赖关系的管理由具体对象来完成,会导致代码的高度耦合和可测试性的降低,这对复杂的面向对象系统的设计是非常不利的. 在Spring中,IoC容器是实现依赖控制反转这个模式的载体,它可以在对象生成或者初始化时直接将数据注入到对象中,也可以通过将对象引用注入到对象数据域中的方式来注入对方法调用的依赖.这种依赖是可以递归的,对象被逐层注入. 关于如何反转对依赖的控制,把控制权从具体业务对象中转交到平

浅谈Spring(四)AOP实例

在<浅谈Spring(三)AOP原理>中我详细的介绍了AOP的基本概念和实现原理,这里给出代码示例. 一.XML方式 1. TestAspect:切面类 package com.spring.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; public class TestAspect { public void doAfter(JoinPoint jp) { System

spring ioc 容器概念

spring ioc 容器 一个java应用程序是有很多类组成的,这些类相互协作.相互作用来提供应用程序的表现行为.那些被其它类组合提供某些行为的类,称之为其它类的依赖(dependencies).利用软件工程中的组合模式(经常是继承模式的反模式)来说,我们经常利用某些类组合成其它类,不管这些类是通过构造函数还是setter方法或其它方法,那么组合成其它类的那些类就是这个组合类的依赖.当组合类要表现出的行为依赖这些类的时候,这些类必须被创建并注入给组合类. 在spring应用中,spring i

Spring IoC容器的初始化过程

Spring IoC容器的初始化包括 BeanDefinition的Resource定位.载入和注册 这三个基本的过程.IoC容器的初始化过程不包含Bean依赖注入的实现.Bean依赖的注入一般会发生在第一次通过getBean向容器索取Bean的时候. 先看以下代码: ApplicationContext context = new ClassPathXmlApplicationContext("ioc.xml"); Car car = (Car) context.getBean(&q

Mybaits3源码分析(二):扫描Mapper关联到spring IOC容器

首先讲讲mapper是怎么从配置到对象的. <!-- 采用自动扫描方式创建mapper bean(单个更新模式) --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.xxx.dao" /> <property name="sqlSe

Spring—IOC容器如何实例话Bean

前言  传统应用程序可以通过new和反射的方式来实例化Bean.而Spring Ioc容器则需要根据配置元数据使用反射机制来创建Bean.在Spring Ioc容器中根据Bean创建Bean实例有以下几种方式. (1).使用构造器实例化Bean (2).使用静态工厂实例化Bean (3).使用实例工厂实例化Bean 一.使用构造器实例化Bean:Spring IoC容器能使用默认空构造器也能使用有参数构造器两种方式创建Bean. (a).使用空构造器进行定义,使用此种方式,class属性指定的类