Struts2 实例(转载)

1、准备工做及实例php

1.解压struts-2.1.6-all.ziphtml

apps目录:struts2自带的例子程序
docs目录:官方文档。java

lib 目录:存放全部jar文件。python

Src 目录:源文件存放地web

2.六个基本包正则表达式

struts2-core-2.1.6.jar :开发的核心类库
freemarker-2.3.13.jar :struts2UI标签的模板使用freemarker编写
commons-logging-1.0.4.jar :日志包
ognl-2.6.11.jar :对象图导航语言,经过它来读写对象属性
xwork-2.1.2.jar :xwork类库,struts2在其上进行构建
commons-fileupload-1.2.1.jar:文件上传组件,2.1.6版本后必须加入此jar数据库

特别须要说明的是目前strust2的最新版本是struts-2.1.6,它做为2.1.X的正式版。特别要注意导入commons-fileupload-1.2.1.jar包,在此jar包中包含了RequestContext类,若是不导入该jar包将会报异常。express

3.初识struts2配置文件apache

(1).web.xml文件 编程

主要完成对StrutsPrepareAndExecuteFilter的配置(在之前的版本中是对FilterDispatcher配置,新版本一样支持用FilterDispatcher配置),它的实质是一个过滤器,它负责初始化整个Struts框架而且处理全部的请求。这个过滤器能够包括一些初始化参数,有的参数指定了要加载哪些额外的xml配置文件,还有的会影响struts框架的行为。除了StrutsPrepareAndExecuteFilter外,Struts还提供了一个ActionContexCleanUp类,它的主要任务是当有其它一些过滤器要访问一个初始化好了的struts框架的时候,负责处理一些特殊的清除任务。

(2).struts.xml文件 

框架的核心配置文件就是这个默认的struts.xml文件,在这个默认的配置文件里面咱们能够根据须要再包括其它一些配置文件。在一般的应用开发中,咱们可能想为每一个不一样的模块单独配置一个struts.xml文件,这样也利于管理和维护。这也是咱们要配置的主要文件。

(3).struts.properties(参default.properties) 

在Struts框架使用了不少属性,咱们能够经过改变这些属性来知足咱们的需求。要改变这些属性,只需在struts.properties文件中指定属性的key和value便可。属性文件能够放在任何一个包含在classpath中的路径上,可是一般咱们都把它放在/WEB-INF/classes目录下面。咱们能够在struts-default.properties文件中找到一个属性的列表。

(4)struts-default.xml 

此文件是struts2框架默认加载的配置文件,它定义了struts2一些核心bean和拦截器,它会自动包含(included)到struts.xml文件中(实质是经过<package  extends="struts-default">),并为咱们提供了一些标准的配置。咱们能够在struts2-core.jar中找到这个文件。

(5)其它配置文件

velocity.properties,struts-default.vm,struts-plugin.xml

4.让MyEclipse提示xml信息

