Struts2是轻量级的MVC框架,它主要解决了请求分发的问题,重心在于控制层和表现层。
轻量级,指的是Struts2具有较低的侵入性,就是它对我们业务代码的依赖程度很低,简单来说,在使用Struts2框架时,我们的业务代码中基本上不需要import它的包。
Struts2实现了MVC,满足了MVC设计思想。在我们使用Struts2做开发的时候,就相当于使用了MVC,这是Struts2自动帮助我们实现的,是默认的、隐含的,我们不需要再写特别的代码来实现MVC了。
作为一个框架,Struts2提供了一系列的API,我们可以使用它们来简化代码的编写,从而提升开发效率。这些API复用程度很高,对业务代码的依赖性也很小,甚至很多是Struts2自动调用的,因此在很大程度上,我们的开发变得可以复用。
Struts2解决请求分发的问题,我们会在后面为什么使用Struts2中讲解。
重心在控制层和表现层,是纵观整个Struts2理论课程来看的,从中我们会体会到这一点,随着大家对Struts2的逐步了解,届时我们再回顾这一点。
MVC是代码的分层思想,是软件设计领域经典的设计模式。它根据代码功能的不同,将一个软件的代码分为3部分,即模型、视图、控制器,这3部分代码的含义和功能如下。
1、M-Model 模型
模型(Model)的职责是负责业务逻辑。包含两层:业务数据和业务处理逻辑。比如实体类、DAO、Service都属于模型层。
2、V-View 视图
视图(View)的职责是负责显示界面和用户交互(收集用户信息)。属于视图的组件是不包含业务逻辑和控制逻辑的JSP。
3、C-Controller 控制器
控制器是模型层M和视图层V之间的桥梁,用于控制流程。比如:在Servlet项目中的单一控制器ActionServlet。
使用MVC,可以将代码按功能划分,从而为代码解耦,便于团队开发以及代码维护。
在学习框架之前,我们使用了Servlet开发WEB项目,并使用Servlet充当控制器来实现MVC。那么对比着Servlet这种方式,我们来分析一下Struts2具有的优缺点。
1、优点
2、缺点
凡事有利必有弊,框架也没有完美的,那么结合上述Struts2与Servlet对比的结论,我们在开发项目时会做出如下选择:如果对程序执行的效率要求的比较高的话,我们优先选择Servlet,反之如果对程序执行的效率要求一般的话,我们可以使用Struts2来提升开发效率并降低维护成本。
框架是一个项目的基础,因此必须在各方面都表现优良。我们在选择框架时,可以按照4个指标来衡量其优劣,即健壮性、易用性、扩展性、侵入性,这4个指标需要保持均衡,任何一个指标不合格,都可能对项目产生灾难性的影响。
Struts2作为时下最流行的框架,在这4个指标上,具有如下的表现:
1、健壮性(4★)
Struts2是一个成熟稳定的框架,目前最稳定的版本是2.1.8。
2、易用性(4★)
Struts2易学好用,几天即可上手。
3、扩展性(5★)
Struts2运用AOP的思想,使用拦截器来扩展业务控制器Action。
4、侵入性(4★)
Struts2对业务代码依赖性很低,基本不需要import它的包。
Struts1是Apache软件基金会(ASF)赞助的一个开源项目。它通过采用JavaServlet/JSP技术,实现了基于Java EEWeb应用的MVC设计模式的应用框架,是MVC经典设计模式中的一个经典产品。由于Struts1结构简单小巧,十分易用,它的市场占有率一度超过20%。
但是,由于Struts1框架本身与JSP/Servlet耦合非常紧密,这制约了它的发展,以至于被后来的框架陆续赶超。 可以看出,Apache这样的顶尖组织推出的顶尖产品,也会由于侵入性的原因发展受限,直接印证了侵入性对于软件发展的影响之大。
WebWork是由OpenSymphony组织开发的,是建立在称为XWork的Command模式框架之上的强大的MVC框架。 由于WebWork晚于Struts1,技术上更为先进。
但是,由于组织知名度、人们的习惯等原因,WebWork市场反响远不及Struts1。
Struts2是Struts的下一代产品,是在Struts1和WebWork的技术基础上进行了合并的全新MVC框架。 它吸收了WebWork的先进技术,延续了Struts1的市场影响力,最终发展成为最流行的框架。
对于Struts2与Struts1的关系,我们要有本质上的认知,这一点也常被当做面试题来询问,即Struts2与Struts1差别巨大,不能理解为Struts1的升级版。 Struts2以Xwork为核心,可以理解为WebWork的升级版。
我们使用Servlet充当控制器来实现MVC,这种开发模式称之为JSP Model 2,这种模式下,Servlet负责处理请求,具体代码执行过程如下图:
图-1
复习Spring实现MVC的方式,如下图
图-2
Struts2实现MVC的方式基本上与Spring一致,主要是实现方式上有一些差异,Struts2采用filter+Action来充当控制器(Controller)。其中filter是前端控制器,负责处理请求的分发,它会根据配置文件struts.xml中预置的内容,把每一类请求分发给特定的Action类,而每一个Action类负责处理一类请求。这样不同的请求,通过filter分发给了不同的Action类来处理,从而将请求的处理自然的拆开,大大降低了控制器(Controller)处理请求代码的耦合度,提升了代码的可维护性。
图-3
针对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
MyEclipse中,点击File ( New ( WebProject。
图-5
在弹出框中输入项目名。
图-6
导入Struts2最小范围核心包(5个),将这些包复制到新建项目的/WEB-INF/lib目录下。
图-7
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>
在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" "http://struts.apache.org/dtds/struts-2.1.7.dtd">
创建业务控制器组件,通常命名为XxxAction,该组件是一个满足JavaBean规范的类。
在Action中定义业务方法,要满足下列条件
编写业务方法
public class HiloAction { public String execute() { System.out.println("调用业务方法..."); return "success"; } }
创建JSP页面 ,在页面上写显示部分的代码。
在struts.xml中配置请求与Action的关系,并在action下,通过result设置转发的页面。
在项目开发中,我们经常要将页面表单中的数据发送给服务端,服务端处理完业务逻辑后,也需要将一些数据发送给页面,因此我们需要在服务端与页面之间传输数据,即传递参数,如
由于采用了MVC的设计模式,因此页面要与服务端的控制器来互传参数。使用Servlet开发时页面要与Servlet互传参数,使用Struts2开发时页面要与业务控制器Action来互传参数。
我们可以直接将表单中的数据项传递给Action,而Action只需要提供基本属性来接收参数即可,这种传参的方式称为基本属性注入。
原理如下图:
图-9
Struts2提供了API,可以自动从请求参数中读取出表单数据,然后将这些数据注入给Action,此种方式需要做如下代码开发:
1、在Action中定义基本属性,并提供set方法。
图-10
2、表单中文本框指定表达式“属性名”。
图-11
如果表单上的数据项很多,我们可以将表单中的数据项封装成实体对象后传递给Action,而Action需要提供实体对象属性来接收参数,这种传参的方式称为域模型注入。
原理如下图:
图-12
Struts2提供了API,可以从请求参数中读取出数据项,然后自动实例化实体对象,并将表单数据注入到实体对象中,然后再将实体对象注入给Action,此种方式需要做如下代码开发:
图-13
图-14
3、在表单中文本框指定表达式“对象名.属性名”
图-15
在JSP页面上,我们可以采用EL表达式来取出并显示Action中的数据。根据Action中属性注入形式的不同,EL表达式取值也有所区别:
1、取基本属性值
${ 属性名 }
2、取域模型对象值
${ 对象名.属性名 }
3、取驱动模型对象值
${ 属性名 }
结论 :
取值时EL表达式的写法,与注入时表达式的写法一致。
图-16
Struts2虽然支持EL表达式,但实际上OGNL表达式才是其默认使用的表达式。
OGNL表达式功能更强大,后面会讲述其原理及使用。
资费管理模块,维护的是电信业务中资费的标准,类似于办理手机号时选择的套餐。 而查询功能,是使用列表的方式将维护好的资费数据进行展现。
对于NetCTOSS项目的详细需求,我们会在后面的项目课中讲解。
按列表形式展现数据,只显示一部分列,注意以下几点:
在实际项目开发时,开发思路要比编写代码更重要,面试时对于项目层面企业往往看重的也是这方面。只有具备的清晰的项目分析及开发思路,在企业中我们才能将需求转化为代码,为企业创造价值。
拿到一份需求,我们分析如何来进行开发,首先是从分析请求的种类开始,然后针对每一类请求,分别加以实现。对于当前的查询需求,我们来分析一下它的请求种类,首先来分析一下查询功能以哪几种方式触发:
上述2种方式都是针对资费的查询,只是条件有所变化,因此可以当做一种请求来处理。即我们只需要创建一个Action来处理查询请求即可,只是这个Action需要同时能处理这2种触发请求的方式,具体说来只是输入的参数不同而已。
针对这类查询的请求,在Struts2框架下,我们应该按照如下的步骤来实现其功能:
图-17
在这个过程中,需要涉及的代码和组件如下:
上面涉及的代码中,DBUtil可以复用,Entity和Action比较简单。而struts.xml、DAO、JSP是我们需要频繁编写的内容,需要重点处理。
针对上面提及的要编写的代码,根据他们彼此依赖的关系,我们推荐按照下面的步骤进行开发。
根据资费表COST,创建与其对应的实体类Cost。
图-18
注意以下几点:
编写数据库访问组件DAO,步骤如下:
1、创建资费模块DAO接口ICostDAO.java
2、声明查询资费方法
3、创建DAO实现类CostDAOImpl,实现接口ICostDAO
4、在main方法中测试该方法。
注:
在实际项目中,往往基本的业务逻辑不变,而实现业务的手段会不断革新。比如当前我们使用JDBC来实现数据库的访问,可能将来引入框架来实现。届时实现的方式与当前完全不同,所以我们不希望直接在原有代码基础上修改,而是会重新写相关实现代码。因此我们可以将业务抽象出来,体现在接口中,而每一种实现手段就是一个接口的实现类。在其他调用DAO的地方只需要调用接口即可,其实例可以通过工厂来维护,这样一旦实现手段发生变化,那无非是修改工厂实例的创建,而业务代码中对DAO的引用处由于使用的是接口,因此无需改变。
方法通常抛出自定义异常。我们开发的基本要求是,捕获到异常之后需要做正确的处理,而不是直接抛出置之不理,即我们需要对自己捕获到的异常负责。那么在团队开发时,同事之间可能互相调用别人的组件,调用自己人的组件时如果发现是团队自定义的异常,则说明同事已经处理过了,那么这样你可以不必再次处理,直接抛出即可,否则就要求你处理这个异常。因此我们定义自定义异常,目的是让同事可以识别出这个异常是被我们处理过的异常,他是不必处理的,从而简化了同事的代码。
1、创建处理查询请求的Action,即FindCostAction
2、定义输入属性
3、定义输出属性
4、根据输入算输出
1、在struts.xml中,配置一个新的package,专门用于封装资费模块中的Action
2、在当前package下,注册查询的Action。
1、创建资费查询页面,如find_cost.jsp。
2、将资费查询静态HTML代码复制到该JSP中。
3、将table中的行删除,保留标题行(第一行)和内容首行(第二行)。
4、使用JSTL循环标签,循环输出内容首行(第二行)。
5、在内容首行中,使用EL表达式输出各列的值 。