Top
  1. Struts2简介
  2. 使用Struts2
  3. 参数传递
  4. 资费列表

1. Struts2简介

1.1. 什么是Struts2

1.1.1. Struts2的概念

Struts2是轻量级的MVC框架,它主要解决了请求分发的问题,重心在于控制层和表现层。

轻量级,指的是Struts2具有较低的侵入性,就是它对我们业务代码的依赖程度很低,简单来说,在使用Struts2框架时,我们的业务代码中基本上不需要import它的包。

Struts2实现了MVC,满足了MVC设计思想。在我们使用Struts2做开发的时候,就相当于使用了MVC,这是Struts2自动帮助我们实现的,是默认的、隐含的,我们不需要再写特别的代码来实现MVC了。

作为一个框架,Struts2提供了一系列的API,我们可以使用它们来简化代码的编写,从而提升开发效率。这些API复用程度很高,对业务代码的依赖性也很小,甚至很多是Struts2自动调用的,因此在很大程度上,我们的开发变得可以复用。

Struts2解决请求分发的问题,我们会在后面为什么使用Struts2中讲解。

重心在控制层和表现层,是纵观整个Struts2理论课程来看的,从中我们会体会到这一点,随着大家对Struts2的逐步了解,届时我们再回顾这一点。

1.1.2. 什么是MVC

MVC是代码的分层思想,是软件设计领域经典的设计模式。它根据代码功能的不同,将一个软件的代码分为3部分,即模型、视图、控制器,这3部分代码的含义和功能如下。

1、M-Model 模型

模型(Model)的职责是负责业务逻辑。包含两层:业务数据和业务处理逻辑。比如实体类、DAO、Service都属于模型层。

2、V-View 视图

视图(View)的职责是负责显示界面和用户交互(收集用户信息)。属于视图的组件是不包含业务逻辑和控制逻辑的JSP。

3、C-Controller 控制器

控制器是模型层M和视图层V之间的桥梁,用于控制流程。比如:在Servlet项目中的单一控制器ActionServlet。

使用MVC,可以将代码按功能划分,从而为代码解耦,便于团队开发以及代码维护。

1.2. 为什么用Struts2

1.2.1. Struts2与Servlet对比

在学习框架之前,我们使用了Servlet开发WEB项目,并使用Servlet充当控制器来实现MVC。那么对比着Servlet这种方式,我们来分析一下Struts2具有的优缺点。

1、优点

2、缺点

凡事有利必有弊,框架也没有完美的,那么结合上述Struts2与Servlet对比的结论,我们在开发项目时会做出如下选择:如果对程序执行的效率要求的比较高的话,我们优先选择Servlet,反之如果对程序执行的效率要求一般的话,我们可以使用Struts2来提升开发效率并降低维护成本。

1.2.2. Struts2自身的优势

框架是一个项目的基础,因此必须在各方面都表现优良。我们在选择框架时,可以按照4个指标来衡量其优劣,即健壮性、易用性、扩展性、侵入性,这4个指标需要保持均衡,任何一个指标不合格,都可能对项目产生灾难性的影响。

Struts2作为时下最流行的框架,在这4个指标上,具有如下的表现:

1、健壮性(4★)

Struts2是一个成熟稳定的框架,目前最稳定的版本是2.1.8。

2、易用性(4★)

Struts2易学好用,几天即可上手。

3、扩展性(5★)

Struts2运用AOP的思想,使用拦截器来扩展业务控制器Action。

4、侵入性(4★)

Struts2对业务代码依赖性很低,基本不需要import它的包。

1.3. Struts2发展史

1.3.1. Struts1

Struts1是Apache软件基金会(ASF)赞助的一个开源项目。它通过采用JavaServlet/JSP技术,实现了基于Java EEWeb应用的MVC设计模式的应用框架,是MVC经典设计模式中的一个经典产品。由于Struts1结构简单小巧,十分易用,它的市场占有率一度超过20%。

