复旦大学软件学院oOAD课程期末报告 (2)与 Servlet API严重耦合,难于测试 因为 Struts1框架是在 Model2的基础上发展起来的,因此它完全是基于 Servlet API 的,所以在 Struts1的业务逻辑控制器内,充满了大量的 Servlet API。 看下面的 Action代码片段: ∥/业务逻辑控制器必须继承 Struts1提供的Acon类 public dass LoginAction extends Action ∥处理用户请求的 execute方法 public ActionForward execute(ActionMapping mapping, Action Form form, Http servletrEquest request ervletResponse response )throws AuctionException 获取封装用户请求参数的 Action Form对象 /将其强制类型转换为登录用的 Action Form LoginForm login Form =Login)form ∥/当用户名为scot密码为 tiger时返回成功 if (scott equals( login Form. getUsername( & tiger".equals(login Form. getPasswordo) ∥0处理成功,返回一个 Action Forward对象 eturn mapping find Forward(success"); ∥处理失败,返回一个 Action Forward对象 eturn mapping find Forward("success 当我们需要测试上面 Action类的 execute方法时,该方法有4个参数: Action Mapping、 Action Form、 HttpservletreQuest和 Http Servletresponse,初始化这4个参数比较困难,尤其 是 HttpservletreqUest和 HttpservletresPonse两个参数,通常由Web容器负责实例化 因为 Http servletrEquest和 Http Servletresponse两个参数是 Servlet APl,严重依赖于We b服务器。因此,一旦脱离了Web服务器, Action的测试非常困难。 (3)代码严重依赖于 Struts1APl,属于侵入式设计 正如从上面代码片段中所看到的, Struts1的 Action类必须继承 Struts1的 Action基类
复旦大学软件学院 OOAD 课程 期末报告 Introduction to Struts2.0 Page | 6 (2)与 Servlet API 严重耦合,难于测试 因为 Struts 1 框架是在 Model 2 的基础上发展起来的,因此它完全是基于 Servlet API 的,所以在 Struts 1 的业务逻辑控制器内,充满了大量的 Servlet API。 看下面的 Action 代码片段: //业务逻辑控制器必须继承 Struts 1 提供的 Action 类 public class LoginAction extends Action { //处理用户请求的 execute 方法 public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response)throws AuctionException { //获取封装用户请求参数的 ActionForm 对象 //将其强制类型转换为登录用的 ActionForm LoginForm loginForm = (LoginForm)form; //当用户名为 scott,密码为 tiger 时返回成功 if ("scott".equals(loginForm.getUsername() && "tiger".equals(loginForm.getPassword()) { //处理成功,返回一个 ActionForward 对象 return mapping.findForward("success"); } else { //处理失败,返回一个 ActionForward 对象 return mapping.findForward("success"); } } } 当我们需要测试上面 Action 类的 execute 方法时,该方法有 4 个参数:ActionMapping、 ActionForm、HttpServletRequest 和 HttpServletResponse,初始化这 4 个参数比较困难,尤其 是 HttpServletRequest 和 HttpServletResponse 两个参数,通常由 Web 容器负责实例化。 因为 HttpServletRequest 和 HttpServletResponse 两个参数是 Servlet API,严重依赖于 We b 服务器。因此,一旦脱离了 Web 服务器,Action 的测试非常困难。 (3)代码严重依赖于 Struts 1 API,属于侵入式设计 正如从上面代码片段中所看到的,Struts 1 的 Action 类必须继承 Struts 1 的 Action 基类
复旦大学软件学院oOAD课程期末报告 Introduction to struts 2.0 实现处理方法时,又包含了大量 Struts1APl:如 Action Mapping、 Action Form和 Action For ward类。这种侵入式设计的最大弱点在于,一旦系统需要重构时,这些 Action类将完全没 有利用价值,成为一堆废品 可见, Struts1的 Action类这种侵入式设计导致了较低的代码复用。 13 Webwork简介 WebWork虽然没有 Struts1那样赫赫有名,但也是出身名门, WebWork来自另外一个 优秀的开源组织: opensymphony,这个优秀的开源组织同样开发了大量优秀的开源项目, 如 Qutarz、 OSWork Flow等。实际上, WebWork的创始人则是另一个Java领域的名人:Rick ard Oberg(他就是 JBoss和 XDoclet的作者)。 相对于 Struts1存在的那些先天性不足而言, WebWork则更加优秀,它采用了一种更 加松耦合的设计,让系统的 Action不再与 Servlet ap耦合。使单元测试更加方便,允许系 统从B/S结构向C/S结构转换。 相对于 Struts1仅支持JsP表现层技术的缺陷而言, WebWork支持更多的表现层技术, 如 Velocity、 FreeMarker和XsLT等。 WebWork可以脱离Web应用使用,这一点似乎并没有太多优势,因为,一个应用通常 开始已经确定在怎样的环境下使用。 WebWork有自己的控制反转( Inversion of contro)容 器,通过控制反转,可以让测试变得更简单,测试中设置实现服务接口的Mock对象完成测 试,而不需要设置服务注册。 WebWork2使用OGNL这个强大的表达式语言,可以访问值栈。OGNL对集合和索引属 性的支持非常强大。 WebWork建立在 WOrk之上,使用 ServletDispatcher作为该框架的核心控制器,处理H TTP的响应和请求。 从处理流程上来看, WebWork与 Struts1非常类似,它们的核心都由控制器组成,其 中控制器都由两个部分组成 核心控制器 ServletDispatcher,该控制器框架提供 业务逻辑控制器 Action,该控制器由程序员提供
复旦大学软件学院 OOAD 课程 期末报告 Introduction to Struts2.0 Page | 7 实现处理方法时,又包含了大量 Struts 1 API:如 ActionMapping、ActionForm 和 ActionFor ward 类。这种侵入式设计的最大弱点在于,一旦系统需要重构时,这些 Action 类将完全没 有利用价值,成为一堆废品。 可见,Struts 1 的 Action 类这种侵入式设计导致了较低的代码复用。 1.3 WebWork 简介 WebWork 虽然没有 Struts 1 那样赫赫有名,但也是出身名门,WebWork 来自另外一个 优秀的开源组织:opensymphony,这个优秀的开源组织同样开发了大量优秀的开源项目, 如 Qutarz、OSWorkFlow 等。实际上,WebWork 的创始人则是另一个 Java 领域的名人:Rick ard Oberg(他就是 JBoss 和 XDoclet 的作者)。 相对于 Struts 1 存在的那些先天性不足而言,WebWork 则更加优秀,它采用了一种更 加松耦合的设计,让系统的 Action 不再与 Servlet API 耦合。使单元测试更加方便,允许系 统从 B/S 结构向 C/S 结构转换。 相对于 Struts 1 仅支持 JSP 表现层技术的缺陷而言,WebWork 支持更多的表现层技术, 如 Velocity、FreeMarker 和 XSLT 等。 WebWork 可以脱离 Web 应用使用,这一点似乎并没有太多优势,因为,一个应用通常 开始已经确定在怎样的环境下使用。WebWork 有自己的控制反转(Inversion of Control)容 器,通过控制反转,可以让测试变得更简单,测试中设置实现服务接口的 Mock 对象完成测 试,而不需要设置服务注册。 WebWork 2 使用 OGNL 这个强大的表达式语言,可以访问值栈。OGNL 对集合和索引属 性的支持非常强大。 WebWork 建立在 XWork 之上,使用 ServletDispatcher 作为该框架的核心控制器,处理 H TTP 的响应和请求。 从处理流程上来看,WebWork 与 Struts 1 非常类似,它们的核心都由控制器组成,其 中控制器都由两个部分组成: — 核心控制器 ServletDispatcher,该控制器框架提供。 — 业务逻辑控制器 Action,该控制器由程序员提供
复旦大学软件学院oOAD课程期末报告 Introduction to struts 2.0 相对 Struts1的 Action与 Servlet Ap紧紧耦合的弱点来说, Web Work的 Action则完全 与 Servlet API分离,因而该 Action更容易测试 ebWork的 Action可以与 Servlet APl分离,得益于它灵巧的设计,它使用一个拦截器 链,负责将用户请求数据转发到 Action,并负责将 Action的处理结果转换成对用户的响应。 当用户向web应用发送请求时,该请求经过 Action ContextCleanUp、 Site Mesh等过滤器 过滤,由 WebWork的核心控制器拦截,如果用户请求需要 WebWork的业务逻辑控制器处 理,该控制器则调用 Action映射器,该映射器将用户请求转发到对应的业务逻辑控制器。 值得注意的是,此时的业务逻辑控制器并不是开发者实现的控制器,而是 WebWork创建的 控制器代理。 创建控制器代理时, WebWork需要得到开发者定义的 xork xm配置文件,控制器代理 以用户实现的控制器作为目标,以拦截器链中的拦截器作为处理( Advice) 喜提示 WebWork中创建控制器代理的方式,就是一种AOP(面向切面编程)编程方式 只是这种AOP中的拦截器由系统提供,因此无需用户参与。如果读者需要获取更多关 于AOP编程的知识,请参阅AOP相关资料,或笔者所著的《spng2.0宝典》一书的 第6章。 开发者自己实现的业务逻辑控制器只是 WebWork业务控制器的目标一一这就是为什么 开发者自己实现的 Action可以与 Servlet ap分离的原因。当开发者自己的 Action处理完HT TP请求后,该结果只是一个普通字符串,该字符串将对应到指定的视图资源 指定的试图资源经过拦截器链的处理后,生成对客户端的响应输出。 上面整个过程的数据流图如图18所示。 与前面的 Struts1框架对比,不难发现 Webwork在很多地方确实更优秀。相对 Struts 1的种种缺点而言, WebWork存在如下优点
复旦大学软件学院 OOAD 课程 期末报告 Introduction to Struts2.0 Page | 8 相对 Struts 1 的 Action 与 Servlet API 紧紧耦合的弱点来说,WebWork 的 Action 则完全 与 Servlet API 分离,因而该 Action 更容易测试。 WebWork 的 Action 可以与 Servlet API 分离,得益于它灵巧的设计,它使用一个拦截器 链,负责将用户请求数据转发到 Action,并负责将 Action 的处理结果转换成对用户的响应。 当用户向 Web 应用发送请求时,该请求经过 ActionContextCleanUp、SiteMesh 等过滤器 过滤,由 WebWork 的核心控制器拦截,如果用户请求需要 WebWork 的业务逻辑控制器处 理,该控制器则调用 Action 映射器,该映射器将用户请求转发到对应的业务逻辑控制器。 值得注意的是,此时的业务逻辑控制器并不是开发者实现的控制器,而是 WebWork 创建的 控制器代理。 创建控制器代理时,WebWork 需要得到开发者定义的 xwork.xml 配置文件,控制器代理 以用户实现的控制器作为目标,以拦截器链中的拦截器作为处理(Advice)。 提示 WebWork 中创建控制器代理的方式,就是一种 AOP(面向切面编程)编程方式, 只是这种 AOP 中的拦截器由系统提供,因此无需用户参与。如果读者需要获取更多关 于 AOP 编程的知识,请参阅 AOP 相关资料,或笔者所著的《Spring 2.0 宝典》一书的 第 6 章。 开发者自己实现的业务逻辑控制器只是 WebWork 业务控制器的目标——这就是为什么 开发者自己实现的 Action 可以与 Servlet API 分离的原因。当开发者自己的 Action 处理完 HT TP 请求后,该结果只是一个普通字符串,该字符串将对应到指定的视图资源。 指定的试图资源经过拦截器链的处理后,生成对客户端的响应输出。 上面整个过程的数据流图如图 1.8 所示。 与前面的 Struts 1 框架对比,不难发现 WebWork 在很多地方确实更优秀。相对 Struts 1 的种种缺点而言,WebWork 存在如下优点:
复旦大学软件学院oOAD课程期末报告 HTTP请求 Web Work ActionContext CleanUp 其他过滤器(如 SitMesh等) Web Work的核心控制器: ServletDispather 调用 Action Action代理 L拦截1Actn映射器 拦截器2 配置管理器 Web Work标签库 拦截3 例如 HTML forms work xml 视图模板 颜色含义 拦截器 Free Marker Servlet过滤器 等等 拦截器 Webwork核心AP 开发者定义文件 拦截器 拦截器 HTTP响应 图1.8 WebWork的数据流图 (1) Action无需与 Servlet API耦合,更容易测试 相对于 Struts1框架中的 Action出现了大量 Servlet apl而言, WebWork的 Action更像 个普通Java对象,该控制器代码中没有耦合任何 Servlet apl。看下面的 Webwork的Acti 示例 public class LoginAction implements Action ∥/该字符串常量将作为 Action的返回值 private final static String LOGINFAIL="loginfail ∥1该Acon封装的两个请求参数
复旦大学软件学院 OOAD 课程 期末报告 Introduction to Struts2.0 Page | 9 图 1.8 WebWork 的数据流图 (1)Action 无需与 Servlet API 耦合,更容易测试 相对于 Struts 1 框架中的 Action 出现了大量 Servlet API 而言,WebWork 的 Action 更像 一个普通 Java 对象,该控制器代码中没有耦合任何 Servlet API。看下面的 WebWork 的 Acti on 示例: public class LoginAction implements Action { //该字符串常量将作为 Action 的返回值 private final static String LOGINFAIL="loginfail"; //该 Action 封装的两个请求参数
复旦大学软件学院oOAD课程期末报告 private String password private String username: / password请求参数对应的getr方法 public String getPasswordo return password / password请求参数对应的 setter方法 public void setPassword(string password) this password password / /username请求参数对应的 getter方法 public String getUsernameo return username / username请求参数对应的 setter方法 public void setUsername(String username) this username username ∥0处理用户请求的 execute方法 if ("yeeku".equalsIgnoreCase(getUsernameo) don) Page 10
复旦大学软件学院 OOAD 课程 期末报告 Introduction to Struts2.0 Page | 10 private String password; private String username; //password 请求参数对应的 getter 方法 public String getPassword() { return password; } //password 请求参数对应的 setter 方法 public void setPassword(String password) { this.password = password; } //username 请求参数对应的 getter 方法 public String getUsername() { return username; } //username 请求参数对应的 setter 方法 public void setUsername(String username) { this.username = username; } //处理用户请求的 execute 方法 public String execute() throws Exception { if ("yeeku".equalsIgnoreCase(getUsername()) && "password".equals(getPassword())) {