谈到spring,首先想到的肯定是ioc,DI依赖注入,aop,但是其实很多人只是知道这些是spring核心概念,甚至不知道这些代表了什么意思,,作为一个java程序员,怎么能说自己对号称改变了java生态的spring不了解呢。

首先说一下spring做了啥,他将我们会频繁用到的javaBean交给spring的容器管理,在bean创建的不同阶段,可以在不同的容器中找到,说到底这些容器就是map缓存,有了spring我们就有了管家,可以过过当老爷的瘾,再也不用担心对象啥时候创建,初始化,销毁的问题了,用就完事了。

说了这么多废话,还是直接撸源码实在,首先看spring源码入口:

  1. public void refresh() throws BeansException, IllegalStateException {
  2. synchronized (this.startupShutdownMonitor) {
  3. //为容器初始化做准备
  4. // Prepare this context for refreshing.
  5. prepareRefresh();
  6.  
  7. /*
  8.  
  9. 1、创建BeanFactory对象
  10. * 2、xml解析
  11. * 传统标签解析:bean、import等
  12. * 自定义标签解析 如:<context:component-scan base-package="com.xiangxue.jack"/>
  13. * 自定义标签解析流程:
  14. * a、根据当前解析标签的头信息找到对应的namespaceUri
  15. * b、加载spring所以jar中的spring.handlers文件。并建立映射关系
  16. * c、根据namespaceUri从映射关系中找到对应的实现了NamespaceHandler接口的类
  17. * d、调用类的init方法,init方法是注册了各种自定义标签的解析类
  18. * e、根据namespaceUri找到对应的解析类,然后调用paser方法完成标签解析
  19. *
  20. * 3、把解析出来的xml标签封装成BeanDefinition对象
  21. * */
  22. // Tell the subclass to refresh the internal bean factory.
  23. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  24.  
  25. // Prepare the bean factory for use in this context.
  26. prepareBeanFactory(beanFactory);
  27.  
  28. try {
  29. // Allows post-processing of the bean factory in context subclasses.
  30. postProcessBeanFactory(beanFactory);
  31.  
  32. // Invoke factory processors registered as beans in the context.
  33. invokeBeanFactoryPostProcessors(beanFactory);
  34.  
  35. // Register bean processors that intercept bean creation.
  36. registerBeanPostProcessors(beanFactory);
  37.  
  38. // Initialize message source for this context.
  39. initMessageSource();
  40.  
  41. // Initialize event multicaster for this context.
  42. initApplicationEventMulticaster();
  43.  
  44. // Initialize other special beans in specific context subclasses.
  45. onRefresh();
  46.  
  47. // Check for listener beans and register them.
  48. registerListeners();
  49.  
  50. // Instantiate all remaining (non-lazy-init) singletons.
  51. finishBeanFactoryInitialization(beanFactory);
  52.  
  53. // Last step: publish corresponding event.
  54. finishRefresh();
  55. }
  56.  
  57. catch (BeansException ex) {
  58. if (logger.isWarnEnabled()) {
  59. logger.warn("Exception encountered during context initialization - " +
  60. "cancelling refresh attempt: " + ex);
  61. }
  62.  
  63. // Destroy already created singletons to avoid dangling resources.
  64. destroyBeans();
  65.  
  66. // Reset 'active' flag.
  67. cancelRefresh(ex);
  68.  
  69. // Propagate exception to caller.
  70. throw ex;
  71. }
  72.  
  73. finally {
  74. // Reset common introspection caches in Spring's core, since we
  75. // might not ever need metadata for singleton beans anymore...
  76. resetCommonCaches();
  77. }
  78. }
  79. }

这一个方法,包含了所有spring的操作,spring的代码果然是美如画,加上trycatch才十几行代码就改变了java的生态。

prepareRefresh();首先看看这个方法,有兴趣的可以自己看看,就是刷新上下文,设置启动日期之类的。

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();我们主要看看这一行代码,进去看看

  1. protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
  2. //核心方法
  3. refreshBeanFactory();
  4. return getBeanFactory();
  5. }

