-
2004-04-01
我眼中的Spring
用Spring有一段时间了,最近在部门内部做个Spring的培训,一个很自然的问题出现大脑之中,Spring好在哪?
我可以摆出许多广告,但那不是我的感觉。于是,我向自己发问,要求一个属于自己的答案。Dependency Injection
原来,它叫IoC。
Martin Flower发话了,是个框架都有IoC,这不足以新生容器反转的“如何定位插件的具体实现”,于是,它有了个新名字,Dependency Injection。
其实,它就是一种将调用者与被调用者分离的思想,Uncle Bob管它叫DIP(Dependency Inversion Principle),并把它归入OO设计原则。
同Spring相比,它更早进入我的大脑。一切都是那么朦胧,直至Spring出现。
慢慢的,我知道了它还分为Interface Injection(type 1),Setter Injection(type 2),Constructor Injection(type 3)。Martin Flower那篇为它更名的大作让我心目关于它的一切趋于完整。
在Spring中,它是一切的基础。Spring的种种优势随之而来。
于我而言,它为我带来更多的是思维方式的转变,恐怕以后我再也无法写出那种一大块的全功能程序了。动态配置
这里提及的动态配置包括两个部分:系统的生成和系统的修改。
基于Spring的应用是依赖配置文件组织起来的,这意味着我们所编写的程序,更多的是在完成具体的功能,而各个功能之间的串连,就要靠配置文件了。
随之而来的一个好处就是,我们可以在不重新编译代码的情况下,改变系统行为。
或许不修改代码可以成为另一个理由,但在我看来,修改Java代码和修改配置文件没有什么本质区别,只要能把配置文件视为另一种语言,不是吗?
有位同事问我,Spring的配置文件的正确性是否只有运行时才能发现,道理上讲是这样的。每次修改配置文件,然后跑起来确定其正确性,这确实是一件费力不讨好的事。如果你是Eclipse的用户,你就幸福多了,已经有人开发了Spring的插件协助你完成这个工作。易测的结构
曾经有一次在现场,我改一个简单的小bug,简单到加在一起改的代码不超过五行。但从我定位到错误到完全把bug修正,用了两个多小时,这使得两个同伴最后只能对我怒目而视。
这其中固然有我自己糊涂的原因,代码不可测也是很重要的一个原因。每次修改了一句话,就要部署到应用服务器上,运行起来看结果。相信每个有在应用服务器上部署应用经验的人都知道那是多么漫长的过程。
如果以前对我说,对于一个好的应用来说,可测试性也非常重要,我摆出一副非常不屑的态度,惨痛的教训彻底的教育了我。
Dependency Injection让整个应用结构清楚了许多,我们可以针对每个具体的模块进行单元测试,而不必像过去一样,只有把整个应用部署到应用服务器上运行起来之后,才能测试。
局部的稳定带来的是更多的信心,当系统一点点整合在一起,信心就越来越足。不存在的接口压力
项目组中的一个同事对我说,用Spring跟没用一样。原因是我们的代码并没有继承Spring中的类,也没有实现Spring中的接口。
这恰好就是Spring的优势之一,这使得我们的应用不必困在Spring上。依赖于特定的API就意味着要在一棵树上吊死。我们原来系统中很难测试的另一个原因就是在代码中遍布HttpServletResponse,这使得我们的代码只有放在Web容器中才能跑起来。
Rod Johnson在评价一个Web框架的优劣时,将是否依赖于Servlet API作为一个及其重要的标准。
依赖于特定API就意味着要依赖于特定的容器或是框架,就像Servlet一定要跑在Web Container里,EJB一定要有AppServer一样。
没有了接口的压力,使得我们应用可以完全脱离Spring运行。在系统开发期间,我不断强调即便没有Spring,我们的应用依然可以自行组装来运行,给我这种底气的理由就是Spring没有侵略性的接口。另一个原因是当时我并没有对把整个系统放到Spring上有十足的把握。^_^消除Singleton
Singleton是二十三个经典的设计模式之一,不幸的是,到了J2EE的世界,由于classloader的原因,它几乎成了一个经典的反模式。曾经在自己的代码中大量运用Singleton,部署中遇到的问题加上太多的重复代码,给我留下了一段不堪回首的经历。
Spring的出现漂亮的解决这个问题,我只要在配置文件中配置一个bean,它缺省行为就是Singleton,我不必再为反模式抓空心思,不必再为了Singleton编码。择其善者而从之
不同于很多技术,Spring并不是一个“要么全部,要么没有”的东西,它是一个分层的结构。我们可以从中选取我们感兴趣的部分,而不必理会其它的部分。我用得最多的部分就是Spring的Core部分,也就是基于bean的配置框架,对于其上的MVC、ORM、DAO等等,我并不了解,但这丝毫不影响我的运用。开阔视野
Spring本身包含很多的东西,从Dependency Injection之类思想性的东西,到现在颇为流行的AOP、ORM之类实现技术。在Spring的路线图中,JMX、JMS、JCA等等已经都纳入了Spring未来的发展计划中。于我而言,沉浸于Spring的世界里,一段时间内不愁没东西可学。
Spring起源于Rod Johnson的《Expert One-on-One J2EE Design and Development》,所以,这本书成了Spring最好的辅导材料。书中的许多观点的提出完全是基于Rod本人的实际经验,比起不少假大空的理论或是广告来得实际得多。Spring邮件列表中有人这样评价,这本书值得“cover-to-cover”的读。如果你和曾经的我一样,迷失于J2EE纷繁复杂的世界中,这是一剂让你清醒过来的良药。这就是我眼中的Spring,这些理由比之许多Spring的广告显得单薄许多,因为我对Spring的了解实在有限,但这足以让我相信在Spring上花费时间是值得的。
-
2004-03-19
又是一年Jolt时
第14届的Jolt大奖评选结果揭晓了!
通用类图书Jolt大奖被《Waltzing with Bears: Managing Risk on Software Projects》拿走,一本讲项目风险的书,Tom Demarco和Timothy Lister可是《Peopleware》的作者,大师出手果然不同凡响。开源先锋Eric S. Raymond的大作《The Art of UNIX Programming》只能屈居亚军了。
技术类图书Jolt大奖是《Test-Driven Development: A Practical Guide》,敏捷的又一次胜利,不过,这可不是Kent Beck的那本。Kent的那本是By Example,这本是A Practical Guide,Amazon的四星之作。我所知道的另一本著作《Code Reading: The Open Source Perspective》拿到了Productivity大奖。另一本大师之作,Erich Gamma和Kent Beck所著的《Contributing to Eclipse: Principles, Patterns, and Plugins》榜上无名,只能怪选题略偏了。
有不少我们熟悉的东西榜上有名,Eclipse夺走了语言与开发环境的Jolt大奖,IntelliJ IDEA是这项Productivity大奖。Hibernate是程序库、框架和组件类的Jolt大奖。IBM的developerWorks是网站类的Jolt大奖。Dreamweaver则走进了名人堂。
Jolt大奖总是一个好东西集散地,按图索骥多半是没错的。
-
2004-03-19
纪念Blogbus回归
我果然是个习惯的动物,习惯了每隔几天就把自己凌乱的思维表达在blog上,习惯了时不时看看自己blog的访问量有多少的增加。
从来没有意识到自己竟然对blog有着如此严重的依赖。
一夜之间,Blogbus停了。我忽然有了种不知所措的感觉,原来习惯的力量如此巨大。
Blogbus停摆的原因众所周知。
游戏自有它的规则,参与其中自然是为了获得它的乐趣。伤及无辜的游戏者总是会受到其他游戏者的鄙夷。为了大家共同的乐趣,我们还是应该遵守游戏的规则。今夜,本来已经做好了睡觉的准备,无意间的尝试让我惊喜的发现blogbus的回归。
欣喜之余,赶紧调动大脑中休息了几日的细胞为此纪念一番,于是有了这篇Blog。
-
2004-03-11
理想和现实
gigix又一次再CSDN上受到了攻击,这次是因为开源。
http://www.csdn.net/develop/article/25/25146.shtm从去年开始,我逐渐成为了一个开源的忠实支持者,虽然我实际参与到开源活动并不是很多,但对于开源的文化,我充满了无限的憧憬。
就我个人而言,已经从开源中受益匪浅了。
我是一个不喜欢被蒙在鼓里的人,所以,总想捅婆最后一层的窗户纸,开源项目给了我这个机会。看看开源项目涉及的范围,从操作系统到数据库,从应用服务器到应用框架,只要心存疑虑,完全可以打破沙锅。曾经在Windows上写过程序,那时的我心中完全没有开源的概念,MS带给我的Windows文化总是若有若无的说,少知道一点好。所以,对Windows下的程序,我总是心有不爽,除了自己的不够努力之外,文化的力量也不可小觑。
开源,让我有了一个向高手学习的机会。想想吧!不经意间打开的那个文件,它的作者就是那个享誉程序设计世界的顶级高手,阅读源码,无异于同这位或许无缘谋面的高手面对面谈心。感慨地球果然是个“村”的同时,不忘偷学个一招半式,对于日后的闯荡江湖有利而无弊。
G-Roller是我参与最多,同时也是受益最多的一个开源项目。
我们项目现在的底层结构完全是从G-Roller借鉴来的。
G-Roller是一个非常好的教材,不但演示了诸如Struts、Hibernate、SpringFramework等几个优秀的开源软件包的用法,同时很好的诠释了IoC的概念。
我就是从G-Roller开始了解SpringFramework,开始理解IoC的。回到gigix在CSDN上遭受攻击的话题上。
大多数的攻击都放在理想和现实的结合点上,做开源,生活怎么办?
大多数中国程序员归根结底一个字,穷。
物质生活无法得到保证的前提下,很难投入很大精力去做开源。像一些大公司投入专人做开源的情况毕竟是少数。说到这,想起了我的一个同学,他在一家大公司,做着Linux开发,职业结合开源,着实令人羡慕。
反过来,恐怕喊穷的这些人真的有一天富了起来,他们会愿意去做开源吗?不见得。
做开源的人多半要有一些理想主义,太过现实的人恐怕不愿意花费时间在无法获得收入的地方。
一个开源项目之初更多的依赖于其成员的付出,但想长时间的运作恐怕就不是那么简单了。
我的一个同事是国内一个知名开源社区的核心成员,他常常对我说,要是有笔资金注入,他们的项目可以做得更好。这就是理想和现实。
-
2004-03-08
模拟语法结构
项目组的一个兄弟提出一个令我目瞪口呆的想法,模拟语法结构。
实际做法很简单,下面以Java语言讲解一番。
学过编程语言的都知道,语句是程序设计中的一个基本单位,大语句由小语句组成。
我们定义语句如下:
public interface Statement {
public void execute(Context context) throws Exception;
}其中context是用来存放执行语句所需的相关信息的一个接口。
一旦看到了语句的出现,我们很自然就会联想到顺序语句、选择语句和循环语句等等,这几种语句也是语句,看起来这是废话,实际上我指的是LSP(Liskov替换原则)。所以,我们让这几种语句也成为语句,下面给出一种可能的顺序语句实现:
public final class Sequence implements Statement {
private Statement[] statements;public Sequence(final Statement[] statements) {
this.statements = statements;
}public void setStatements(final Statement[] statements) {
this.statements = statements;
}public void execute(Context context) throws Exception {
for (int i = 0; i < this.statements.length; i++) {
this.statements[i].execute(context);
}
}
}结构很简单,顺序语句由一堆“语句”组成,execute就是顺序调用就OK了。
我们可以用同样的方法实现选择语句、循环语句。到这里,我们可能还无法看出这种结构的优点:语言已经提供了很好的结构,为什么要模拟?
这么做最吸引我的地方就在于它的可配置性。IoC,Robert Martin叫它DIP,对了,Martin Flower给它改了个名叫Dependency Injection,最近比较火爆,《程序员》2004年第三期上有一篇gigix翻译Martin Flower的那篇《Inversion of Control Containers and the Dependency Injection pattern》比较详细的讨论了它。
稍微对它熟悉一点的话,我们不难看出,上面Sequence的实现用到了setter和constructor,它们分别代表了IoC的type 2和type 3,通过这种方式,我们就可以很轻松的配置自己的应用了。
我知道这么说,还会让人有一头雾水的感觉。
我们应用的新版本是在SpringFramework的基础上构建起来的,SpringFramework就是一个很好的IoC容器。它让我们可以通过配置文件来构建整个应用。
假设BasicProcessor1和BasicProcessor2是两个实现了Statement接口的类。使用SpringFramework,我们可以这样构建自己的应用。
<bean id="processor1" class="BasicProcessor1">
<bean id="processor2" class="BasicProcessor2"><bean id="flow" class="Sequence">
<property name="statements">
<ref bean="processor1"/>
<ref bean="processor2"/>
</property>
</bean>其中,processor1和processor2可以分别看作BasicProcessor1和BasicProcessor2的instance。flow是Sequence的一个instance。
当系统生成时,SpringFramework会自动调用setStatements将processor1和processor2作为一个数组设置到flow中去。这样就省去了我们手工配置系统的繁琐工作。
说起来很神奇,其实这是利用了JavaBean的一些特性。回到我们前面的讨论上。
当这种配置方法和模拟语法结构相结合,威力就显现出来了。
我们完全可以在不改动系统源代码的情况下,修改系统生成的配置文件,从而达到改变流程的目的。
如果有了新的需求,我们可以单独开发一个Statement,然后再把它配置到系统中,而原有的代码无需任何改动。
多么美妙的一种想法啊!放任我们的思绪继续前行!
真正控制我们应用结构的是我们的配置文件。在SpringFramework中,在一个应用中可以有多个配置文件,我们可以把组件定义放在一起,而把流程控制的部分放在一起。于是,我们只要修改流程控制的部分就可以达到对整个系统的重新设定。
前面已经说过,模拟语法结构无非是顺序语句、选择语句、循环语句等等,从本质上说,它就是一门语言,而我们的流程配置文件就是这种语言的源代码,唯一的缺陷就是相对烦琐一些。
如果我们再向前迈一步,把模拟的语法结构变成真正的语法结构,那它就会成为一门真正的语言。编译器的工作就是将一种语言转化为另一种语言,而我们流程配置文件本身就是模拟语法结构的源文件。我们完全可以开发一个小型的编译器,将这种语言编译生成我们流程控制文件。
达到这一步的话,以后的应用开发就成了用这种小语言来编写控制流程,只有真正的新东西才需要我们编写Java代码,这将极大的降低我们开发的工作量。或许这会引起某些人的不屑,前面说过模拟语法结构是为了不编译代码,如果开发自己的编译器编译和用Javac编译有什么不同?
复杂程度。
二者所在的层次不同,我们用自己的语言控制的流程,而不是细节。
如果把我们的语言比作高级语言的话,那么直接用Java编写的代码无异于现在的汇编,想必大家对二者的开发速度都有自己的比较吧!模拟语法结构,这种做法本身上没有什么技术难度,难的在于把它提炼出来,惯性思维常常会局限我们的思路。
或许不经意间,又一个好想法浮现于脑海之中。
不要犹豫,赶快操起键盘实现它!







