Struts2技术内幕:深入解析Struts架构设计与实现原理
上QQ阅读APP看书,第一时间看更新

第一部分 知识准备篇

对任何事物的研究,总是需要由表及里、由浅入深地进行。作为本书研究对象的Struts2,是一个Web开发框架。因而在本书的开篇,我们试图向读者阐述一些Web开发的基本概念以及开发框架的学习方法。这些内容不仅是全书的精神纲领,也是我们进行后续讨论的基础核心。从这一部分的篇名“知识准备篇”可以看出,本篇的主要目的是为之后我们深入研究框架的实现机理打好坚实的理论基础。

“工欲善其事,必先利其器”。在本书的第1章,我们将首先为读者介绍搭建Struts2开发环境的步骤。一个好的开发环境,是进行框架研究的必要条件。尤其是当我们需要对一个开源框架进行源代码级别的分析时,好的开发环境就犹如一把利剑,能够为我们扫清所有的障碍。除了介绍环境搭建和源码调试的具体方法之外,我们在第1章中还将给出获取Struts2相关资料的途径和方法,并对Struts2项目的结构做初步的介绍。

面对纷繁复杂的框架,有的读者或许已经迷失其中,有的读者或许已经精通使用这些框架的方法并总结出一些行之有效的最佳实践。本书的第2章,我们试图带领读者去探寻日常开发中最为本质的问题:什么是面向对象的概念?什么是框架?我们为什么要引入框架进行编程?在解决这些根本问题的过程中,我们也会同时给出一系列Web开发模式和Web开发过程中的最佳实践,并以此为基础总结出Web开发中可能遇到的主要问题。在第2章的最后,我们还试图总结出正确学习开源框架的方法,这些方法不仅能够帮助那些迷茫于框架之中的程序员找到正确的方向,同时对那些有一定经验的程序员也有相当的借鉴作用。

在了解了框架存在的意义之后,本书的第3章将正式揭开Struts2的神秘面纱,并从宏观上讲述Struts2作为一个表示层框架是如何解决Web开发中的种种问题的。第3章的内容将涵盖Struts2的方方面面,从Struts2的历史发展、Struts2的外部环境、Struts2的宏观运行主线、Struts2的微观构成等不同的角度,为读者介绍Struts2的概况。

本篇中所有内容的侧重点,都将围绕着“方法论”这三个字展开。因为本书的主旨不仅仅是研究Struts2这个框架本身,更是想通过对Struts2的研究,总结出一套完整的并且行之有效的Web开发的经验和方法。所谓“授人以鱼不如授人以渔”,希望读者在阅读本书的过程中,始终能够站在一个比Struts2本身更加高的高度来看待整个Web开发,这样才能在不断发展的历史洪流中立于不败之地。

第1章 厉兵秣马—-开发环境准备

1.1 准备源代码阅读环境

在开发与分析任何一个开源框架之前,都需要安装与配置基本的开发环境和源代码的阅读环境。这一系列内容将包括:安装与配置JDK、安装开发调试IDE、安装与配置Web服务器、搭建开发调试环境等。

1.1.1 安装与配置JDK

Struts2的运行环境要求Java 5以上的版本,所以JDK的版本必须为1.5或者1.5以上。打开http://www.oracle.com/technetwork/java/javase/downloads/index.html页面,我们可以下载到最新的JDK安装程序,下载页面如图1-1所示。本书所有的示例代码都运行在JDK 1.6之上,大家可以下载这个版本的JDK运行本书中的所有代码示例。

图1-1 JDK下载首页

安装完JDK后,需要正确配置Java运行时必需的环境变量值,它们是:JAVA_HOME、CLASSPATH和PATH。无论是什么操作系统,都能够支持多个JDK版本的共存,读者可以根据应用程序的实际需求对不同的JDK版本进行管理。

正确管理JDK版本的方式,是在JVM运行时指定JDK版本对应的环境变量。为了方便起见,我们往往为操作系统本身指定一个系统级别的环境变量。例如,Windows平台上的系统环境变量可以在“系统属性”的“高级”选项卡中找到,并在其中配置JAVA_HOME、PATH和CLASSPATH值。图1-2是Windows XP操作系统中为系统添加CLASSPATH环境变量的例子。