好像还看不到啥关键的,再看看refreshBeanFactory();

  1. protected final void refreshBeanFactory() throws BeansException {
  2.  
  3. //如果BeanFactory不为空,则清除BeanFactory和里面的实例
  4. if (hasBeanFactory()) {
  5. destroyBeans();
  6. closeBeanFactory();
  7. }
  8. try {
  9. //创建DefaultListableBeanFactory
  10. DefaultListableBeanFactory beanFactory = createBeanFactory();
  11. beanFactory.setSerializationId(getId());
  12.  
  13. //设置是否可以循环依赖 allowCircularReferences
  14. //是否允许使用相同名称重新注册不同的bean实现.
  15. customizeBeanFactory(beanFactory);
  16.  
  17. //解析xml,并把xml中的标签封装成BeanDefinition对象
  18. loadBeanDefinitions(beanFactory);
  19. synchronized (this.beanFactoryMonitor) {
  20. this.beanFactory = beanFactory;
  21. }
  22. }
  23. catch (IOException ex) {
  24. throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
  25. }
  26. }

1.首先做了判断,如果已经有了BeanFactory,就给他干掉,毕竟一山不容二虎嘛。

2.其次就是创建BeanFactory,其实里面就是new 了一个DefaultListableBeanFactory对象

3.设置循环依赖标识(默认就是支持的)

4.解析xml,封装成BeanDefinition对象(这个对象可是所有bean的胚胎,孕育着所有的bean)

好了,看到这四步应该都知道哪个重要了吧,我们进去看看xml解析是怎么做的:

  1. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
  2. // Create a new XmlBeanDefinitionReader for the given BeanFactory.
  3. //创建xml的解析器,这里是一个委托模式
  4. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
  5.  
  6. // Configure the bean definition reader with this context's
  7. // resource loading environment.
  8. beanDefinitionReader.setEnvironment(this.getEnvironment());
  9.  
  10. //这里传一个this进去,因为ApplicationContext是实现了ResourceLoader接口的
  11. beanDefinitionReader.setResourceLoader(this);
  12. beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
  13.  
  14. // Allow a subclass to provide custom initialization of the reader,
  15. // then proceed with actually loading the bean definitions.
  16. initBeanDefinitionReader(beanDefinitionReader);
  17.  
  18. //主要看这个方法
  19. loadBeanDefinitions(beanDefinitionReader);
  20. }

1.首先用了一记委托模式(很多地方都用到了,就不一一介绍了,平时看源码的时候,稍微注意一点应该很容易发现),将xml解析的工作交给了XmlBeanDefinitionReader,一直往下调用loadBeanDefinitions直到:org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions

  1. public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
  2. Assert.notNull(resources, "Resource array must not be null");
  3. int count = 0;
  4. for (Resource resource : resources) {
  5. //模板设计模式,调用到子类中的方法
  6. count += loadBeanDefinitions(resource);
  7. }
  8. return count;
  9. }

这边使用了模板设计模式,点进去任意选择一个类,可以看到都是实现了AbstractBeanDefinitionReader类的,选择xml解析实现:

  1. //获取Resource对象中的xml文件流对象
  2. InputStream inputStream = encodedResource.getResource().getInputStream();
  3. try {
  4. //InputSource是jdk中的sax xml文件解析对象
  5. InputSource inputSource = new InputSource(inputStream);
  6. if (encodedResource.getEncoding() != null) {
  7. inputSource.setEncoding(encodedResource.getEncoding());
  8. }
  9. //主要看这个方法
  10. return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  11. }
  12. finally {
  13. inputStream.close();
  14. }

获取xml解析对象,继续向下:

  1. //把inputSource 封装成Document文件对象,这是jdk的API
  2. Document doc = doLoadDocument(inputSource, resource);
  3.  
  4. //主要看这个方法,根据解析出来的document对象,拿到里面的标签元素封装成BeanDefinition
  5. int count = registerBeanDefinitions(doc, resource);
  6. if (logger.isDebugEnabled()) {
  7. logger.debug("Loaded " + count + " bean definitions from " + resource);
  8. }
  9. return count;