当咱们在编写struts.xml时,发现eclipse并不会给出帮助提示,那是由于MyEclipse默认并不支持struts2,因此咱们须要手工导入dtd以支持提示。步骤:[window][preferences][MyEclipse][Files and Editors][XML][xml Catelog]而后在右边点add添加:location为dtd文件所在的位置(struts-2.0.dtd文件struts2-core-2.1.6.jar中能够获得),KeyType选择URI,Key为struts-2.0.dtd文件中文档声明的内容(http://struts.apache.org/dtds/struts-2.0.dtd),在struts.xml文件中也有此key值。

5.如何使用alt+/提示

在MyEclipse6.5中,默认的提示为Ctrl+Space,而它会与咱们的输入法切换冲突,使提示失效。找到key,先取消Content Assist命令的绑定,再用“alt+/”来绑定。

6.实例

步骤一,新建myStruts2项目,并导入struts2的六个基本jar包。
步骤二,创建LoginAction文件,主要代码以下:
package com.asm;

import com.opensymphony.xwork2.Action;

public class LoginAction implements Action {

private String username;

private String password;

...省略get/set方法

public String execute() throws Exception {

if (username.equals("struts2")) {

return "loginSuccess";

} else {

return "loginFailure";

}

}

}
说明:实现了Action接口,主要是为了保证execute的正肯定义,其实咱们也能够不实现此接口,只要能保证execute方法书写的正确书写(方法名,返回值)。

步骤三,在struts.xml文件中注册LoginAction。此配置文件要放在src目录下,实质就是成为classpath环境变量下的文件。主要代码以下:
<?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>

<package name="myFirst" namespace="/" extends="struts-default">

<action name="login" class="com.asm.LoginAction">

<result name="loginSuccess">/success.jsp</result>

<result name="loginFailure">/failure.jsp</result>

</action>

</package>

</struts>
说明:package后面会有详细说明。action元素中的name属性值指定了此action所指定的请求路径为“login.action”。后面login.jsp中的<form action=...>属性值就会参照此name属性。

步骤4、提供jsp页面
login.jsp主要代码:
<body>

<form action="<%=request.getContextPath()%>/login.action" method="get">

户名:<input type="text" name="username"><br>

密码:<input type="password" name="password"><br>

<input type="submit" value="login">

</form>
</body>
failure.jsp主要代码

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<%@ taglib uri="/struts-tags" prefix="s" %>
<html>

<body>

登陆失败,错误的用户名:<s:property value="username"/><br>

<a href="<%=request.getContextPath()%>/login.jsp">返回</a>

</body>

</html>
说明:使用了标签库,在struts2中使用标签库很是简单,只须要像上面那样导入标签库即可以使用全部的struts2的全部标签

success.jsp主要代码

<body> 登陆成功!</body>

步骤5、配置web.xml。完成核心监听器注册。内容以下:
<?xml version="1.0" encoding="UTF-8"?>

<web-app id="WebApp_9" version="2.4"

xmlns="http://java.sun.com/xml/ns/j2ee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<filter>

<filter-name>struts2</filter-name>

<!-- <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>

-->

<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>

</web-app>

说明:注释掉的部分为之前2.1.4版本中用的核心filter类。StrutsPrepareAndExecuteFilter类的init方法将会读取类路径下默认的配置文件struts.xml,并以javabean形式存放在内存中,之后struts2对用户的每次请求将使用内存中数据,而不是重读struts.xml文件。

步骤6、发布测试。
简要分析执行流程:
当输入login.jsp访问jsp页面填写完相关信息并提交给login.action时,它会首先被在web.xml中配置的过滤器监听到,过滤器会去查找strust.xml文件,并结合namespace查找名为login的action,查找到此action便交给其处理,LoginAction内部会执行execute方法,并返回结果result(result也是参照的struts.xml中action下的result配置)。 关于表单传参,主要是参照的action中的方法名,而非属性名。

7.开启struts2自带的开发模式常量

在之前的开发中,当修改一些配置时老是不能及时地更新到服务器,咱们总会从新部署或重启来更新改变的内容,在struts2中能够经过一个常量来达到此目的。即在struts.xml中的<struts>元素下增长以下内容:<constant name="struts.configuration.xml.reload" value="true" /> 这样配置后,当配置文件修改保存时就会及时更新到服务器中。其它一些常量:

<!-- 指定WEB应用的编码集,至关于调用HttpServletRequest.setCharacterEncodint方法,若是使用了velocityfreemarker,它也用于指定输出的编码格式 -->

<constant name="struts.i18n.encoding" value="UTF-8" />

<!-- 指定请求后缀为.action,指定多个请求后缀用逗号分隔 -->

<constant name="struts.action.extension" value="action" />

<!--设置浏览器是否缓存静态内容,建议:开发阶段关闭,运行时开启  -->

<constant name="struts.serve.static.browserCache" value="false" />

<!--struts.xml配置文件修改后,系统是否从新加载该文件,开发阶段打开此功能  -->

<constant name="struts.configuration.xml.reload" value="true" />

<!--  开发提示:出错时打印更详细的信息-->

<constant name="struts.devMode" value="true" />

<!-- 指定请求的后缀能够是.do.action -->

<constant name="struts.action.extension" value="do,action" />

注意:在struts2.1.6版本中存在一个bug:即配置了struts.i18n.encoding常量也不能解决中文乱码问题,缘由是此版本在获取请求参数后才调用了setCharacterEncoding()方法进行编码设置。解决此bug的方法是配置一个filter,并在doFilter方法中增长以下代码:request.setCharacterEncoding(“UTF-8”); 在之后的2.1.8版本中解决了此问题及2.1.6中存在的其它bug,建议新项目使用2.1.8版本。

8.vo传参模式

Copy上面的myStruts2项目,更名为myStruts2Vo项目。做以下修改:在LoginAction中有两个字段:username,password。把此两个属性重构到com.asm.vo.User类中,而后在LoginAction中提供User对象及相应的get/set方法。如今须要注意的是在login.jsp中会有以下的修改:

户名:<input type="text" name="user.username"><br>

密码:<input type="password" name="user.password"><br>

关键就是改掉name属性值。其它基本无变更。 后话:假如此此User对象并不能和Model层的相应对象彻底对应,咱们还应借助此User对象在Action中构建出Model层的相应对象,这样,在exectue方法中便能经过构建的Model对象做为参数与Model层交互。

9.ModerDriven传参模式(不建议采用)

Copy上面的myStruts2Vo项目,更名为myStruts2Model项目。重点是修改LoginAction,修改后的主要内容以下:
package com.asm;

import com.asm.vo.User;

import com.opensymphony.xwork2.Action;

import com.opensymphony.xwork2.ModelDriven;

public class LoginAction implements Action, ModelDriven<User> {

private User user = new User();

public String execute() throws Exception {

if (user.getUsername().equals("struts2")) {

return "loginSuccess";

} else {

return "loginFailure";

}

}

public User getModel() {

return user;

}

}

说明:它实现了ModelDriven接口,并使用了泛性机制(必须),所以要求jdk1.5以上。
如今须要注意的是在login.jsp中name属性值为User中两个字段,和第一个实例同样。说明:此方式通常不会使用,在此略做了解。

10.为何要使用struts2代替struts1.x

(1)struts2的execute方法中的参数不会依赖于servletAPI,实现了也servlet解耦,是一种无侵入式的设计。

(2)struts2提供了拦截器,利用拦截器能够进行AOP编程,实现权限拦截等功能。

(3)struts2提供了类型转换器,咱们能够很容易地对请求参数转换成须要的类型。

(4)提供了同种表现层技术支持,如JSP、freeMarker、velocity等

(5)能够对指定的方法进行校验,能够轻松地实现表单校验功能

(6)提供了全局范围、包范围和action范围的国际化资源文件管理实现。

2、struts.xml配置及例程

1.配置文件的优先级

在struts2中一些配置(好比常量)能够同时在struts-default.xml(只读性),strtus-plguin.xml(只读性),struts.xml,struts.properties和web.xml文件中配置,它们的优先级逐步升高,便是说后面的配置会覆盖掉前面相同的配置。

2.配置形式

下面以对struts.i18n.encoding=UTF-8的配置为例进行说明:

在struts.xml配置形式以下:

<constant name="struts.i18n.encoding" value="gbk"></constant>

在struts.properties的配置形式以下:

struts.i18n.encoding=UTF-8

在web.xml中配置以下:
<filter>

<filter-name>struts2</filter-name>

<filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter

</filter-class>

<init-param>

<param-name>struts.i18n.encoding</param-name>

<param-value>UTF-8</param-value>

</init-param>

</filter>
说明:官方声称配置了此常量能够解决中文乱码问题,但实事上并不能达到目的,在前面的三个项目中,若是咱们在表单中输入中文,其结果是会出现乱码。解决此问题参看[.7的注意]。这是struts2.1.6中的一bug,它的下一版2.1.8已解决此问题。

3.package配置相关

属性名

是否必须

说明

Name

Package的惟一标识,不容许同名

Extends 

指定要继承的包

Namespace

指定名称空间

Abstract

声明包为抽象否

下面咱们创建struts2package项目来进行package相关测试:

说明:在上面的配置文件中所用到的Test1Action和Test2Action这两个Action都只是继承了com.opensymphony.xwork2.ActionSupport类,而ActionSupport默认返回的就是“success”,因此当点击上面的连接分别转到了forward目录下的test1.jsp和test2.jsp。下面重点来看这个package元素的namespace属性及action的name属性,它们共同定义了action所映射到的实质文件。上图展现了连接地址和action的对应关系,因此当咱们要想访问一个action所关联到的jsp文件时,应该用namespace+action的name 关于它的内容测试能够参考struts2package项目。 
补充:一般状况下,action元素的name是属性值是不能出现“/”的,因此但愿经过action中name属性来实现多级映射,须要在sturts.xml中增长以下属性:

<constant name="struts.enable.SlashesInActionNames" value="true"/> 这样配置后就能够再action的name元素中使用“/”了。好比:
<package name="tt3" extends="struts-default">

<action name="test3/test3" class="com.asm.Test3Action">

<result name="success">/forward/test3.jsp</result>

</action>

</package>

而后输入<a href="<%=path%>/test3/test3.action">test3</a><br>连接地址就能够访问了

强调:namespace默认值“”,即不配置namespace属性。它的意思是:若是action不能进行完整路径匹配,则会来此namespace下进行匹配,好比:.../test/test/test.action,若是参照namespace及action的name不能找到也之彻底对应的action,它会再到依次追溯到上级目录中查找,便是说它会以…/test/test.action这样的路径来对应namespace和action的name进行查找。若是返回到最终的目录仍找不到,它就会到namespace="/"对应的包下查找名为test的action,若是仍找不到,它就会去默认的namespace下查找名为test的action,若是找到则执行此action。另外,namespace也能够配置成namespace="/"。它表明配置为项目的根。 总结action的名称探索顺序:彻底对应、逐步追溯到上级目录查找、"/"下查找、默认namespace下查找。

为何要提出namespace,主要是避免多人共同开发项目出现名字冲突。若是不使用namespace,多我的所写的action中可能出现重名的现象,这样当项目合并时就会出现冲突。而有了namespace能够在项目开发时由项目经理给每个人分不一样的namespace,这样每一个开发人员只须要保证本身所写的action不一样名便可。
namespace引起的连接问题:当咱们为action配置了namespace时,访问此action的形式总会是以下形式:.../webappname/xxx/yyy/ActionName.action 而当此action成功执行跳转到某个jsp页面时,如想在此jsp页面写连接,必定要写绝对路径,由于相对路径是相对.../webappname/xxx/yyy/,而若是之后咱们修改了action的namespace时,相对路径又要变,因此连接不能写成相对路径。 如下介绍绝对路径的写法:一般用myeclipse开发时创建一个jsp文件,默认总会有以下内容:

<%

String path = request.getContextPath();

String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

%>
咱们写绝对路径能够参此内容。还能够参<head>下的<base href="<%=basePath%>"> 来完成绝对路径的书写。

4.分工合做include:指定多个配置文件

好比让jack来单独开发一个action,在jack.xml中的配置文件为:
<struts>

<package name="jack" namespace="/jack" extends="struts-default">

<action name="test4" class="com.asm.Test4Action">

<result name="success">/forward/test4.jsp</result>

</action>

</package>

</struts>

而后在struts.xml文件中增长以下内容:<include file="jack.xml"></include> 它实质就是把jack.xml中的<package>及其内容写进struts.xml中的<struts>根元素下。

连接:<a href="<%=path %>/jack/test4.action">test4</a> 这样即可以访问到了forward目录下的test4.jsp了。

5.tomcat认证访问

接上例:namespce的做用除了在前面提到的避免协同开发名字冲突外,还为认证提供一个条件。好比jack开发的东西所关联到的页面须要权限才能被访问。因为多为tomcat中的内容,下面只列出步骤。
步骤一,tomcatconf目录下tomcat-users.xml内容以下:
<?xml version='1.0' encoding='utf-8'?>

<tomcat-users>

  <role rolename="manager"/>

  <role rolename="admin"/>

  <user username="jack" password="jack" roles="admin,manager"/>

  <user username="tom" password="tom" roles="manager"/>

</tomcat-users>
步骤二,在web.xml中增长以下内容:
<security-constraint>

<web-resource-collection>

<web-resource-name>jack</web-resource-name>

<url-pattern>/jack/*</url-pattern>

<http-method>POST</http-method>

<http-method>GET</http-method>

</web-resource-collection>

<auth-constraint>

<role-name>admin</role-name>

</auth-constraint>

</security-constraint>

<security-role>

<role-name>admin</role-name>

</security-role>

<login-config>

<auth-method>BASIC</auth-method>

<realm-name>input authentication message</realm-name>

</login-config>

这样配置完成后,当咱们访问.../jack中的任何内容都会要求输入密码认证信息,认证时输入tomcat-users.xml配置的admin权限的用户名和密码便可访问(这里就只有jack用户名能够访问)

6.初识拦截器

拦截器能在action被调用以前和被调用以后执行一些“代码”。Struts2框架的大部分核心功能都是经过拦截器来实现的,如防止重复提交、类型转换、对象封装、校验、文件上传、页面预装载等等,都是在拦截器的帮助下实现的。每个拦截器都是独立装载的(pluggable),咱们能够根据实际的须要为每个action配置它所须要的拦截器。 

在myStruts2项目下,从新对配置文件做以下修改:
<package name="myFirst" namespace="/" extends="struts-default">

<interceptors>

<interceptor name="timer"

class="com.opensymphony.xwork2.interceptor.TimerInterceptor" />


<interceptor name="params"                                                                               class="com.opensymphony.xwork2.interceptor.ParametersInterceptor" />

</interceptors>

<action name="login" class="com.asm.LoginAction">

<interceptor-ref name="timer"></interceptor-ref>

<interceptor-ref name="params"></interceptor-ref>

<result name="loginSuccess">/success.jsp</result>

<result name="loginFailure">/failure.jsp</result>

</action>

</package>

首先在package中定义了两个拦截器,而后在login action中引用了这两个拦截器,须要说明的是这里使用的拦截器都是系统自带的拦截器。其实在extends所继承的struts-default中就包含了不少拦截器,也包括咱们这里所用的拦截器,但若是在此action中不使用params拦截器,将会报空指针错,由于params拦截器的做用是传递表单参数,若是不使用此拦截器就不能在action中获得表单参数,因此引用时会报空指针错。虽然extends继承的strust-default自带有params拦截器,可是当咱们本身引用了拦截器时,继承struts-default将不会再为咱们分配默认的拦截器(有点相似构造器),可是咱们仍然能够经过<interceptor-ref name="defaultStack"/>来继续使用struts-defalut的拦截器。补充:因为上面的package继承于struts-default,而咱们这里所用到的timer和params都是在struts-defalut中定义过,因此即便咱们在<interceptors>中没有定义过这两个拦截器,也能够直接在action中引用。

使用</interceptor-stack>组合多个拦截器:好比咱们想把上面的params和timer这两个拦截器组合:

<interceptor-stack name="timer_param">

<interceptor-ref name="timer" />

<interceptor-ref name="params" />

</interceptor-stack>

而后再在action引用<interceptor-ref name="timer_param"/>”,效果和分别引用两个是同样的。其实咱们使用strtus-default中的<interceptor-ref name="defaultStack"/>也是使用interceptor-stack方式。

7.Action中的method属性

在struts1.x中咱们知道经过继承DispatchAction能够实现把多个Action进行统一操做,在struts2中实现action的统一操做也很简单。咱们以crud操做为例,把crud集中到一个Action中。
步骤1、创建CRUDAction,内容以下:
package com.asm;

import com.opensymphony.xwork2.ActionSupport;

public class CRUDAction extends ActionSupport {

public String add() {

return "success";

}

public String del() {

return "success";

}

public String update() {

return "success";

}

public String query() {

return "success";

}

}

步骤2、配置此Action,为了清晰明了,专为此Action,创建一个配置文件crud.xml,主要内容以下:
<struts>

<package name="crud" extends="struts-default" namespace="/crud">

<action name="add" class="com.asm.CRUDAction" method="add">

<result name="success">/crud/addSuccess.jsp</result>

</action>

<action name="del" class="com.asm.CRUDAction" method="del">

<result name="success">/crud/delSuccess.jsp</result>

</action>

<action name="update" class="com.asm.CRUDAction" method="update">

<result name="success">/crud/updateSuccess.jsp</result>

</action>

<action name="query" class="com.asm.CRUDAction" method="query">

<result name="success">/crud/querySuccess.jsp</result>

</action>

</package>

</struts>

分析:上面的method方法的值来源于CRUDAction中方法的名字,这样当咱们访问上面的每个Action时,它实质是和method指定的方法关联上。

步骤3、把crud.xml配置文件并入struts.xml中,只需增长以下代码:
<include file="jack.xml"></include>

步骤4、编写相应的jsp页面,在此略去crud文件夹下的四个跳转jsp页面(addSuccess.jsp等),重点是crud.jsp页面。内容以下:
<html>

<%

String path=request.getContextPath();

%>

<body>

<a href="<%=path %>/crud/add.action">添加数据</a><br>

<a href="<%=path %>/crud/del.action">删除数据</a><br>

<a href="<%=path %>/crud/query.action">查询数据</a><br>

<a href="<%=path %>/crud/update.action">修改数据</a><br>

</body>

</html>
步骤5、发布测试。
补充扩展,动态调用DMI:不使用method实现统一.咱们在crud.xml中增长以下内容:
<action name="op" class="com.asm.CRUDAction">

<result name="success">/crud/op.jsp</result>

</action>

而后再在crud.jsp中定义以下连接:

<a href="<%=path %>/crud/op!add.action">添加数据</a><br>

<a href="<%=path %>/crud/op!del.action">删除数据</a><br>

<a href="<%=path %>/crud/op!query.action">查询数据</a><br>

<a href="<%=path %>/crud/op!update.action">修改数据</a><br>

注意查看上面的连接地址,它们都是针对op action,而后再加地上“!+CRUDAction中相应的方法名”,最后再写上.action便可以访问到统一页面op.jsp。这样作虽然能减小页面,可是因为它们实质用到的是同一个Action,因此这就意味着咱们要使用的拦截器相同,相同的跳转result。实际中这种方式不多使用,在此略做了解。若是不想使用动态方法调用,咱们能够经过常量来关闭,即在struts.xml中增长以下配置:
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>

扩展2:在CRUDAction中使用do。举例:咱们在CRUDAction中增长一个新的方法,内容以下:

public String doMain(){

return "success";

}

而后再在在crud.xml中增长以下内容:

<action name="main" class="com.asm.CRUDAction" method="main">

<result name="success">/crud/main.jsp</result>

</action>
注意:配置中method属性值是doMain中去掉do后M小写。而后再在crud.jsp中增长以下连接:
<a href="<%=path %>/crud/main.action">main页面</a><br>

随后即可以访问到.../crud/main.jsp页面了。

8.使用ForwardAction实现页面屏蔽。

咱们在jsp页面之间写连接总会是.../xxx.jsp,而若是咱们想屏蔽掉具体的jsp,只须要所jsp页面配置成一个ForwardAction便可实现。示例以下:在根目录下有一个index.jsp主页,咱们strtus.xml中做以下配置:

<package name="def" extends="struts-default">

<action name="forward">

<result >/index.jsp</result>

</action>

</package>

说明:若是没有未action指定class,默认就是ActionSupport类,若是没有为action指定method属性,则默认执行execute方法,若是没有指定result的name属性,默认值为success。知道了这些再结合ActionSupport的源码就不难理解实现转发的原理了。
随后再在前面第7点扩展中用到的op.jsp中增长以下代码:
<a href="<%=request.getContextPath()%>/forward.action">forward</a>

最后再测试访问op.jsp,在op.jsp中页面中直接点连接即可以跳到index.jsp,观察地址栏发现此时跳到index页面是进行的服务器跳转,若是咱们在上面的配置中的result增长type属性变成<result type="redirect">/index.jsp</result>,实现的跳转就是客户端跳转。 补充:像这种forward形式的action实质是执行的ActionSupport 这个Action。所以配置它的result能够参看此类的api文档,好比它经常使用的result name有:success、login、input等。

8.使用default-Action配置统一访问

default-action-ref,当访问没有找到对应的action时,默认就会调用default-action-ref指定的action.一样在上面的package中增长以下内容:

<default-action-ref name="error"></default-action-ref>

<action name="error">

<result>/other/error.jsp</result>

</action>

上面一段内容就是说当咱们访问的action不能被找到时便指向名为error的action中去,接着咱们在下面配置了这个error Action。可是要注意,一个package内只配置一个<default-action-ref>,若是配置多个,就没法预测结果了. 此时咱们只要输入.../myStruts2/luanFangWen.action这样的形式,它就会去访问这个默认的<default-action-ref>,一般咱们会为它配置一个错误页面,以提示用户访问的页面不存在。 在web开发中,咱们还能够把这个默认的action访问配置成主页,这样当用户访问一些不存在的action时,总会跳到主页上去。

经过此配置,只要是访问一个不存在的action便会转向到.../other目录下的error.jsp页面。可是若是访问是其它的不存在资源则还是报tomcat所标识的404错误,咱们能够在web.xml中做以下配置:
<error-page>

<error-code>404</error-code>

<location>/other/404error.jsp</location>

</error-page>

这样配置后,访问错误页面将跳到.../other/404error.jsp页面中去。补充说明:若是咱们用ie访问时,若是选中了[工具][IE选项][高级][浏览][显示友好的http错误信息],则配置的错误页面将失效,由于找不到资源时会报HTTP404错误,而ie截取到此错误进行了它自身的友好处理,因此咱们设置<error-page>就失效。

小结Action

在struts2中一个普通的java类只要有public String execute()这样的方法均可以配置成一个Action,另外咱们能够实现Action接口来使java类成为一个Action,但一般的作法是继承ActionSupport类,这也是之后的项目中惯用的方法,也是推荐的首选方法。 与struts1.x不一样的是:在struts2中每个Action被请求访问时都会new出这个Action对象,因此Action自己不存在线程安全的问题。

9.使用通配符

创建struts2wildcard项目,此实例基本仿照前面前面第7点的实例改写而成。为了使用通配符,只须要改写配置文件便可。此实例未使用通配时的配置文件以下:

<action name="addUser" class="com.asm.UserAction" method="addUser">

<result name="success">/user/addUser.jsp</result>
</action>

<action name="delUser" class="com.asm.UserAction" method="delUser">

<result name="success">/user/delUser.jsp</result>

</action>

<action name="queryUser" class="com.asm.UserAction" method="queryUser">

<result name="success">/user/queryUser.jsp</result>

</action>

<action name="updateUser" class="com.asm.UserAction" method="updateUser">

    <result name="success">/user/updateUser.jsp</result>

</action>

咱们注释掉上面的配置,使用通配符只需以下内容便可达到相同的效果:
<action name="*User" class="com.asm.UserAction" method="{1}User">

<result name="success">/user/{1}User.jsp</result>

</action>

原理:当有.../addUser.action请求时,若是不能在当前应用中找到彻底相同的addUser名字的Action时,通配符配置这时就起做用了,按通配原则,它便和上面的name="*User"相配成功,这里不难明了*此时代指的内容是add,再来看method偏偏是引用第一个*的内容,因此它的method此时的完整名为addUser,它恰好和com.asmUserAction中的addUser方法相对,因此它会去addUser方法,再来看下面的result配置所指代的页面,它也用到了{1},因此它的完整页面是/addUser.jsp。其实若是咱们有良好的编程命名习惯,全部的Action咱们都只须要进行一次配置。举例:规定全部的Action类都用XXXAction来命名,类中全部的CRUD方法都用add/del/update/query。Jsp页面也用add/del/update/query_XXX.jsp这样的形式。即配置文件能够写成以下形式:
<action name="*_*" class="com.asm.{2}Action" method="{1}">

<result name="success">.../{1}_{2}.jsp</result>

</action>

Name中第一个*表明CRUD操做的名字,第二个*表明类的名字。因此访问连接地址举例以下:

.../del_User.action将访问到User类的del方法,成功后跳到del_User.jsp页面。补充说明{0}是表明name中全部的*组合。

10.使用0配置:ZERO Annotation





11.Result配置详解

说明:在前面的许多案例中咱们所用到的Action基本都继承自ActionSupport这个类,而在这个类中咱们定义了五个字段:SUCCESS,NONE,ERROR,INPUT,LOGING。咱们能够直接返回这些字段值,这些字段值实质是被定义成:String SUCCESS=”success”这样的形式,因此咱们只要在Result元素中用它们的小写便可。
<result>标准完整形式以下:

<result name="success" type="dispatcher">

<param name="location">/default.jsp</param>

</result>

若是咱们都采用默认的形式,最终能够简写成:<result>/default.jsp</result>

探讨type类型:

Type类型值

做用说明

对应类

chain

用来处理Action链

com.opensymphony.xwork2.ActionChainResult

dispatcher

用来转向页面,一般处理JSP

org.apache.struts2.dispatcher.ServletDispatcherResult

redirect

重定向到一个URL

org.apache.struts2.dispatcher.ServletRedirectResult

redirectAction

重定向到一个Action

org.apache.struts2.dispatcher.ServletActionRedirectResult 

plainText

显示源文件内容,如文件源码

org.apache.struts2.dispatcher.PlainTextResult

freemarker

处理FreeMarker模板

org.apache.struts2.views.freemarker.FreemarkerResult

httpheader

控制特殊http行为的结果类型

org.apache.struts2.dispatcher.HttpHeaderResult

stream

向浏览器发送InputSream对象,一般用来处理文件下载,还可用于返回AJAX数据。

org.apache.struts2.dispatcher.StreamResult

velocity

处理Velocity模板

org.apache.struts2.dispatcher.VelocityResult

xslt   

  处理XML/XLST模板

 org.apache.struts2.views.xslt.XSLTResult 

以上对type类型做简要的说明,下面来看实例:当一个Action处理后要返回的Result是另外一个Action时,做如何配置,关键就是配置type类型。下面创建struts2result项目说明
步骤一:创建两个Action:TestAction、Test2Action
步骤二:web.xml配置省略。struts.xml主要配置内容以下:
<struts>

<package name="resultTest" extends="struts-default">

<action name="test" class="com.asm.TestAction">

<result name="success" type="chain">

<param name="actionName">test2</param>

</result>

</action>

<action name="test2" class="com.asm.Test2Action">

<result name="success">/test2Suc.jsp</result>

</action>

</package>

</struts>
说明:在名为“test”的action中,咱们配置result元素的type类型值为chain,意为将继续把Action传递到下一个名为test2的Action中去,在test2.action中会把页面转向到test2Suc.jsp中去。在type类型为chain时,它的param有4个值可配,除了这里用到的name=”actionName”外(必须配置,不然报错),还有name=namespace|method|skipActions。其中namespace指定要转向到action的名字空间,因为此处是转到Action位于同一个namespace下,而namesapace的默认值the current namespace,因此能够省略不写(须要说明的是若是要跳到别的名称空间的action中去,除了使用namespace指定外,还能够用:/要跳去action所在名称空间的值/要跳去的action的name值)。Method用于指定转向到一个目标action所调用的方法,默认是调用下一个action的execute方法,因此此处仍能够省略。SkipActions是一个可选的属性,通常不用。具体能够参看chain所对应类的api帮助。
在本实例中,咱们还在TestAction中设定一个username字段,并在execute方法执行为它赋了值,并在test2Suc.jsp中引用了此值。其实这种作法在web开发中仍是颇有用处,好比能够代替隐藏域。须要注意的是之因此在action的传递中能把设定的这个值保存下去,主要是由于转向都是服务器跳转。若是咱们跳转时采起了客户端跳转,好比在test2 action的result中指定type类型为redirect,要想传递参数能够在result指向的jsp页面中附加参数便可,咱们能够在test2 action的result中写成:
<result name="success" type="redirect">
/test2Suc.jsp?username=${username}
</result>   随后在test2Suc.jsp页面中引用时会出现三个问题:1.EL表达式引用失效,(EL表达式应该使用${param.username}形式)。咱们也可使用<%=request.getParameter("username")%>获取参数值。  2.因为在前面的TestAction中设定的值为中文,而附加到这里的uri请求的参数后面时会出现乱码问题。(可使用URI编码再解码解决此问题)3.值栈取值失效:由于每一次request共享同一个值栈,因此服务器端的forward跳转也是能共享同一值栈得。可是着当test action执行后把请求交由test2 action时,test2 action采起的是redirect重定向到test2Suc.jsp页面,这时其实就是重发的一次request,因此在test action保存的值栈内容所有失效。这也就是为何咱们要附加参数的缘由。而参数是保存在actionContext中,因此采用了#的方式来取出值。图示说明:

步骤三,编写连接页面index.jsp。发布测试:

全局result

若是咱们全部的action均有可能跳到相同的页面,则不防使用全局result。为了方便引用咱们专门创建一个package来存放公共的result。在会用到个全局的跳转时,只须要把继承自这个公共的package便可。

创建公共包,代码以下:

<package name="pubResult" extends="struts-default" abstract="true">

<global-results>

<result name="error">/error.jsp</result>

</global-results>

</package> 

因为它下面没的action配置,因此咱们能够像默认的struts-default包同样,声明abstract=true,这样声明表示此packgage下不会有action,它通常是用来让别的package继承。随后再在要用到全局result中引用这个公共的package。代码以下:

<package name="testGlobal" extends="pubResult" >

<action name="error" class="com.asm.ErrorAction"></action>

<action name="error2" class="com.asm.Error2Action"></action>

</package>这样操做至关于把全局的result加到了此package下的全部action中去。

动态Result:了解

步骤一:创建DynaAction,主要代码以下:
package com.asm;

public class DynaAction extends ActionSupport {

private String username;

private String nextAction;

public String execute() throws Exception {

if (username.equals("admin")) {

nextAction = "admin";

} else if (username.equals("user")) {

nextAction = "user";

} else {

nextAction = ERROR;

}

return SUCCESS;

}

...省略get/set方法

}

步骤2、创建jsp页面dyna.jsp,主要是为了向DynaAction中传递username参数。

步骤3、相关配置以下:

<package name="dynaTest" extends="pubResult">

<action name="dyna" class="com.asm.DynaAction">

<result name="success" type="chain">${nextAction}</result>

</action>

<action name="admin" >

<result>/admin.jsp</result>

</action>

<action name="user">

<result>/user.jsp</result>

</action>

</package>

分析:当dyna.jsp把参数传递到DynaAction中去时,若是传递的值为admin,咱们便设定了nextAction的值admin,在配置文件中咱们经过${nextAction}(用在struts配置文件中的ognl,其实nextAction的值是存在值栈中,咱们经过${}这样的形式取出。在此只做了解)来获取值便为admin,随后再继续把请求传递到下一个Action中去(此时也即admin.action),为了方便咱们设定了两个ForwardActionadmin.action和user.action。这样即可以跳到指定的jsp页面。 原理:dyna.action执行后会继续把请求传递给下一个Action,而下一个Action的究竟是哪个Action,是经过DynaAction中动态指定的,好比这里是根据传递的username的值指定。

12.异常处理

步骤1、创建struts2exception项目下,在该项目下创建登陆页面login.jsp。主要代码以下:
<form action="<%=request.getContextPath() %>/login.action">

username:<input type="username" name="username"><br>

<input type="submit" value="login">

</form>

步骤2、创建LoginAction,代码以下:
package com.asm;

public class LoginAction extends ActionSupport {

private String username;

public String execute() throws Exception {

if (username.equals("exception")) {

throw new ClassNotFoundException("类未被找到");

} else if (username.equals("global")) {

throw new Exception("全局异常");

} else {

return SUCCESS;

}

}
...省力get/set方法

}

步骤3、struts.xml配置文件以下:

<struts>

<package name="ex" extends="def">

<action name="login" class="com.asm.LoginAction">

<exception-mapping result="myException"

exception="java.lang.ClassNotFoundException">

</exception-mapping>

<result name="myException">/exception.jsp</result>

<result name="success">/main.jsp</result>

</action>

</package>

<package name="def" extends="struts-default" abstract="true">

<global-results>

<result name="myGlobal">/globalException.jsp</result>

</global-results>

<global-exception-mappings>

<exception-mapping result="myGlobal"

exception="java.lang.Exception">

</exception-mapping>

</global-exception-mappings>

</package>

</struts>

分析:异常处理机制较为简单,因此在此只略做说明。当登陆时输入“exception”时,在LoginAction中会抛出会一个ClassNotFoundException异常,此异常咱们采起的局部异常处理的方式,若是登陆时输入“globla”,则会抛出Exception异常,此异常咱们采起的是全局异常的处理方式,在ex包中咱们继承了全局异常所在的包。提示:<exception-mapping>中的result属性的值来源于<result>元素中的name属性的值。从实例能够看出,咱们通常把这种全局性的异常放在一个抽象包中供其实包继承。

3、在Action获取Scope对象

引言:在前面的Action操做中,关键就是Action中的exectue方法,可是此方法并无request、session、application等对象做为参数,天然就不能利用这些对象来操做。下面咱们创建struts2scope项目,并用四种方式来获取这些对象:

方式1、与Servlet解耦合的非IOC方式

获取的scope对象与容器无关,经过ActionContext获取。
LoginAction代码以下:

package com.asm;

public class LoginAction extends ActionSupport {

private String username;

ActionContext context;

Map request;

Map session;

Map application;

public String execute() throws Exception {

context=ActionContext.getContext();

request=(Map) context.get("request");

session=context.getSession();

application=context.getApplication();

request.put("req", "requst属性");

session.put("ses", "sesion属性");

application.put("app", "application属性");

return SUCCESS;

}

...省略usernameget/set方法

}

struts.xml配置以下:

<struts>

<package name="scope" extends="struts-default">

<action name="login" class="com.asm.LoginAction">

<result>/loginSuc.jsp</result>

</action>
</package>

</struts>

login.jsp内容以下:

<form action="<%=request.getContextPath() %>/login.action">

用户名:<input type="text" name="username"><br>

<input type="submit" value="login">

</form>

loginSuc.jsp的主要内容以下:
${requestScope.req}

${sessionScope.ses}

${applicationScope.app}
<h4>如下使用scope.getAttribute的形式来接受</h4>

request <%=request.getAttribute("req") %><br>

session <%=session.getAttribute("ses") %><br>

application:<%=application.getAttribute("app") %><br>

分析:经过ActionContextgetContext静态方法获得ActionContext对象,而后ActionContext对象调用get方法来获取一个存储在request范围中的对象。咱们使用el或经过request.getAttribute这样的方式都可以获取对象值,这说明了这些Map request对象实际是存储在request范围内的对象。

方式2、与Servlet解耦合的IOC方式

咱们创建Login2Action,主要代码以下:
package com.asm;

public class Login2Action extends ActionSupport implements RequestAware,SessionAware,ApplicationAware {

private String username;

Map request;

Map session;

Map application;

public String execute() throws Exception {

request.put("req", "requst属性");

session.put("ses", "sesion属性");

application.put("app", "application属性");

return SUCCESS;

}

public void setRequest(Map<String, Object> request) {

this.request=request;

}

public void setSession(Map<String, Object> session) {

this.session=session;

}

public void setApplication(Map<String, Object> application) {

this.application=application;

}

...省略usernameget/set方法

}
注册此Action的name为login2,随后修改登陆提交为.../login2.action。即可以发布测试。说明:此方法其实和方式一很类似,只是在方式一中咱们须要手动的为Map request赋值,可是在方式二中它是经过实现接口,在重写接口中的方法中完成对Map requset的赋值,因此称之IOC方式。借助此例,略谈下依赖注入与控制反转:所谓依赖注入就是一个对象本身自己的初始化是依赖其它对象。好比这里Map request这些对象会依赖struts2来给其初始化,称为依赖注入,而依赖注入的就表示,这些对象的控制权再也不由此类自己掌握,而是交给了别的对象,便是控制权反转了。 强调:方式二是开发中主要用的方式,应重点掌握

方式3、与Servlet耦合的非IOC方式

创建Login3Action,代码以下:
package com.asm;
public class Login3Action extends ActionSupport {

private String username;

HttpServletRequest request;

HttpSession session;

ServletContext application;

public String execute() throws Exception {

request = ServletActionContext.getRequest();

session = request.getSession();

application = ServletActionContext.getServletContext();

request.setAttribute("req", "requst属性");

session.setAttribute("ses", "sesion属性");

application.setAttribute("app", "application属性");

return SUCCESS;

}

...省略usernameget/set方法。

}
此方法获取的纯粹的Scope对象,它与容器相关,这些Scope对象操做更强。一样只须要注册此Action并修改登陆页面即可进行测试。

方式4、与Servlet耦合的IOC方式

创建Login4Action,代码以下:

package com.asm;
public class Login4Action extends ActionSupport implements ServletRequestAware,ServletContextAware{

private String username;

ActionContext context;

HttpServletRequest request;

HttpSession session;

ServletContext application;

public String execute() throws Exception {

context=ActionContext.getContext();

session=request.getSession();

request.setAttribute("req", "requst属性");

session.setAttribute("ses", "sesion属性");

application.setAttribute("app", "application属性");

return SUCCESS;

}

public void setServletRequest(HttpServletRequest request) {

System.out.println("测试:"+request);

this.request=request;

}

public void setServletContext(ServletContext application) {

System.out.println("测试:"+application);

this.application=application;

}

...省略usernameget/set方法

}

一样只须要注册此Action并修改登陆页面即可发布测试 

4、OGNL与ValueStack(VS)

1.值栈入门

下面咱们创建struts2ognl项目来练习ognl的使用。
步骤1、搭建strust2的开发环境
步骤2、创建LoginAction,主要代码以下:
package com.asm;

public class LoginAction extends ActionSupport{

private User user;

public String execute() throws Exception {

return SUCCESS;

}

...省略userget/set方法

}

步骤3、配置此Action,struts.xml的主要内容以下:
<struts>

<constant name="struts.devMode" value="true"></constant> 

<package name="ognl" extends="struts-default">

<action name="login" class="com.asm.LoginAction">

<result>/loginSuc.jsp</result>

</action>

</package>

</struts>
步骤4、编写login.jsp页面,主要代码以下:
<body>

<form action="<%=request.getContextPath()%>/login.action"  method="get">

用户名:<input type="text" name="user.username"><br>

密  码:<input type="password" name="user.password"><br>

<input type="submit" value="login">

</form>

</body>

步骤5、编写loginSuc.jsp页面,主要代码以下:
<body> 

调试:<s:debug></s:debug>

获取值栈中的username属性:<s:property value="user.username"/> <br>
</body>

步骤6、发布测试及说明
当咱们输入用户名并成功跳到logSuc.jsp页面后,会获得登陆时输入的用户名信息。下面简要说明这一过程:
(1).login.jsp登陆提交登陆信息给login.action

(2).struts2监听到客户端的login.action请求,按配置文件要求,把此请求交给LoginAction处理。这表示会去new LoginAction(), 当struts2  new出此Action对象后会把这个对象放在context map中,只是这个Action很是特殊,因此放在值栈中,而放在值栈中的对象是能够直接引用的,放在其它context map中的对象引用时会要求加#。
(3).当new LoginAction时,表示它也会初始化此类中的对象,好比这里会去初始化User对象,可是要注意的是若是咱们在用户名和密码什么都不输,再来用debug来看值栈中的user是,发现它仍会new此对象,由于尽管咱们没用输入值,可是后台的set方法仍是要被调用,因此会new出此对象,可是若是咱们直接输入.../struts2ognl/login.action时咱们会发现跳转到loginSuc.jsp页面时,用debug来看值栈中此User user,发现它的值为null。第二点要注意的是咱们必须为User类提供默认的构造方法,不然将会出现以下错误: java.lang.InstantiationException: com.asm.vo.User
总结:1.Action会在请求时被建立,且会把建立的对象放到值栈中。
2.Action中的对象字段只有在须要时才会以new 的形式初始化,并且这些对象字段必须提供默认的构造方法。
3.ValueStack对象贯穿整个Action的生命周期(每一个Action类的对象实例会拥有一个ValueStack对象)。当Struts 2接收到一个.action的请求后,会先创建Action类的对象实例,但并不会调用Action方法,而是先将Action类的相应属性放到ValueStack对象的顶层节点(vs对象至关于一个栈)。

补充:值栈(根)对象也能够直接使用EL表达式访问,好比这里能够直接经过${user.username}来获取username的值,咱们知道el表达式只能访问四种scope范围内的对象,那为何这里能访问到值栈对象呢?缘由是struts2对HttpServletRequet进行了一次封装,封装的代码主要是重写了getAttribute方法,简述重写此方法的核心代码:首先在原始的HttpServletRequest对象中查找el表达式中的属性,若是找不到再经过ActionContext获取值栈对象,进而再从值栈对象中查找el表达式中要访问的属性。

2.OGNL入门

下面咱们在com.asm.vo.User类中增长一个字段private Address addres;,并提供此字段的get/set方法,随后再在login.jsp中增长以下代码:
城  市:<input type="text" name="user.addres.city"><br>
而后再在loginSuc.jsp中增长以下代码:

获取城市属性:<s:property value="user.addres.city"/><br>
而后测试,会获得登陆时输入的城市信息(中文会有乱码)。下面借助此例谈ognl的定义:在这个例子中,咱们的LoginAction中有一个User对象,而在User对象中又有一个Address对象,这些对象之间依靠这种类的字段进行关联,或者说是依靠字段属性进行导航,这也就是OGNL的定义:Object Graph Navigation Language:对象图导航图语言,它是创建在值栈技术之上的一种全新语言。
补充:用%{}能够取出存在值堆栈中的Action对象,直接调用它的方法.咱们在loginSuc.jsp中增长以下内容调用LoginAction中的get方法:
调用值栈对象中的方法:<s:property value="%{get()}"/> 
LoginACtion中增长的get方法以下:
public String get(){

return "这是User中的get方法";

}

3.普通方法访问

首先在User中增长一个成员方法,代码以下:
public String get(){

return "这是User中的get方法";

}
在LoginAction中也有相似的get方法,随后再在loginSuc.jsp中增长以下代码:
调用值栈对象中的普通方法(2)<s:property value="user.username.length()"/><br>

调用值栈对象中的普通方法(1)<s:property value="user.get()"/><br>

调用LoginAction中的普通方法:<s:property value="get()"/><br>
最后测试,发现这些方法均可以访问到。

4.静态方法访问

在LoginAction中增长以下方法:
public static String getSta() {

return "这是LoginAction中的静态方法";

}

而后在loginSuc.jsp中增长以下代码:

调用Action中的静态方法:<s:property value="@com.asm.LoginAction@getSta()"/><br>

调用LoginAction中的静态方_方式(2)<s:property value="@vs@getSta()"/><br>

说明:咱们在方式二中用到@vs,只有那些值栈中的对象才能够这样写。
而后访问,发现访问不到,由于在struts2.1.6的版本中,struts.ognl.allowStaticMethodAccess的默认值为false,咱们只需在struts.xml中增长以下内容:
<constant name="struts.ognl.allowStaticMethodAccess" value="true"/>
再来访问时即可以访问到。

5.默认类Math的访问

在loginSuc.jsp中增长以下代码:
调用Math类中的静态方法:<s:property value="@java.lang.Math@min(1,2)"/><br>

调用Math类中的静态方法_方式(2):<s:property value="@@min(1,2)"/><br>

调用Math类中的字段:<s:property value="@@PI"/><br>

说明:由于是默认的类,因此能够省略类名

6.调用普通类的构造方法

创建一个新的类:Student,在此省略代码。
而后在loginSuc.jsp中增长以下代码:
调用普通类中的构造方法  :
<s:property value="new com.asm.vo.Student('jack','20','85.5')"/><br>

调用普通类中的构造方法并访问其字段 :
<s:property value="new com.asm.vo.Student('jack','20','85.5').name"/>

说明:第一种是只new出对象,显示的时候实际上是调用对象的toString方法。

7.集合对象初步

首先在LoginAction中增长以下字段并提供相应的get/set方法:
private List myList = new ArrayList();

private Set mySet = new HashSet();

private Map myMap = new HashMap();
而后再在execute方法中初始化这些集合对象,代码以下:
myList.add("list1");

myList.add("list2");

myList.add("list3");

myList.add("list4");

mySet.add("set1");

mySet.add("set3");

mySet.add("set1");

mySet.add("set2");

myMap.put("m1", "map1");

myMap.put("m3", "map3");

myMap.put("m2", "map2");
最后在loginSuc.jsp中增长以下代码:
获取List<s:property value="myList"/><br>

获取List中的第一个元素:<s:property value="myList[0]"/><br>

获取Set<s:property value="mySet"/><br>

获取Set中的第一个元素(set无序,不能取到)<s:property value="mySet[0]"/><br>

获取Map<s:property value="myMap"/><br>

获取Map中的key=m1的元素的值:<br>

方式一:<s:property value="myMap.m1"/>

方式二:<s:property value="myMap['m1']"/><br><hr>

获取List的大小:
<s:property value="myList.size"/>|<s:property value="myList.size()"/><br>

获取Map中全部键:<s:property value="myMap.keys"/><br>

获取Map中全部值:<s:property value="myMap.values"/><br>
最后测试,这些东西很少做解释。

8.集合对象进阶

首先在LoginAction中增长以下字段并提供相应的get/set方法:
private List studentList = new ArrayList();
而后再在execute中为其初始化赋值,代码以下:
studentList.add(new Student("jack", 20, 86.0f));

studentList.add(new Student("lily", 22, 96.5f));

studentList.add(new Student("tom", 23, 56.5f));

最后在loginSuc.jsp中增长以下代码:
获取List中的Student对象:<s:property value="studentList"/><br>

利用投影获取List中的name属性:<s:property value="studentList.{name}"/><br>

利用投影获取List中的age属性:<s:property value="studentList.{age}"/><br>

利用投影获取List中的第一个对象的name属性:<s:property value="studentList.[0]{name}"/>   或者<s:property value="studentList.{name}[0]"/><br>

利用选择获取Listgrade>60student信息:

<s:property value="studentList.{?#this.grade>60}"/><br>

利用选择获取Listgrade>60student名字信息:

<s:property value="studentList.{?#this.grade>60}.{name}"/><br>

利用选择获取Listgrade>60的第一个student名字信息:

<s:property value="studentList.{?#this.grade>60}.{name}[0]"/><br>

利用选择获取Listgrade>60的第一个student名字信息(链表)

<s:property value="studentList.{^#this.grade>60}.{name}"/><br>

利用选择获取Listgrade>60的最后一个student名字信息(链表)

<s:property value="studentList.{$#this.grade>60}.{name}"/><br>

说明:这里重点是说明?#的使用,结合此例来看,studentList中有许多Stutdent对象,咱们能够用条件来限制取哪些对象,这些条件必须以?#开始,而且条件要用{}括起。而this是指在判断studentList中的对象是否符合条件的当前对象。?#是指取出符合条件的全部Student对象,而^#是指取出符合条件的第一个对象,$#是指取出符合条件的最后一个对象。

9.N语法top语法

咱们在loginSuc.jsp中增长以下下代码:
N语法[0]<s:property value="[0]"/><br>

N语法[1]<s:property value="[1]"/><br>
N语法[0].top<s:property value="[0].top"/><br>

N语法[1].top<s:property value="[1].top"/><br>
N语法top<s:property value="top"/><br>

N语法取值:<s:property value="[0].user.username"/><br>

N语法取值:<s:property value="top.user.username"/><br>
说明:规定栈顶的对象为[0],而咱们只使用[0]的意思是从值栈中第一个对象取,一直取至栈底。N的意思是从值栈中的第N个对象开始,取到栈底为止。若是要想访问某个对象,须要使用[N].top,它的意思是取出符合N语法的栈顶对象,好比在这里,[0]会取出两个对象,而[0].top是取出这两个对象的栈顶对象。纯top能够简洁地取出值栈中的栈顶对象。
为何要提出N语法,当咱们经过chain链访问时,值栈中可能有两个以上的Action对象,若是这些对象中存在相同的属性,N便能正确区分他们。一般,这些Action对象的入栈顺序是:先访问先入栈。

从上面的N语法取值实例中,咱们知道[N]top语法的一个重要做用就是能经过它们引用值栈对象中的属性。结合前面的五种[N]top语法实例,不难理解这里的取值实例。
补充:在此实例中,咱们用<s:debug>调试会发现,值栈中还有一个DefaultTextProvider对象(由于此Action继承自ActionSupport),它的做用是获取资源文件中的内容(其实本质是ActionSupport重写了getText()方法),这也就是在国际化问题中咱们能直接调用它的getText()方法的缘由。

10.获取Stack Context中的信息

咱们知道,除了能够从值栈中获取信息,还能够从Stack Context中获取信息,只是要加上#,下面咱们经过scope对象来演示。首先是在LoginAction中增长以下字段:
Map myRequest;
Map mySession;
随后再用前面提到的“在Action中获取Scope对象”的方式二来完成这些对象的初始化。即实现RequestAware和SessionAware接口。而后再在execute方法中增长以下内容:
myRequest.put("req", "Req属性");

mySession.put("ses", "Ses属性");
最后在loginSuc.jsp中增长以下代码:
获取Request属性:<s:property value="#request.req"/><br>

获取Session属性:<s:property value="#session.ses"/><br>

获取parameters属性:<s:property value="#parameters.mes"/>
说明:咱们获取这些对象都用了#,由于这些对象都是存在通常的Context Map中,而不是存在值栈中。别最后一个信息的获取是由于咱们在login.jsp中增长了以下代码:
<input type="hidden" name="mes" value="the message is transfer by hidden">
关于这些scope的更多信息能够参看下表:

名称 

做用 

例子 

parameters

包含当前HTTP请求参数的Map

#parameters.id[0]做用至关于request.getParameter("id") 

request

包含当前HttpServletRequest的属性(attribute)的Map 

#request.userName至关于request.getAttribute("userName") 

session

包含当前HttpSession的属性(attribute)的Map 

#session.userName至关于session.getAttribute("userName") 

application

包含当前应用的ServletContext的属性(attribute)的Map 

#application.userName至关于application.getAttribute("userName") 

Attr

用于按request > session > application顺序访问其属性

#application.userName至关于application.getAttribute("userName") 

11.总结$ # %的区别

$用于i18n和struts配置文件

#取得ActionContext的值

%将原来的文本串解析为ognl,对于原本就是ognl的文本不起做用。形式:%{要解析的文本串}

12.总结OGNL[重点]

OGNLObject Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。Struts2使用OGNL做为默认的表达式语言。

相对于EL表达式,它提供了平时咱们须要的一些功能,如:支持对象方法调用,支持各种静态方法调用和值访问,支持操做集合对象。OGNL有一个上下文的概念,这个上下文件实质就是一个Map结构,它实现了java.utils.Map接口,在struts2中上下文的实现为ActionContext,下面是上下文的结构示意图:

当struts2接受一个请求时,会迅速建立ActionContext,ValueStack,action。而后把action存放进ValueStack,因此action的实例变量能够接受OGNL访问。

访问上下文中的对象须要使用#号标注命名空间,如#application、#session。另外OGNL会设定一个根对象,在struts2中根对象就是ValueStack值栈对象,若是要访问根对象中对象的属性,则能够省略#命名空间,直接访问该对象的属性便可。在struts2中,根对象的实现类为OgnlValueStack,该对象不是咱们想象的只存放单个值,而是存放一组对象,在OgnlValueStack类里有一个List类型的变量,就是使用这个List变量来存放一组对象。在root变量(List类型)中处于第一位的对象叫栈顶对象,一般咱们在Ognl表达式里直接写上属性的名称便可访问root变量里对象的属性,搜索顺序是从栈顶对象开始寻找,若是栈顶对象不存在该属性,就会从第二个对象寻找,若是没有找到就从第三个对象寻找,依次往下寻找。 注意:struts2中 ,OGNL表达式须要配合struts的标签才可使用。

5、拦截器

在前面咱们已经初步使用过拦截器,下面继续细细探讨。

1.概述strust2中的拦截器

拦截器是Struts2框架的核心,它主要完成解析请求参数、将请求参数赋值给Action属性、执行数据校验、文件上传等工做。Struts2设计的灵巧性,拦截器起了关键性的做用,当须要扩展Struts2功能时,只须要提供对应拦截器,并将它配置在Struts2容器中便可;若是不须要该功能时,也只须要取消该拦截器的配置便可。   
Struts2内建了大量的拦截器,这些拦截器以name-class对的形式配置在struts-default. xml文件中,其中name是拦截器的名字,就是之后咱们使用该拦截器的惟一标识;class则指定了该拦截器的实现类,若是咱们定义的package继承了Struts2的默认struts-default包,则能够自由使用它下面定义的拦截器,不然必须本身定义这些拦截器。

2.自定义拦截器

自定义拦截器须要特别注意的是不要忘记引入struts2默认的拦截器。为了实现某些操做,咱们能够自定义拦截器,自定义拦截器有三种方式定义。分别为实现Interceptor接口,继承抽象类AbstractInterceptor,继承MethodFilterInteceptor类。

方式一,实现Interceptor接口。

准备工做,新建struts2interceptor项目。构建一个登陆环境:当咱们点登陆连接时,便成功登陆(为了方便,这里不进行验证)。即在link.jsp页面中写以下连接:<a href="<%=request.getContextPath()%>/login.action">登陆</a> 而后,咱们点击此连接即可以登陆。login.action在strutst.xml中的的配置以下:
<package name="interceptor" extends="struts-default">
    <action name="login" class="com.asm.LoginAction">

<result name="success">/success.jsp</result>

</action>
</package>
com.asm.LoginAction为了简单,com.asm.LoginAction老是返回SUCCESS;这样请求这个Action总会返回到.../success.jsp页面。
编写拦截器:MyInterceptor类,内容以下:
package com.asm;

import com.opensymphony.xwork2.ActionInvocation;

import com.opensymphony.xwork2.interceptor.Interceptor;

public class MyInterceptor implements Interceptor {

public void destroy() {

}

public void init() {

}

public String intercept(ActionInvocation invocation) throws Exception {

System.out.println("开始拦截");

String result = invocation.invoke();

System.out.println("结束拦截");

return result;

}

}
为了使用此拦截器,咱们必须将此拦截器进行注册,随后再在要使用此拦截器的Action中引用。即首先在<package>中注册,内容以下:
<interceptors>

<interceptor name="myIpt" class="com.asm.MyInterceptor"></interceptor>

</interceptors>

注册完成后,若是咱们要在login.action中使用此拦截器,只须要在<action>中增长以下内容:
<interceptor-ref name="myIpt"></interceptor-ref>

这样便成功为LoginAction配置了咱们自定义的拦截器MyInterceptor,下面只需发布测试。

实例流程分析:当咱们为LoginAction配置了拦截器时,而且有客户端请求此Action时,会首先被此拦截器拦住,而后执行System.out.println("开始拦截"),随后咱们调用invocation.invoke()方法,它会把请求继续传递给下一个拦截器,下一个拦截器也会继续执行相应代码后再调用invoke方法继续传递,直到请求到达最后一个拦截器,它会把请求传递给Action,好比,咱们这里只用到了一个拦截器,当它执行完成后,会把请求直接转交到LoginAction处理,LoginAction处理完成后,它会返回结果给MyInterceptor拦截器。

方式2、继承AbstractInterceptor抽象类

建立拦截器类MyAbstractInterceptor:主要代码以下:
package com.asm;
import com.opensymphony.xwork2.ActionInvocation;

import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class MyAbstractInterceptor extends AbstractInterceptor {

public String intercept(ActionInvocation invocation) throws Exception {

System.out.println("Abs开始拦截");

String result = invocation.invoke();

System.out.println("Abs结束拦截");

return result;

}

}

而后注册此拦截器,在<interceptors>元素进行进行配置,内容以下:
<interceptor name="myAbs" class="com.asm.MyAbstractInterceptor"></interceptor>

随后再在LoginAction中引用此拦截器,即在<action name="login" ...>配置以下内容:
<interceptor-ref name="myAbs"></interceptor-ref>

最后发布测试。

方式3、继承MethodFilterInteceptor类

建立拦截器类MyMethodFilterInterceptor,主要代码以下:
package com.asm;

import com.opensymphony.xwork2.ActionInvocation;

import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;

public class MyMethodFilterInterceptor extends MethodFilterInterceptor{

protected String doIntercept(ActionInvocation invocation) throws Exception {

System.out.println("method开始拦截");

String result=invocation.invoke();

System.out.println("method结束拦截");

return result;

}

}

而后注册此拦截器,在<interceptors>元素进行进行配置,内容以下:
<interceptor name="myMet" class="com.asm.MyMethodFilterInterceptor">
</interceptor>

随后再在LoginAction中引用此拦截器,即在<action name="login" ...>配置以下内容:
<interceptor-ref name="myMet"></interceptor-ref>

最后发布测试。

分析:当配置到此,实质便为LoginAction配置了三个拦截器,当咱们点击登陆时会在控制台打印出以下语句:
开始拦截

Abs开始拦截

method开始拦截

--先执行拦截器,再执行此Action

method结束拦截

Abs结束拦截

结束拦截
其实当咱们点击登陆时,原本是要访问LoginAction,最后会把LoginAction的执行结果传递给访问者。可是当咱们配置了拦截器时,当咱们去访问Action时,会首先被拦截,随后拦截器执行一些操做后才会继续把请求传递下去。下面做图说明拦截流程:

结合现实理解:好比咱们要去某楼层找某人(LoginAction)取一个资源(LoginAction处理后返回的结果,这里表现为success.jsp),(1)进楼层时会被大门保安拦截检查(第一个拦截器:MyInterceptor拦截器),(2)检查经过后再进电梯时会被电梯保安员检查(第二个拦截器:MyAbstractInterceptor拦截器),(3)检查经过后再上到某楼层会被楼层保安员检查(第三个拦截器:MethodAction拦截器

),(4)检查经过后会找到某个(LoginAction),并与某个交谈(LoginAction处理),随后某我的和咱们会带着请求的资源出去,(5)出去时,会依次被楼层,电梯,大门保安员检查,最终检查经过。某我的把资源给咱们(实质就是返回请求资源给客户端)。 其实拦截器的执行流程和过滤器差很少,因此咱们不防用过滤器的眼光来看这些拦截器。 
注意:咱们在为LoginAction配置拦截器时,都没使用默认的拦截器,是缘由这里的测试能够不用,可是之后在咱们使用自定义的拦截器是,必定要加上默认的拦截器,不然会致使许多不可预知的结果。
补充:从上面的图并结合代码,咱们能够看出拦截器的核心过程应该是ActionInvocation对这些拦截器回调处理,下面咱们创建com.asm.interceptor.simulation来模拟这一过程,具体的代码参源文件,在此略去。在此咱们做图分析ActionInvocation的实现过程:

补充2:上面分别使用了三种方式来建立自定义的拦截器,第一种方式是最原始的实现方式,第二种方式的好处是咱们能够没必要重写全部的方法,较经常使用。第三种方式进行了扩展,下面将会展现它的扩展性能。

3.使用来MethodFilterInterceptor灵活拦截

步骤1、创建MethodAction,代码以下:
package com.asm;

import com.opensymphony.xwork2.ActionSupport;

public class MethodAction extends ActionSupport{

public String m1(){

return SUCCESS;

}

public String m2(){

return SUCCESS;

}

public String m3(){

return SUCCESS;

}

}

步骤2、注册此Action,并为此Action配置拦截器。配置内容以下:
<action name="*_*" class="com.asm.MethodAction" method="{2}">

<result name="success">/{2}Suc.jsp</result>

<interceptor-ref name="myMet">

</interceptor-ref>

</action>

咱们为此Action配置了前面写的MyMethodFilterInterceptor拦截器,并在link.jsp中增长以下连接:
<a href="<%=request.getContextPath()%>/Method_m1.action">m1</a><br>

<a href="<%=request.getContextPath()%>/Method_m2.action">m2</a><br>

<a href="<%=request.getContextPath()%>/Method_m3.action">m3</a><br>

当点m1时会访问到m1Suc.jsp页面, 点m二、m3会分别访问到m2Suc.jsp、m3Suc.jsp页面。如今假如咱们想访问m二、m3时不被拦截,咱们只需修改MyMethodFilterInterceptor注册:修改内容为:
<interceptor name="myMet"  class="com.asm.MyMethodFilterInterceptor">

<param name="excludeMethods">m2,m3</param>
</interceptor>
它的做用和增长<param name="includeMethods">m1</param>等价。上面是指定m2,m3方法调用时不被拦截,这里是指定只拦截m1。除了这种在注册拦截器时指定拦截外,还能够在引用拦截器时指定,即以下形式:
<interceptor-ref name="myMet">

<param name="excludeMethods">m2,m3</param>

<param name="includeMethods">m1</param>
</interceptor-ref>
上面的两处<param>配置是等价的,可是若是〈param〉配置冲突,谁起做用?即若是咱们对m1配置了excludeMethods同时又配置了includeMethods时,谁起做用,咱们能够进行这些冲突的验证。如下是验证结果:
引用配置(在Action引用拦截器时配置)时,以includeMethods的配置为准。一旦咱们为拦截器使用了<param>配置,而对m1这样的方法不配置任何,就不会被拦截。可是若是不使用<param>,它们所有都要被拦截。
注册配置时(在注册拦截器时配置),状况和“引用配置”彻底同样。
引用配置和注册配置冲突时,以引用配置为准。

4.使用默认的execAndWait拦截器

当咱们进行数据库查询等相关的操做时,若是服务器负荷太重可能不能及时把数据查询出来,进而会在状态拦显示“正在打开...”,但却一直转不到相关的页面,这将给客户端带来不便,甚于不少人会所以不肯使用网站的全部服务。对此咱们能够在客户提交时,立刻转到一个页面,并在该页面显示“您的请求已提交,服务器正在查询,请等待...”的内容,这样客户将不会陷于无赖的等待中。 对于此要求,struts2能够轻松帮咱们完成。下面新建struts2wait项目演示此实例。
创建LoginAction,代码以下:
package com.asm;
public class LoginAction extends ActionSupport {

public String execute() throws Exception {

Thread.sleep(5000);

return SUCCESS;

}

}

说明:为了模拟服务器负荷太重,查询时间要很长。咱们在使用了线程休眠的方式。

随后配置此Action,配置的主要内容以下:

<action name="login" class="com.asm.LoginAction">

<interceptor-ref name="defaultStack"></interceptor-ref>

<interceptor-ref name="execAndWait"></interceptor-ref>

<result name="wait">/wait.jsp</result>

<result name="success">/success.jsp</result>
</action>

注意:在配置前咱们先是使用了默认的拦截器,再此强调在咱们为Action配置拦截器时,应该老是配上默认的拦截器。随后咱们使用了execAndWait拦截器,如须要配置此拦截器,此拦截器必定要配置在最后,不然会出现一些难预知的结果。若是使用此拦截器,咱们一般还会配置wait的result结果集,由于“On the initial request or any subsequent requests (before the action has completed), the wait result will be returned. The wait result is responsible for issuing a subsequent request back to the action, giving the effect of a self-updating progress meter”,大概意思就是当咱们请求的Action在未执行完,就是未返回结果时,会首先把wait result返回,而在wait result所指定的页面中一般会再次发送请求给原始的Action。因此wait.jsp的主要内容以下:
<head>

<meta http-equiv="refresh" content="1;login.action">

</head>

<body> 查询请求已提交,正在查询数据,请等待... </body>

在此页面中,咱们指定了每隔1秒便发送请求到login.action中去。这样,客户端即可以及时获取查询结果。结合此实例,咱们简要分析流程:当咱们发出请求到此Login.Action中去时,首先会被exeAndWait拦截器拦截到,这样它便跳转到wait.jsp页面,在wait.jsp页面中每隔1秒咱们会继续发送此Action的请求,当再次请求到达LoginAction时,若是它已经返回,则会跳到此Action返回的页面,若是LoginAction未返回,则继续停留在wait.jsp中,再隔1秒又再次发送请求到LoginAction中去。

其实若是服务器能很快查询出结果,咱们则不须要用到wait.jsp页面,咱们只需在<interceptor-ref name="execAndWait"></interceptor-ref>中增长以下一段配置:
<param name="delay">6000</param> 这样便延迟请求到达wait.jsp页面,这样当请求到达时它会在LoginAction中执行6秒时间再到wait.jsp,而6秒LoginAction足以执行完并返回结果,因此当拦截器

执行时首先检查到此Action已经返回结果。则拦截器会直接用此返回页面,若是此时发现LoginAction并未执行完,它便会把wait resutl指定的页面返回。须要说明的是,一般咱们设定的延迟最多一秒,这里为了演示,设置的很长。图示此拦截器原理:
 
关于此拦截器的详细的配置及文档说明能够参看ExecuteAndWaitInterceptor类的api信息。

5. TokenInterceptor防止表单重复提交。

因为某些缘由,用户在进行相似表单提交的操做后,觉得表单未被提交,会进行屡次的重复提交。为了不用户屡次提交给服务器带来负荷。咱们会对表单提交这样的操做进行一些处理,以告诉用户不要重复提交。下面咱们创建struts2token项目,使用struts2token拦截器来实现此案例。
步骤一,编写login.jsp页面,内容以下:
<%@ page language="java" pageEncoding="UTF-8"%>

<%@ taglib uri="/struts-tags" prefix="s" %>

<html>

<body>

<form action="<%=request.getContextPath()%>/login.action" >

姓名:<input type="text" name="username"><br>

密码:<input type="password" name="password"><br>

<input type="submit" value="登陆">

<s:token></s:token>

</form>

</body>

</html>
说明,此登陆页面中的关键技术就是使用了标签库中的<s:token></s:token>标签,它的做用就是在用户访问此页面时会生成一个sessionId,在提交时会服务器会据此验证表单是否已提交。“To set a token in your form, you should use the token tag. This tag is required and must be used in the forms that submit to actions protected by this interceptor”,这句话的大概意思就是咱们必需要在提交的表单中使用这个token tag,这样提交到的Action便能配置TokenInterceptor拦截器验证表单是否重复提交。
步骤二,编写LoginAction,主要代码以下:
package com.asm;
public class LoginAction extends ActionSupport {

public String execute() throws Exception {

System.out.println("---->执行execute方法...");

return SUCCESS;

}

}
步骤三,struts.xml主要配置内容以下:
<struts>

<package name="tokenTest" extends="struts-default">

<action name="login" class="com.asm.LoginAction">

<result name="success">/success.jsp</result>

<result name="invalid.token">/subError.jsp</result>

<interceptor-ref name="token"></interceptor-ref>

<interceptor-ref name="defaultStack"></interceptor-ref>

</action>

</package>

</struts>

说明:在此Action下,咱们配置了token拦截器,另注意到在此Action下咱们还配置了一个“invalid.tokenresult,由于“This interceptor uses a fairly primitive technique for when an invalid token is found: it returns the result invalid.token, which can be mapped in your action configuration”。它的大概意思就是:提交时服务器若是根据token标签产生的sessionId判断出表单已提交,它则返回invalid.token指向的视图。好比这里,若是重复提交则会转到.../subError.jsp中去。另不要忘记了引入默认的拦截器栈。补充:关于token拦截器更多细节能够访问org.apache.struts2.interceptor.TokenInterceptor类的api说明。
步骤四,编写配置中所用到jsp页面,这些页面编写简单,在此省去。
步骤5、发布测试,请注意访问login.jsp页面时,查看源文件时会发现增长了两个隐藏域信息。 
步骤6、更换拦截器:咱们还可使用tokenSession拦截器,它的功能比上面的加强,它能保证持有相同sessionId的并发请求等待第一个完成以后才能被提交处理,可是它返回的是action执行后的result.接着上例,咱们只须要在配置中做以下修改:把上面的token拦截器改为<interceptor-ref name="tokenSession"></interceptor-ref> 便可。随后即可以测试,测试时会发现若是咱们重复提交,它老是返回到上一次的success.jsp页面,可是它并非通过LoginAction中的execute处理后返回(咱们System.out.print语句在重复提交时并未打印出来),而是此拦截器判断出是重复后直接返回上一次提交转向的页面。

6.使用拦截器实现权限验证

为了说明此问题,咱们创建struts2auth项目,流程图以下:

简短说明:当咱们访问main.jsp页面,并试图经过此页面中的连接地址:note.action来访问到.../WEB-INF/note.jsp页面时,因为访问的note.action配置了拦截器,因此会被拦截,若是拦截器判断登陆则能够访问,不然会跳到登陆页面。若是咱们从登陆页面直接到main.jsp页面,再来访问note.action时,一样被拦截可是因为登陆过,因此能够访问到此action对应的内容。由这里的分析能够看出关键点就登陆成功时给出标志提供给拦截器判断是否成功登陆。
步骤一,搭建好相关的开发环境,并准备好登陆页面login.jsp,代码以下:
<form action="<%=request.getContextPath()%>/login.action" method="post">

姓名:<input type="text" name="username"><br>

密码:<input type="password" name="password"><br>

<input type="submit" value="登陆">

</form>
步骤二,创建相应的Action:LoginAction。代码以下:
package com.asm;

public class LoginAction extends ActionSupport {

private String username;

Map session;

public String execute() throws Exception {

if(username.equals("admin")){

session = ActionContext.getContext().getSession();

session.put("loginSign", "loginSuccess");

return SUCCESS;

}else{

return LOGIN;

}

}
...省略usernameget/set方法

}

说明:咱们这里是设定了只有登陆用户名为admin时,此Action才设置登陆标志。另这里获取Session对象采起的是“与Servlet解耦合的非IOC方式”。
步骤三,编写拦截器类,代码以下:
package com.asm.interceptor;

public class AuthInterceptor extends AbstractInterceptor {

public String intercept(ActionInvocation invocation) throws Exception {

Map session = invocation.getInvocationContext().getSession();

// session=ActionContext.getContext().getSession();

if (session.get("loginSign") == null) {

return "login";

} else {

String result = invocation.invoke();

return result;

}

}

}

步骤四,配置此Action相关,主要配置内容以下:
<struts>

<package name="tokenTest" extends="struts-default">

<interceptors>

<interceptor name="auth"

class="com.asm.interceptor.AuthInterceptor">

</interceptor>

<interceptor-stack name="authStack">

<interceptor-ref name="auth"></interceptor-ref>

<interceptor-ref name="defaultStack"></interceptor-ref> 

</interceptor-stack>

</interceptors>

<action name="login" class="com.asm.LoginAction">

<result name="success">/main.jsp</result>

<result name="login">/login.jsp</result>

</action>

<action name="note">

<result>/WEB-INF/note.jsp</result>

<result name="login">/login.jsp</result>

<interceptor-ref name="authStack"></interceptor-ref>

</action>

</package>

</struts>

说明:结合前面的一些代码来看,当咱们为note.action配置了前面写所的AuthInterceptor拦截器时,若是咱们要访问note.action,拦截器会首先判断是否登陆,若是登陆则继续把请求传递下去,若是没有登陆则会返回到登陆页面。
步骤5、编写相关的其它jsp页面,而后发布测试。此实例应重点是进一步掌握拦截器的配置使用。做为“实现资源权限访问”,此实例不具参考价值。

7.拦截器中的注解

AnnotationWorkflowInterceptor:Invokes any annotated methods on the action。意思是此拦截器能够调用在Action中任何有注解的方法。下面咱们来演示它的使用,具体步骤以下:
步骤一,创建struts2annotationInt项目,并创建LoginAction类,代码以下:
package com.asm;
...省略导入的包

public class LoginAction extends ActionSupport {

private String username;

@Before

public String myBefore() {

System.out.println("调用myBefore方法");

return LOGIN;

}

@After

public void myAfter() throws InterruptedException {

Thread.sleep(5000);

System.out.println("----调用myAfter方法");

}

@BeforeResult

public void myBeforeResult() {

System.out.println("----调用myBeforeResult方法");

}

public String execute() throws Exception {

System.out.println("调用execute方法");

return SUCCESS;

}

public String getUsername() {

return username;

}

public void setUsername(String username) {

System.out.println("---调用set方法" + username);

this.username = username;

}

}

说明:要想使用方法成为被拦截器监视的注解方法,只需在方法关加上@...这样的形式并导入相关的类便可。

步骤二,编写相关的jsp及配置该Action,主要配置内容以下:
<struts>

<package name="ano" extends="struts-default">

<interceptors>

<interceptor name="anno" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor">

</interceptor>

<interceptor-stack name="annoStack">

<interceptor-ref name="anno"></interceptor-ref>

<interceptor-ref name="defaultStack"></interceptor-ref>

</interceptor-stack>

</interceptors>

<action name="login" class="com.asm.LoginAction">

<result name="success">/success.jsp</result>

<result name="login">/login.jsp</result>

<interceptor-ref name="annoStack"></interceptor-ref>

</action>

</package>

</struts>

结合配置说明:当咱们为LoginAction配置了AnnotationWorkflowInterceptor拦截器时,LoginAction中的全部注解方法才真正生效。下面重点是来讨论这些方法的执行顺序及做用。

加@Before注解的方法:will be invoked before the action method. If the returned value is not null, it is returned as the action result code。意思是在actionexecute方法执行以前被调用,可是此方法若是返回不为空的话,它的返回结果将是真正的返回结果,好比这里咱们return LOGIN,这样不管以什么用户名登陆,它总会返回到login result(这里为login.jsp页面。可是从执前结果来看,在返回前仍执行了标记为@BeforeResult的方法:will be invoked after the action method but before the result execution。意思是在返回结果集前调用此方法。下面咱们把public String myBefore()方法中的return LOGIN注释掉,并让修改此方法的返回类型为void。随后登陆测试(注意要从新部署当前项目),能够发现执行结果以下:
调用myBefore方法

---调用set方法

调用execute方法

----调用myBeforeResult方法
----调用myAfter方法

从执行的顺序来看,标记为@After的方法最后执行,而且能够发现:它会延时5秒执行,可是在延时执行时,浏览器并无成功跳到success.jsp页面,而是在5秒后,控制台打印出myArter方法中的内容同步跳转到success.jsp页面。@After :will be invoked after the action method and result execution。意为在execute方法执行而且返回结果后此方法被调用。可是从测试来看,标记为@After的方法是会影响到结果的返回(延时返回)。 强调:注意方法的执行顺序,相关的内容能够参看AnnotationWorkflowInterceptor类的api文档。

8.使用PreResultListener实现回调

在进行本实例前请前复习:五.2自定义拦截器。由于PreResultListener对象通常是绑定在拦截器上使用。

下面咱们新建struts2PreResultListener项目进行测试。

步骤一,创建类,实现PreResultListener接口,主要代码以下:

package com.asm;

import com.opensymphony.xwork2.ActionInvocation;

import com.opensymphony.xwork2.interceptor.PreResultListener;

public class MyPreResultListener implements PreResultListener {

public void beforeResult(ActionInvocation invocation, String res) {

// System.out.println(invocation.getAction());

// System.out.println(invocation.getResultCode());

/**回调Action中的方法:

 * LoginAction lg = (LoginAction) invocation.getAction(); try {

 * lg.execute(); } catch (Exception e) { e.printStackTrace(); }

 */

System.out.println("检验到PreResultListener被执行");

}

}