图1-2 Windows XP上配置CLASSPATH

多JRE共存时的版本管理

Java语言对于运行环境的管理比较宽松,在一个操作系统中可以同时运行多个Java程序的进程,每个Java进程所依赖的JRE版本也可以各不相同。当某一个Java进程启动时,操作系统会依次按照Java启动进程的当前目录、当前目录的父目录、PATH值中所指定的目录进行JRE寻址,找到第一个返回的JRE版本并运行。因此,一个简单而有效的指定JRE寻址的方式是在启动Java进程的脚本中通过指定当前运行程序的PATH值来定制特定版本的JRE的执行环境,从而达到对不同版本的JRE进行管理的目的。

JRE管理和CLASSPATH的加载顺序问题是Java开发中最为基本的问题,它牵涉的是Java程序所依赖的最为基本的底层环境的配置。尤其是当一个应用程序运行在一个高级的商业应用服务器如Websphere或Weblogic之上时,我们应该密切关注程序的JRE运行参数和版本以及CLASSPATH的加载顺序(先加载优先原则),因为这些商业应用服务器往往有自定义的JRE管理机制和CLASSPATH的加载方式,而这两大内容,也将直接决定Web应用的运行特征。因此,在商用服务器中,我们往往通过自行修改服务器的启动脚本来设定服务器运行所依赖的JAR版本和Library加载方式(在某些服务器中,可以通过控制台进行配置)。

安装并配置完成后,我们可以在命令行窗口中输入java -version命令来检测一下当前的JDK运行版本。如果配置完全正确,当前客户端的JRE运行版本会显示出来,如图1-3所示。

图1-3 JDK安装成功

1.1.2 安装Eclipse与源码调试

在成功安装和配置JDK后,我们还需要安装进行Java开发调试的IDE。目前比较常用的Java开发IDE主要有Eclipse和NetBeans等,读者可以任意选择自己习惯的IDE作为开发工具。在本书中,我们以Eclipse为例,着重介绍在Eclipse中开发与调试源码的方法。读者也可以举一反三,在其他IDE中做相应的尝试。

