一直想写这么一篇文章,来总结一下近几年来自己对Spring的理解(包括源码赏析,模式挖掘, 扩展实践等)。最近在做一个多级反馈队列的设计,刚好是构建在Spring之上。趁此机会写下这篇文章,希望大家多提宝贵意见。同时,为了不落俗套(分析Spring源码的文章已经泛滥了,但大多至于流程推敲,蕴含在其中的设计哲学往往少有人问津),这里采用正反推演的方法进行分析,目的很明确,将其中蕴含的设计理念一一“拖”出来.为我所用~
撇开Spring的源码先不谈。我们先来谈谈如何精进一门技艺的问题。自己有个习惯,在学习某门技术前,先去官方看一下其Introduce,了解该技术产生的背景。当然对于文档比较棒的开源项目,我们还可以了解到跟多的信息(比方说其其适用场景等)。其实也就间接地告诉我们了它的优缺点。有了这些大致了解后,接下来要做的就是从Demo入手,先内部勾勒出它的设计思路(如何做?拿张纸画下来,异或?),然后翻开源码,从大处着手,逐步深挖。很多同学都说,自己学习一门新技术后,过一阵子就忘了?why?不妨使用上面的思路。当然,我们也不可忽视个人的技术敏感性在其中发挥了不小的作用。ok,这里给出一种思路,希望能对大家学习开源项目有些许帮助。
好,进入我们的主题吧~Spring的 IOC和AOP两个核心概念,想必大家都很熟悉了。IOC将控制权从关联依赖对象的生命周期中解放出来,放到容器中(等等,什么是容器?容器的作用是什么?这个你有想过吗?),进而实现对其生命周期的控制。凡事适度即可,工作中看到很多IOC被滥用,导致可测性、可维护性降低的case不在少数。这里需要引起大家的重视。好,下面我们简单回忆一下Spring IOC容器控制Bean的第一个阶段,即Bean的(注意,Spring中的Bean概念上更趋近于Eric Evans提到的领域模型.其中蕴含了仓储,聚合根,领域服务等核心理念。其实大家大可以不用理会这么多概念,想想看,其实这些都只是指导我们进行OOD的一些思考,更多的是一种架构模式)的定位,装载,解析,注册。
这里,我们要从spring的顶级容器BeanFactory说起,瞧瞧它的名字,落脚点是Factory,那么我们可以拍着大腿想一想,这个东西就是用来生产Bean的,对吧.别急着深入,我们再来看另外一个容易混淆的概念,FactoryBean,落脚点是Bean,看到了吧,我前面说过,bean是spring的领域对象,factory纳入到容器管理后,就对应了一个FactoryBean.好,我们回到BeanFactory上来,首先我们简单回忆一下它继承体系上的三个顶级孩子, AutowireCapableBeanFactory,
HierarchicalBeanFactory,ListableBeanFactory. AutowireCapableBeanFactory,正如其名,具有自动装配能力的容器.这里有一个命名技巧,当你需要的类具有某种能力时,后缀可以缀上able,诸如JDK中常见的 Comparable, Iterable, Adjustable,Callable,Cloneable等或者直接缀上Capable.着重关注一下的是该容器一般很少被我们直接扩展,可以这么理解,它是容器继承体系的核心层(想想看,最核心的,也是最通用的,一般用不着你去扩展的吧).
HierarchicalBeanFactory,更通俗易懂了,里面就两个扩展方法,可以获得父类Bean工厂,根据名字判断bean是否由本BeanFactory装载. ListableBeanFactory,有列举该容器下定义的bean的功能.当然,除了getBeanDefinitionCount 和containsBeanDefinition方法外,不推荐大家直接使用该IOC.鸟瞰过后,我们直接深入内部,来看个究竟吧.刚才说了第一阶段包括四个核心步骤,bean的定位,拍着大腿想想,让我们来设计的话,不就是设计一个BeanLoader去搞搞吗,没错,但是bean从哪里发现呢?IO流?没错,这些统统都称为Resource.我们就先定义一个ResourceLoader类.等等,既然它抽象到resource的层面了,我们先来看看都有哪些resource吧.我得上图了,再不上图,就要挨骂了.
好了,通常情况下,我们使用的是ClassPathResource去定位Bean配置文件的.ok,有了资源,我们需要加载了吧,前面说了,搞个ResourceLoader的东东吧.来看张截图,在DefaultResourceLoader中有个方法值得我们关注一下,如下图:
这里没什么好说的,获取到资源后,我们需要加载它了吧,ok,这个时候,需要BeanDefinitionReader出场了,Spring将Bean的载入交给了这个家伙,先来看看继承体系吧.
来看看AbstractBeanDefinitionReader是怎么进行bean的load操作吧.截图如下,我们只看loadBeanDefinitions方法的核心部分.
再往下进行之前,先提个问题吧.在使用spring的过程中,我们往往希望对bean配置文件进行归类,比方说专门用于做基础建设用的配置(dataSource,transaction,annotation-driven,advisor等),专门的Action层配置,专门的Service层配置,专门的SOA服务接口契约配置,专门的….等等.如果这些文件中有多个bean相同的话,这里是怎么处理的呢.等等,在看这个问题前,我们需要注意一下,这里为了防止资源文件的并发访问,使用了ThreadLocal,只不过被Sping简单包了一个名字,叫NamedThreadLocal,好进入具体的doLoadBeanDefinitions方法,我们来看刚才那个问题的答案.如图:
等等,我们不是再讨论Bean的载入吗?怎么调到registerBeanDefinitions,真细心,没错这里已经来到Bean的解析过程了,等等,明明是注册,你为什么硬要说是解析呢?spring这里的处理逻辑比较绕,它是先完成解析,然后才在DefaultListableBeanFactory里面完成注册的.为什么要解析,我们知道XML的DOM模型并不符合BeanDefinition这个Spring的领域模型,在使用之前,当然需要对DOM->BeanDefinition进行解析适配喽.上图:
继续往里面走,这里实质上的解析是交给一个叫BeanDefinitionParserDelegate的类.等等,在此驻足两秒钟先,关注一下preProcessXml和postProcessXml这两个方法.钩子函数啊,同志们~继续往下走,我们来看一个比较关键的入口方法parseBeanDefinitions,如图:
好,这里我们只是简单提及下Spring的这个扩展点.后面会有比较详细的扩展demo,这里我们还是把主要精力集中在parseDefaultElement方法上吧.
未完,待续...
参考资料:
1. http://www.ibm.com/developerworks/cn/java/j-lo-spring25-ioc/
2. http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html
3. http://www.ibm.com/developerworks/cn/java/j-cq01307/index.html
4. http://www.openwebx.org/docs/autoconfig.html
5. http://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/index.html