再看如何将bean封装成BeanDefinition对象的:

  1. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  2. //又来一记委托模式,BeanDefinitionDocumentReader委托这个类进行document的解析
  3. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  4. int countBefore = getRegistry().getBeanDefinitionCount();
  5. //主要看这个方法,createReaderContext(resource) XmlReaderContext上下文,封装了XmlBeanDefinitionReader对象
  6. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  7. return getRegistry().getBeanDefinitionCount() - countBefore;
  8. }

又一个委托模式,这是个老板啊,啥都让别人来做,再往下看:registerBeanDefinitions(doc, createReaderContext(resource));一直往下点:

  1. protected void doRegisterBeanDefinitions(Element root) {
  2. // Any nested <beans> elements will cause recursion in this method. In
  3. // order to propagate and preserve <beans> default-* attributes correctly,
  4. // keep track of the current (parent) delegate, which may be null. Create
  5. // the new (child) delegate with a reference to the parent for fallback purposes,
  6. // then ultimately reset this.delegate back to its original (parent) reference.
  7. // this behavior emulates a stack of delegates without actually necessitating one.
  8. BeanDefinitionParserDelegate parent = this.delegate;
  9. this.delegate = createDelegate(getReaderContext(), root, parent);
  10.  
  11. if (this.delegate.isDefaultNamespace(root)) {
  12. String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
  13. if (StringUtils.hasText(profileSpec)) {
  14. String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
  15. profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  16. // We cannot use Profiles.of(...) since profile expressions are not supported
  17. // in XML config. See SPR-12458 for details.
  18. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
  19. if (logger.isDebugEnabled()) {
  20. logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
  21. "] not matching: " + getReaderContext().getResource());
  22. }
  23. return;
  24. }
  25. }
  26. }
  27.  
  28. preProcessXml(root);
  29.  
  30. //主要看这个方法,标签具体解析过程
  31. parseBeanDefinitions(root, this.delegate);
  32. postProcessXml(root);
  33.  
  34. this.delegate = parent;
  35. }

好像很复杂,好像啥也没干。可以看到preProcessXml和postProcessXml方法里面啥也没有,明显是个钩子方法,主要看parseBeanDefinitions(root, this.delegate);

  1. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  2. if (delegate.isDefaultNamespace(root)) {
  3. NodeList nl = root.getChildNodes();
  4. for (int i = 0; i < nl.getLength(); i++) {
  5. Node node = nl.item(i);
  6. if (node instanceof Element) {
  7. Element ele = (Element) node;
  8. if (delegate.isDefaultNamespace(ele)) {
  9.  
  10. //默认标签解析
  11. parseDefaultElement(ele, delegate);
  12. }
  13. else {
  14.  
  15. //自定义标签解析
  16. delegate.parseCustomElement(ele);
  17. }
  18. }
  19. }
  20. }
  21. else {
  22. delegate.parseCustomElement(root);
  23. }
  24. }

默认标签和自定义标签解析还是比较复杂的,看来一篇是讲不完了,下一篇再好好讲讲这一块。

总结:

写到现在,spring连标签解析都没开始,我们已经看到两个设计模式了,用心去看的话,工厂模式也是到处都在,单例模式贯穿了spring。在spring里面用到了很多设计模式和技巧,我们在看源码的同时,稍稍注意一下这些技巧的使用,并想一想,如果是我们要实现这些功能会怎么做,像spring这样的做法是否比你的想法更好呢,或者,如果你是大牛,你是否有更好的方法去实现spring的这一功能,如果有,去github上提交spring,那你在it界就小有名气了,嘿嘿!