步骤二,copy前面在自定义拦截器中用到的三个拦截器,并绑定MyPreResultListener对象,首先是在MyInterceptor类中,咱们只须要修改intercept方法便可,代码以下:

public String intercept(ActionInvocation invocation) throws Exception {

invocation.addPreResultListener(new MyPreResultListener());

System.out.println("开始拦截");

String result = invocation.invoke();

System.out.println("结束拦截");

return result;

}

随后在MyMethodFilterInterceptor类中做相似修改。为了区别,咱们在MyAbstractInterceptor类中不绑定MyPreResultListener对象。

步骤三,编写struts.xml文件,主要配置内容以下:
<struts>

<package name="interceptor" extends="struts-default">

<interceptors>

<interceptor name="myIpt" class="com.asm.MyInterceptor">

</interceptor>

<interceptor name="myAbs"

class="com.asm.MyAbstractInterceptor">

</interceptor>

<interceptor name="myMet"

class="com.asm.MyMethodFilterInterceptor">

</interceptor>

</interceptors>

<action name="login" class="com.asm.LoginAction">

<interceptor-ref name="myIpt"></interceptor-ref>

<interceptor-ref name="myAbs"></interceptor-ref>

<interceptor-ref name="myMet"></interceptor-ref>

<result name="success">/success.jsp</result>

