引自 spring IoC源码分析 (3)Resource解析

定义好了Resource之后,看到XmlFactoryBean的构造函数

  1. public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
  2. super(parentBeanFactory);
  3. this.reader.loadBeanDefinitions(resource);
  4. }
跟到XmlBeanDefinitionReader 的 loadBeanDefinitions(EncodedResource encodedResource) 方法
  1. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  2. Assert.notNull(encodedResource, "EncodedResource must not be null");
  3. if (logger.isInfoEnabled()) {
  4. logger.info("Loading XML bean definitions from " + encodedResource.getResource());
  5. }
  6. //线程安全 ,但这里 currentResources应该本来就是线程安全的,所以推测不是为了线程安全
  7. //应该是为了线程能使用同一个 currentResources  ,从这里可以看出作者对 ThreadLocal 的理解深刻
  8. Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  9. if (currentResources == null) {
  10. currentResources = new HashSet<EncodedResource>(4);
  11. this.resourcesCurrentlyBeingLoaded.set(currentResources);
  12. }
  13. //这里其实就是为了避免循环加载,如果重复加载了相同的文件就会抛出异常
  14. //这里看了半天才明白这个set的意图,蛋疼啊
  15. if (!currentResources.add(encodedResource)) {
  16. throw new BeanDefinitionStoreException(
  17. "Detected recursive loading of " + encodedResource + " - check your import definitions!");
  18. }
  19. try {
  20. InputStream inputStream = encodedResource.getResource().getInputStream();
  21. try {
  22. InputSource inputSource = new InputSource(inputStream);
  23. if (encodedResource.getEncoding() != null) {
  24. inputSource.setEncoding(encodedResource.getEncoding());
  25. }
  26. return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  27. }
  28. finally {
  29. inputStream.close();
  30. }
  31. }
  32. catch (IOException ex) {
  33. throw new BeanDefinitionStoreException(
  34. "IOException parsing XML document from " + encodedResource.getResource(), ex);
  35. }
  36. finally {
  37. currentResources.remove(encodedResource);
  38. if (currentResources.isEmpty()) {
  39. this.resourcesCurrentlyBeingLoaded.set(null);
  40. }
  41. }
  42. }

其 实关键方法是 doLoadBeanDefinitions(inputSource, encodedResource.getResource()) ,但是上面的注释绝对花了我将近1个钟头才理解作者想表达的意思,刚开始一看到ThreadLocal 就想到线程安全,然后就想currentResources 永远是线程安全的啊,丫就这么被带坑里去了。从上面可以看到关键方法是doLoadBeanDefinitions,这个方法的关键代码其实就几行

  1. try {
  2. //判断xml文件是DTD还是XSD样式,如果没定义将使用XSD
  3. int validationMode = getValidationModeForResource(resource);
  4. Document doc = this.documentLoader.loadDocument(
  5. inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
  6. return registerBeanDefinitions(doc, resource);
  7. }

看下getValidationModeForResource 一路跟踪下来到XmlValidationModeDetector的detectValidationMode方法

  1. while ((content = reader.readLine()) != null) {
  2. content = consumeCommentTokens(content);
  3. if (this.inComment || !StringUtils.hasText(content)) {
  4. continue;
  5. }
  6. if (hasDoctype(content)) {
  7. isDtdValidated = true;
  8. break;
  9. }
  10. if (hasOpeningTag(content)) {
  11. // End of meaningful data...
  12. break;
  13. }
  14. }

就是分析下xml文件里有没有DOCTYPE关键字,没有的话就认为是xsd格式的。然后就到了documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); 这个方法

  1. public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
  2. ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
  3. DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
  4. if (logger.isDebugEnabled()) {
  5. logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
  6. }
  7. DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
  8. return builder.parse(inputSource);
  9. }

其实就是使用DocumentBuilderFactory 去解析xml,这块不怎么熟,查了下网上的介绍也没太详细的。

接着跟踪到registerBeanDefinitions 方法,关键部分:documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  1. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  2. this.readerContext = readerContext;
  3. logger.debug("Loading bean definitions");
  4. Element root = doc.getDocumentElement();
  5. BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
  6. preProcessXml(root);
  7. parseBeanDefinitions(root, delegate);
  8. postProcessXml(root);
  9. }
preProcessXml和postProcessXml是提供扩展用得,这里没有具体实现,从字面上理解也是给之类提供预处理和事后处理用
的。具体解析工作是parseBeanDefinitions,跟踪到private void
parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
 这个方法,我们主要关注对bean的解析,所以直接看processBeanDefinition(ele, delegate)
  1. /**
  2. * Process the given bean element, parsing the bean definition
  3. * and registering it with the registry.
  4. */
  5. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  6. /* BeanDefinitionHolder
    是BeanDefinition对象的封装类,封装了 BeanDefinition,Bean的名字和别名。用它来完成向IoC容器注
    册。 BeanDefinitionParserDelegate对XML元素的信息按照Spring的Bean规则进行解析*/
  7. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  8. if (bdHolder != null) {
  9. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  10. try {
  11. // Register the final decorated instance.
  12. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  13. }
  14. catch (BeanDefinitionStoreException ex) {
  15. getReaderContext().error("Failed to register bean definition with name '" +
  16. bdHolder.getBeanName() + "'", ele, ex);
  17. }
  18. // Send registration event.
  19. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
  20. }
  21. }