但是,由于Struts1框架本身与JSP/Servlet耦合非常紧密,这制约了它的发展,以至于被后来的框架陆续赶超。 可以看出,Apache这样的顶尖组织推出的顶尖产品,也会由于侵入性的原因发展受限,直接印证了侵入性对于软件发展的影响之大。

1.3.2. WebWork

WebWork是由OpenSymphony组织开发的,是建立在称为XWork的Command模式框架之上的强大的MVC框架。 由于WebWork晚于Struts1,技术上更为先进。

但是,由于组织知名度、人们的习惯等原因,WebWork市场反响远不及Struts1。

1.3.3. Struts2

Struts2是Struts的下一代产品,是在Struts1和WebWork的技术基础上进行了合并的全新MVC框架。 它吸收了WebWork的先进技术,延续了Struts1的市场影响力,最终发展成为最流行的框架。

对于Struts2与Struts1的关系,我们要有本质上的认知,这一点也常被当做面试题来询问,即Struts2与Struts1差别巨大,不能理解为Struts1的升级版。 Struts2以Xwork为核心,可以理解为WebWork的升级版。

2. 使用Struts2

2.1. Struts2与MVC

2.1.1. Servlet如何实现MVC

我们使用Servlet充当控制器来实现MVC,这种开发模式称之为JSP Model 2,这种模式下,Servlet负责处理请求,具体代码执行过程如下图:

图-1

2.1.2. Spring如何实现MVC

复习Spring实现MVC的方式,如下图

图-2

2.1.3. Struts2如何实现MVC

Struts2实现MVC的方式基本上与Spring一致,主要是实现方式上有一些差异,Struts2采用filter+Action来充当控制器(Controller)。其中filter是前端控制器,负责处理请求的分发,它会根据配置文件struts.xml中预置的内容,把每一类请求分发给特定的Action类,而每一个Action类负责处理一类请求。这样不同的请求,通过filter分发给了不同的Action类来处理,从而将请求的处理自然的拆开,大大降低了控制器(Controller)处理请求代码的耦合度,提升了代码的可维护性。

图-3

2.2. Struts2的使用步骤

2.2.1. 大致步骤

针对Struts2实现MVC的图示,我们来分析一下Struts2开发所涉及的组件及大致步骤,参考图-4,具体步骤请见后面的详细步骤说明。

1、Struts2框架下,请求需要提交给filter,而这个filter需要在web.xml中进行配置。对于filter这个组件不需要我们自己实现,Struts2已经提供了一个默认的filter供我们使用,该filter名为org.apache.struts2.dispatcher.ng.filter. StrutsPrepareAndExecuteFilter。

2、filter的目的是将请求分发给业务控制器Action,而找到每类请求对应的Action需要提前预置在struts.xml中,即我们需要在struts.xml中预置请求与Action的关系。

3、filter依赖struts.xml找到Action类之后,会自动实例化Action,并调用Action的业务方法,因此我们需要针对每类请求创建Action,并在业务方法中实现当前业务的逻辑代码。

4、Action在实现业务逻辑时,往往需要调用访问数据库、调用业务组件等,因此我们需要提前实现DAO、业务组件等组件。

5、Action处理完业务逻辑之后,filter需要再将请求转发给JSP,由JSP负责显示处理的结果。因此,我们需要在struts.xml中配置每类Action转发的JSP,并创建JSP实现显示代码。

图-4

2.2.2. 1、创建WEB项目