</action>

</package>

</struts>

步骤四,编写相应的jsp页面,发布测试。

说明:此实例的只是简要地演示了PreResultListener的使用,因此相对简单。对于其它相关操做,咱们能够从MyPreResultListener类注释掉的内容中找到一此端倪。强调:从执行结果来看,PreResultListener对象会在返回结果前执行,请注意结合拦截器执行的顺序来看。此实例目前做为了解。

6、使用标签

1.基础表单标签

准备工做:创建struts2tag项目,搭建好struts2的开发环境。在html咱们经常使用的基础表单标签主要有文本域、密码域、提交、重置四种。它们在strust2中能够经过标签来生成。下面创建login.jsp页面,与这四种标签相关的内容以下:

<%@ page language="java"  pageEncoding="utf-8"%>

<%@ taglib uri="/struts-tags" prefix="s" %>

<html>

  <body>

<s:form action="login" method="post" namespace="/my">

<s:textfield label="用户名name="user.username" required="true" requiredposition="right"/>

<s:password label="密码name="user.password" required="true" /> 
<s:reset value="重置align="left"/>
<s:submit value="注册align="left"/>

</s:form>

  </body>

</html>

说明:label中的内容是显示在表单前的提示内容,required设为true,表示此表单项为必填内容。

2.单选按钮和复选框:

<s:radio list="#{1:'',0:''}"  value="1"  label="性别name="user.sex"/>
<s:checkboxlist list="#{1:'足球',2:'排球',3:'蓝球',4:'网球'}" name="user.love" label="爱好"/>

3.三种方式实现下拉列表

<s:bean id="p" name="com.asm.NativePlaceFormAction"></s:bean>

<s:bean name="com.asm.NativePlaceMapFormAction" id="pMap"></s:bean>

<s:bean name="com.asm.NativePlaceProFormAction" id="pp"></s:bean>

<s:select list="#p.place" label="籍贯name="user.place"/>

<s:select list="#pMap.place" label="籍贯2" name="user.place"/>

<s:select list="#pp.place" listKey="pId" listValue="pName" label="籍贯3" name="user.place" headerKey="-1" headerValue="------" emptyOption="true"/>

说明:三种方式实现下拉列表分别对应了三个java类,这三个类的内容为:
NativePlaceFormAction主要代码为:
package com.asm;

public class NativePlaceFormAction extends ActionSupport {

private List<String> place;

public NativePlaceFormAction(){

place=new ArrayList<String>();

place.add("山东省");

place.add("山西省");

place.add("河南省");

place.add("河北省");

place.add("四川省");

place.add("云南省");

}

...省略placeget/set方法

}