Eclipse是一个界面友好的开源IDE,并支持成千上万种不同的插件,为我们进行代码分析和源码调试提供了极大的便利。我们可以在Eclipse的官方网站(http://www. eclipse.org/)找到Eclipse的各个版本并下载安装。

本书不会对Eclipse的使用细节进行详细介绍,不过为了帮助读者更好地进行开源框架的研究,尤其是对开源框架源码的解读,我们在本章中将陆续给出一些结合Eclipse进行源码阅读的方法和最佳实践。相信这些方法对各个层次的读者都会有一定的启示。

在本节中,我们将首先列出一些Eclipse IDE中常用的快捷键(参见图1-4、图1-5和图1-6),这些快捷键在进行源码分析时非常有用。

图1-4 在Eclipse中使用Ctrl+Shift+T

图1-5 在Eclipse中使用Ctrl+T

图1-6 在Eclipse中使用Ctrl+ Shift + G

Ctrl+Shift+T—根据类名查找相应的类

Ctrl+T—选中某个类或方法,使用此快捷键能够查看类或方法的组织结构

Ctrl+Shift+G—选中某个类或方法,使用此快捷键能够查找所有调用类或者方法的出处

快捷键是日常开发调试中最为仰仗的一个技巧。Eclipse中的快捷键可谓博大精深,我们在这里不再一一列举。读者可以在实际开发中不断摸索并牢记这些快捷键,因为它们也是日常开发中必不可少的一部分。读者也可参照Eclipse中的这些快捷键,在其他的IDE中找到相应的快捷键设置。

1.1.3 安装与配置Web服务器

本书所涉及的绝大多数内容都属于Web开发的相关范畴,因而在JDK和Eclipse安装完成之后,我们需要紧接着安装Web服务器用以提供Web程序的运行环境。

在众多的Web应用服务器中,Apache开源社区的Tomcat是最常用的Web服务器之一,读者可以在http://tomcat.apache.org找到各个版本的Tomcat服务器,本书以Tomcat 6.028为例,请大家下载适合自己的版本并安装。

安装完成后,可以在Tomcat安装目录下的bin目录中找到运行Tomcat的命令,运行成功的示意图如图1-7所示。

另外一款非常流行的轻量级的Web应用服务器是Jetty。相对于Tomcat来说,Jetty具有无须安装(其API以一组JAR包的形式发布)、速度更快等特点,成为当前众多程序员进行Web开发调试的首选。在1.1.4节中,我们将结合Eclipse IDE为大家讲解使用Jetty进行Web开发和调试的过程。

图1-7 Tomcat安装成功

1.1.4 在Eclipse中使用Jetty搭建Web开发环境

在Eclipse IDE中建立了一个Web项目后,我们可以通过引入Jetty搭建起一个不依赖任何插件的Web开发调试环境。使用Jetty搭建Web开发环境的基本步骤如下。

1.1.4.1 构建项目的基本目录结构

搭建Web开发调试环境的第一个步骤是建立一个Web项目的基本目录结构,如图1-8所示。

图1-8 Web项目的基本目录结构

这里简单解释一下这些基本目录结构的作用:

src(source folder)—存放所有的Java源代码

conf(source folder)—存放所有的配置文件

test(source folder)—存放所有的Java测试代码和调试代码

web—Web项目的根目录,其子目录WEB-INF以及classes和lib是构成Web项目所必需的基本结构

lib—存放JAR包,但是这里存放的JAR文件仅在开发调试时使用

读者可以根据项目的实际情况,创建适合于项目自身需求的目录结构,并导入到Eclipse中使之成为Eclipse项目,之后我们的开发调试都将围绕着Web项目的目录结构展开。

目前,使用Maven作为开发环境进行构建的开发人员越来越多。Maven是一个非常优秀的项目构建和项目管理的工具。我们在这里并未介绍Maven的主要原因在于Maven的使用需要额外的开发环境搭建,甚至可能需要编写符合项目自身条件的POM文件,而这些内容并非本书介绍的重点。因而,对其有兴趣的读者可以自行使用Maven进行项目构建,而使用Maven进行项目构建和JAR文件依赖关系的管理,也是我们向读者推荐的做法。

1.1.4.2 引入Jetty服务器

搭建Web开发环境的第二步,是引入Jetty服务器作为我们的Web容器。引入Jetty的方法非常简单,只需要将Jetty服务器相关的JAR文件加入到项目的CLASSPATH中即可。整个过程如图1-9所示。

图1-9 将构成Jetty服务器必要的JAR包加到CLASSPATH中

这个步骤完成之后,我们就可以将Jetty作为我们的Web容器提供者。在这里,我们可以看到Jetty服务器相对于其他Web服务器的一个重要区别在于它不需要额外安装,引入Jetty的过程只不过是将几个JAR文件加入到CLASSPATH中而已。这样一来,也就不存在平台依赖性和兼容性问题了。

1.1.4.3 启动Jetty进行开发调试

启动Jetty服务器进行基本的Web开发调试的步骤非常简单,只需要在项目中编写一个JettyStarter的Java类,并将这个类作为一个基本的Application执行即可。JettyStarter的源码如代码清单1-1所示。

代码清单1-1 JettyStarter.java

          package runtime;
          import org.mortbay.jetty.Connector;
          import org.mortbay.jetty.Server;
          import org.mortbay.jetty.nio.SelectChannelConnector;
          import org.mortbay.jetty.webapp.WebAppContext;
          /**
          * Jetty Server 启动类
          *
          * @author
          */
          public class JettyStarter {
              /**
                * @param args
                * @throws Exception
                */
              public static void main(String[] args) throws Exception {
                  long begin = System.currentTimeMillis();
                  Connector connector = new SelectChannelConnector();
                  connector.setPort(Integer.getInteger("jetty.port", 80).intValue());
                  WebAppContext webapp = new WebAppContext("web", "/struts_example");
                  Server server = new Server();
                  server.setConnectors(new Connector[] { connector });
                  server.setHandler(webapp);
                  server.start();
              System.out.println("Jetty Server started, use " + (System.currentTimeMillis()
                  - begin) + " ms");
              }
          }

有了JettyStarter这个类之后,我们可以直接将这个类以Application方式来运行。此时,Jetty服务器就会启动(如图1-10所示),从而成为一个Web容器的提供者。当然,我们也可以使用调试模式(Debug)执行这个类,从而使得整个Jetty服务器处于调试模式。

图1-10 JettyStarter启动界面

启动成功后,我们会在控制台看到成功启动的日志:

          [main] INFO org.mortbay.log - Logging to
        org.slf4j.impl.SimpleLogger via
        org.mortbay.log.Slf4jLog
          [main] INFO org.mortbay.log - jetty-6.1.7
          [main] INFO org.mortbay.log - Started
        SelectChannelConnector@127.0.0.1:8080
        Jetty Server started, use 1547 ms

此时,Jetty服务器被引入作为Web应用的开发调试服务器,Web开发调试环境也就成功搭建起来。我们可以通过浏览器对Web应用进行访问,而其中的IP地址、端口、Web应用的入口名称,都可以在JettyStarter这个Java类中进行API级别的相应设置。

如果JettyStarter运行在调试模式下,我们就可以在Web项目中加入熟悉的调试元素,例如加入断点(Breakpoint)、加入监视(Watch)、单步运行(Step by),从而对整个项目的运行状态进行调试和监控。

相比许多其他的开发环境搭建方式,本书所介绍的Web开发环境至少拥有以下的好处:

开发环境的建立并不依赖于任何IDE或者相关的插件,只需要运行某个Java类就可以进行调试

开发环境的建立不依赖于任何外部Web服务器的安装,无论将项目迁移到什么样的IDE或者操作系统,只要是有Java运行环境的地方,都可以直接运行

在项目中内置开发环境的做法,降低了程序员的学习成本

1.2 获取Struts2

1.2.1 Struts2的相关资源下载

在Struts的官方网站(http://struts.apache.org)可以找到所有Struts项目相关的信息。由于历史原因,Struts项目分为两个截然不同并互不兼容的版本Struts1.X和Struts2. X(我们通常把Struts1.X的版本称为Struts,而Struts2.X的版本称为Struts2)。读者在获取相关信息时,需要正确区分不同版本的Struts项目,本书讲述的所有内容都是围绕着Struts2.X进行的。有关Struts项目的发展历史和它们之间的不同,在其他章节中将会做出更加详细的介绍。Struts的官方网站如图1-11所示。

图1-11 Sturts2的主页

在左侧的“Documentation”分类中,我们能够找到Struts2的最新版本Struts 2.2.1 (GA)的链接,点击该链接后就进入到了Struts2相应版本的主页,其中可以找到Struts2的下载链接(一个很大的蓝色Download Now按钮),如图1-12所示。

图1-12 Struts 2.2.1主页

点击“Download Now”按钮,将会看到Struts2的下载页面,其中包含各种不同类型的Struts2的下载包,如图1-13所示。

图1-13 Struts2的下载页面

在这里点击下载struts-2.2.1-all.zip,就能获得Struts2项目所有的相关资源。在其中,我们可以看到Struts2的分发包、Struts2的文档(Reference)、Struts2的源码、Struts2的示例项目等。接下来,我们就来看看Struts2项目基本的目录组织结构。

1.2.2 Struts2项目的目录组织结构

打开struts-2.2.1-all.zip并将其解压到任意目录后,我们可以在其中找到Struts2的所有相关资源,如图1-14所示。

Struts2项目的目录结构中主要包含了4个目录:apps、docs、lib和src,这些目录中所存放的基本内容分别介绍如下。

apps—存放了所有Struts2的示例项目

位于apps目录下的所有war都是可以部署到Web服务器中直接运行的Web应用。这些Struts2的示例项目对学习Struts2有相当大的指导作用。当我们对Struts2的特性使用有疑问时,可以通过直接研究这些项目的源码获得足够的支持。

图1-14 Struts2分发包的目录结构

docs—存放了所有Struts2与XWork的文档

docs目录中存放的Struts2与XWork的相关文档基本上是以Wiki Page的形式出现的。这主要是由于Struts2来源于Webwork2,因而其文档的表现形式也是一脉相承的。Wiki Page形式的文档的好处在于可以将需要表述的框架特性划分为若干个专题,针对每个专题,有相应的理论知识讲解和示例代码的印证。

lib—存放了所有Struts2相关的JAR文件以及Struts2运行时所依赖的JAR文件

lib目录是一个完整的依赖资源集合。Struts2项目运行所需要的JAR文件都位于这个目录下。我们可以看到,Struts2的许多特性都是以插件的形式提供的,因而在lib目录下除了Struts2项目的基本依赖之外,绝大多数都是插件资源。

src—存放了所有Struts2的源码,以Maven所指定的项目结构目录存放

Struts2本身是根据Maven所指定的项目目录结构进行编写的,所以src目录的组织结构也与Maven所规定的目录结构相同。这种组织结构的好处在于我们可以在src目录中找到所有Struts2相关资源的单个源码文件。源码的另外一种组织形式我们将在下一节中着重介绍,读者可以就两种不同的形式进行比较。

1.3 Struts2源码的初步研究

1.3.1 源码的组织形式

如果仔细浏览Struts2分发包中的src目录,我们会发现在Struts2的分发包中提供的源码是以目录形式存放的。这种源码组织形式允许读者访问单个源码文件,并可以在资源管理器中清晰地看到源码的目录组织结构。除了这种基于目录形式的源码组织方式以外,还有另外一种组织源码的方式,就是将所有的源码打包成一个JAR文件或者ZIP文件。这种单个文件形式的源码在结合IDE进行管理时更加简单,故而受到广大程序员的欢迎。

虽然Struts2的分发包中没有提供这种形式的源码,我们依然可以在网络上查找得到,例如在http://search.maven.org/中,我们可以查询到Struts2各个版本的分发包和对应的源码文件。打开上述链接后,我们可以根据实际要求进行源码查询,如图1-15所示。

图1-15 Struts2源代码查询

点击查询后,我们就能在查询结果中看到以JAR文件形式进行管理的资源文件,如图1-16所示。

图1-16 以JAR文件形式管理的Struts Core的各项资源列表

其中不仅包含Struts2的JAR文件(struts2-core-2.2.3.jar),还包含对应的源码文件(struts2-core-2.2.3-sources.jar),大家可以直接点击链接进行下载。

提示 细心的读者或许可以发现,上面提供的含有Struts2源码的网站,实际上是一个代码资源库的一部分(Maven2提供的一个网络代码库,用于在Maven2的项目建立时,作为一个基本依赖)。实际上在这个站点中,我们还可以找到许多其他开源框架的各种形式的源代码,本书中所依赖的绝大多数的源代码都是从此处获取的。

1.3.2 调试Struts2源码

在Struts2的分发包中,我们可以获取Struts2的绝大多数信息,包括Struts2的Library文件、Struts2运行时依赖的Library文件和Struts2的源码等。接下来,我们将重点介绍一下如何在Eclipse IDE中调试Struts2的源码。读者也可以顺着思路,在其他的IDE中获得相应的支持。

什么是调试Struts2源码呢?我们在日常开发中,经常听到的是调试某一段应用程序。将应用程序运行于调试(Debug)模式下,我们就可以采用IDE提供的一些特性进行程序调试:设置断点(Breakpoint)、加入监视(Watch)、单步运行(Step by)等等。通过对程序的调试,能够迅速了解程序运行的状态,从而帮助我们快速定位问题并解决问题。我们在这里所说的调试Struts2的源码,实际上是在向读者推荐一种阅读源码的方式。

传统的阅读开源框架源码的方式,是以逐个package或者逐个module的方式对框架的源码进行阅读。不过我们在这里明确表示反对这种阅读源码的方式。因为任何一个程序只有处于运行状态时,才能突显出其内在的价值。我们鼓励读者从程序运行角度来看问题,而不是死板地对着源代码本身。对于一个程序员来说,只有抓住一个程序或者框架的运行时状态,才能够对它们有更加深刻的理解。一个静态的程序是死的,只有处于运行状态的程序,我们才能看到其真正的生命力。接下来,我们就来看看在Eclipse中对一个开源框架进行源码调试的步骤和方法。

首先在Eclipse中创建一个新的项目,加入运行Struts2所需要的JAR文件,并将它们加到项目的CLASSPATH中,成功后的界面如图1-17所示。

图1-17 Eclipse中新建项目截图

接下来,我们来为加入到CLASSPATH中的这些JAR文件附上其所对应的源代码。以“struts2-core-2.2.1.jar”为例,右键单击“struts2-core-2.2.1.jar”,选择Properties(Alt+ Enter)选项卡,弹出JAR文件的属性选项框,如图1-18所示。

图1-18 为JAR文件附上源码包

选择选项卡左侧的“Java Source Attachment”选项,点击输入框右侧的按钮,指定相应的源代码所在的目录或者源代码所在的JAR文件(ZIP文件),点击OK按钮,Library文件就被附上了相应的源代码(此时,我们会发现JAR文件形式的源码组织形式在管理上的方便性,我们甚至可以把源码文件存放到Workspace的某个Repository目录后被所有的Library文件引用)。

在Library文件被附上源代码之后,展开Library文件,并双击其中的class文件,就能看到class文件所对应的源码,如图1-19所示。

图1-19 在Eclipse中查看源码

Library文件被附上其对应的源码后,我们就可以使用Eclipse IDE中的功能和快捷键对源码进行查看和分析了。

除了基本的源码查看功能,Eclipse IDE还能够对正在运行的程序进行源码级别的调试。例如,当一个程序运行在调试(Debug)模式时,我们可以直接打开Struts2的源码,使用断点功能,进入单步调试模式,并查看当前Struts2中类的运行状态,如图1-20所示。

图1-20 在Eclipse中调试源码

为Library附上源码,是Eclipse IDE在源码级别调试上的一个亮点。这为广大程序员进行框架级别的源码调试带来了极大的便利。在进行框架研究时,我们实际上根本无须将庞大的框架源程序作为一个项目下载到本地并花大力气把项目搭建起来。通过为Library附上源码的方式,我们可以轻易地查看框架的源码、获得框架中的实现类之间的逻辑结构和层次关系。

调试源码是除了日志输出以外,最有效甚至是唯一的了解程序内部运行时状态的方式。因而,调试源码也是本书最为推荐的一种源码级别学习方法,读者不仅可以将这种学习方法运用在Struts2上面,也可将其用于其他的开源框架上。

1.4 小结

本章中,我们首先向读者介绍了开发Struts2应用程序所必需的开发环境的搭建过程。这一过程包含:安装与配置JDK、安装与配置Eclipse、安装与配置Web服务器。除此之外,着重介绍了在Eclipse中搭建Web开发环境并进行源码级别调试的详细过程。

读者应该明确的是:学习一个框架,尤其是分析框架的内部实现机理,最为有效的途径就是对框架进行源码级别的调试并在调试的过程中深入探究框架内部元素在程序运行过程中的执行顺序和执行状态。这也是本书花许多笔墨向读者介绍开发环境搭建过程的初衷。希望广大读者能够融会贯通并举一反三,根据实际情况搭建起自己顺手的开发环境。

阅读完本章后,大家是否已经能够熟练掌握开发环境的搭建了呢?

如何在Eclipse中使用Jetty搭建Web开发环境?

Struts2的分发包中包含哪些主要内容?

开源项目的源代码有哪些组织形式?

如何获取一个开源框架的源码?

如何在项目中进行框架的源码调试?