转载自:https://blog.csdn.net/justloveyou_/article/details/55002006
摘要:
作为 Struts2 的首篇文章,本文首先以 LoginDemo 为例介绍了构建一个简单的 Struts2 应用的一般步骤和流程,并结合 Struts2 框架的原理给出每个步骤的目的与内涵。紧接着以 MVC 为支撑、以该 Demo 为出发点概述了 Struts2 的运行机制,给出了 Struts2 应用的一次 “请求-响应” 的完整流程,揭示了 Struts2的 请求与视图相分离的本质。最后,针对该Demo所涉及到的知识点Namespace进行了深入探究,并总结出 Struts2 的请求路由规则。
一. 第一个Struts2应用的构建 — 登录Demo
创建一个 Struts2 应用一般需要以下几个步骤:
(1) 下载、解压 struts-2.1.6-all.zip 并为 Web 应用添加 Struts2 支持。将目录 struts-2.1.6\apps\struts2-blank-2.1.6\WEB-INF\lib 下的 commons-fileupload.jar、commons-io.jar、freemarker.jar、commons-logging-1.1.jar、ognl.jar、struts2-core.jar 和 xwork-core.jar 七个jar包复制到Web应用的WEB-INF/lib路径下。如果需要在Web应用中使用 struts2 的更多特性,则需要将相应的jar文件复制到web应用的WEB-INF/lib路径下。
(2) 编辑 Web 应用的web.xml配置文件,配置 Struts 2 的核心 Filter,该Filter被用来拦截用户请求。由于Web应用是基于请求/响应模式的,所以为了让 Struts2 框架介入 Web 应用中,需要在web.xml中配置该框架的核心filter。下面是Struts2 在web.xml文件中的配置片段:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name></display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 定义Struts2的核心Filter -->
<filter>
<!-- 可任意命名,但必须与 filter-mapping 中的 filter-name 一致-->
<filter-name>Struts2</filter-name>
<!--struts2-core-2.1.6.jar中 包org.apache.struts2.dispatcher.ng.filter下的一个类-->
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<!-- 使Struts2的 Filter 拦截所有请求 -->
<filter-mapping>
<filter-name>Struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
(3) 定义处理用户请求的 Action 类,以便调用 Model 中的方法来处理请求。实际上,StrutsPrepareAndExecuteFilter 和 Action 共同构成了 Struts2 的控制器(Controller)。其中, StrutsPrepareAndExecuteFilter 为核心控制器,负责拦截、处理所有用户请求;Action 为业务控制器,负责调用 Model处理实际业务并返回处理结果。 Action 的实现手段通常有以下三种:
① 一个包含无参的execute()方法(返回类型为 String)的普通Java类;
② 实现 com.opensymphony.xwork2.Action 接口并重写execute()方法的Java类;
③ 继承 com.opensymphony.xwork2.ActionSupport 类并重写execute()方法的Java类。★★★ 推荐使用方式 ★★★
在该登录Demo中,Action 的实现采用的是第三种方式,代码如下所示。关于 Action 更详细、全面的介绍见我的下一篇博文,此不赘述。
public class LoginAction extends ActionSupport{
private static final long serialVersionUID = 1L;
// 使用 Action 来封装 HTTP 请求参数
private String username;
private String password;
// getter and setter
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
// 处理用户请求的逻辑
@Override
public String execute() throws Exception {
if(username.equals("rico") && password.equals("root")){
System.out.println("登录成功...");
return SUCCESS;
}else if(username.equals("") || password.equals("")){
System.out.println("用户名或密码未填写...");
return INPUT;
}else {
System.out.println("登录失败...");
return ERROR;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
(4) 配置 Action,并编辑 struts.xml。配置 Action 实际上就是 使具体请求与具体的Action相关联,从而使得核心控制器(StrutsPrepareAndExecuteFilter) 可以根据该配置来创建合适的 Action实例,并调用对应的业务控制方法处理用户请求。在配置Action过程中,首先复制目录 struts-2.1.6\apps\struts2-blank-2.1.6\WEB-INF\src\java 下的 struts.xml 到 Web应用的src目录,然后编辑 struts.xml 如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<!--开发模式下,修改 struts.xml 后不需重启Tomcat便可立即生效 -->
<constant name="struts.devMode" value="true" />
<package name="default" namespace="/" extends="struts-default">
<action name="login" class="com.tju.rico.action.LoginAction">
<!-- ................................................. -->
<!-- 配置 Action 处理结果与物理视图资源之间的对应关系... -->
<!-- ................................................. -->
</action>
</package>
</struts>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
(5) 配置 Action 处理结果 与 物理视图资源 之间的对应关系。当 Action 处理用户请求后,通常会返回一个处理结果,即 逻辑视图名(Action 接口中的五个静态字段:success,error,input,login 和 none) ,而这个逻辑视图名必须与具体的物理视图资源关联才有价值。最终在 struts.xml 中的配置如下所示:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<!--开发模式下,修改 struts.xml 后不需重启Tomcat便可立即生效 -->
<constant name="struts.devMode" value="true" />
<package name="default" namespace="/" extends="struts-default">
<action name="login" class="com.tju.rico.action.LoginAction">
<result name="success">/welcome.jsp</result>
<result name="error">/error.jsp</result>
<result name="input">/login.jsp</result>
</action>
</package>
</struts>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
(6) 到此为止,第一个Struts2应用 — 登录Demo 便完成,其组织结构与运行结果如下图所示:
二. Struts2 应用的运行逻辑
1、Struts2 应用的运行逻辑
上面所述的 Struts2 应用的构建流程是以“请求-响应”为主线的,下面给出 Struts2 应用的一次 “请求-响应” 的完整流程,如下图所示:
可以从上面图示看出,Struts2 的核心在于 将用户请求与响应视图相分离。也就是说,用户的所有请求都只向控制器发送,由控制器调用模型组件、视图组件向用户展现响应。特别需要注意以下几点:
在 Struts2 应用中,StrutsPrepareAndExecuteFilter 和 Action 一起充当 Controller,并由其调用 Model、View 响应用户请求;
在 Struts2 应用中,StrutsPrepareAndExecuteFilter 通过反射生成 Action 实例并通过该实例调用相应的方法(一般是 execute() 方法)处理用户请求;
业务控制器Action 并不与物理视图相关联,也就是说,业务处理结果(数据)与显示逻辑相分离。特别地,业务处理结果与怎样的视图关联依然由 StrutsPrepareAndExecuteFilter 决定(通过在 struts.xml 中配置)。
2、Struts2 应用的运行逻辑实例
我们反过头结合 登录Demo来看 Struts2 的运行逻辑,如下图所示:
(1) 用户在客户端(浏览器)键入请求地址,例如:http://servername/WebAppname/login;
(2) 客户端将上述请求进一步封装成HTTP请求,并发送给服务器(Tomcat);
(3) 服务器找到对应已部署的Web应用,并参考其 web.xml;
(4) 请求被 Struts2 Filter 过滤,参考 struts.xml 进行对请求响应;
(5) 匹配 Namespace 与 请求URL,从而确定对应的 Package,并在该Package内进一步匹配 Action;
(6) 成功匹配 Action 后,通过Action实例调用其 execute() 方法处理用户请求,并返回逻辑视图名;
(7) 根据逻辑视图名匹配物理视图资源,并forward到该视图;
(7) 服务器生成响应并将对应视图通过客户端(浏览器)展现给用户。
三. NameSpace: 命名空间与请求路由规则
Struts2 之所以提供命名空间的功能,主要是为了处理在同一个WebApp中包含同名的 Action 的情形。其以命名空间的方式来管理 Action,使得同一个命名空间里不能有同名的 Action, 而不同的命名空间里可以有同名的 Action。但是 Struts2 不支持为单独的 Action 设置命名空间,而是通过为Package指定namespace属性来为Package下面的所有Action指定共同的命名空间,从而解决Action命名冲突问题。实际上,我们在 Struts2 中所提到的 Package 与 Java 中的 package 作用类似。我们可以将每个Action划分到一个Package中,例如,每个命名空间下都可以有一个名为 ”help” 的 Action,并且有着各自不同的实现。此外,如果在Package中没有指定namespace属性,则该包下的所有Action处于默认的命名空间下,即等价于namespace的属性值为空字符串(“”)的情形。Struts2还支持根命名空间(“/”)。有几点需要注意:
- Namespace为空就意味着只要在该默认命名空间下找到可以匹配的 Action,该请求就一定会被响应(具体响应结果取决于匹配精度,精度越高,越优先被用来响应请求), 而不管是否能够找到与请求URL相匹配(无论精确匹配还是模糊匹配)的 Package;
- 除Namespace的属性值为空(“”)的情形外, Namespace 必须以 “/” 开头;
- Namespace 与 Package 一般以 模块名 命名。
1) 请求路由规则
在匹配一个Action时,遵循如下规则 (以”http://server/WebAppName/path1/path2/path3/test“为例 ):
① 寻找Namespace为”/path1/path2/path3”的Package。如果存在这个Package,则在这个Package中 寻找名字为test的Action,若找到,则执行;否则 (不存在该Package 或者 这个 Package 中不存在该Action),转步骤2;
② 寻找Namespace为”/path1/path2”的Package。如果存在这个Package,则在这个Package中 寻找名字为test的Action,若找到,则执行;否则 (不存在该Package 或者 这个 Package 中不存在该Action),转步骤3;
③ 寻找Namespace为”/path1”的Package。如果存在这个Package,则在这个Package中 寻找名字为test的Action,若找到,则执行;否则 (不存在该Package 或者 这个 Package 中不存在该Action),就去缺省 Namespace 或者 Namespace为空字符串的Package下面去找名为test的Action,如果还是找不到,则页面提示找不到 Action。
特别地,
(1). 若存在根命名空间,当一个request直接请求 ContextPath (即:”http://server/WebAppName/“) 下面的资源时,Struts2会首先到根命名空间下去寻找匹配的Action,若找到则执行,否则,报错;(2). 若不存在根命名空间,假如直接请求 “http://server/WebAppName/“, 则Struts2会直接从web.xml找到相应的welcome-file并返回。
2) 请求路由规则小结
总的来说,请求路由规则为 精度越高越优先 (精度相同时,看先后顺序),可总结为如下两条:
FilterDispatcher在请求路由时,将请求URL按上述规则与Namespace进行逐个匹配。一旦匹配成功,要么正常响应(找到特定Action),要么报404(未找到特定Action)。至此,本次请求路由结束;
当一个request直接请求ContextPath (“http://server/WebAppName/“) 下面的资源时,若存在根命名空间,则参照第一条;若不存在根命名空间,则Struts2会直接从web.xml找到相应的welcome-file并返回。至此,本次请求路由结束;
四. 小结
作为 Struts2 入门文章,本文首先以HelloWorld为例介绍了创建一个简单的 Struts2 应用的一般步骤和流程,紧接着以该 HelloWorld Web应用为出发点简述了 Struts2 的运行机制,揭示了 Struts2 的核心:将请求与视图相分离。最后,针对该Web应用所涉及到的知识点Namespace进行了深入探究,并总结出Struts2的请求路由规则。
更复杂、全面的 Struts2 应用实例和关于Action的详细解读见我的下一篇博客。
引用
《轻量级 Java EE 企业应用实战(第四版)》
struts 2读书笔记—–struts2的开发流程
Struts2学习(一):创建一个Struts2应用