什么是框架?框架从何而来?为什么要使用框架?这是一系列简单而又复杂的问题。简单,是因为它们本身似乎不应该成为问题。框架实实在在存在,并且在开发中发挥着重要的作用,我们的日常工作,遵循着框架所规定的编程模式,在其指导之下,我们能够编写更为强大的程序。说其复杂,是因为框架本身又是如此纷繁复杂,我们在使用框架的同时,往往会迷失其中。任何事物都有蕴含在其内部的本质。无论框架本身有多复杂,我们所需要探寻的,都是其最为内在的东西。框架为什么会产生?我们来看一个最最简单的例子。
在Java中,如果要判定一个输入是否为null或空字符串,我们会使用下面的代码:
- if(str == null || str.length() == 0) {
- // 在这里添加你的逻辑
- }
这段代码非常普通,简单学习过Java语法的程序员都能够读懂并编写。那么这段代码是如何运作的呢?我们所编写的Java程序,首先获得的是来自于Java的基本支持:语法支持与基本功能的API级别的支持(str.length()方法实际上就是JDK所提供的字符串的基本API)。换句话说,我们编写的所有程序,都依赖于一个最最基本的前提条件:JDK所提供的API支持。
当一个需求被重复1000次,那么我们就需要重复1000次针对需求的解决办法,这是一个显而易见的道理。然而当上面的代码片段散落在我们的程序中1000次,我们不免会思考,是不是有什么简单有效的途径可以把事情做得更加漂亮一些呢?我们可以针对代码片段做一次简单的逻辑抽取重构,如代码清单2-4所示。
代码清单2-4 StringUtils.java
- // 定义一个类和一个静态工具方法来抽象出将被重复调用的逻辑
- public abstract class StringUtils {
- // 封装了一个静态方法
- public static boolean isEmpty(String str) {
- return str == null || str.length() == 0;
- }
- }
- // 引用静态方法取代之前的代码片段
- if(StringUtils.isEmpty(string)) {
- // 在这里添加你的逻辑
- }
在上面的代码段中,我们定义了一个静态方法,将之前写的那段逻辑封装起来。这一层小小的封装虽然看上去是一个“换汤不换药”的做法,但是从深远意义上来说,我们至少可以从以下两个方面获得好处:
可读性
静态方法的签名从一个角度向我们揭示了一段逻辑的实际意义。比如在这个例子中,isEmpty表示“判定某个输入是否为空”。与之前的代码片段相比,如果我们在一个1000行的程序代码片段中观察这2种不同的代码形式,那么前者往往会被你无视,它完全无法引起你的思维停顿,而后者却能够显而易见地在逻辑上给你足够且直观的提示。
可扩展性
如果我们对上述需求稍作改动,程序同时需要对输入为空格的字符串做出同样的判定。我们同样将上述的需求应用1000次,那么前者将导致我们在整个应用中进行搜索并替换修改1000次,而后者只需要针对我们封装的逻辑修改1次即可。
从上面的例子我们可以看出,虽然仅仅对代码做了一次简单的重构,却在上述的两个方面为我们解决了潜在的问题。这一现象或许直到现在你才意识到,但很多程序员前辈在很早以前就意识到了。因而,早就有人为此编写了类似的代码。比如说,类似的方法就存在于Apache的commons-lang的JAR包中,如代码清单2-5所示。
代码清单2-5 StringUtils.java
- package org.apache.commons.lang;
- public class StringUtils {
- // 这里省略了许多其他的代码
- public static boolean isEmpty(String str) {
- return str == null || str.length() == 0;
- }
- }
当我们将Apache的commons-lang的JAR包加到CLASSPATH中时,就能在程序的任何地方“免费地”使用上述方法。也就是说,我们自己无须自行编写代码对JDK进行扩展,因为Apache的commons-lang已经为我们做了。既然如此,我们唯一所需要做的,只是把别人做的东西加到CLASSPATH中并且使用它而已。
这是一个很熟悉的过程,不是吗?我们在搭建程序运行的基本环境时,指定程序所依赖的JAR文件是其中的一个重要步骤。而这一步骤,实际上包含了Java开发中最最基本而浅显的道理:
结论 当我们加载一个JAR包到CLASSPATH时,实际上是获得了JAR中所有对JDK的额外支持。
我们的程序就像一个金字塔形状。位于最底部的当然是JVM,提供运行Java程序的基础环境,包括对整个Java程序的编译运行。在这个之上的是JDK,JDK是构建在JVM之上的基本的对象行为的定义(我们在搭建开发环境时所安装的JDK就是这个)。而再往上,是一个具备层次结构的JAR层,所有被加载到CLASSPATH中的JAR文件都搭建在JDK层次之上,它们之间可能形成互相依赖,但不管怎么说,它们的作用都是提供JDK以外的功能支持。最后,在金字塔尖的,才是我们日常编写的应用程序,它将依赖于金字塔低端的所有程序。这样一个结构如图2-3所示。
(点击查看大图)图2-3 Java应用的金字塔结构 |
仔细观察一下处于中间的JAR层,这个层次的组成结构与其他的层次不同。它是由一块块砖头堆砌而成,上层的砖块搭建在下层的砖块之上。如果我们把其中的每一块砖都比作一个JAR文件,它们之间也就形成了明显的具备层次的依赖关系。
这个层次中的任何JAR文件本身可能并不为最终的程序提供具体的功能实现,但它却为我们编写程序提供了必要的支持。如果查看一个标准的J2EE程序运行时所依赖的CLASSPATH中的JAR包,会发现我们所熟悉的那些“框架”,实际上都蕴涵其中。我们在这里给出一个最简单的示例程序在Eclipse中的CLASSPATH截图,如图2-4所示。
图2-4 Eclipse中的CLASSPATH示例 |
从图中我们看到,JRE System Library是整个应用程序最基本的运行环境。而无论是Struts2还是Spring,它们都以JAR文件的形式被加载到程序运行所依赖的CLASSPATH中,并为我们的应用程序使用。如果我们用更加通俗的话来表述这一现象,则是:
结论 框架只是一个JAR包而已,其本质是对JDK的功能扩展。
当我们说一个程序使用了Spring框架,隐藏在背后的潜台词实际上是说,我们把Spring的分发包加入到CLASSPATH,并且在程序中使用了其功能。框架,其实就是这么回事!就是如此简单!
到现在为止,框架似乎还没有任何在我们的知识范畴以外的东西,它们的本质是如此一致,以至于我们很容易遗忘把一个JAR文件加入到CLASSPATH中的初衷:解决在某个领域的开发中所碰到的困境。正如我们在一开始使用的那个例子一样,框架作为一个JAR包,实际上是许许多多解决各种问题的类和方法的集合。当然,更多时候,它们包含了编写这些JAR包的作者所创造的许多最佳实践。
结论 框架是一组程序的集合,包含了一系列的最佳实践,作用是解决某个领域的问题。
只有解决问题才是所有框架的共同目标。框架的产生就是为了解决一个又一个在开发中所遇到的困境。不同的框架,只是为了解决不同领域的问题。所以,对于广大程序员来说,千万不要为了学习框架而学习框架,而是要为了解决问题而学习框架,这才是一个程序员的正确学习之道。
原文参见 http://book.51cto.com/art/201201/311881.htm
或者购买《Struts2技术内幕》 机械工业出版社 陆舟