引自 spring IoC源码分析 (3)Resource解析
定义好了Resource之后,看到XmlFactoryBean的构造函数
- public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
- super(parentBeanFactory);
- this.reader.loadBeanDefinitions(resource);
- }
- public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
- Assert.notNull(encodedResource, "EncodedResource must not be null");
- if (logger.isInfoEnabled()) {
- logger.info("Loading XML bean definitions from " + encodedResource.getResource());
- }
- //线程安全 ,但这里 currentResources应该本来就是线程安全的,所以推测不是为了线程安全
- //应该是为了线程能使用同一个 currentResources ,从这里可以看出作者对 ThreadLocal 的理解深刻
- Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
- if (currentResources == null) {
- currentResources = new HashSet<EncodedResource>(4);
- this.resourcesCurrentlyBeingLoaded.set(currentResources);
- }
- //这里其实就是为了避免循环加载,如果重复加载了相同的文件就会抛出异常
- //这里看了半天才明白这个set的意图,蛋疼啊
- if (!currentResources.add(encodedResource)) {
- throw new BeanDefinitionStoreException(
- "Detected recursive loading of " + encodedResource + " - check your import definitions!");
- }
- try {
- 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();
- }
- }
- catch (IOException ex) {
- throw new BeanDefinitionStoreException(
- "IOException parsing XML document from " + encodedResource.getResource(), ex);
- }
- finally {
- currentResources.remove(encodedResource);
- if (currentResources.isEmpty()) {
- this.resourcesCurrentlyBeingLoaded.set(null);
- }
- }
- }
其 实关键方法是 doLoadBeanDefinitions(inputSource, encodedResource.getResource()) ,但是上面的注释绝对花了我将近1个钟头才理解作者想表达的意思,刚开始一看到ThreadLocal 就想到线程安全,然后就想currentResources 永远是线程安全的啊,丫就这么被带坑里去了。从上面可以看到关键方法是doLoadBeanDefinitions,这个方法的关键代码其实就几行
- try {
- //判断xml文件是DTD还是XSD样式,如果没定义将使用XSD
- int validationMode = getValidationModeForResource(resource);
- Document doc = this.documentLoader.loadDocument(
- inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
- return registerBeanDefinitions(doc, resource);
- }
看下getValidationModeForResource 一路跟踪下来到XmlValidationModeDetector的detectValidationMode方法
- while ((content = reader.readLine()) != null) {
- content = consumeCommentTokens(content);
- if (this.inComment || !StringUtils.hasText(content)) {
- continue;
- }
- if (hasDoctype(content)) {
- isDtdValidated = true;
- break;
- }
- if (hasOpeningTag(content)) {
- // End of meaningful data...
- break;
- }
- }
就是分析下xml文件里有没有DOCTYPE关键字,没有的话就认为是xsd格式的。然后就到了documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); 这个方法
- public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
- ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
- DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
- if (logger.isDebugEnabled()) {
- logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
- }
- DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
- return builder.parse(inputSource);
- }
其实就是使用DocumentBuilderFactory 去解析xml,这块不怎么熟,查了下网上的介绍也没太详细的。
- public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
- this.readerContext = readerContext;
- logger.debug("Loading bean definitions");
- Element root = doc.getDocumentElement();
- BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
- preProcessXml(root);
- parseBeanDefinitions(root, delegate);
- postProcessXml(root);
- }
的。具体解析工作是parseBeanDefinitions,跟踪到private void
parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
这个方法,我们主要关注对bean的解析,所以直接看processBeanDefinition(ele, delegate)
- /**
- * Process the given bean element, parsing the bean definition
- * and registering it with the registry.
- */
- protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
- /* BeanDefinitionHolder
是BeanDefinition对象的封装类,封装了 BeanDefinition,Bean的名字和别名。用它来完成向IoC容器注
册。 BeanDefinitionParserDelegate对XML元素的信息按照Spring的Bean规则进行解析*/ - 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));
- }
- }
先
来看delegate.parseBeanDefinitionElement(ele)
方法,BeanDefinitionParserDelegate这个类 里包含了对各种Spring Bean定义规则的处理。比如我们最熟悉
的对Bean元素的处理是怎样完成的,也就是怎样处理在XML定义文件中出现的
<bean></bean>这个最常见的元素信息。在这里会看到对那些熟悉的BeanDefinition定义的处
理,比如id、name、aliase等属性元素。把这些元素的值从XML文件相应的元素的属性中读
取出来以后,设置到生成的BeanDefinitionHolder中去。这些属性的解析还是比较简单的。
对于其他元素配置的解析,比如各种Bean的属性配置,通过一个较为复杂的解析过程,这个
过程是由parseBeanDefinitionElement来完成的。解析完成以后,会把解析结果放到
BeanDefinition对象中并设置到BeanDefinitionHolder中去。其实就是根据spring自己对xml文件的定义进行解析。
这个 BeanDefinition数据对象中封装的数据大多都是与<bean>定义相关的,也有很多就是我们在定义Bean时看到
的那些Spring标记,比如常见的init-method、destroy-method、factory-method,等等,这个
BeanDefinition数据类型是非常重要的,它封装了很多基本数据,这些基本数据都是IoC容器
需要的。有了这些基本数据,IoC容器才能对Bean配置进行处理,才能实现相应的容器特性。spring对资源文件的解析和加载基本到此,下一篇继续分
析spring对bean的获取
贴一张spring 内部调用的图先
spring IoC源码分析 (3)Resource解析的更多相关文章
-
Spring IOC 源码分析
Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文 ...
-
Spring IoC 源码分析 (基于注解) 之 包扫描
在上篇文章Spring IoC 源码分析 (基于注解) 一我们分析到,我们通过AnnotationConfigApplicationContext类传入一个包路径启动Spring之后,会首先初始化包扫 ...
-
spring IOC源码分析(ApplicationContext)
在上一篇文章中,我们以BeanFactory这条主线进行IOC的源码解析的,这里,将以ApplicationContext这条线进行分析.先看使用方法: @Test public void testA ...
-
Spring IOC源码分析之-刷新前的准备工作
目录 ClassPathXmlApplicationContext的注册方式 加载父子容器 配置路径解析 容器刷新 刷新容器之刷新预处理 ClassPathXmlApplicationContext的 ...
-
《Java Spring框架》Spring IOC 源码分析
1.下载源码 源码部署:https://www.cnblogs.com/jssj/p/11631881.html 并不强求,最好是有源码(方便理解和查问题). 2. 创建子项目 Spring项目中创建 ...
-
spring ioc 源码分析之-- beanDefinition的加载过程以及ComponentScan,@componet,@import @Bean等注解解析过程
背景:我们启动主启动类后,相应的bean就被扫描进来了,原理是啥? 实现该功能的主要核心类就是:ConfigurationClassPostProcessor,我们看看他的继承体系: 它实现了Bean ...
-
spring IOC源码分析(1)
1.何谓Spring IOC 何谓Spring IOC?书上谓之“依赖注入”,那何谓“依赖注入”? 作为一个Java程序猿,应该遇到过这样的问题,当你在代码中需要使用某个类提供的功能时,你首先需要ne ...
-
深入理解Spring IOC源码分析
Spring容器初始化 本文使用的是Spring 5.1.7版本 写在前面:我们看源码一般有3种方式. 第一种直接用class文件,IDEA会帮我们反编译成看得懂的java代码 第二种是用maven的 ...
-
Spring IOC源码分析(二):Bean工厂体系结构设计
一. 概述 Spring容器通常指的是ApplicationContext的体系结构设计,即整个Spring框架的IOC功能,是通过ApplicationContext接口实现类来提供给应用程序使用的 ...
随机推荐
-
Android6.0动态权限申请
goggle在Android6.0要求部分权限需要动态申请,直接下载AndroidManifest.xml中无效 6.0权限的基本知识,以下是需要单独申请的权限,共分为9组, 每组只要有一个权限申请成 ...
-
区分苹果Safari浏览器
区分苹果Safari浏览器 (function() { var Sys = {}; var ua = navigator.userAgent.toLowerCase(); var s; (s = ua ...
-
iOS相册、相机、通讯录权限获取
iOS相册.相机.通讯录权限获取 说明 这是本人写的一个工具,用以便利的处理各种权限获取的操作,目前提供相册.照相机.通讯录的权限获取操作,参考了 http://www.jianshu.com/p/a ...
-
世界最大射电望远镜(Arecibo)用于探測地外文明
请看下图: 这是眼下世界最大口径(305米)射电望远镜的外观图,位于美国中西部的PuertoRico州,建成于1963年,工作波段在3厘米至1米波段范围内,正好适合用于探測地外文明(SETI)的科学研 ...
-
swift之属性
知识点总结: 1.存储属性 struct Town{ let region = "South" //只读属性 var population = //读写属性 } 2.惰性存储属性 ...
-
C#学习笔记(2)
1.获取当前可用串口端口号 using System.IO.Ports; string[] portNames = SerialPort.GetPortNames(); 2.TextBox换行.设置光 ...
-
在window上使用 linux shell 删除文件夹递归地
目的 在windows环境下, 整理代码,代码中含有 .svn 文件夹, 此文件夹在所有受控目录下都存在, 需要统一删除. 但是对windows的shell不熟悉,想用linux rm来删除, 如何实 ...
-
Redis应用一例(存证数量用计数器实现)
public Long getCreationCounter() { String host =PropertyUtils.getPropertyValue("redis.server.ho ...
-
QNetworkAccessManager
[1]头文件 想要利用QNetworkAccessManager类,必须在pro文件中添加对应库network,如下: QT += network 如果利用的VS + QT开发环境,请参考随笔< ...
-
Echarts使用dataset数据集管理数据
1.可以看官网api的入门例子 使用常见的对象数组的格式 option = { legend: {}, tooltip: {}, dataset: { // 这里指定了维度名的顺序,从而可以利用默认的 ...