解析Java的Spring框架的基本结构
在java届,有位名叫rod johnson的牛人,发现最初的java企业级开发处于混沌状态。
于是,它决心编写一个能够解决问题的通用的基础架构。
因为它深信面向接口编程能够将变化控制到最小,同时也利于扩展和变化。于是,它编写了如下的接口。
在混沌状态最先要创造的是一切对象的母亲beanfactory,有了它,就能够得到一切它孕育的对象和属性,也就是说首先要造盖亚--大地之母。
有了最初的母亲beanfactory,johnson想,如果我要得到一组bean对象而不单单是某个或某几个呢?另外,如果母亲的孩子也要孕育对象呢?于是,johnson创造了listablebeanfactory以操作一组bean对象,比如getbeansoftype就能够根据得到同类型的一组bean;创造了hierarchicalbeanfactory来解决多个beanfactory的层次问题,比如getparentbeanfactory就能够得到beanfactory的父factory。
这个beanfactory最终是要在某个应用上使用的,那么,需要给予beanfactory在一个应用中活动的能力。在beanfactory中,只需要考虑跟bean相关的行为,比如怎么得到bean,bean的类型等;而如果要赋予其在应用中的能力,则就需要考虑更多,比如应用的名字,启动时间,id等等跟应用本身相关的行为和属性,于是johnson想到了创造applicationcontext。johnson想,这个applicationcontext一定要能做许多事,要能够处理参数化和国际化的文本信息,于是增加了messagesource接口;要能发布事件以便解耦组件,于是有了applicationeventpublisher接口;要能得到资源文件,于是有了resourcepatternresolver接口;要能有在不同环境有不同处理对象的能力,于是有了environmentcapable接口。
applicationcontext继承了所有这些接口。
但是最重要的是,无论是beanfactory还是applicationcontext,它们都需要有可配置的能力,于是有了子接口configurablebeanfactory和configurableapplicationcontext;另外,web在当时是非常重要的趋势,而且相比其他应用有些独特,需要得到servletcontext,于是有了webapplicationcontext。
到目前为止,johnson都是面向接口进行行为的抽象思考,并未具体实现他们。
看着创造出来的beanfactory和applicationcontext,johnson意识到这一天的工作远远没有结束,因为并没有真正解决怎么能够让整套体系运转起来,于是,johnson开始思索如何实现他们。
johoson首先想到的还是这个实现应该具备什么能力?当然要把之前提到的autowirecapablebeanfactory,listablebeanfactory和configurablebeanfactory都包括进去。因此创造了configurablelistablebeanfactory。其次,需要考虑对于bean对象的几种能力,一是起别名的能力;二是保存单例对象的能力;三是缓存的能力;他们分别在simplealiasregistry类,defaultsingletonbeanregistry类和factorybeanregistrysupport类中实现。
最终,创造出了defaultlistablebeanfactory,它是spring中一切ioc工厂的原型,是beanfactory第一个真正的孩子,这个孩子非常重要,已经成为独立的创建ioc容器的基础,如果有扩展和使用,大多是继承它或者组合使用它。
如果要初始化一个defaultlistablebeanfactory,可以用如下代码
classpathresource res = new classpathresource("applicationcontext.xml"); defaultlistablebeanfactory f = new defaultlistablebeanfactory(); xmlbeandefinitionreader r = new xmlbeandefinitionreader(f); r.loadbeandefinitions(res);
接下来johnson想,beanfactory有了一个功能比较全面的默认实现,那么applicationcontext呢?于是johnson孜孜不倦的创造了3个重要的applicationcontext实现:filesystemxmlapplicationcontext, classpathxmlapplicationcontext, annotationconfigwebapplicationcontext(其实还有很多,比如处理portlet的, 处理web的)
johnson最先考虑的是如何去做spring的启动流程,它应该放到一个比较抽象的层次以便下层的所有类能够复用。于是他用一个abstractapplicationcontext实现了configurableapplicationcontext。还有一个很重要的功能,即将一个文件以资源的形式加载进来,这需要将资源抽象为resource类,将定位资源的具体实现抽象到resourceloader,abstractapplicationcontext同样需要继承defaultresourceloader以提供这个功能。abstractapplicationcontext完成了整个启动流程(上帝将它安排在第二天完成),唯独没有做对beanfactory的管理。于是,它的子类abstractrefreshableapplicationcontext专门做了这件事,实现了refreshbeanfactory, closebeanfactory, getbeanfactory专门对beanfactory的生命周期做了一些管理,但是abstractrefreshableapplicationcontext仍然没有加载所有配置好的bean。到哪里加载配置好的资源,实际上到了下层的子类去做,比如filesystemxmlapplicationcontext,就是到文件系统去读一个xml形式的applicationcontext;classpathxmlapplicationcontext则是到类加载路径下去读这个applicationcontext。而annotationconfigwebapplicationcontext则从类文件的annotation中加载bean,spring的扫描也从此开始。
看着主要的框架已经建立起来,johnson满意的笑着睡着了。
头一日,johnson完成了一个spring的整体框架。
第二日,johnson准备实际去处理前面遗留的问题。比如spring的容器初始化过程。如图,johnson将这个过程分为很多子过程,这些子过程都在围绕着如何将bean载入这一宏伟的目标而努力。
这个过程放在abstractapplicationcontext中的refresh方法中。代码如下,johnson将refresh的过程分为很多子过程,并且这些子过程在同一个抽象层级上,这种写法是为了给后人一个榜样。
public void refresh() throws beansexception, illegalstateexception { synchronized (this.startupshutdownmonitor) { // prepare this context for refreshing. preparerefresh(); // tell the subclass to refresh the internal bean factory. configurablelistablebeanfactory beanfactory = obtainfreshbeanfactory(); // prepare the bean factory for use in this context. preparebeanfactory(beanfactory); try { // allows post-processing of the bean factory in context subclasses. postprocessbeanfactory(beanfactory); // invoke factory processors registered as beans in the context. invokebeanfactorypostprocessors(beanfactory); // register bean processors that intercept bean creation. registerbeanpostprocessors(beanfactory); // initialize message source for this context. initmessagesource(); // initialize event multicaster for this context. initapplicationeventmulticaster(); // initialize other special beans in specific context subclasses. onrefresh(); // check for listener beans and register them. registerlisteners(); // instantiate all remaining (non-lazy-init) singletons. finishbeanfactoryinitialization(beanfactory); // last step: publish corresponding event. finishrefresh(); } catch (beansexception ex) { // destroy already created singletons to avoid dangling resources. destroybeans(); // reset 'active' flag. cancelrefresh(ex); // propagate exception to caller. throw ex; } } }
如果更高层次一些看,实际上这些过程围绕着几个方面来做:1. 刷新的生命周期;2. 对beanfactory的初始化及准备;3. 生成并注册beandefinition;4. beanfactory后处理器;5.设置消息,事件及监听器。
1. 刷新的生命周期
preparerefresh,这个过程主要记录日志表示spring启动了,初始化property资源(比如serlvet中一些资源初始化),以及property资源的验证(比如只写了key没有value)。
onrefresh,目的是提供给一些特殊的applicationcontext,使他们有能够在刷新过程中能够扩展的能力。目前使用到的大多是为servlet application context设置theme
finishrefresh,做一些收尾的工作,如初始化lifecycleprocessor,发布refresh结束的事件等。
cancelrefresh,主要是在异常产生时将当前的状态改变为非active。
2. 对beanfactory的初始化及准备
johnson想,我们应该让beanfactory在初始化时就把bean透明的加载并注册好,这样对外界而言,我的封装就非常成功,因此这步实际上做了很多事。下图省略了许多步骤,只列出关键点。
abstractapplicationcontext会调用refreshbeanfactory,它首先会检查并关闭已有的beanfactory,其次新建一个beanfactory,然后利用该factory装载所有beandefinition
其中,loadbeandefinitions会交给子类做不同的实现,比如abstractxmlapplicationcontext主要是通过xml读取;annotationconfigwebapplicationcontext的实现则会调用扫描器扫描类中的bean
3. 生成并注册beandefinition
当解析完xml配置以后,defaultbeandefinitiondocumentreader的parsedefaultelement方法会根据xml中的元素做对应的处理。其中,遇到bean元素时会最终调用beandefinitionreaderutils中的registerbeandefinition方法,该方法传入的参数为beandefinitionregistry,实际上是回调了defaultlistablebeanfactory的registerbeandefinition方法来注册beandefinition(defaultlistablebeanfactory实现了beandefinitionregistry)。
4. beanfactory后处理器
beanfactory后处理器是spring提供出来的让其子类灵活扩展的方式。spring中分为2个步骤:postprocessbeanfactory,invokebeanfactorypostprocessors。registerbeanpostprocessors则是实例化并调用所有的beanpostprocessor,用来在bean初始化前和初始化后对bean做扩展。
5. 设置消息,事件及监听器
设置默认消息源为delegatingmessagesource,如工厂里已经有messagesource则使用该messagesource,事件多播器为simpleapplicationeventmulticaster,如工厂里已经有applicationeventmulticaster,则使用该applicationeventmulticaster,并注册所有的应用程序listener以便能够接收事件
消息源:messagesource是国际化资源文件的重要方法,spring在applicationcontext就支持消息源。
spring中提供了messagesource的默认实现,使用java.util.resourcebundle来提取消息。spring通过配置一个特殊id为messagesource的bean并制定i18n的文件名,就能够从applicationcontext.getmessage()直接访问message。如果在jsp中,还能通过spring:message这个tag访问到message。
事件:事件是比较重要的解耦机制,spring在核心applicationcontext就引入了它,其原理比较简单,一方面是事件产生方,能够发送事件;一方面似乎事件监听方,能够响应事件。而具体实现基本上都是在产生方hold住一个事件监听者集合,并将所有监听方“注册”(即加入)到这个事件监听者集合。
spring中将applicationcontext当做事件产生方,使用applicationlisteners作为监听者集合,applicationeventmulticaster用来做事件发布。
事件发布的几个步骤:
订阅:最初addapplicationlistener将applicationlistener加入监听者集合。
发布:applicationcontext继承了applicationeventpublisher,因而实现了publishevent,该方法首先会遍历本applicationcontext的applicationlisteners集合,对每个listener调用onapplicationevent,因此每个listener都会被通知到;这步完成后会对applicationcontext的parent的所有applicationlisteners做同样的事件发布。
事件发布非常常用,不仅我们自己的应用可以使用这个事件发布,spring框架自身也在使用事件发布。下面是一些spring中事件的应用:
在finishrefresh中会发布contextrefreshedevent表明refresh结束借此通知listener。在applicationcontext中start和stop方法会发布事件表示context开始或结束。
johnson将spring的主框架与运行流程创造完毕之后,发觉spring中提供了许多灵活扩展的地方。于是johnson准备在第三日将这些灵活扩展的用法公布出来。
1. beanpostprocessor。beanpostprocessor提供了bean创建完成后的扩展接口,当你需要在bean创建完后对其做一定处理,则beanpostprocessor是首选的方式。
2. aware。注入的bean需要了解其容器的某些部分,spring通过aware完成回调,如beannameaware,可以让bean得知自己的名字, beanfactoryaware可以让bean了解到beanfactory, applicationcontextaware,可以让bean操作applicationcontext。通过这种方式,注入spring的bean能够做更加广泛的事情。
对于beanpostprocessor的扩展,spring自身有一个例子,即如何识别aware bean的例子。aware bean是比较特殊的bean,需要spring对其额外注入一些属性,那么注入的过程spring会怎么做呢?实际上spring并没有将他写在核心的处理过程里面,而是放到了applicationcontextawareprocessor这个beanpostprocessor,通过beanpostprocessor的postprocessbeforeinitialization最终invokeawareinterfaces以判断该bean的类型并注入相应的属性。这种做法利用了beanpostprocessor完成了另一个扩展用法,实在是高超。
private void invokeawareinterfaces(object bean) { if (bean instanceof aware) { if (bean instanceof environmentaware) { ((environmentaware) bean).setenvironment(this.applicationcontext.getenvironment()); } if (bean instanceof embeddedvalueresolveraware) { ((embeddedvalueresolveraware) bean).setembeddedvalueresolver( new embeddedvalueresolver(this.applicationcontext.getbeanfactory())); } if (bean instanceof resourceloaderaware) { ((resourceloaderaware) bean).setresourceloader(this.applicationcontext); } if (bean instanceof applicationeventpublisheraware) { ((applicationeventpublisheraware) bean).setapplicationeventpublisher(this.applicationcontext); } if (bean instanceof messagesourceaware) { ((messagesourceaware) bean).setmessagesource(this.applicationcontext); } if (bean instanceof applicationcontextaware) { ((applicationcontextaware) bean).setapplicationcontext(this.applicationcontext); } } }
关于aware的用法则更多了,比如如下代码能够感知applicationcontext,spring在创建完这个bean之后便会注入applicationcontext,于是我们就可以使用该context完成事件发布。
public class hellobean implements applicationcontextaware { private applicationcontext applicationcontext; private string helloword = "hello!world!"; public void setapplicationcontext(applicationcontext context) { this.applicationcontext = context; } public void sethelloword(string helloword) { this.helloword = helloword; } public string gethelloword() { applicationcontext.publishevent( new propertygettedevent("[" + helloword + "] is getted")); return helloword; } }
3. beanfactorypostprocessor,这个postprocessor通常是用来处理在beanfactory创建后的扩展接口。一个例子如下,当注入这个bean以后,它便会在beanfactory创建完毕自动打印注入的bean数量:
public class beancounter implements beanfactorypostprocessor{ @override public void postprocessbeanfactory(configurablelistablebeanfactory beanfactory) throws beansexception { system.out.println(beanfactory.getbeandefinitioncount()); } }
4. factorybean。factorybean是一种特殊的bean,这种bean允许注入到spring容器并用其生成真正的bean,所以可以这样定义,factorybean本身是一种bean,这种bean又有能够提供bean的能力。下面从factorybean的调用开始,讲到spring是如何使用这个bean的。
要想区分普通bean和factorybean,spring也必须有判断他们并特殊处理的过程,这个过程就在abstractbeanfactory的getobjectforbeaninstance中
protected object getobjectforbeaninstance( object beaninstance, string name, string beanname, rootbeandefinition mbd) { // don't let calling code try to dereference the factory if the bean isn't a factory. if (beanfactoryutils.isfactorydereference(name) && !(beaninstance instanceof factorybean)) { throw new beanisnotafactoryexception(transformedbeanname(name), beaninstance.getclass()); } // now we have the bean instance, which may be a normal bean or a factorybean. // if it's a factorybean, we use it to create a bean instance, unless the // caller actually wants a reference to the factory. if (!(beaninstance instanceof factorybean) || beanfactoryutils.isfactorydereference(name)) { return beaninstance; } object object = null; if (mbd == null) { object = getcachedobjectforfactorybean(beanname); } if (object == null) { // return bean instance from factory. factorybean<?> factory = (factorybean<?>) beaninstance; // caches object obtained from factorybean if it is a singleton. if (mbd == null && containsbeandefinition(beanname)) { mbd = getmergedlocalbeandefinition(beanname); } boolean synthetic = (mbd != null && mbd.issynthetic()); object = getobjectfromfactorybean(factory, beanname, !synthetic); } return object; }
可以看出来,如果是普通bean,就直接返回了,而如果是factorybean,最终调用会调用factory.getobject从而返回具体对象。如果将整个spring看做一个抽象工厂,生产抽象的bean时,则factorybean就是具体工厂,生产你需要的对象。
spring中factorybean用法很多,举个比较常见的例子,集成hibernate的sessionfactory时一般会注入localsessionfactorybean,但是这个sessionfactory实际上不是普通的bean,可以简单在配置文件中注入就能生产,它有很多定制的部分,于是spring让这个bean成为一个factorybean并控制其生产的对象。
推荐阅读
-
解析Java的Spring框架的基本结构
-
详解Java的Spring框架下bean的自动装载方式
-
Java的Spring框架中bean的继承与内部bean的注入
-
Spring源代码解析(十):Spring Acegi框架授权的实现 博客分类: Spring框架 spring
-
Spring源代码解析(十):Spring Acegi框架授权的实现 博客分类: Spring框架 spring
-
Spring源代码解析(九):Spring Acegi框架鉴权的实现 博客分类: Spring框架 spring
-
webwork2和spring的集成 博客分类: Java技术 SpringEXTWeb框架Servlet
-
Java的Hibernate框架中集合类数据结构的映射编写教程
-
深入理解Java的Spring框架中的IOC容器
-
深入解析Java的Hibernate框架中的持久对象