来看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解析的更多相关文章

  1. Spring IOC 源码分析

    Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文 ...

  2. spring IOC源码分析(ApplicationContext)

    在上一篇文章中,我们以BeanFactory这条主线进行IOC的源码解析的,这里,将以ApplicationContext这条线进行分析.先看使用方法: @Test public void testA ...

  3. Spring IOC源码分析之-刷新前的准备工作

    目录 ClassPathXmlApplicationContext的注册方式 加载父子容器 配置路径解析 容器刷新 刷新容器之刷新预处理 ClassPathXmlApplicationContext的 ...

  4. spring IOC源码分析(1)

    1.何谓Spring IOC 何谓Spring IOC?书上谓之“依赖注入”,那何谓“依赖注入”? 作为一个Java程序猿,应该遇到过这样的问题,当你在代码中需要使用某个类提供的功能时,你首先需要ne ...

  5. spring IOC源码分析(2)

    refresh这个方法包含了整个BeanFactory初始化的过程,定位资源由obtainFreshBeanFactory()来完成, protected ConfigurableListableBe ...

  6. spring IOC源码分析(3)

    1.IOC容器的依赖注入 Spring中,依赖注入是在用户第一次向IOC容器索要Bean时触发的(通过getBean方法). 在BeanFactory中我们看到getBean(String…)函数,它 ...

  7. 深入Spring IOC源码之ResourceLoader

    在<深入Spring IOC源码之Resource>中已经详细介绍了Spring中Resource的抽象,Resource接口有很多实现类,我们当然可以使用各自的构造函数创建符合需求的Re ...

  8. Spring mybatis源码篇章-MybatisDAO文件解析(二)

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(一) 默认加载mybatis主文件方式 XMLConfigBuilder ...

  9. Spring mybatis源码篇章-MybatisDAO文件解析(一)

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-SqlSessionFactory 加载指定的mybatis主文件 Mybatis模板文件,其中的属性 ...

随机推荐

  1. 决策树 -- C4.5算法

    C4.5是另一个分类决策树算法,是基于ID3算法的改进,改进点如下: 1.分离信息   解释:数据集通过条件属性A的分离信息,其实和ID3中的熵:   2.信息增益率   解释:Gain(A)为获的A ...

  2. Android -- 动画效果收获(1)

    加载选项菜单         MenuInflater inflater = getMenuInflater();    inflater.inflater(R.menu.menu,menu); An ...

  3. ubuntu SecureCRT破解

    操作过程: 操作过程都在终端中执行.Ubuntu 的破解 : 下载程序:   wget 链接: https://pan.baidu.com/s/1nvdJl7j 密码: 2ryk 运行破解 /usr/ ...

  4. 解析 MACH_O 文件

    现在做iOS开发的挺多,了解一下在苹果平台上程序运行的原理 解析 MACH_O 文件 这篇文章描述了如何解析 Mach-O 文件并稍微解释了一下它的格式.这不是一份权威指南,不过当你不知从何开始时,它 ...

  5. SqlDependency缓存数据库表小案例

    SqlDependency的简介: SqlDependency是outputcache网页缓存的一个参数,它的作用是指定缓存失效的数据库依赖项,可以具体到数据库和表. SqlDependency能解决 ...

  6. Entity Framework 5 自定义代码生成模板 转

    Entity Framework 5 发布有一定时间了,但学习资源确实不多,更何况英语差的我,看英语确实费力,不管怎么样,问题还是解决了,查看很多人写的文章(让我看的想放弃,更想找到答案),都没有到到 ...

  7. mysql vachar

    --本文在CSDN文章的基础加以补充 一. varchar存储规则: 4.0版本以下,varchar(20),指的是20字节,如果存放UTF8汉字时,只能存6个(每个汉字3字节) 5.0版本以上,va ...

  8. AVL树的旋转操作详解

    [0]README 0.0) 本文部分idea 转自:http://blog.csdn.net/collonn/article/details/20128205 0.1) 本文仅针对性地分析AVL树的 ...

  9. JavaWeb(七)Cookie,EL表达式,标准标签库

    Cookie Cookie概述 Cookie译为小型文本文件或小甜饼,Web应用程序利用Cookie在客户端缓存服务器端文件.Cookie是以键值对形式存储在客户端主机硬盘中,由服务器端发送给客户端, ...

  10. centos7设置定时任务

    第一种方式修改/etc/crontab文件,这种方式是系统的周期任务,只能root用户才可以执行 SHELL=/bin/bashPATH=/sbin:/bin:/usr/sbin:/usr/binMA ...