NativePlaceMapFormAction主要代码为:
package com.asm;

public class NativePlaceMapFormAction extends ActionSupport {

private Map<Integer, String> place;

public NativePlaceMapFormAction() {

place = new HashMap<Integer, String>();

place.put(1, "山东省");

place.put(2, "山西省");

place.put(3, "河南省");

place.put(4, "河北省");

place.put(5, "四川省");

place.put(6, "云南省");

}
...省略placeget/set方法

}
NativePlaceProFormAction主要代码为:
package com.asm;
public class NativePlaceProFormAction extends ActionSupport {

private List<Object> place;

public NativePlaceProFormAction(){

place=new ArrayList<Object>();

new Province(1,"山东省","济南");

place.add(new Province(1,"山东省","济南"));

place.add(new Province(2,"山西省","太原"));

place.add(new Province(3,"河南省","郑洲"));

place.add(new Province(4,"河北","石家庄"));

place.add(new Province(5,"四川","成都"));

place.add(new Province(6,"云南","昆明"));

}

...省略placeget/set方法

}

说明:此三种实现效果同样,可是在它们提交时传递给服务器的参数不一样,具体能够参看login.jsp页面的源码。另外,这三种实现其实都依赖了<s:bean>设定的对象,若是咱们不但愿依赖<s:bean>来设定,能够经过配置action来实现:下面咱们以NativePlaceFormAction说明:首先在struts.xml中配置此action,配置内容以下:
<action name="npf" class="com.asm.NativePlaceFormAction">

<result>/login2.jsp</result>

</action>
随后,咱们在login.jsp中增长以下内容:
<a href="<%=request.getContextPath()%>/my/npf.action">另外一个注册页面</a>
其中login2.jsp中的关键内容为:
<s:select list="place" label="籍贯name="user.place"/>

咱们能够发现:在login2.jsp中填写list的值时并无用ognl表达式,由于咱们经过npf.action来访问时,此Action已经被写入到了值栈中,因此咱们能够直接引用。 后面所用到的实例,咱们都会把这样的类作成Action,这样若是咱们想经过这种方式访问便只须要在struts.xml中配置便可。

4.二级联动

<s:bean name="com.asm.TwoSelectAction" id="ts"></s:bean>

<s:doubleselect 

list="#ts.place"

 listKey="pId" listValue="pName" 
name="user.place"

doubleList="#ts.citys[top]" 

doubleListKey="cId" doubleListValue="cName"

doubleName="user.city"

label="籍贯4(二级联动)">

</s:doubleselect>

它所依赖的TwoSelectAction类的主要代码以下:
package com.asm;

public class TwoSelectAction extends ActionSupport {

private List<Province> place;

private Map<Province,List<City>> citys;

...省略place citysget/set方法

public TwoSelectAction(){

place= new ArrayList<Province>();

citys=new HashMap<Province,List<City>> ();

Province p1=new Province(1,"山东省","济南");

Province p2=new Province(2,"山西省","太原");

Province p3=new Province(3,"河南省","郑洲");

Province p4=new Province(4,"河北","石家庄");

Province p5=new Province(5,"四川","成都");

Province p6=new Province(6,"云南","昆明");

place.add(p1);

place.add(p2);

place.add(p3);

place.add(p4);

place.add(p5);

place.add(p6);

//山东省的市:

City c1=new City(1,"济南");

City c2=new City(2,"招远市");

City c3=new City(2,"寿光市");

List p1City=new ArrayList();

p1City.add(c1);

p1City.add(c2);

p1City.add(c3);

//山西省的市:

City c4=new City(4,"太原市");

City c5=new City(5,"大同市");

City c6=new City(6,"晋中市");

List p2City=new ArrayList();

p2City.add(c4);

p2City.add(c5);

p2City.add(c6);

//河南省的市:

City c7=new City(7,"郑州市");

City c8=new City(8,"卫辉市");

City c9=new City(8,"信阳市");

List p3City=new ArrayList();

p3City.add(c7);

p3City.add(c8);

p3City.add(c9);

//河北省的市:

City c10=new City(10,"石家庄");

City c11=new City(11,"晋州市");

City c12=new City(12,"鹿泉市");

List p4City=new ArrayList();

p4City.add(c10);

p4City.add(c11);

p4City.add(c12);

//四川省的市:

City c13=new City(13,"成都");

City c14=new City(14,"南充");

City c15=new City(15,"绵阳");

List p5City=new ArrayList();

p5City.add(c13);

p5City.add(c14);

p5City.add(c15);

//云南省的市:

City c16=new City(16,"昆明市");

City c17=new City(17,"安宁市");

City c18=new City(18,"曲靖市");

List p6City=new ArrayList();

p6City.add(c16);

p6City.add(c17);

p6City.add(c18);

citys.put(p1,p1City );

citys.put(p2,p2City );

citys.put(p3,p3City );

citys.put(p4,p4City );

citys.put(p5,p5City );

citys.put(p6,p6City );

}

}

简要分析:此实例有些繁琐,主要思想:咱们的place对象主要为一级列表服务,只要理解了前面的下拉列表,这里不难理解一级列表。而二级列表中咱们使用#ts.citys[top]取出的一个List对象,这样也正是下拉列表所要求的对象类型(List,Map),而top是很是关键的,它明确指出咱们取出的是栈顶的对象,这样就能根据一级列表的值来动态生成这个List对象。

5.其它表单标签

<s:select name="singer"  list="{}" label="歌星headerKey="0" headerValue="--歌手名单--" emptyOption="true">

<s:optgroup list="#{1:'任贤齐',2:'刘德华',3:'周杰伦'}" label="男歌手"/>

<s:optgroup list="#{1:'萧亚轩',2:'蔡依林',3:'she'}" label="女歌手"/>

</s:select>

<s:combobox label="来源调查list="{'朋友介绍','电视广告','网络广告'}" name="from" />

<s:updownselect 

list="{'java','C#','VC','php','vb','vc','python'}" 

moveDownLabel="下移一位

moveUpLabel="上移一位

selectAllLabel="所有选中

label="您经常使用编程语言排名"

/>

<s:optiontransferselect 

leftTitle="选择喜欢作的事:"

list="{'听歌','看电影','编程','玩游戏','chat'}"

name="love" 

headerKey="0"

headerValue="喜欢作的事"

emptyOption="true"

rightTitle="选择讨厌作的事:"

doubleList="{'跳舞','唱歌','打篮球','旅游','shopping'}" 

doubleName="hate"

doubleHeaderKey="0"

doubleHeaderValue="不喜欢的事"

doubleEmptyOption="true"

label="我的兴趣说明"

leftUpLabel="上移"

leftDownLabel="下移"

rightUpLabel="上移"

rightDownLabel="下移"

addToLeftLabel="添加"

addToRightLabel="添加"

addAllToLeftLabel="添加(All)"

addAllToRightLabel="添加(All)—"

selectAllLabel="全选"

/>

<s:checkbox label="接受服务条款value="false" name="user.accept"/>

有了前面的标签学习,这些标签很容易理解,只需结合显示效果和查看源码来加深它们的理解。可是特别要注意的是<s:checkbox>标签与</s:checkboxlist>的区别。

补充:使用struts2生成的表单标签会在标签内嵌套一些特殊的格式,在使用了struts2生成的标签所在网页内查看源代码能够发现多了一些如<tr><td>这样的格式化代码。若是不想struts2增长这些多余的格式化代码,能够在struts.xml中配置以下内容:

<!-- struts2生成的表单标签使用默认的主题,即不附加格式化标签 -->

<constant name="struts.ui.theme" value="simple"/> 

