J2EE表现层设计思考 |
作者:佚名 发布时间:2005-04-02 来源:不详
|
开发者在设计表现层时 问题和模型关系的紧密程度 整性、可管理性和扩展性。 这样做,因为这样更为抽象 将每个需要考虑的问题列出
|
,可以使用不同的模型,这时需 也各有不同,它们可以影响系统 虽然这些设计问题大部分都可以 ,我们选择以非正式的文档形式 来。
|
要考虑一些相关的设计问题。这些 的各个方面,包括有安全、数据完 用模型的形式表示,但我们不打算 表示。我们只是根据不同的模型,
|
用户Session指的是跨 据用户Session的概念讨论
|
越一个客户和服务器多个请求间 这个问题。
|
的一个对话。我们将在以下部分根
|
在客户端保存Session HTML页面中。
|
的状态指的是将Session的状态
|
串行化并且嵌入到返回给客户的
|
此外,这个策略还消除了跨越多个服 衡时就会遇到这种情况。
|
务器复制状态的问题,例如多个服务器间实现负载均
|
在客户端保存Session状态通常有两 下面讨论这些策略。第三个策略则是在每 action=someServlet?var1=x&var2=y met 其它两个方法的许多限制。
|
个方法--HTML的隐藏字段和HTTP cookies--我们将在 个页面的URL中嵌入Session状态信息,例如<form hod=GET>。虽然第三个方法比较少见,但它也有着
|
HTML的隐藏字段(HTML Hidden Fields) |
虽然这个方法实现起来 有着许多的缺点。这些缺点 的影响。因为每次发出请求
|
相对容易,不过使用HTML隐藏字 在保存大量的状态时尤为突出。 和响应时,都需要在网络中传送
|
段在客户端保存Session状态仍然 保存大量的状态将会对性能有很大 这些状态信息。
|
此外,当你利用隐藏的字段来保存Se 因此所有的对象引用都必须被“字符串化 文的形式显示在HTML的源代码中。
|
ssion状态时,这些持久的状态值只能是字符串值, ”,而这些信息除非经过特别的加密,否则都是以明
|
与隐藏字段的方法一样 法有着许多相同的缺点。特 在每次的请求和响应时,都
|
,使用HTTP Cookies的方式也是 别是,在保存大量的状态信息时 必须在网络上传送全部的Sessio
|
相对简单的。不幸的是,这两个方 将会对性能产生很大的影响,因为 n状态信息。
|
在客户端保存Session 大小是有限制的,这样就限 使用cookies来保存Session
|
状态时,我们也会遇到大小和类 制了可以被持久保存的数据量, 状态时,这些持久的状态信息只
|
型的局限问题。cookie headers的 而且和隐藏字段的方法一样,当你 能使用字符串值。
|
当你在客户端保存Sess 暴露给客户端,你就需要一
|
ion状态时,你必须考虑到由此 些方法来加密数据,从而保证数
|
带来的安全问题。如果你不想数据 据的安全。
|
虽然在客户端保存Session状态相对 费时间去解决。对于需要处理大量数据的 的。
|
容易实现,不过它有着很多的缺点,这些都要我们花 项目,特别是企业的系统,使用这种方式是得不偿失
|
当Session状态保存在服务器端时, 到发生以下的情形:
|
它使用一个Session ID得到,并且会一直保持住,直
|
要注意的是服务器关闭后,一些内存中的Session管理机制可能不能恢复。 |
很明显,对于要保存大 的。当状态被保存在服务器 避免了由此带来的安全问题 影响。
|
量Session状态的应用,将它们 上时,你不会有客户端Session ,而且也不会遇到由于在每个请
|
的Session状态放在服务器是更好 管理的大小和类型限制。此外,还 求间传送Session状态带来的性能
|
使用该方式,你可以更加灵活地作处理,并且便于扩展和提高性能。 |
如果你在服务器上保存 ,即你运行该应用的服务器 Session状态的复制问题, 各样的解决方案。也就是说 户只与一个服务器打交道, 件,在用户的Session中, 也被称为server affinity
|
Session状态,你必须要决定如 。如果群集的软件是运行在负载 这是一个多维的问题,不过,众 ,在应用服务器的级别上有解决 它在流量管理软件上用得比较多 该用户发出的每个请求都会被路 。
|
何使该状态信息被每个服务器得到 均衡的硬件上,那么就要处理这个 多的应用服务器现在都提供了各种 的方法。其中的一个方法是保证用 ,例如Resonate [Resonate]的软 由到同一个服务器处理。这种方式
|
另一个可选的方式是在商业层或者资 商业层保存Session的状态,而一个关系
|
源层保存Session状态。企业JavaBeans组件可用来在 数据库则可用在资源层。
|
有很多时候我们都要限 这样的情形。
|
制或者控制客户端访问某些应用
|
资源。下面我们就来讨论其中两种
|
限制或者控制客户访问 这个问题会发生在以下情况 或者是根据用户的角色限制
|
的一个原因是防止一个视图或者 ,例如仅有注册或者登陆后的用 用户访问部分的视图。
|
部分的视图被一个客户直接访问。 户才可允许访问一个特别的视图,
|
在描述过这个问题后,我们将讨论第 者的讨论和重复的form提交有关,因为多
|
二种情况,它和控制应用中一个用户的流程有关。后 次提交将会导致不必要的重复事务。
|
在一些情况下,资源被 个方法是加入应用逻辑到处 置运行时的系统,对于一些 些资源的访问必须被通过另 受限制的资源不允许通过一
|
限制为完全不允许某些用户访问 理控制器或者视图的程序中,禁 资源,仅允许经由另一个应用资 一个表现层的应用资源进行,例 个浏览器直接调用。
|
。有几个方法可以做到这一点。一 止某些用户访问。另一个方案是设 源内部调用。在这种情形,对于这 如一个servlet控制器。对于这些
|
处理这个问题的一个常见方法是使用 个常见的方式是在一个视图中置入一个保 考虑选择何种方式来控制访问之前,我们
|
一个控制器来作为该类访问控制的一个委托者。另一 护设置。我们这里主要讨论基于视图的控制策略。在 首先来描述一下这些策略。
|
对于在一个视图的处理 资源,而另一个是限制访问
|
中置入一个保护逻辑,有两个常 部分的资源。
|
见的应用。一个是防止访问整个的
|
在每个视图中包含一个All-or-Nothing保护 |
在一些情况下,置入到 。也就是说,这个逻辑限制 装到一个中央化的控制器中 使用这个策略。通常这个情 如果客户仍然需要登陆到网 的tag(标记)就可以做到
|
视图处理代码中的逻辑以all-or 某个特别的用户访问一个特别的 ,这样便于集中化管理。如果只 形都是发生在一个非技术人员需 站来浏览这些页面,那么只需要 控制访问。如3.1的例子所示。
|
-nothing的模式允许或者拒绝访问 视图。通常这一类型的保护最好封 有很少的页面需要防护,那么可以 要更新网站一小部分的静态文件。 在每个页面的顶部加入一个自定义
|
例子3.1 在每个视图中包含一个All-or-Nothing保护 |
<%@ taglib uri="/WEB-INF/corej2
|
eetaglibrary.tld"
|
在其它情况下,置入到 以和上面的all-or-nothing 中的一个房间作类比。all- 策略则是告诉用户在进入房 的例子。
|
视图处理代码的逻辑可拒绝访问 策略一起使用。为说明这一点, or-nothing的保护策略告诉用户 间后,允许他们看到什么东西。
|
一个视图的某些部分。这个策略可 我们这里使用控制访问一个建筑物 是否可以进入房间,而第二个保护 以下就是一些你可以利用这个策略
|
根据用户的角色,视图的某部分可能 访问到其员工的子视图,而作为一个员工 信息,如例子3.2所示。
|
不显示。例如,一个经理在收看管理信息时,他可以 ,他只可以看到自己组织的信息,而不可以访问其它
|
<%@ taglib uri="/WEB-INF/corej2
|
eetaglibrary.tld"
|
<corePatterns:guard role="manager"> |
<b>This should be
|
seen only by managers!</b>
|
根据系统的环境,显示 备,那么使用多个CPU的部
|
的规划可以被修改。例如,如果 分设备就可以不显示。
|
用户使用的是一个单CPU的硬件设
|
要限制某个客户直接访问一个特别的 访问到这些资源,例如一个使用RequestD Web容器中内置
|
视图,你可以配置表现层只有通过内部的资源才可以 ispatcher的servlet控制器。此外,你还可以使用
|
的安全技术,根据servlet2.2或者以 述文件中(deployment descriptor)。
|
后的规范。安全限制被定义在称为web.xml的配置描
|
basic和form-based的 范,你可以到以下网址去查 ts/servlet/index.html)
|
认证方法在Servlet规范中也有 看当前规范的细节(http://jav 。
|
描述。在此我们不打算重复这个规 a.sun.com/produc
|
你已经明白了加入安全 绍了如何通过配置令它和al 作为all-or-nothing保护,
|
限制到你的应用时会有什么用处 l-or-nothing保护相关。最后, 以限制一个资源的访问。
|
,我们简要讨论了这个问题并且介 我们描述了一个简单和常用的方法
|
应用或许被配置在一个 来控制访问。资源可以被某 一部分也可以根据用户的角 如上面all-or-nothing情景 些安全角色不分配给任何一 源将禁止所有的浏览器直接 安全的角色以限制直接的浏
|
安全限制中,而这个安全限制允 些角色的用户访问,并且禁止其 色来限制访问。如果某些资源完 中提到的一样,那么这些资源可 个用户。这样只要不分配这个安 访问。例子3.3就是一个web.xml 览器访问。
|
许使用编程的方法根据用户的角色 它的角色访问。另外,某个视图的 全禁止全部的直接浏览器请求,例 以只允许一些安全角色访问,而这 全角色,那么以这种方式配置的资 配置文件的一部分,它定义了一个
|
角色的名字是“sensit sensitive3.jsp。除非一个 直接访问这些JSP页面。不 servlet控制器处理的请求 页面。
|
ive”,受限制资源的名字是sen 用户或者组被分配到“sensitiv 过,由于内部的请求并不受这些 将会导向到这些受限制的页面,
|
sitive1.jsp, sensitive2.jsp和 e”角色,否则这些客户都不可以 安全的限制,一个初始时由某 这样它们就可以间接访问这些JSP
|
最后,要注意的是,不 兼容的现象。不过支持Serv
|
同厂家的产品在实现Servlet2.2 let2.3的服务器则没有这个兼容
|
版本的规范时,在这个方面有些不 性的问题。
|
例子 3.3 通过不分配安全角色提供All-or-Nothing控制 |
<web-resource-collection> |
<web-resource-name>SensitiveRe
|
sources </web-resource-name>
|
<description>A Col
|
lection of Sensitive Resourc
|
es </description>
|
<url-pattern>/trad
|
e/jsp/internalaccess/ sensit
|
ive1.jsp</url-pattern>
|
<url-pattern>/trade/jsp/intern
|
alaccess/ sensitive2.jsp</url-pattern>
|
<url-pattern>/trad
|
e/jsp/internalaccess/ sensit
|
ive3.jsp</url-pattern>
|
<http-method>GET</http-method> |
<http-method>POST</http-method> |
</web-resource-collection> |
<role-name>sensitive</role-name> |
有一个简单和常见的方法可以限制一 样,这个方法无需修改任何的配置文件。 目录下。例如,要防止浏览器直接访问一 个名字为securityissues的Web应用。我 /securityissues/WEB-INF/internalacce
|
个客户直接访问某个资源,例如JSP。和3.3的例子一 这个方法只是需要将资源放置在Web应用的/WEB-INF/ 个称为info.jsp的视图,我们假设这个文件是属于一 们可以将该JSP文件放在以下的子目录: ssonly/info.jsp。
|
对于/WEB-INF/目录及 问。不过,如果需要,一个 all-or-nothing的方式,因
|
其子目录是禁止浏览器直接访问 控制器servlet仍然可以导向到 为以这种方式配置的资源都完全
|
的,因此info.jsp也不可以直接访 这个资源。这种控制使用的是 禁止浏览器直接访问。
|
用户使用浏览器时,可 过的form,这样就会带来一 的页面之前按下停止的按钮 止这些重复的提交,我们可
|
以经常使用向后的按钮,因此就 个重复事务处理的问题。同样, ,接着再次提交同一个form。对 以使用一个控制servlet来提供
|
有可能重复提交一个他们已经提交 一个用户也可能在接收到一个确认 于这些情况,我们都想跟踪并且禁 一个控制点,以解决这个问题。
|
同步记号(Synchronizer (or Dvu) Token) |
这个策略是为了解决重复的form提交 中,并且包含在返回到客户的每一个form Session中的同步标记作对比。在form首 不一样,那么该form就会禁止提交,一个 按下浏览器中的后退按钮并尝试重新提交
|
问题。一个同步的记号被设置在一个用户的Session 中。当form被提交时,form中的同步标记就和 次提交的时候,这两个标记应该是一样的。如果标记 错误就会返回给用户。在用户提交一个form时,如果 同一个form时,标记就会出现不匹配的现象。
|
另一方面,如果两个标 ,Session中的标记值就会
|
记值匹配,那么我们就可以确信 被修改为一个新的值,同时允许
|
整个流程是正确的。在这种情况下 提交该form。
|
你也可以使用这个策略来控制对某些 。例如,假设一个用户将某个应用的页面 。当用户直接通过收藏夹来访问页面A, 处在一个不同步的状态,或者它根本就不
|
页面的直接访问,就好象上面资源保护中描述的一样 A收藏到收藏夹中,而页面A只允许通过页面B和C访问 这时页面的访问顺序就是不正确的,这样同步标记将 存在。不论怎样,访问都被禁止了。
|
通常我们都希望同时在客户和服务器 端验证那样专业,不过它可以提供高级别 器端的验证通常要广泛得多。虽然在一个 议只使用客户端的验证。这样做的一个主 的,用户可以在任何时候通过设置,从而
|
端进行验证。虽然客户端的验证处理看来没有服务器 的检查,例如验证form中的某个字段是否为空。服务 应用中,两种类型的处理都是适当的,不过这里不建 要原因是由于客户端的验证是使用客户端的脚本语言 跳过这些脚本。
|
这里不打算很详细地讨 要考虑到的问题,如果你想
|
论验证的策略。我们只是在这里 更深入地了解,你可以参考现有
|
提及一下这是一个在设计系统时需 的文献。
|
输入的验证在客户端进行。通常这个 上面已经提到,客户端的验证是服务器端
|
操作都是通过嵌入的脚本代码例如JavaScript进行。 验证的一个补充,不过不应该独立使用。
|
输入的验证在服务器端进行。有几个 Form-Centric Validation(以Form为中 (基于抽象类型的验证)。
|
典型的策略可用作服务器端验证。这些策略是 心的验证)和validation based on abstract types
|
Form-Centric Validation的策略强 。典型地,这些方法依据其包含的逻辑, 证的方法都是和用户某个特定的form相关 段或者只可以使用数字的字段。在这种情 个必填的字段时,它都是在应用中独立处 不过当应用变大时,就会带来重复代码的
|
迫一个应用包含许多的方法来验证form各部分的状态 都是交迭的,这样不利于重新使用和模块化。一个验 ,没有共同的代码来处理常见的操作,例如必填的字 况下,当某个字段被用在多个不同的form,并且是一 理的。这个策略实现起来相对容易,并且效率也高, 问题。
|
要提供一个更灵活的、可重用的和易 式就是下面提到的“根据抽象类型来验证
|
于维护的方法,数据模型应该作更大的抽象。这个方 ,form-centric验证的例子见例子3.4。
|
Example 3.4 Form-Centric Validation |
/**If the first name or last nam
|
e fields were left
|
blank, then an error
|
will be returned to client.
|
|
With this strategy,
|
these checks for the existen
|
ce
|
of a required field are duplicat
|
ed. If this valid-
|
ation logic were abstracted into
|
a separate component,
|
it could be reused across forms
|
(see Validation Based
|
on Abstract Types strategy)**/ |
Vector errorCollection = new Vector(); |
if ((firstname == null) || (firs
|
tname.trim.length() < 1))
|
errorCollection.addE
|
lement("firstname required")
|
;
|
if ((lastname == null) || (lastn
|
ame.trim.length() < 1))
|
errorCollection.addElement("last
|
name required");
|
这个方法可应用在客户 最好应用在服务器端。
|
端或者服务器端,不过在一个基
|
于浏览器或者瘦客户端的环境下,
|
输入和限制的信息由模型状态中抽象 和模型正在使用的应用逻辑分离开来,从
|
出来并且放到一个通用的架构中。这样将模型的验证 而减少了它们的耦合。
|
模型验证通过将元数据 一些简单的数据存储访问到 状态和限制的信息由应用逻
|
及限制和模型状态作对比进行。 ,例如一个属性文件。这种方法 辑中分离出来。
|
模型的元数据和限制通常可以通过 的好处是系统更通用了,因为它将
|
例如有一个组件或者子系统封装验证 一个数字的输入在某个范围,或者是一个 要验证某个模型的不同方面时,每一个组 集中式的验证技术。集中式的验证技术可 者通常声明、使用配置文件等。
|
的逻辑,这些逻辑包括判断一个字符串是否是空的, 格式化为某种方式的字符串等。当不同的应用组件需 件并不需要写自己的验证代码。取而代之的是,使用 以通过一些方式设置,例如通过编程,一些发生器或
|
这样验证的技术就更加通用,它集中 。使用这个方法的一个缺点是效率和性能 强大,但是理解和维护起来有点难懂。
|
在模型的状态和它的需求,而与应用的其它部分无关 要受到影响,而且,通常更通用的方法,虽然功能更
|
以下是一个例子。一个 要求全部是数字的字段”等 的值和某类特别的验证对应
|
使用XML的配置文件描述了各种 。此外,可以设计每个验证的管 起来。验证某个特定字段的例子
|
的验证,例如“必填的字段”,“ 理类。最后,一个影射将HTML表格 代码如例子3.5所示。
|
例子 3.5 Validation Based on Abs
|
tract Types
|
//formFieldName="form1.firstname" |
Validator.getInstance().validate
|
(firstNameString, formFieldName);
|
Helper Properties--完整性和一致性 |
JavaBean Helper类通 提供了一个技术,可以自动 helper对象的属性中。JSP
|
常是用来保存一个客户请求传送 地将这些参数值由一个servlet 语法如下所示:
|
过来的中间状态。JSP的运行引擎 请求对象拷贝到这些JavaBean的
|
<jsp:setProperty name="helper"
|
property="*"/>
|
上面的语句告诉JSP引 JavaBean的名字是“helper
|
擎将所有匹配的参数值拷贝到一 ”),如例子3.6所示。
|
个JavaBean的相应属性中(这个
|
例子3.6 Helper Properties - A Si
|
mple JavaBean Helper
|
public void setFirst(String aString) |
public void setLast(String aString) |
那么怎样才算匹配呢?如果一个requ 么就认为它们是匹配的。通常都会将每个 的类型作比较。
|
est参数的名字和类型和helper bean的属性一样,那 参数和每个bean属性的名字及bean属性的setter方法
|
虽然这个技巧非常简单 request参数的值是空的话 空的String时,如果它和一 。实际上在这种情况下,结 是跨越多个请求重用的,这 会带来的问题。
|
,不过它可以产生一些令人迷惑 那将会怎样的呢?许多开发者可 个bean属性匹配,将会令该bean 果是不会修改匹配的bean属性。 种含糊的做法将令数据值不完整
|
的东西。首先,要注意的如果 能认为当一个request参数是一个 属性接收一个空的String值或Null 还有,由于JavaBean helper对象 和不正确。图3.1展示了这类技巧
|
请求1包含有名字为“f 请求2仅包含有“last”参 的值则没有变化。它并没有 值。如3.1的图所示,如果b
|
irst”和“last”的参数值,并 数的值,因此只设置了bean中的 设置为一个空字段串或者是null ean的值在请求间并没有手动复
|
且这些值设置了相应的bean属性。 其中一个参数,而“first”参数 ,这是由于在请求的参数中并没有 位,这是将会带来不一致的问题。
|
在设计你的应用时,要 接口是如何处理的。例如, 务器端的值是不会被清空的 含相应的checkbox参数。因 HTML规范,见http://www.w
|
考虑的另一个问题是当form中的 当一个form拥有多个checkbox, 。当request对象由这个接口产 此,这些checkbox相关的参数值 3.org/)。
|
项目没有选择的时候,HTML form 在没有选择任何的checkbox时,服 生时,在request对象中就没有包 都不会发送到服务器端(完整的
|
由于没有参数值传送到服务器,所以 bean属性将保持不变。在这种情况下,除 不一致和不正确的数据值。一个简单的解
|
如上所述,在使用<jsp:setProperty> 时,匹配的 非开发者手动修改这些值,否则应用中将有可能出现 决方法是在每个请求间复位所有JavaBean的状态值。
|
所谓的坏习惯,指的是未达到最优化 模式和好习惯记录下来时,我们就会自然
|
的方案,它们与设计模式的建议是冲突的。当我们将 地抛弃掉那些未达到最优化的习惯。
|
在每个部分,我们都简 refactoring和模式等,提 习惯,这里只是点到即止,
|
要描述一下坏习惯,并且提供相 供进一步的信息和更好的选择。 有兴趣的可继续深入研究。
|
关的指引,包括有设计问题, 我们并不会非常深入地讨论每个坏
|
自定义tag helper可以 大量的视图包含有类似的he 多个地方。
|
包含在JSP视图的顶部,以执行 lper引用,维护这些代码将会变
|
访问控制和其它类型的检查。如果 的非常困难,因为修改可以发生在
|
修改控制代码,使用一 类似的控制代码时,例如只 个可重用的helper类完成。
|
个controller和相关的Command 有一部分的JSP视图禁止一些用
|
helpers。当需要在多个地方包含 户访问的时候,将这个工作通过一
|
表现层的数据结构,例 放到商业层或者其它层中, 商业服务的方法接受一个Ht 那些非Web上的客户)都必 ,商业层的服务需要知道如 并且增加了层间的耦合。
|
如HttpServletRequest,应该被 将增加这些层间的耦合,这样就 tpServletRequest类型的参数, 须将它们的请求状态封装到一个 何与这些表现层的数据结构交互
|
限制到表现层上。如果将这些细节 会减少可利用服务的重用性。如果 这样使用这个服务的客户(即使是 HttpServletRequest对象中。此外 ,从而令商业层的代码变得复杂,
|
不让表现层的数据结构和商业层共享 且再共享。你也可以选择由表现层数据结
|
,而是拷贝相关的状态到一个更常见的数据结构中并 构中将相关的状态分离出来,作为独立的参数共享。
|
将诸如HttpServletReq 用中两个不同方面的耦合。 的细节,它们可重用的机会
|
uest的请求处理数据结构和域对 域对象应该是可重用的组件,如 就会减少,而且,维护和调试高
|
象共享,这样将不必要地增加了应 果它们的实现依赖协议或者层相关 耦合的应用更加困难。
|
不通过传送一个HttpSe 一个更为常用的数据结构中 HttpServletRequest对象中 提供给域对象。
|
rvletRequest对象作为一个参数 ,并且将这个对象共享给域对象 将相关的状态分离出来,并且将
|
,而是拷贝request对象的状态到 。你也可以选择由 每一个的状态作为一个独立的参数
|
浏览器客户环境的其中一个缺点是对 交了一个订单,这个交易将会导致由一个 。在接收到确认页面后,如果用户点击后
|
缺少对客户浏览某个应用时的控制。一个用户可能提 信用卡帐号中扣除费用,并且将产品运送到客户手中 退的按钮,同一个form将会再次提交。
|
在企业环境中,安全是 问到,那么这些信息应该保 果不作一些保护,那么客户
|
其中一个最重要的问题。如果某 护起来。对于特别的配置文件, 将可以不经意或者故意地接收到
|
些信息没有必要让一个客户直接访 属性文件,JSP或者class文件,如 敏感的信息。
|
错误地认为<jsp:setProperty>将会复位Bean属性 |
<jsp:setProperty> 性中,不过,当参数的值为 将会被忽略,但很多开发者 串值。
|
标准标签可将request的参数值 空时,得到的结果是很容易使人 都错误地认为匹配的JavaBean属
|
拷贝到同名的JavaBean helper属 迷惑的。例如,一个值为空的参数 性将会得到一个null或者空的字符
|
在使用<jsp:setPrope 性。
|
rty>标签时,不要只是想当然
|
,在使用前,你应该初始化bean属
|
对于多个JSP视图中重复使用的控制 的代码被加入到一个控制器中,那么它就 一个servlet控制器进行单元测试,特别 HTTP协议无关的helper类,将会复杂得多
|
代码,在很多时候都会放到一个控制器中。如果太多 会变得很庞大并且难以维护、测试和调试。例如,对 是一个“臃肿的控制器”,相对于测试一个独立的和 。
|
在处理一个请求时,控制器通常都是 它的控制类一起工作。可以使用Command Servlet引擎的JavaBean command对象要
|
首先处理的地方,不过它应该也是一个委托点,与其 对象来封装控制器委托的控制代码。测试这些独立于 容易得多,因为它需要测试的模块代码更少。
|
|
|
|
|
|