Struts2_day01
案例: 使用Struts2完成客户列表显示的功能.
1.1 案例需求
1.1.1 需求概述
CRM系统中有客户的显示的功能,效果如图:
我们实际的开发中会使用Struts2作为WEB层的架构。
1.2 相关知识点
1.2.1 Struts2框架的概述
Struts2是一种基于MVC模式的轻量级Web框架,它自问世以来,就受到了广大Web开发者的关注,并广泛应用于各种企业系统的开发中。目前掌握Struts2框架几乎成为Web开发者的必备技能之一。接下来将针对Struts2的特点、安装以及执行流程等内容进行详细的讲解。
1.2.1.1 什么是Struts2
在介绍Struts2之前,先来认识一下Struts1。Struts1是最早的基于MVC模式的轻量级Web框架,它能够合理的划分代码结构,并包含验证框架、国际化框架等多种实用工具框架。但是随着技术的进步,Struts1的局限性也越来越多的暴露出来。为了符合更加灵活、高效的开发需求,Struts2框架应运而生。
Struts2是Struts1的下一代产品,是在 Struts1和WebWork技术的基础上进行合并后的全新框架(WebWork是由OpenSymphony组织开发的,致力于组件化和代码重用的J2EE Web框架,它也是一个MVC框架)。虽然Struts2的名字与Struts1相似,但其设计思想却有很大不同。实质上,Struts2是以WebWork为核心的,它采用拦截器的机制来处理用户的请求。这样的设计也使得业务逻辑控制器能够与ServletAPI完全脱离开,所以Struts2可以理解为WebWork的更新产品。
Struts2拥有优良的设计和功能,其优势具体如下:
- 项目开源,使用及拓展方便,天生优势。
- 提供Exception处理机制。
- Result方式的页面导航,通过Result标签很方便的实现重定向和页面跳转。
- 通过简单、集中的配置来调度业务类,使得配置和修改都非常容易。
- 提供简单、统一的表达式语言来访问所有可供访问的数据。
- 提供标准、强大的验证框架和国际化框架。
- 提供强大的、可以有效减少页面代码的标签。
- 提供良好的Ajax支持。
- 拥有简单的插件,只需放入相应的JAR包,任何人都可以扩展Struts2框架,比如自定义拦截器、自定义结果类型、自定义标签等,为Struts2定制需要的功能,不需要什么特殊配置,并且可以发布给其他人使用。
- 拥有智能的默认设置,不需要另外进行繁琐的设置。使用默认设置就可以完成大多数项目程序开发所需要的功能。
上面列举的就是Struts2的一系列技术优势,只需对它们简单了解即可,在学习了后面的知识后,会慢慢对这些技术优势有更好的理解和体会。
那么除了Struts2之外,还有那些优秀的WEB层框架呢?
1.2.1.2 常见的WEB层的框架
- Struts2
- Struts1
- Webwork
- SpringMVC
WEB层框架都会有一个特点,就是基于前端控制器模式实现的。
1.2.1.3 WEB层的框架都会基于前端控制器的模式:
什么是前端控制器模式呢?我们来看下图,在图中传统方式的开发,有一次请求就会对应一个Servlet。这样会导致出现很多Servlet。而Struts2将所有的请求都先经过一个前端控制器,在前端控制器中实现框架的部分功能,剩下具体操作要提交到具体的Action中。那么所有的请求都会经过前端控制器,那用什么来实现前端控制器呢?过滤器就是最好的一个实现方式,因为需要所有的请求都可以被过滤器拦截,然后在过滤器中实现部分的功能。所以Struts2的前端控制器也是有过滤器来实现的。
了解这些之后,我们来进行一个Struts2的快速入门,来感受一下Struts2的优势吧。
1.2.2 Struts2快速入门:
1.2.2.1 下载Struts2的开发包:
Struts2的官网: https://struts.apache.org/
我们这里使用struts-2.3.24版本的Struts2为大家讲解,这个版本可以在当日授课资料中找到。下载好了以后需要进行解压。
1.2.2.2 解压Struts2的开发包:
解压后的目录结构如下:
Struts2的文件夹结构
从图中可以看出,展示的是解压后的Struts2.3.24的目录结构,为了让大家对每个目录的内容和作用有一定的了解,接下来针对这些目录进行简单介绍,具体如下:
- apps:该文件夹存用于存放官方提供的Struts2示例程序,这些程序可以作为学习者的学习资料,可为学习者提供很好的参照。各示例均为war文件,可以通过zip方式进行解压。
- docs:该文件夹用于存放官方提供的Struts2文档,包括Struts2的快速入门、Struts2的文档,以及API文档等内容。
- lib:该文件夹用于存放Struts2的核心类库,以及Struts2的第三方插件类库。
- src:该文件夹用于存放该版本Struts2框架对应的源代码。
有了Struts2的开发环境,接下来我们可以进行Struts2的开发了。
1.2.2.3 创建一个web工程引入相应jar包:
首先,需要我们创建一个WEB工程,引入相关的jar包文件。引入哪些jar包呢?将struts-2.3.24框架目录中的lib文件夹打开,得到Struts2 开发中可能用到的所有JAR包(此版本有107个JAR包)。实际的开发中,我们根本不用引入这么多的jar包。
要进行struts2的基本的开发,可以参考struts-2.3.24中的apps下的一些示例代码,其中struts2-blank.war是一个struts2的空的工程。我们只需要将struts2-blank.war解压后进入到WEB-INF下的lib中查看。
这些包就是struts2的基本的开发包了,那么这些包都是什么含义呢
Struts2 项目依赖的基础JAR包说明
文件名 | 说明 |
---|---|
asm-3.3.jar | 操作java字节码的类库 |
asm-commons-3.3.jar | 提供了基于事件的表现形式 |
asm-tree-3.3.jar | 提供了基于对象的表现形式 |
struts2-core-2.3.24.jar | Struts2框架的核心类库 |
xwork-core-2.3.24.jar | WebWork核心库,Struts2的构建基础 |
ognl-3.0.6.jar | 对象图导航语言(Object Graph Navigation Language),struts2框架通过其读写对象的属性 |
freemarker-2.3.22.jar | Struts2标签模板使用的类库 |
javassist-3.11.0.GA.jar | javaScript字节码解释器 |
commons-fileupload-1.3.1.jar | Struts2文件上传组件依赖包 |
commons-io-2.2.jar | Struts2的输入输出,传文件依赖的jar |
commons-lang-2.4.jar | 包含一些数据类型工具,是对java.lang包的增强 |
log4j-api-2.2.jar | Struts2的日志管理组件依赖包的api |
log4j-core-2.2.jar | Struts2的日志管理组件依赖包 |
从表可以看出, 此版本的Struts2项目所依赖的基础JAR包共13个。Struts2根据版本的不同所依赖的基础JAR包可能不完全相同,不过基本上变化不大,读者可以视情况而定。
需要注意的是,通常使用Struts2的Web项目并不需要利用到Struts2的全部JAR包,因此没有必要一次将Struts2的lib目录下的全部JAR包复制到Web项目的WEB-INF/lib路径下,而是根据需要,再添加相应的JAR包。
那么Struts2的基本jar包已经引入完成了,我们使用Struts2都是从页面发起请求到服务器,再由服务器处理请求,响应到页面的这个过程。接下来我们就从页面开发进行Struts2的开发吧。
1.2.2.4 创建一个页面:放置一个链接.
首先需要在WebContent下创建一个目录demo1,在demo1下创建一个新的jsp。在jsp中编写一个Action的访问路径。
<h1>Struts2的入门案例</h1>
<a href="${pageContext.request.contextPath }/strutsDemo1.action">访问Struts2的Action.</a>
点击该链接,需要提交请求到服务器的Action。那么接下来需要编写Action去处理请求。
1.2.2.5 编写一个Action:
在src下创建一个包cn.com.javahelp.struts2.action,在该包下新建一个StrutsDemo1的类。在这个类中编写一个公有的,返回值为String类型的方法,这个方法的名称叫做execute,且该方法没有任何参数。(因为这个方法最终要被反射执行)
public class StrutsDemo1 {
/**
* 提供一个默认的执行的方法:execute
*/
public String execute(){
System.out.println("StrutsDemo1中的execute执行了...");
return null;
}
}
Action类编写好了以后,Struts2框架如何识别它就是一个Action呢,那么我们需要对Action类进行配置。
1.2.2.6 完成Action的配置:
这个时候,我们还需要观察apps中的示例代码,在WEB-INF的classes中,有一个名称为struts.xml的文件,这个文件就是struts2的配置文件。
我们在开发中需要将struts.xml文件引入到工程的src下,因为src下内容发布到web服务器中就是WEB-INF下的classes中。将struts.xml中的原有的内容删除掉就,然后配置上自己编写的Action类就可以了。
配置内容如下:里面的具体的标签,我们会在后面的地方详细介绍。
<struts>
<!-- 配置一个包:package -->
<package name="demo1" extends="struts-default" namespace="/">
<!-- 配置Action -->
<action name="strutsDemo1" class="cn.com.javahelp.struts2.action.StrutsDemo1">
</action>
</package>
</struts>
Action类已经配置好了,配置好了以后大家考虑一下,现在是否可以执行呢?其实现在还不行,因为之前我们介绍过,WEB层的框架都有一个特点就是基于前端控制器的模式,这个前端控制器是由过滤器实现的,所以我们需要配置Struts2的核心过滤器。这个过滤器的名称是StrutsPrepareAndExecuteFilter。
1.2.2.7 配置核心过滤器:
Struts2框架要想执行,所有的请求都需要经过这个前端控制器(核心过滤器),所以需要配置这个核心过滤器。因为这个过滤器完成了框架的部分的功能。那么我们接下来对过滤器进行配置。我们打开web.xml,在web.xml中进行如下配置。
<!-- 配置Struts2的核心过滤器:前端控制器 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
那么到这,我们程序就可以执行了,但是到了Action以后,页面并没有跳转,只是会在控制台输出Action中的内容,往往我们在实际的开发中,处理了请求以后,还需要进行页面的跳转,如何完成Struts2的页面的跳转呢?
这个时候我们需要修改Action类中的execute方法的返回值了,这个方法返回了一个String类型,这个String类型的值就是一个逻辑视图(逻辑视图:相当于对一个真实的页面,取了一个别名。),那么我们来修改一个Action类。
1.2.2.8 修改Action,将方法设置一个返回值:
修改Action中的execute方法的返回值,我们先任意给其返回一个字符串,比如返回一个success的字符串。这个字符串就作为一个逻辑视图名称。
public class StrutsDemo1 {
/**
* 提供一个默认的执行的方法:execute
*/
public String execute(){
System.out.println("StrutsDemo1中的execute执行了...");
return "success";
}
}
返回一个success的字符串了,这个success的字符串又怎么能代表一个页面呢?这个时候我们又要对struts2进行配置了。这个时候需要修改struts.xml,对Action的配置进行完善。
1.2.2.9 修改struts.xml
打开struts.xml文件,对<action>标签进行完善,在<action>标签中配置一个<result>标签,这个标签中的name属性就是之前方法返回的那个字符串的逻辑视图名称success。标签内部就是跳转的页面。
<struts>
<!-- 配置一个包:package -->
<package name="demo1" extends="struts-default" namespace="/">
<!-- 配置Action -->
<action name="strutsDemo1" class="cn.com.javahelp.struts2.action.StrutsDemo1">
<!-- 配置结果页面的跳转 -->
<result name="success">/demo1/demo2.jsp</result>
</action>
</package>
</struts>
到这,我们的整个程序就执行完毕了。我们可以启动服务器并且测试项目。
打开页面:
页面跳转:
到这里Struts2的入门案例已经编写完成了,那么我们来总结下Struts2的整个流程。
1.2.3 Struts2开发流程分析
1.2.3.1 Struts2的执行流程:
从客户端发送请求过来 先经过前端控制器(核心过滤器StrutsPrepareAndExecuteFilter)过滤器中执行一组拦截器(一组拦截器 就会完成部分功能代码),到底哪些拦截器执行了呢,在Struts2中定义很多拦截器,在其默认栈中的拦截器会得到执行,这个我们可以通过断点调试的方式测试,拦截器执行完成以后,就会执行目标Action,在Action中返回一个结果视图,根据Result的配置进行页面的跳转.
以上内容是Struts2的执行流程,但是可能大家在编写测试的时候会遇到一些问题,比如在编写XML配置文件的时候没有提示。
1.2.3.2 配置struts.xml中的提示(在不联网情况下)
开发发过程中如果可以上网,struts.xml会自动缓存dtd,提供提示功能。如果不能够上网,则需要我们手动配制本地dtd,这样才能够使struts.xml产生提示。具体配置方法如下:
1.首先,在Eclipse中,依次点击工具栏中的window和下方的Preferences弹出对话框。然后在左侧的搜索框中输入xml,显示出所有与xml有关的选项后,点击XML Catalog,会出现如图1-11所示界面。
XML Catalog窗口
2.接下来在已下载的Struts2解压包中的lib包中找到其核心包struts2-core-2.3.24.jar,使用解压工具将其解压成文件夹形式。解压后,我们会看到文件夹中有几个以.dtd结尾的文件。我们所使用的是struts-2.3.dtd。
3.将此dtd使用EditPlus等文本工具打开后,找到图中选中内容,将其http地址复制。如图1-12所示。
struts-2.3.dtd文件
4.点击Eclipse中弹出对话框中右侧的Add按钮,此时会弹出Add XML Catalog Element界面。点击File System按钮,找到本地刚才解压文件夹中的struts-2.3.dtd,然后将界面中的Key type改为URI,并将刚才复制的地址黏贴到Key中。如图所示。
Add XML Catalog Element窗口
在图中点击OK后,关闭已打开的struts.xml,然后再重新打开struts.xml,此时再编写struts.xml内容的时候,就会有提示了。
那么提示已经会配置了,也就可以进行Struts2的开发了,开发过程中需要自己配置struts.xml文件,在这个文件中就有很多常见的配置。接下来我们就来学习Struts2的常见配置。
1.2.4 Struts2的常见配置
通过前面的学习,我们已对Struts2框架有了一定的了解,但是对于各知识点的细节,还需要进一步地学习。接下来,将针对Struts2中struts.xml文件的配置等内容进行详细的讲解。在学习具体详细的配置之前,要对Struts2的配置文件的加载顺序有一定的了解,这样对后面学习Struts2的配置都是有帮助的。
1.2.4.1 Struts2的配置文件的加载顺序:
每次从客户端发送请求到服务器都要先经过Struts2的核心过滤器StrutsPrepareAndExecuteFilter,这个过滤器有两个功能:预处理和执行。在预处理中主要就是来加载配置文件的。对应的是过滤器中的init方法,而执行是用来执行一组拦截器完成部分功能的,对应的是过滤器的doFilter方法。所以我们如果要去了解Struts2的配置文件的加载顺序,那么我们需要查询过滤器的init方法。
在init方法中,调用了init的initDispatcher的方法来加载配置文件,进入到该代码中:
我们会发现这个方法又调用了dispatcher的init方法。进入init方法内部:
这一系列的代码就是用来加载Struts2的配置文件的。
init_DefaultProperties(); // [1]
加载org.apache.struts.default.properties.配置的是struts2的所有常量
init_TraditionalXmlConfigurations(); // [2]
加载struts-default.xml、struts-plugin.xml、struts.xml
init_LegacyStrutsProperties(); // [3]
加载用户自定义struts.properties
init_CustomConfigurationProviders(); // [5]
加载用户配置的提供对象
init_FilterInitParameters() ; // [6]
加载web.xml
init_AliasStandardObjects() ; // [7]
加载标准对象。
根据上面的代码我们可以得出配置文件的加载顺序如下
* default.properties
* struts-default.xml
* struts-plugin.xml
* struts.xml ---- 配置Action以及常量.(******)
* struts.properties ---- 配置常量
* web.xml ---- 配置核心过滤器及常量.
前三个配置文件我们不用关心,是Struts2内部的配置文件,我们无法修改,能修改的文件就是struts.xml,struts.properties,web.xml配置文件。这几个配置文件的加载是有一定的顺序的。这三个配置文件都可以修改Struts2的常量的值,要记住的是,后加载配置文件中常量的值会将先加载的配置文件中常量的值给覆盖。
知道了这些我们就可以来看Struts2的详细的配置了。首先来看Action的配置。
1.2.4.2 Action的配置:
Struts2框架的核心配置文件是struts.xml文件,该文件主要用来配置 Action和请求的对应关系。
【<package>的配置】
Struts2框架的核心组件是Action和拦截器,它使用包来管理Action和拦截器。每个包就是多个Action、多个拦截器、多个拦截器引用的集合。在struts.xml文件中,package元素用于定义包配置,每个package元素定义了一个包配置。package元素的常用属性,如表所示。
package元素的常用属性
属性 | 说明 |
---|---|
name | 必填属性,它指定该包的名字,此名字是该包被其他包引用的key。 |
namespace | 可选属性,该属性定义该包的命名空间。 |
extends | 可选属性,它指定该包继承自其他包。继承其他包,可以继承其他包中的Action定义、拦截器定义等。 |
abstract | 可选属性,它指定该包是否是一个抽象包,抽象包中不能包含Action定义。 |
表中就是package元素的常用属性,其中,在配置包时,必须指定name属性,就是包的标识。除此之外,还可以指定一个可选的extends属性,extends属性值必须是另一个包的name属性值,但该属性值通常都设置为struts-default,这样该包中的Action就具有了Struts2框架默认的拦截器等功能了。除此之外,Struts2还提供了一种所谓的抽象包,抽象包不能包含Action定义。为了显式指定一个包是抽象包,可以为该package元素增加abstract="true"属性。
在package中还有namespace的配置,namespace属性与action标签的name属性共同决定了访问路径。namespace有如下三种配置。
- 默认名称空间 :默认的名称空间就是namespace=""
- 跟名称空间 :跟名称空间就是namespace="/"
- 带名称的名称空间 :带名称的名称空间就是namespace="/demo1"
【Action的配置】
Action映射是框架中的基本“工作单元”。Action映射就是将一个请求的URL映射到一个Action类,当一个请求匹配某个Action名称时,框架就使用这个映射来确定如何处理请求。在struts.xml文件中,通过<action>元素对请求的Action和Action类进行配置。
<action>元素中共有4个属性,这4个属性的说明如表所示。
action元素属性说明
属性 | 说明 |
---|---|
name | 必填属性,标识Action,指定了Action所处理的请求的URL。 |
class | 可选属性,指定Action对应Action类。 |
method | 可选属性,指定请求Action时调用的方法。 |
converter | 可选属性,指定类型转换器的类。 |
其中name属性和namespace属性共同决定了访问路径,class对应的是Action类的全路径。Method指定了执行Action的那个方法,默认是execute方法。
基本的Struts2的配置我们已经了解了,在实际的开发中我们需要大量的用到Struts2的常量,那么我们接下来学习一下Struts2的常量。
1.2.4.3 Struts2常量的配置:
Struts2的这些常量大多在默认的配置文件中已经配置好,但根据用户需求的不同,开发的要求也不同,可能需要修改这些常量值,修改的方法就是在配置文件对常量进行重新配置。
Struts2常量配置共有3种方式,分别如下:
- 在struts.xml文件中使用<constant>元素配置常量。
- 在struts.properties文件中配置常量。
- 在web.xml文件中通过< init-param>元素配置常量。
为了让大家更好地掌握这3种Struts2常量配置的方式,接下来分别对它们进行讲解,具体如下。
1、在struts.xml文件中通过<constant>元素配置常量
在struts.xml文件中通过<constant>元素来配置常量,是最常用的方式。在struts.xml文件中通过<constant…/>元素来配置常量时,需要指定两个必填的属性name和value。
- name:该属性指定了常量的常量名。
- value:该属性指定了常量的常量值。
在struts.xml文件中配置的示例代码如下:
<struts>
<!-- 设置默认编码集为UTF-8 -->
<constant name="struts.i18n.encoding" value="UTF-8" />
<!-- 设置使用开发模式 -->
<constant name="struts.devMode" value="true" />
</struts>
在上述示例代码中,配置了常量struts.i18n.encoding和struts.devMode,用于指定Struts2应用程序的默认编码集为UTF-8,并使用开发模式。值得一提的是,struts.properties文件能配置的常量都可以在struts.xml文件中用<constant>元素来配置。
2、在struts.properties文件中配置常量
struts.properties文件是一个标准的properties文件,其格式是key-value对,即每个key对应一个value,key表示的是Struts2框架中的常量,而value则是其常量值。在struts.properties文件中配置常量的方式,具体如下所示:
### 设置默认编码集为UTF-8
struts.i18n.encoding=UTF-8
### 设置action请求的扩展名为action或者没有扩展名
struts.action.extension=action,,
### 设置不使用开发模式
struts.devMode=false
### 设置不开启动态方法调用
struts.enable.DynamicMethodInvocation=false
在上述代码片段中,“=”号左边的是key,右边的是每个key对应的value,另外,代码片段中的“###”表示的是properties文件中的注释信息,用于解释说明。
需要注意的是,和struts.xml文件一样,struts.properties文件也应存放于WEB-INF/classes路径下。
3、在web.xml文件中通过初始化参数配置常量
在web.xml文件中配置核心过滤器StrutsPrepareAndExecuteFilter时,通过初始化参数来配置常量。通过<filter>元素的<init-param>子元素指定,每个<init-param>元素配置了一个Struts2常量。在web.xml文件中通过初始化参数配置常量方式,具体如以下代码片段所示:
<filter>
<!-- 指定Struts2的核心过滤器 -->
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
<!--通过init-param元素配置Struts2常量,配置默认编码集为UTF-8 -->
<init-param>
<param-name>struts.i18n.encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
在上述web.xml文件的代码片段中,当配置StrutsPrepareAndExecuteFilter时,还通过<init-param>子元素配置了常量struts.i18n.encoding,指定其值为UTF-8。需要注意的是,在web.xml文件中配置常量时,<init-param>标签必须放在<filter>标签下。
Struts2所支持的常量数量众多,在struts2-core-2.3.24.jar压缩文件的org/apache/struts2路径下有一个default.properties文件,该文件里为Struts2的所有常量都指定了默认值,读者可以通过查看该文件来了解Struts2所支持的常量。
之前我们就已经介绍过了Struts2的配置文件的加载顺序,后加载的配置文件的常量的值会覆盖先加载的配置文件中常量的值。所以这个地方大家要注意。
在实际的开发中我们更习惯使用struts.xml修改struts2的常量。但是在实际开发中还会有一个问题,就是如果一个项目是团队开发的,也就是很多人开发的,那么团队中的很多人就都需要去修改struts.xml。那么最后在项目整合的时候就会很麻烦。所以Struts2中也支持分模块开发的配置。
1.2.4.4 分模块开发的配置
在实际开发中,我们通常很多人都需要修改同一个配置文件就是struts.xml。因为这个文件是Struts2的核心配置文件。而且这个文件一旦改错了一点,那么会导致整个项目都会出现问题,所以Struts2提供了<include>标签解决这个问题。
<include>元素用来在一个struts.xml配置文件中包含其他的配置文件,包含配置体现的是软件工程中的“分而治之”原则。Struts2允许将一个配置文件分解成多个配置文件,从而提高配置文件的可读性。Struts2默认只加载WEB-INF/classes下的struts.xml文件,但一旦通过多个xml文件来配置Action,就必须通过struts.xml文件来包含其他配置文件。
为了让大家更直观地理解如何在struts.xml文件中进行包含配置,接下来通过一段示例代码来说明,具体如下:
<struts>
<!-- 包含了4个配置文件 -->
<!-- 不指定路径默认在src下时的方式 -->
<include file="struts-shop.xml"/>
<include file="struts-user.xml"/>
<include file="struts-shoppingcart.xml"/>
<!-- 配置文件在具体包中时的方式 -->
<include file=”cn/com/javahelp/action/struts-product.xml”>
</struts>
在上述代码片段中,struts.xml文件包含了4个配置文件,这4个配置文件都包含在<include>元素中。配置<include>元素时,指定了一个必需的file属性,该属性指定了被包含配置文件的文件名。上述include元素的file属性中,前3个没有指定文件所在路径时,表示该文件在项目的src路径下,如果配置文件在具体的包中,那么引入配置文件时,需要包含文件所在包的路径。
需要注意的是,每一个被包含的配置文件都是标准的Struts2配置文件,一样包含DTD信息、Struts2配置文件的根元素等信息。通过将Struts2的所有配置文件都放在Web项目的WEB-INF/classes路径下,struts.xml文件包含了其他的配置文件,在Struts2框架自动加载struts.xml文件时,完成加载所有的配置信息。
Action的基本配置以及熟悉了,那么通过配置Struts2框架就可以找到具体的Action类了,那么Action类又要如何编写呢?接下来学习Action类的编写的方式。
1.2.5 Struts2的Action的访问:
在Struts2的应用开发中,Action作为框架的核心类,实现对用户请求的处理,Action类被称为业务逻辑控制器。一个Action类代表一次请求或调用,每个请求的动作都对应于一个相应的Action类,一个Action类是一个独立的工作单元。也就是说,用户的每次请求,都会转到一个相应的Action类里面,由这个Action类来进行处理。简而言之,Action就是用来处理一次用户请求的对象。
实现Action控制类共有3种方式,接下来,分别对它们进行讲解,具体如下。
1.2.5.1 Action的编写的方式:
【Action的是一个POJO的类】
在Struts2中,Action可以不继承特殊的类或不实现任何特殊的接口,仅仅是一个POJO。POJO全称Plain Ordinary Java Object(简单的Java对象),只要具有一部分getter/setter方法的那种类,就可以称作POJO。一般在这个POJO类中,要有一个公共的无参的构造方法(采用默认的构造方法就可以)和一个execute()方法。定义格式如下:
public class ActionDemo1 {
public String execute(){
System.out.println("ActionDemo1执行了...");
return null;
}
}
execute()方法的要求如下:
- 方法的权限修饰符为public。
- 返回一个字符串,就是指示的下一个页面的Result。
- 方法没有参数。
也就是说,满足上述要求的POJO都可算作是Struts2的Action实现。
通常会让开发者自己编写Action类或者实现Action接口或者继承ActionSupport类。
【Action类实现一个Action的接口】
为了让用户开发的Action类更规范,Struts2提供一个Action接口,用户在实现Action控制类时,可以实现Struts2提供的这个Action接口。
Action接口定义了Struts的Action处理类应该实现的规范,Action接口中的具体代码如下所示。
public class ActionDemo2 implements Action{
@Override
public String execute() throws Exception {
System.out.println("ActionDemo2执行了...");
return null;
}
}
从上述代码中可以看出,Action接口位于com.opensymphony.xwork2包中。这个接口里只定义了一个execute()方法,该接口的规范规定了Action处理类应该包含一个execute()方法,该方法返回一个字符串。除此之外,该接口还定义了5个字符串常量,它们的作用是统一execute()方法的返回值。
Action接口中提供了5个已经定义的常量如下:
* SUCCESS :success,代表成功.
* NONE :none,代表页面不跳转
* ERROR :error,代表跳转到错误页面.
* INPUT :input,数据校验的时候跳转的路径.
* LOGIN :login,用来跳转到登录页面.
由于Xwork的Action接口简单,为开发者提供的帮助较小,所以在实际开发过程中,Action类很少直接实现Action接口,通常都是从ActionSupport类继承。
【Action类继承ActionSupport类】(推荐)
public class ActionDemo3 extends ActionSupport{
@Override
public String execute() throws Exception {
System.out.println("ActionDemo3执行了...");
return NONE;
}
}
ActionSupport类本身实现了Action接口,是Struts2中默认的Action接口的实现类,所以继承ActionSupport就相当于实现了Action接口。ActionSupport类还实现了Validateable、ValidationAware、TextProvider、LocaleProvider和Serializable等接口,来为用户提供更多的功能。
ActionSupport类中提供了许多默认方法,这些默认方法包括获取国际化信息的方法、数据校验的方法、默认的处理用户请求的方法等。实际上,ActionSupport类是Struts2默认的Action处理类,如果让开发者的Action类继承该ActionSupport类,则会大大简化Action的开发。
Action的类已经会编写了,如果要执行这个Action,可以通过前面的配置来完成,但是之前的方式有一个缺点,就是多次请求不能对应同一个Action,因为实际的开发中,一个模块的请求通常由一个Action类处理就好了,否则会造成Action类过多。那么接下来我们讲一下Action访问的一些细节。
1.2.5.2 Action的访问:
Action的访问不是难题,因为之前已经访问过了,但是出现一个问题一次请求现在对应一个Action,那么如果请求很多对应很多个Action.现在要处理的问题就是要让一个模块的操作提交到一个Action中。
其实我们学过在<action>的标签中有一个属性method,通过method的配置来指定Action中的某个方法执行。
【解决Action的访问的问题的方式一:通过配置method属性完成】
编写页面:
<h3>客户的管理</h3>
<a href="${ pageContext.request.contextPath }/saveCustomerAction.action">添加客户</a> <br/>
<a href="${ pageContext.request.contextPath }/updateCustomerAction.action">修改客户</a> <br/>
<a href="${ pageContext.request.contextPath }/deleteCustomerAction.action">删除客户</a> <br/>
<a href="${ pageContext.request.contextPath }/findCustomerAction.action">查询客户</a> <br/>
编写Action:
public class CustomerAction extends ActionSupport{
public String save(){
System.out.println("CustomerAction中save方法执行了...");
return NONE;
}
public String update(){
System.out.println("CustomerAction中update方法执行了...");
return NONE;
}
public String delete(){
System.out.println("CustomerAction中delete方法执行了...");
return NONE;
}
public String find(){
System.out.println("CustomerAction中find方法执行了...");
return NONE;
}
}
配置Action:
<package name="demo3" extends="struts-default" namespace="/">
<action name="saveCustomerAction" class="cn.com.javahelp.struts2.demo3.CustomerAction" method="save"></action>
<action name="updateCustomerAction" class="cn.com.javahelp.struts2.demo3.CustomerAction" method="update"></action>
<action name="deleteCustomerAction" class="cn.com.javahelp.struts2.demo3.CustomerAction" method="delete"></action>
<action name="findCustomerAction" class="cn.com.javahelp.struts2.demo3.CustomerAction" method="find"></action>
</package>
但是这种方式我们会发现,同一个Action类就被配置了很多次,只是修改了后面的method的值。那么能不能配置简单化呢?也就是一个Action类,只配置一次就好了?这个时候我们就需要使用通配符的配置方式了。
【解决Action的访问的问题的方式二:通过通配符的配置完成】
编写页面:
<h3>联系人的管理</h3>
<a href="${ pageContext.request.contextPath }/linkman_save.action">添加联系人</a> <br/>
<a href="${ pageContext.request.contextPath }/linkman_update.action">修改联系人</a> <br/>
<a href="${ pageContext.request.contextPath }/linkman_delete.action">删除联系人</a> <br/>
<a href="${ pageContext.request.contextPath }/linkman_find.action">查询联系人</a> <br/>
编写Action:
public class LinkManAction extends ActionSupport{
public String save(){
System.out.println("保存联系人...");
return NONE;
}
public String update(){
System.out.println("修改联系人...");
return NONE;
}
public String delete(){
System.out.println("删除联系人...");
return NONE;
}
public String find(){
System.out.println("查询联系人...");
return NONE;
}
}
配置Action:
<!-- 通配符的配置 -->
<action name="linkman_*" class="cn.com.javahelp.struts2.demo3.LinkManAction" method="{1}"></action>
在<action>的name属性中使用的*代表任意字符,method中的{1}代表name属性中的出现的第一个*所代替的字符。
这个时候我们就只配置一个就可以了,在上述代码中,当客户端发送/linkman_save.action这样的请求时,action元素的name属性就被设置成linkman_save,method属性就被设置成save。当客户端发送/linkman_update.action这样的请求时,action元素的name属性就被设置为linkman_ update,method属性也被设置成update。
当然使用通配符是开发中常用的方式,Struts2还有一种解决这类问题的办法,这种大家可以作为扩展内容来学习。
【解决Action的访问的问题的方式三:动态方法访问】
动态方法访问在Struts2中默认是不开启的,如果想要使用需要先去开启一个常量。
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
动态方法访问主要的控制是在页面端,所以编写Action和配置Action都很简单,关键是访问路径的编写。
编写Action:
public class UserAction extends ActionSupport{
public String save(){
System.out.println("保存用户...");
return NONE;
}
public String update(){
System.out.println("修改用户...");
return NONE;
}
public String delete(){
System.out.println("删除用户...");
return NONE;
}
public String find(){
System.out.println("查询用户...");
return NONE;
}
}
配置Action:
<!-- 动态方法访问的配置 -->
<action name="userAction" class="cn.com.javahelp.struts2.demo3.UserAction"></action>
页面路径写法:
<h3>用户的管理</h3>
<a href="${ pageContext.request.contextPath }/userAction!save.action">添加用户</a> <br/>
<a href="${ pageContext.request.contextPath }/userAction!update.action">修改用户</a> <br/>
<a href="${ pageContext.request.contextPath }/userAction!delete.action">删除用户</a> <br/>
<a href="${ pageContext.request.contextPath }/userAction!find.action">查询用户</a> <br/>
到这对Struts2基本的开发和流程应该没有问题了,我们可以通过一个案例对今天的内容进行一个总结。完成客户列表的显示操作。
1.3 案例实现
1.3.1 搭建开发环境
1.3.1.1 步骤一:创建WEB工程,引入jar包
1.3.1.2 步骤二:引入相应的页面
1.3.1.3 步骤三:创建包和相关的类
1.3.1.4 步骤四:配置核心过滤器
1.3.1.5 步骤五:引入相应的配置文件
1.3.2 案例代码实现
1.3.2.1 步骤六:修改菜单页面修改提交路径
1.3.2.2 步骤七:编写Action中的代码
/**
* 客户管理的Action类
* @author jt
*
*/
public class CustomerAction extends ActionSupport{
/**
* 查询客户列表的方法:
* @return
*/
public String findAll(){
// 创建业务层的类的对象
CustomerService customerService = new CustomerServiceImpl();
// 调用业务层的方法查询所有客户
List<Customer> list = customerService.findAll();
// 获得request对象,并且保存到request中。
ServletActionContext.getRequest().setAttribute("list", list);
return "findAll";
}
}
1.3.2.3 步骤八:编写业务层的类
业务层的接口
/**
* 客户管理业务层的接口
* @author jt
*
*/
public interface CustomerService {
List<Customer> findAll();
}
业务层的实现类
/**
* 客户管理业务层的实现类
* @author jt
*
*/
public class CustomerServiceImpl implements CustomerService {
@Override
public List<Customer> findAll() {
CustomerDao customerDao = new CustomerDaoImpl();
return customerDao.findAll();
}
}
1.3.2.4 步骤九:引入Hibernate的jar包和配置文件
引入jar包
引入配置文件
1.3.2.5 步骤十:添加映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.com.javahelp.vo.Customer" table="cst_customer">
<id name="cust_id" column="cust_id">
<!-- 主键生成策略 -->
<generator class="native"/>
</id>
<property name="cust_name" column="cust_name"/>
<property name="cust_source" column="cust_source"/>
<property name="cust_industry" column="cust_industry"/>
<property name="cust_level" column="cust_level"/>
<property name="cust_phone" column="cust_phone"/>
<property name="cust_mobile" column="cust_mobile"/>
</class>
</hibernate-mapping>
1.3.2.6 步骤十一:引入工具类并修改配置文件
修改配置文件
1.3.2.7 步骤十二:编写DAO
编写DAO接口
/**
* 客户管理的DAO的接口
* @author jt
*
*/
public interface CustomerDao {
List<Customer> findAll();
}
编写DAO实现类
/**
* 客户管理的DAO的实现类
* @author jt
*
*/
public class CustomerDaoImpl implements CustomerDao {
@Override
/**
* DAO中查询所有客户的方法
*/
public List<Customer> findAll() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
List<Customer> list = session.createQuery("from Customer").list();
tx.commit();
session.close();
return list;
}
}
1.3.2.8 步骤十三:配置Action的有页面跳转
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="crm" extends="struts-default" namespace="/">
<action name="customer_*" class="cn.com.javahelp.web.action.CustomerAction" method="{1}">
<result name="findAll">/jsp/customer/list.jsp</result>
</action>
</package>
</struts>
1.3.2.9 步骤十四:在页面中显示相应的数据
引入jstl的jar包
编写显示数据的代码
1.3.2.10 步骤十五:测试程序
启动服务器访问页面