MyEclipse中,点击File ( New ( WebProject。

图-5

在弹出框中输入项目名。

图-6

2.2.3. 2、导入Struts2核心包

导入Struts2最小范围核心包(5个),将这些包复制到新建项目的/WEB-INF/lib目录下。

图-7

2.2.4. 3、配置前端控制器

Struts2使用filter来充当前端控制器,因此在web.xml中配置一个filter即可。 Struts2预置了该filter的实现类,来自于Struts2的jar包,名为 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter。

引入Struts2框架后,希望所有的请求都由它的filter来处理,因此使用“/*”来指定该filter处理所有的请求。

	<filter>
		<filter-name>Struts2</filter-name>
		<filter-class>
		</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>Struts2</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

2.2.5. 4、创建struts.xml

在src下,创建名为struts.xml的配置文件。

图-8

struts.xml的格式

配置struts.xml的版本信息及DTD引用。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
    "/struts.apache.org/dtds/struts-2.1.7.dtd">

2.2.6. 5、编写业务控制器Action

创建业务控制器组件,通常命名为XxxAction,该组件是一个满足JavaBean规范的类。

在Action中定义业务方法,要满足下列条件

编写业务方法

public class HiloAction {
	public String execute() {
		System.out.println("调用业务方法...");
		return "success";
	}
}

2.2.7. 6、编写JSP页面

创建JSP页面 ,在页面上写显示部分的代码。

2.2.8. 7、配置struts.xml

在struts.xml中配置请求与Action的关系,并在action下,通过result设置转发的页面。

3. 参数传递

3.1. 为什么要传递参数

3.1.1. 为什么要传递参数

在项目开发中,我们经常要将页面表单中的数据发送给服务端,服务端处理完业务逻辑后,也需要将一些数据发送给页面,因此我们需要在服务端与页面之间传输数据,即传递参数,如

由于采用了MVC的设计模式,因此页面要与服务端的控制器来互传参数。使用Servlet开发时页面要与Servlet互传参数,使用Struts2开发时页面要与业务控制器Action来互传参数。

3.2. 页面向Action传参

3.2.1. 基本属性注入

我们可以直接将表单中的数据项传递给Action,而Action只需要提供基本属性来接收参数即可,这种传参的方式称为基本属性注入。

原理如下图:

图-9

Struts2提供了API,可以自动从请求参数中读取出表单数据,然后将这些数据注入给Action,此种方式需要做如下代码开发:

1、在Action中定义基本属性,并提供set方法。

图-10

2、表单中文本框指定表达式“属性名”。

图-11

3.2.2. 域模型注入

如果表单上的数据项很多,我们可以将表单中的数据项封装成实体对象后传递给Action,而Action需要提供实体对象属性来接收参数,这种传参的方式称为域模型注入。

原理如下图:

图-12

Struts2提供了API,可以从请求参数中读取出数据项,然后自动实例化实体对象,并将表单数据注入到实体对象中,然后再将实体对象注入给Action,此种方式需要做如下代码开发:

图-13

图-14

3、在表单中文本框指定表达式“对象名.属性名”

图-15

3.3. 页面从Action取值

3.3.1. 使用EL表达式显示Action值

在JSP页面上,我们可以采用EL表达式来取出并显示Action中的数据。根据Action中属性注入形式的不同,EL表达式取值也有所区别:

1、取基本属性值

${ 属性名 }

2、取域模型对象值

${ 对象名.属性名 }

3、取驱动模型对象值

${ 属性名 }

结论 :

取值时EL表达式的写法,与注入时表达式的写法一致。

图-16

3.3.2. 使用OGNL表达式显示Action值

Struts2虽然支持EL表达式,但实际上OGNL表达式才是其默认使用的表达式。

OGNL表达式功能更强大,后面会讲述其原理及使用。

4. 资费列表

4.1. 需求讲解

4.1.1. 需求描述

资费管理模块,维护的是电信业务中资费的标准,类似于办理手机号时选择的套餐。 而查询功能,是使用列表的方式将维护好的资费数据进行展现。

对于NetCTOSS项目的详细需求,我们会在后面的项目课中讲解。

4.1.2. 查询功能要求

按列表形式展现数据,只显示一部分列,注意以下几点:

4.2. 开发思路

4.2.1. 查询功能需要几种请求

在实际项目开发时,开发思路要比编写代码更重要,面试时对于项目层面企业往往看重的也是这方面。只有具备的清晰的项目分析及开发思路,在企业中我们才能将需求转化为代码,为企业创造价值。

拿到一份需求,我们分析如何来进行开发,首先是从分析请求的种类开始,然后针对每一类请求,分别加以实现。对于当前的查询需求,我们来分析一下它的请求种类,首先来分析一下查询功能以哪几种方式触发:

上述2种方式都是针对资费的查询,只是条件有所变化,因此可以当做一种请求来处理。即我们只需要创建一个Action来处理查询请求即可,只是这个Action需要同时能处理这2种触发请求的方式,具体说来只是输入的参数不同而已。

4.2.2. 查询功能请求的过程

针对这类查询的请求,在Struts2框架下,我们应该按照如下的步骤来实现其功能:

图-17

在这个过程中,需要涉及的代码和组件如下:

上面涉及的代码中,DBUtil可以复用,Entity和Action比较简单。而struts.xml、DAO、JSP是我们需要频繁编写的内容,需要重点处理。

4.3. 开发步骤

针对上面提及的要编写的代码,根据他们彼此依赖的关系,我们推荐按照下面的步骤进行开发。

4.3.1. 1、Entity

根据资费表COST,创建与其对应的实体类Cost。

图-18

注意以下几点:

4.3.2. 2、DAO

编写数据库访问组件DAO,步骤如下:

1、创建资费模块DAO接口ICostDAO.java

2、声明查询资费方法

  1. 创建自定义异常DAOException
  2. List<Cost> findAll() throws DAOException;

3、创建DAO实现类CostDAOImpl,实现接口ICostDAO

  1. 定义SQL
  2. 调用DBUtil获取连接
  3. 执行SQL查询
  4. 处理结果集,封装成Cost对象,并加入集合
  5. 关闭连接
  6. 返回集合

4、在main方法中测试该方法。

注:

在实际项目中,往往基本的业务逻辑不变,而实现业务的手段会不断革新。比如当前我们使用JDBC来实现数据库的访问,可能将来引入框架来实现。届时实现的方式与当前完全不同,所以我们不希望直接在原有代码基础上修改,而是会重新写相关实现代码。因此我们可以将业务抽象出来,体现在接口中,而每一种实现手段就是一个接口的实现类。在其他调用DAO的地方只需要调用接口即可,其实例可以通过工厂来维护,这样一旦实现手段发生变化,那无非是修改工厂实例的创建,而业务代码中对DAO的引用处由于使用的是接口,因此无需改变。

方法通常抛出自定义异常。我们开发的基本要求是,捕获到异常之后需要做正确的处理,而不是直接抛出置之不理,即我们需要对自己捕获到的异常负责。那么在团队开发时,同事之间可能互相调用别人的组件,调用自己人的组件时如果发现是团队自定义的异常,则说明同事已经处理过了,那么这样你可以不必再次处理,直接抛出即可,否则就要求你处理这个异常。因此我们定义自定义异常,目的是让同事可以识别出这个异常是被我们处理过的异常,他是不必处理的,从而简化了同事的代码。

4.3.3. 3、Action

1、创建处理查询请求的Action,即FindCostAction

2、定义输入属性

3、定义输出属性

4、根据输入算输出

4.3.4. 4、struts.xml

1、在struts.xml中,配置一个新的package,专门用于封装资费模块中的Action

2、在当前package下,注册查询的Action。

4.3.5. 5、JSP

1、创建资费查询页面,如find_cost.jsp。

2、将资费查询静态HTML代码复制到该JSP中。

3、将table中的行删除,保留标题行(第一行)和内容首行(第二行)。

4、使用JSTL循环标签,循环输出内容首行(第二行)。

5、在内容首行中,使用EL表达式输出各列的值 。