6.其它经常使用标签的使用(代码参名为“补充”的文件夹下的tag.jsp

1<s:set>标签

此标签主要用于设置一些属性值。

Scope:指定变量被设置的范围,该属性能够接受applicationsessionrequestpageAction。若是没有设置该属性,则默认放置在OGNL Context中,咱们能够经过#号来引用。

Value:赋给变量的值,若是没有设置该属性,则将ValueStack栈顶的值赋给变量。

Id/name/var:属性的引用名称,id/name均过期,建议用var来取代他们。

2<s:property>

Default:可选属性,若是须要输出的属性值为null,则显示属性指定的值

Escape:可选属性,指定是否格式化html代码。

Value:可选属性,指定须要输出的属性值,若是没有指定该属性,则默认输出ValueStack栈顶的值

Id:可选属性,指定该元素的标识

(3)<s:Iterator>

Value:可选属性,指定迭代的集合,若是没有指定该属性,则使用ValueStack栈顶的集合

Id:可选属性,指定集合里元素的id(已被标记为过期)

Status:可选属性,该属性指定迭代时当前迭代对象的一个实例,并把此实例放在ognl的上下文中,咱们能够经过#号来引用这个实例。该实例包含以下几下方法:

Int  getCount:返回当前迭代了几个元素。

Int  getIndex:返回当前被迭代的元素的索引

Boolean  isEven:返回当前被迭代的元素的索引是不是偶数

Boolean  isOdd:返回当前被迭代的元素的索引是不是奇数

Boolean  isFirst:返回当前被迭代的元素是不是第一个元素

Boolean  isLast:返回当前被迭代的元素是不是最后一个元素

说明:由于iterator会把每次迭代的实例放在值栈的栈顶,而<s:property>默认访问的是值栈的栈顶元素。因此以下代码可行:

<s:set var="list" value="{'第一个','第二个','第三个'}"/>

<!-- iterator迭代的特色:会把迭代的对象放到值栈的栈顶 -->

<s:iterator value="#list">

<s:property/>

</s:iterator>

若是想用status来实现一些功能,可参下面的代码:

<br/>-------------------奇数红色,偶数蓝色---------------<br/>

<s:iterator value="#list" status="i">

<font color='<s:if test="#i.even">blue</s:if><s:else>red</s:else>' >

<s:property/>

</font><br/>

</s:iterator>

4url标签

<br/><br/>-----------使用url---------------<br/>

<s:set var="age" value="25" scope="request"/>

<s:url action="asm" namespace="/" >

<s:param name="age" value="#request.age"></s:param>

</s:url>

说明:它会根据action及namespace并附加上下文路径构建一个连接。

<br/><!-- value的值中必定要用单引号引发,这样才表示它的值是一个字串 -->

<s:set var="bdUrl" value="'http://www.baidu.com'" />

<s:url value="#bdUrl" /> <br/>

<s:url value="%{#bdUrl}" />  

说明:因为url标签的value属性默认不支持ognl,因此咱们要使用%{}来表示{}中的#bdUrl是一个ognl表达式。

7、国际化

尽管国际化不是重点内容,可是也有必要了解它的使用。在struts2中国际化有三种级别:分别是针对某个Actionaction级别,针对packagepackage级别,针对webappwebapp级别。下面咱们创建struts2i18n项目来演示国际化在struts2中的使用。

1.action级别下的国际化

步骤1、首先是创建login.jsp及LoginAction,因为它们常用,在此省去它们的代码。
步骤2、创建资源文件,因为LoginAction在com.asm包中,因此咱们应在com.asm包下咱们创建两个资源文件:一个是中文LoginAction_zh_CN.properties、一个是英文LoginAction_zh_CN.properties。注意它们的名字相对固定,前面与Action的名字相同,后面是语言和国家代码。
英文资源文件内容以下:
login_page=login page

login_username=userName

login_password=password

login_sex=sex

login_male=male

login_female=female

login_submit=login

login_reset=reset

login_suc=Welcome {0}

中文资源文件,须要特别注意:咱们应使用Myeclipse自带的MyEclipse properties Editer编辑器来打开此资源文件,并在properties视图下进行编辑,这样它会把中文进行编码(咱们切换到source视图下能够看到经编码后的中文)。 这一步很是重要,不然会出现乱码。
步骤三,修改login.jsp中的内容:
<%@ page language="java" pageEncoding="utf-8"%>

<%@ taglib uri="/struts-tags" prefix="s"%>

<html>

<body>

<s:text name="login_page"/><br>

<s:label key="login_username"/>

<s:form action="/login.action" method="post">

<!-- 

<s:textfield label="用户名" name="username" required="true" />

 -->

 <s:textfield label="%{getText('login_username')}" name="username" />

 

 <!-- 

<s:password label="密码" name="password" required="true"/>

 -->

 <s:password key="login_password" name="password" />

 

 <!-- 

<s:radio list="#{1:'',2:''}" value="1" label="性别" name="sex" />

 -->

 <s:radio list="#{1:getText('login_male'),2:getText('login_female')}" value="1" label="%{getText('login_sex')}" name="sex" />

<s:submit  key="login_submit" /> <s:reset key="login_reset"/>

</s:form>

</body>

</html>

说明:对资源文件的引用,咱们采起了两种方式:有的是经过在label中使用%{getText('资源文件中的key')}这样的形式,有的是经过key=资源文件中的key这种形式。须要注意在radio标签中list对资源文件的引用。另外须要注意:

<s:text name="login_page"/><br>

<s:label key="login_username"/>

它们的区别:前面是纯文本,后者是一个块。咱们能够经过查看login.jsp的源码来证实。
步骤4、当咱们直接访问login.jsp时会报错,由于在login.jsp中用到了资源文件,而资源文件又依赖于LoginAction,因此咱们只能经过此Action来跳到login.jsp。可是使用包范围、全局范围的资源文件时,能够直接访问login.jsp文件实现国际化。操做步骤以下:

首先在LoginAction中增长一个方法:

public String doGoLogin() {

return LOGIN;

}

随后再在struts.xml中配置以下内容:
<package name="i18n" extends="struts-default" namespace="/">

<action name="login" class="com.asm.LoginAction">

<result name="success">success.jsp</result>

<result name="login">login.jsp</result>

</action>

</package>

接着再编写一个link.jsp页面,内容以下:
<a href="<%=request.getContextPath() %>/login!goLogin.action">登陆</a>

直接访问Action中的方法 格式:doX(大写)xxx ---- ActionName!x(小写)xxx.action  注意此方法和前面二.7中相关方法的区别。 咱们经过此Action跳转到login.jsp这样便能成功访问到login.jsp页面。
步骤5、在success.jsp中使用资源文件,主要内容以下:
<s:text name="login_suc">

<s:param value="%{username}"></s:param>

</s:text>

说明:在前面的资源文件中,咱们配置了login_suc=Welcome {0},其中{0}表示占位参数,这里咱们使用<s:param>来给此参数赋值。
步骤6、测试:在ie的internet选项中改变语言实现国际化的访问。

2.配置package的资源文件

一样在创建com.asm包下创建两个资源文件(package级别的资源文件名必须以package开头):取名为:package_zh_CN.properties,它的内容为:pack=pack属性值 和package_en_US.properties,它的内容为:pack=packageAttributeValue 

而后再在login.jsp页面中增长以下内容:
<h4>测试包资源文件</h4>

<s:text name="pack"></s:text>

这样便完成了package级别的资源文件配置,最后发布测试。

3.app级别的资源文件

在src目录下创建两个资源文件,取名为myapp_en_US.properties,它的内容为:
app=appAttributeValue 和myapp_zh_CN.properties,它的内容为:

而后还须要在strust.xml中增长以下配置:
<constant name="struts.custom.i18n.resources" value="myapp"></constant>

注意:name是固定值,而value来自于这个资源文件的基名。

最后在login.jsp中增长以下内容:

<h4>测试app级别资源文件</h4>

<s:text name="app"></s:text>

这样便完成了app级别的资源文件配置,随后发布测试。

说明:action级的资源文件优先级别最高,app最低。Pack级别的资源文件可做用于同一个包,app级别的资源文件可做用于当前项目。

补充:在jsp页面中直接访问某个资源文件,struts2为咱们提供了i18n标签,使用此标签咱们能够在类路径下直接从某个资源文件中获取国际化数据,而无需任何配置:

<s:i18n name="XXX"> --xxx为类路径下资源文件的基名

<s:text name="">

<s:param></s:param>

</s:text>

</s:i18n>

而若是要访问的资源文件在类路径的某个包下(如action或package级别的资源文件),能够这样访问:

<s:i18n name="com/asm/资源文件基名">--com.asm为包名

4.使用资源文件的原理 

咱们创建ReadResourceFileTest类,代码以下:

package com.asm;

import java.util.Locale;

import java.util.ResourceBundle;

public class ReadResourceFileTest {

public static void main(String[] args) {

ResourceBundle rb=ResourceBundle.getBundle("com.asm.LoginAction", Locale.US);

System.out.println(rb.getString("login_suc"));

}

}

补充:在Action类(必须继承自ActionSupport)中获取资源文件的值的方法,可使用以下代码:

String value = this.getText("资源文件的键名");  

//获取资源文件的对应的值。若是想给资源文件中的占位符赋值,可使用getText的重载方法。

ActionContext.getContext().put("XXX",value);//存放在request范围,供jsp获取此值

5.选择使用资源文件

其实在咱们成功访问到login.jsp页面后,只要在地址栏中增长参数request_locale=en_US即可以正确切换到登陆页面为英文。固然咱们能够再连接根据此参数写这个资源文件的连接。固然咱们也可借助一个新Action来实现,操做步骤以下:在login.jsp中增长以下代码:
<a href="change.action?request_locale=zh_CN">

<s:text name="chinese"></s:text>

</a>
<a href="change.action?request_locale=en_US">

<s:text name="english"></s:text>

</a>
change.action对应的配置为:
<action name="change" class="com.asm.ChangeLangAction">

<result>/login.jsp</result>

</action>
ChangeLangAction的主要代码以下:
package com.asm;

public class ChangeLangAction extends ActionSupport {

public String execute() throws Exception {

return SUCCESS;

}

}

以上是第一种方法,特别要注意,因为使用了不一样Action,因此要资源文件这时只有pack级别和app级别的才起做用,因此这时还应把action级别的资源文件内容增长到app级别的资源文件中去。下面使用第二种方法,原理基本和上面同样,只需在此ChangeLangAction中增长一个新的字段String lang及相应的get/set方法,再增长一个新的方法changeLang,代码以下:
public String changeLang() throws Exception {

Locale locale = null;

System.out.println(lang);

if (lang.equals("zh")) {

// 显示中文

locale = Locale.CHINA;

System.out.println("======" + lang+locale);

} else {

// 显示英文

locale = Locale.US;

}

ActionContext.getContext().setLocale(locale); ServletActionContext.getRequest().getSession().setAttribute("WW_TRANS_I18N_LOCALE", locale);

return SUCCESS;

}

配置内容为:
<action name="cl" class="com.asm.ChangeLangAction" method="changeLang"> 

<result>/login.jsp</result>

</action>
在login.jsp中对应的连接为:

<a href="cl.action?lang=zh">

<s:text name="chinese"></s:text>

</a>
<a href="cl.action?lang=en">

<s:text name="english"></s:text>

</a>
这样操做后,当咱们成功访问到login.jsp后,即可以点击连接来随意切换访问英文或中文页面。

8、验证机制

注意:要想实现校验,action必须继承自ActionSupport类。

1.基于手工编码的校验

咱们创建struts2validate项目 ,其中reg.jsp页面主要代码以下:

<body>

  <s:head/>

<h3>注册页面</h3>

<s:form method="post" action="reg" >

<s:bean name="com.asm.AgeAction" id="aa"></s:bean>

<s:textfield name="user.username" label="用户名"/>

<s:property value="errors.user.username"/>

<s:password name="user.password" label="密码"/>

<s:password name="user.password2" label="确认密码"/>

<s:select list="#aa.ageMap" name="user.age" label="年龄headerValue="填写真实年龄"  headerKey="0"/>

<s:reset value="重置align="left" />

<s:submit value="注册align="left"/>

</s:form>

</body>
说明:<s:head/>能够用来对验证信息进行一些美化效果处理,另在此页面中咱们用到了一个AgeAction用来动态生成“年龄”表单项,在前面的表单标签中已用过相似的作法。AgeAction的代码以下:

package com.asm;

public class AgeAction extends ActionSupport {

private Map<Integer, String> ageMap;

public AgeAction() {

ageMap = new HashMap();

for (int i = 1; i <= 120; i++) {

ageMap.put(new Integer(i), i + "");

}

}

...省略ageMapget/set方法

}

Reg action的配置以下:

<package name="validate" extends="struts-default">

<action name="reg" class="com.asm.RegAndLoginAction" method="reg">

<result name="success">/regSuc.jsp</result>

<result name="login">/reg.jsp</result>

</action>
</package>

根据配置,咱们来看它的对应Action: RegAndLoginAction,代码以下:

package com.asm;

public class RegAndLoginAction extends ActionSupport {

private User user;

public String reg() throws Exception {

if (user.getUsername() == null || user.getUsername().equals("")) {

this.addFieldError("user.username", "用户名不能为空");

} else if (!Pattern.matches("^[a-zA-Z][a-zA-Z0-9_]{3,14}$", user.getUsername())) {

this.addFieldError("user.username", "用户名只能是以字母开头,后面能够跟字母、数字或下滑线,长度只能是4-15");

} else if (user.getPassword() == null || user.getPassword().equals("")) {

this.addFieldError("user.password", "密码不能为空");

} else if (!user.getPassword().equals(user.getPassword2())) {

this.addFieldError("user.password2", "两次输入的密码不一致,请从新输入");

} else if (user.getAge() < 16) {

this.addFieldError("user.age", "未满16岁,不能注册");

}

if (this.hasFieldErrors()) {

return LOGIN;

}

System.out.println("reg success....");

return SUCCESS;

}

...省略userget/set方法

}
说明:当reg.jsp提交给此Action对应的reg方法处理时,它会调用addFieldError把错误信息加到FiledError中去,关于这点,咱们能够在前台reg.jsp页面中用<s:debug>调试时,能够看到值栈中的此Action对象中的fieldErrors对应着咱们添加的错误信息,所以这点也就为咱们取出验证信息提供一个参考,便是说咱们能够取出此验证信息,对它进行美化处理,而不是按struts2默认来显示。 后面,咱们接着对登陆页面用login方法进行了相似的验证(在此省略),因此此action取名为regAndLoginAction.
补充:当咱们把login.jsp页面的验证写完后,能够发现reglogin这两个方法显示至关的繁琐,对此咱们能够专门把验证分别放在validateRegvalidateLogin方法中去。咱们新建一个Action来演示,新的RegAndLogin2Action主要代码以下:
package com.asm;

public class RegAndLogin2Action extends ActionSupport {

private User user;

@Override

public void validate() {

System.out.println("校验的统一出口,对全部方法进行校验:这里能够放一些公共的验证");

}

public void validateReg() {

...省略,对reg方法进行验证

}

public void validateLogin() {

...省略,对login方法进行验证

}

public String reg() throws Exception {

System.out.println("reg success....");

return SUCCESS;

}

public String login() throws Exception {

System.out.println("login success....");

return SUCCESS;

}
...省略userget/set方法

}

说明:当reg.jsp提交给此Action对应的reg方法处理时,它会首先调用此reg方法专属的验证方法valiadteReg(注意取名规则:validate+方法名<首字母大写>),此方法验证完成后,会调用validate方法,此方法完成后才会调用reg方法。所以通常状况下,咱们会把一些公共的验证放在validate方法中,而这些全部的验证方法也只进行验证处理,并把错误信息封装到fieldError字段中(或者其它字段)。reg这些真正执行的方法只进行一些其它处理(好比把注册信息写进数据库)。测试时须要修改把前面的配置注释掉,写上下面的配置:
<action name="login" class="com.asm.RegAndLogin2Action" method="login">

<result name="success">/logSuc.jsp</result>

<result name="input">/login.jsp</result>

</action>

<action name="reg" class="com.asm.RegAndLogin2Action" method="reg">

<result name="success">/regSuc.jsp</result>

<result name="input">/reg.jsp</result>

</action>
说明:配置中有一个input result的配置,由于带有validate的方法进行验证时,若是验证失败,会返回input所对应的result结果集。

简析校验流程:

(1)类型转换器请求参数执行类型转换,并把转换后的值赋给action中属性。

(2)若是在执行类型转换过程当中出现异常,系统会将异常信息保存到ActionContext,conversionError拦截器将异常信息添加到fieldErrors里,无论类型转换是否出现异常都会进入第(3)步。

(3)系统经过反射技术调用action中的validateXxx()方法

(4)再调用action中的validate()方法

(5)通过上面4步,若是系统中的fieldErrors存在错误信息(即存放错误信息的集合size大于0),系统自动将请求转发至名为input的视图。若是系统中的fieldErrors没有任何错误信息,系统将执行action中的处理方法。

注意:通过以上过程的分析,能够知道若是类型转换失败,也会到input视图。

2.基于XML配置形式的校验

新建struts2validateXML项目,在此项目中,基本的代码和上面的struts2validate项目类似,只是在上一个项目中咱们在Action的具体方法中进行了验证处理,如今先修改RegAndLoginAction的代码以下:
package com.asm;

public class RegAndLoginAction extends ActionSupport {

private User user;

public String reg() throws Exception {

System.out.println("reg success....");

return SUCCESS;

}

public String login() throws Exception {

System.out.println("login success....");

return SUCCESS;

}

...省略userget/set方法

}

下面咱们在action所在的包下创建一个对此Action进行校验的xml文件,文件名为:RegAndLoginAction-validation.xml,取名原则就是actionClassName-validation.xml 。它会对此Action中的全部方法进行校验。主要代码以下:
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE validators PUBLIC

   "-//OpenSymphony Group//XWork Validator 1.0.3//EN"

   "http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">

<validators>

<field name="user.username">

<field-validator type="requiredstring">

<message>用户名不能为空</message>

</field-validator>

<field-validator type="regex">

<param name="expression">^[a-zA-Z][a-zA-Z0-9_]{3,14}$</param>

<message>
用户名只能是以字母开头,后面能够跟字母、数字或下滑线,长度只能是4-15

</message>

</field-validator>

</field>

<field name="user.password">

<field-validator type="requiredstring">

<message>密码不能为空</message>

</field-validator>

</field>

</validators>

进行此配置,至关于在RegAndLoginAciton中增长用validate ()方法进行验证。若是咱们想对某个方法进行验证,配置文件应取名为actionClassName-ActionName-validation.xml,好比咱们对reg方法进行验证,在前面用到validateReg方法,这里只需增长RegAndLoginAction-reg-validation.xml配置文件便可,它的做用和validateReg方法相同,在此省略此配置文件内容。
关于验证的配置文件中用到的验证类型能够参看文档或者叁看压缩包中的配置参照文件,下面对校验器类型进行简单说明:

Required-必须校验器:要求field的值不能为null

Requiredstring-必须字串校验器:不能为null,用长度大于0,默认状况下会对字串去先后空格

int、[long、short、double]:整数值[long型、短整形、double型]型值必须在指定范围。参数min指定最小值,参数max指定最大值 

date-日期校验器:日期校验类型,符合日期格式,用可使用min/max来指定日期范围

expression-OGNL表达式校验器:expression参数指定ognl表达式,该逻辑表达式基于值栈进行求值,返回true时校验经过,不然不经过,该校验器不可用在字段校验器风格的配置中

fieldexpression-字段ognl表达式校验器:要求field知足一个ognl表达式,expression参数指定ognl表达式,该逻辑表达式基于值栈进行求值,返回true校验经过,不然不经过

email-邮件地址校验器:非空用为合法的邮件地址

url-网址校验器:非空用为合法的url地址

visitor-复合属性校验器:它指定一个校验文件用于校验复合属性中的属性

conversion-转换校验器:指定在类型转换失败时,提示的错误信息

stringlength-字符器长度校验器:要求字段必须在指定的范围内,不然校验失败。minLength参数指定最小长度,maxLength参数指定最大长度。Trim参数指定校验field以前是否去除字串先后的空格

regex-正则表达式校验器:校验字段是否与expression参数指定的正则表达式匹配。caseSensitive参数指定进行匹配时是否区分大小写,默认为true,即区分大小写。

补充:基于xml校验的一些特色

当为某个Action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml两种规则的校验文件时,系统会按下面的顺序寻找校验文件:(1)ActionClassName-validation.xml (2)ActionClassName-ActionName-validation.xml

系统寻找到第一个校验文件时还会继续搜索后面的校验文件,当探索到全部校验文件时,会把校验文件里的全部校验规则汇总,而后所有应用于action方法的校验。若是两个校验文件中指定的校验规则冲突,则会只使用后面文件中的校验规则。

当action继承了另外一个action,父类action的校验文件会先被搜索到。

假定UserAction继承BaseAction:

<action name="user" class="com.asm.UserAction" method="execute">访问上面的action,系统会先搜索父类的校验文件:BaseAction-validation.xml,BaseAction-user-validation.xml,接着搜索子类的校验文件:UserAction-validation.xml,UserAction-user-validation.xml.应用于上面action校验规则为四个文件的总和。

9、文件上传下载(了解)

首先创建struts2UpDownLoad项目,搭建好struts2基本的开发环境。

1. 上传实例

2. 步骤一:upload.jsp代码以下:
<s:form action="upload" method="post" enctype="multipart/form-data">

<s:file name="file" label="上传的头像(格式:jpg,gif,bmp"></s:file>

<s:submit value="上传"/> <s:reset value="取消"/>

</s:form>
注意:在form标签中咱们用到了enctype实体,这是上传时必须用到得。
步骤二,创建struts.xml。对upload.action的配置以下:
<action name="upload" class="com.asm.UploadAction">

<param name="savePath">img</param>

<result>/upSuc.jsp</result>

<result name="input">upload.jsp</result>

<interceptor-ref name="defaultStack">

<param name="fileUpload.maximumSize">1024*1024</param>

<param name="fileUpload.allowedTypes">
image/bmp,image/pjpeg,image/gif

</param>

</interceptor-ref>

</action>

在这里惟一须要说明的是<interceptor-ref>下的参数问题,在之前若是要为某个特定的拦截器传递参数须要在<interceptor>下配置pararm参数,在此处咱们用.形式来配置fileUpload拦截器的参数。这用作便可以保证默认的拦截器栈起做用,也能够向fileUpload拦截器传递参数。第一个参数是限制上传图片的大小(除了能够这样限制图片大小,也能够配置一个常量的方法来限制上传文件的大小,配置的内容为:<constant name="struts.multipart.maxSize" value="文件大小"/>),第二个参数是限制上传图片的格式只能为bmp,pjpeg,gif关于这些参数能够参看fileupload拦截器对应类的api文档。

另还需注意:在action下的“<param name="savePath">img</param>”代码能够为UploadAction的savePath字段传递值,这样做的好处是在项目正式发布后,咱们能够经过修改struts.xml中的配置来灵活给savePath赋值。而若是直接在java源代码中初始化savePath的值,在项目运行后就不能简单修改。这种用法主要是为了给客户提供一个灵活可配的特定初始化方式。
步骤3、编写UploadAction,主要代码以下:
package com.asm;

public class UploadAction extends ActionSupport {

private String savePath;

private File file;

private String fileFileName;

private String fileContentType;

public String execute() throws Exception {

String path=ServletActionContext.getServletContext().getRealPath(savePath);

String savaFileName=path+"\\"+fileFileName;

//System.out.println(savaFileName); 

BufferedInputStream bis=null;

BufferedOutputStream bos=null;

try{

bis=new BufferedInputStream(new FileInputStream(file));

bos=new BufferedOutputStream(new FileOutputStream(savaFileName));

byte []buf=new byte[(int) file.length()];

int len=0;

while((len=bis.read(buf))!=-1){

bos.write(buf,0,len);

}}catch(Exception e){

e.printStackTrace();

}finally{

if(bis!=null)

bis.close();

if(bos!=null)

bos.close();

}

return SUCCESS;

}

...省略以上四个字段的get/set方法

}

说明:其实上传的难点就是在此action的处理上。首先是从配置文件中读取文件的保存路径,而后联合fileFileName(命名规则是上传的文件对应的字段名+FileName,若是要获得上传文件的类型,固定的写法应是上传的文件对应的字段名+ContentType,好比这里应为fileContentType)来肯定完整的保存路径,并最终为建立BufferedOutputStream做准备。BufferedInputStream是经过前台upload.jsp页面传递的file构建。特别要注意处理流,若是书写不当可能会使上传文件循环写入,使得硬盘容量不够。还要注意对流的关闭问题。 补充:关于文件的操做可使用commons-io.jar包的FileUtils类下的copyFile方法来进行文件的拷贝,好比这里调用copyFile方法(file,要保存的目录)

上传成功后,upSuc.jsp的主要内容以下:
<body>

<h4>上传成功,如下是你刚上传的图片:</h4>
<img src="<%=request.getContextPath() %>/<s:property value="savePath+'/'+fileFileName"/>"> <br>

保存路径为:<s:property value="savePath+'/'+fileFileName"/>

 </body>

说明:当上传成功后,会显示上传的图片。

扩展实例:若是想上传多个文件,能够在Action中使用File[] files来接受上传的文件(jsp中对应的上传文件的参数均应为files)。对应的上传文件的名字,使用String[] fileFileName。而后循环files数组保存File文件对象。

2.下载实例

下载页面的doload.jsp的主要代码以下:
<a href="download.action?downloadName=img/a.bmp">下载图片</a><br>

<a href="download.action?downloadName=img/music.rar">下载千千静听</a><br>
对应的download action配置以下:

<action name="download" class="com.asm.DownloadAction">

<result name="success" type="stream">

<param name="inputName">targetFile</param>
<param name="contentType">
              image/bmp,application/x-zip-compressed
            </param>

</result>

</action>
说明:type类型指明告终果集为流类型,而且为流类型结果集配置了参数,inputName指定流的来源,这里来源为targetFile,因此在下面的Action中有getTargetFile方法, contentType指明下载时的文件类型。

DownloadAction的主要代码以下:
package com.asm;

public class DownloadAction extends ActionSupport {

private String downloadName;

public String execute() throws Exception {

return SUCCESS;

}

public InputStream getTargetFile(){

return ServletActionContext.getServletContext().getResourceAsStream(downloadName);

}

public void setDownloadName(String downloadName) {

this.downloadName = downloadName;

}

}
说明:下载实例在此略做了解,具体能够借助apache组织提供的上传下载开源项目理解。

10、类型转换 

创建struts2conversion项目,并搭建好struts2的基本开发环境

1.基于Action的直接属性转换

创建t.jsp页面,内容以下:
<s:form action="phone" method="post">

<s:textfield name="thePhone" label="电话"/>

<s:submit value="提交"/>

<s:reset value="重置"/>

</s:form>

此action对应的配置以下:

<action name="phone" class="com.asm.action.PhoneAction">

<result name="success">tSuc.jsp</result>

<result name="input">/t.jsp</result>

</action>

对应的PhoneAction类的代码以下:
package com.asm.action;

public class PhoneAction extends ActionSupport {

private Telephone thePhone;

public String execute() throws Exception {

return SUCCESS;

}

  ...省略thePhoneget/set方法

}

说明,若是直接这样执行将会出错,由于前台t.jsp传的String默认是不能被转成这里的Phone对象,因此咱们必须使用类型转换,并且咱们配置了input result就是告诉咱们若是类型转换失败,将会停在t.jsp页面,并且会报错。下面接着看怎么类型转换。在这里咱们要把010-123456这样的电话换成:区号:010 电话:123456这样的形式时。具体的操做步骤以下:
建立类型转换类TelephoneConversion,代码以下:
package com.asm.conversion;

public class TelephoneConversion extends StrutsTypeConverter {

public Object convertFromString(Map context, String[] values, Class toClass) {

System.out.println("执行字串到Telephone对象的转换");

Telephone tp=new Telephone();

String [] tel=values[0].split("-");

tp.setSecNum(tel[0]);

tp.setPhone(tel[1]);

return tp;

}

public String convertToString(Map context, Object obj) {

System.out.println("执行Telephone对象到字串的转换");

Telephone tp=(Telephone) obj;

return "区号:"+tp.getSecNum()+"\t电话:"+tp.getPhone();

}

}
说明:类型转换类必须实现TypeConverter接口,而这里的StrutsTypeConverter类即是TypeConverter接口实现类DefaultTypeConverter的子类。此类中有两个方法,一个方法实现把字串转成其它对象,一个方法实现把其它对象转成字串。在convertFromString方法中,咱们实现把客户端传递的字串转成Telephone对象,这样就能让PhoneAction的setThePhone方法得以正确执行。然后面的方法是为了咱们要取值时进行的处理,好比在tSuc.jsp中咱们要获得此值,须要把Telephone对象转换字串。其实若是没有convertToString方法,只要咱们重写Telephone的toString方法也能达到目的。
写完类类型转换类后,咱们还应告知struts2,因此咱们还需创建一个properties文件。咱们在PhoneAction的同包下创建PhoneAction-conversion.properties文件,它的主要代码以下:
thePhone=com.asm.conversion.TelephoneConversion
说明:这句话的意思是说咱们要把PhoneAction(经过properties文件名能够知道要转换的是此Action)下的thePhone使用TelephoneConversion进行转换。其实咱们也能够配置全局的properties文件说明,好比咱们在src目录下创建xwork-conversion.properties文件(名字固定),它的内容以下:
com.asm.vo.Telephone=com.asm.conversion.TelephoneConversion

说明:它的意思是只要遇到Telephone对象,就要用后面的转换器来实行转换。

2.基于Action的间接属性vo转换

t2.jsp主要内容以下:
<s:form action="up" method="post">

<s:textfield name="user.thePhone" label="电话"/>

<s:submit value="提交"/>

<s:reset value="重置"/>

</s:form>
咱们创建UserPhoneAction类,它的主要代码以下:
package com.asm.action;

public class UserPhoneAction extends ActionSupport {

private User user;

public String execute() throws Exception {

return SUCCESS;

}
...省略userget/set方法

}

User类的代码以下:
package com.asm.vo;

public class User {

private Telephone thePhone;

...省略thePhoneget/set方法。

}

说明:经过这两个类及t2.jsp页面,咱们知道,前台传递的thePhone对象不时直接传递,而是采用了vo模式,因此当咱们配置类型转换时,要特别注意。由于前面咱们使用了全局的类型转换,因此这里不会出错,可是当咱们去掉前面的全局转换时,配置类型转换的properties文件就应在User类对应的包下配置User-conversion.properties文件,它的主要内容以下:

thePhone=com.asm.conversion.TelephoneConversion
说明及总结:类型转换的配置文件若是不采用全局的配置时,咱们就应以要转换的类型的直接持有类为基准:好比,这里的thePhone的直接持有类为User对象,因此咱们就应以User为基准写properties文件名。

11、注解配置

在此先略去注解配置的实例,具体能够参看官方提供的文档。其实在熟悉struts及相关的一些内容后,再来看文档是比较容易理解得。只是要注意使用注解Annotition时:(1)要多导入一个jar包:struts2-convention-plugin-2.1.6.jar。(2)须要在web.xml中增长以下内容:

<filter>

<filter-name>struts2</filter-name>

<filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter

</filter-class>
<!-- 增长了如下内容 -->

<init-param>

<param-name>actionPackages</param-name>

<param-value>com.struts2.action</param-value>

</init-param>

</filter>

12、总结
本教程对struts2的基本知识进行了一些说明,关于struts2的更多详细内容应参看struts2的官方文档及提供的app实例。
下面对struts2的基本执行流程做一简要说明,此流程说明能够结合官方提供的struts2结构图来看:

n 客户端提交一个(HttpServletRequest)请求,如上文在浏览器中输入
http://localhost: 8080/appName/...就是提交一个(HttpServletRequest)请求。 

n 请求被提交到一系列(主要是3层)的过滤器(Filter),如(ActionContextCleanUp、其余过滤器(SiteMesh等)、 FilterDispatcher)。注意:这里是有顺序的,先ActionContext CleanUp,再其余过滤器(Othter Filters、SiteMesh等),最后到FilterDispatcher。 

n FilterDispatcher是控制器的核心,就是MVC的Struts 2中实现控制层(Controller)的核心。(有点struts1.x中ActionServlet的感受) 

n FilterDispatcher询问ActionMapper是否须要调用某个Action来处理这个(HttpServlet Request)请求,若是ActionMapper决定须要调用某个Action,FilterDispatcher则把请求的处理交给ActionProxy。 

n ActionProxy经过Configuration Manager(struts.xml)询问框架的配置文件,找到须要调用的Action类。例如,用户注册示例将找到UserReg类。 

n ActionProxy建立一个ActionInvocation实例,同时ActionInvocation经过代理模式调用Action。但在调用以前,ActionInvocation会根据配置加载Action相关的全部Interceptor(拦截器)。 关于ActionInvocation的执行过程咱们在5、2自定义拦截器最后的补充中已经进行了较详细说明。

n 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果result。

相关文章
相关标签/搜索