从零开始学spring源码之xml解析(一):入门的更多相关文章

  1. 从零开始学spring源码之xml解析(二):默认标签和自定义标签解析

    默认标签: 上一篇说到spring的默认标签和自定义标签,发现这里面东西还蛮多的.决定还是拆开来写.今天就来好好聊聊这两块是怎么玩的,首先我们先看看默认标签: private void parseDe ...

  2. 从零开始学spring源码之ioc预热:bean的拓展和beanProcessor注册

    上篇聊完了bean的解析,说起来做的事情很简单,把xml文件里面配置的标签全部解析到spring容器里面,但是spring做的时候,花了那么大代价去做,后面看看到底值不值得呢. 接下来看看prepar ...

  3. Spring如何解析XML文件——Spring源码之XML初解析

    首先,在我的这篇博客中已经说到容器是怎么初步实现的,并且要使用XmlBeanDefinitionReader对象对Xml文件进行解析,那么Xml文件是如何进行解析的,将在这片博客中进行一些陈述. 数据 ...

  4. 【Spring 源码】Spring 加载资源并装配对象的过程(XmlBeanDefinitionReader)

    Spring 加载资源并装配对象过程 在Spring中对XML配置文件的解析从3.1版本开始不再推荐使用XmlBeanFactory而是使用XmlBeanDefinitionReader. Class ...

  5. spring源码学习五 - xml格式配置,如何解析

    spring在注入bean的时候,可以通过bean.xml来配置,在xml文件中配置bean的属性,然后spring在refresh的时候,会去解析xml配置文件,这篇笔记,主要来记录.xml配置文件 ...

  6. 【spring源码系列】之【xml解析】

    1. 读源码的方法 java程序员都知道读源码的重要性,尤其是spring的源码,代码设计不仅优雅,而且功能越来越强大,几乎可以与很多开源框架整合,让应用更易于专注业务领域开发.但是能把spring的 ...

  7. Spring源码-IOC部分-Xml Bean解析注册过程【3】

    实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...

  8. Spring源码解析——循环依赖的解决方案

    一.前言 承接<Spring源码解析--创建bean>.<Spring源码解析--创建bean的实例>,我们今天接着聊聊,循环依赖的解决方案,即创建bean的ObjectFac ...

  9. Spring源码学习-容器BeanFactory(四) BeanDefinition的创建-自定义标签的解析.md

    写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...

随机推荐

  1. 使用docker-maven-plugin打包

    今天在部署的时候遇到点问题,总结一下,docker部署的步骤,如果对您有帮助,关注一下,就是对我最大的肯定, 谢谢! 微服务部署有两种方法: (1)手动部署:首先基于源码打包生成jar包(或war包) ...

  2. C# Wpf 文件保存对话框

    C# Wpf库中无文件保存对话框,需引用winform,引用winform后多处提示引用不明确,将winform引用改别名. // 引用winform,改别名 using Forms = System ...

  3. 使用Ubuntu手动安装NextCloud

    p.p1 { margin: 0; font: 11px Menlo; color: rgba(0, 0, 0, 1) } span.s1 { font-variant-ligatures: no-c ...

  4. python的if和else语句

    1.单分支选择结构: if表达式: 语句块 2.双分支结构 if表达式: 语句块1 else: 语句块2 3.多分支结构 if 表达式1: 语句块1 elif 表达式2: 语句块2 elif 表达式3 ...

  5. [从源码学设计]蚂蚁金服SOFARegistry之Data节点变更

    [从源码学设计]蚂蚁金服SOFARegistry之Data节点变更 目录 [从源码学设计]蚂蚁金服SOFARegistry之Data节点变更 0x00 摘要 0x02 引子 0x03 业务范畴 3.1 ...

  6. eclipse中安装Springboot的插件

    1help在Eclipse Marketplace中搜索spring-tool-suite,点击install,然后接受协议,等待重启eclipse即可

  7. Sentinel滑动窗口算法

    在前面搞清楚了Sentinel的使用后,大致理了一下Sentinel的责任链,搞清楚了这个,基本就已经梳理清楚sentinel-core模块的大部分内容,顺着这条链路可以继续梳理很多东西. 知其然.知 ...

  8. [leetcode]24. Swap Nodes in Pairs交换链表的节点

    感觉这个题后台的运行程序有问题,一开始自己想的是反转链表那道题的方法,只是隔一个节点执行一次,但是没有通过,TLE了,但是很奇怪,并没有死循环,就是最后返回的时候超时. 最后的思路就是很简单的进行交换 ...

  9. [leetcode33Search in Rotated Sorted Array]在排序旋转后序列中找目标值

    直接上代码 /** * Created by lvhao on 2017/6/30. * Suppose an array sorted in ascending order is rotated a ...

  10. javabean 数组对应yml中的写法

    gate-info: gate-list: - channel: channel-one io-flag: I - channel: channel-two io-flag: E 上面的是 yml 文 ...