1 Mvc与servlet
1.1 Servlet的优点
1、 是mvc的基础,其他的框架比如struts1,struts2,webwork都是从servlet基础上发展过来的。所以掌握servlet是掌握mvc的关键。
2、 Servlet把最底层的api暴漏给程序员,使程序员更能清楚的了解mvc的各个特点。
3、 程序员可以对servlet进行封装。Struts2就是从servlet中封装以后得到的结果。
4、 市场上任何一个mvc的框架都是servlet发展过来的,所以要想学好struts2这个框架,了解servlet的运行机制很关键。
1.2 Servlet的缺点
1、 每写一个servlet在web.xml中都要做相应的配置。如果有多很servlet,会
导致web.xml内容过于繁多。
2、 这样的结构不利于分组开发。
3、 在servlet中,doGet方法和doPost方法有HttpServletRequest和HttpServletResponse参数。这两个参数与容器相关,如果想在servlet中作单元测试,则必须初始化这两个参数。
4、 如果一个servlet中有很多个方法,则必须采用传递参数的形式,分解到每一个方法中。
2 重构servlet
针对servlet以上的特点,我们可以对servlet进行重构,使其开发起来更简单。更容易,更适合团队协作。
重构的目标:
1、 只写一个serlvet或者过滤器,我们这里选择过滤器。
2、 不用再写任何的servlet,这样在web.xml中写的代码就很少了。
3、 原来需要写serlvet,现在改写action。
4、 在action中把HttpServletRequest参数和HttpServletResponse参数传递过去。
5、 在过滤器中通过java的反射机制调用action。
6、 详细过程参照cn.itcast.action包中的内容
3 Struts2介绍
1、 struts2是apache组织发明的开源框架。是struts的第二代产品。
2、 struts2是在struts和webwork基础上整合的全新的框架。
3、 struts2的配置文件组织更合理,是企业开发很好的选择。
4、 struts2的拦截器为mvc框架注入了全新的概念。
4 Struts2入门
4.1 新建一个工程为struts2
4.2 修改工程的编码为utf-8
4.3 导入jar包
| |
| |
在新建的工程下创建一个文件夹名为lib,把相应的jar包到入到lib文件夹中,并且放入到classpath中,jar包有:
|
|
4.4 创建test.jsp文件
4.5 创建HelloWorldAction
package cn.itcast.struts2.action;
import com.opensymphony.xwork2.Action;
public class HelloWorldAction implements Action{
public String execute() throws Exception {
// TODO Auto-generated method stub
System.out.println("hello world action");
return "success";
}
}
|
|
4.6 编写success.jsp文件
<body>
This is my JSP page. <br>
base命名空间下的HelloWorld运行完成!
</body>
|
|
4.7 编写struts配置文件
4.8 编写web.xml文件
4.9 运行结果
<body>
测试struts2,输出有命名空间的helloWorld
<br>
<a
href="${pageContext.request.contextPath}/base/helloWorldAction.action">helloWorld</a>
<br>
测试struts2,输出没有命名空间的helloWorld
<br>
<a href="${pageContext.request.contextPath}/helloWorldAction.action">helloWorld</a>
</body>
|
|
4.10 加载struts.xml过程
说明:
1、 在web服务器启动的时候,执行的是过滤器中的init方法。在这里回顾
一个问题:一个过滤器中的init方法在什么时候执行?总共执行几次?
2、 在启动服务器的时候,执行了过滤器中的init方法,加载了三个配置文件
struts-default.xml、struts-plugin.xml、struts.xml
3、 因为这三个文件的dtd约束是一样的,所以如果这三个文件有相同的项,后面覆盖前面的。因为先加载前面的,后加载后面的。
4、 struts.xml文件必须放在src下才能找到。
5 Struts2基本配置
5.1 Extends用法
说明:
1、 上述内容中,因为在struts.xml文件中,所有的包都继承了struts-default包(在struts-defult.xml文件中),所以程序员开发的action具有struts-default包中所有类的功能。
2、 而struts-default.xml文件在web服务器启动的时候就加载了。
3、 在struts-default.xml文件中,定义了struts2容器的核心内容。
4、 可以做这样的尝试:把extends=”struts-default”改为extends=””会怎么样呢?
5.1.1 例子
在struts.xml文件中
| |
| |
<package name="test" namespace="/test" extends="base"></package>
|
|
在上述的配置中,重新设置了一个包,名称为test,这个包继承了base。所以当访问http://localhost:8080/struts2/test/helloWorldAction.action时也能输出正确的结果,并且命名空间和base包中的命名空间没有任何关系了已经。
如果在struts2的配置文件中不写extens=”struts-default”会出现什么样的结构呢?
5.2 ActionSupprot
在struts框架中,准备了一个ActionSupport类
| |
| |
代码段一:
public class ActionSupport implements Action, Validateable, ValidationAware, TextProvider, LocaleProvider, Serializable {
代码段二:
/**
* A default implementation that does nothing an returns "success".
* <p/> ActionSupport是一个默认的Action实现,但是只返回了一个字符串success
* Subclasses should override this method to provide their business logic.
* <p/>子类需要重新覆盖整个方法,在这个方法中写相应的逻辑
* See also {@link com.opensymphony.xwork2.Action#execute()}.
*
* @return returns {@link #SUCCESS}
* @throws Exception can be thrown by subclasses.
*/
public String execute() throws Exception {
return SUCCESS;
}
代码段三:
public static final String SUCCESS = "success";
|
|
说明:
1、 代码段一说明了ActionSupport也实现了Action接口(以前写的类实现了Action接口)
2、 代码段二说明如果程序员写自己的action继承了ActionSupport,需要重新覆盖execute方法即可。
3、 这个方法默认的返回的是success;
| |
| |
在配置文件中,还可以这么写:
<action name="actionSupprotAction">
<result name="success">/baseconfig/successActionSupport.jsp</result>
</action>
可以看到action标签中没有class属性,在struts-default.xml中,
<default-class-ref class="com.opensymphony.xwork2.ActionSupport" />
说明:如果在action标签中没有写class属性。那么根据上述的配置文件,struts2会启用
ActionSupport这个类。所以action标签中的class属性可以不写。
|
|
5.3 include
在struts.xml中可以按照如下的形式引入别的struts的配置文件
| |
| |
<include file="cn/itcast/struts2/action/include/struts-include.xml"></include>
|
|
这样在加载struts.xml文件的时候,struts-include.xml文件也能被加载进来。实例见
Baseconfig/testInclude.jsp文件
5.4 命名空间
在说明extends用法的时候,我们引用了这样一个url:http://localhost:8080/struts2/base/helloWorld.action。如果我们把url改成这样:
http://localhost:8080/struts2/base/a/helloWorld.action。行吗?答案是可以的。再改成这样的url:http://localhost:8080/struts2/base/a/b/helloWorld.action ,行吗?答案也是可以的。如果这样呢http://localhost:8080/struts2/helloWorld.action可以吗?这样就不行了。为什么?
步骤:
1、 struts2会在当前的命名空间下查找相应的action
2、 如果找不到,则会在上级目录中查找,一直查找到根目录
3、 如果根目录也找不到,报错。
4、 如果直接访问的就是根目录,找不到,这样的情况就会直接报错。不会再去子目录中进行查找。
6 结果类型
6.1 说明
1、 每个action方法都返回一个String类型的值,struts一次请求返回什么值是由这个值确定的。
2、 在配置文件中,每一个action元素的配置都必须有result元素,每一个result对应一个action的返回值。
3、 Result有两个属性:
name:结果的名字,和action中的返回值一样,默认值为success;
type:响应结果类型,默认值为dispatcher.
6.2 类型列表
在struts-default.xml文件中,如下面所示:
<result-types>
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
<result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
<result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
<result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
<result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
<result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
<result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
<result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
<result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
<result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />
</result-types>
|
|
说明:
1、 从上述可以看出总共10种类型
2、 默认类型为ServletDispatcherResult即转发。
3、 结果类型可以是这10种结果类型的任意一种。
6.2.1 Dispatcher类型
6.2.1.1 说明
Dispatcher类型是最常用的结果类型,也是struts框架默认的结果类型。
6.2.1.2 例子
页面参照:resulttype/testDispatcher.jsp
Action参照:DispatcherAction
配置文件:struts-resulttype.xml
在配置文件中,可以有两种写法:
| |
| |
第一种写法:
<result name="success">/resulttype/successDispatcher.jsp</result>
第二种写法:
<result name="success">
<param name="location">/resulttype/successDispatcher.jsp</param>
</result>
|
|
下面的图说明了location的来历:
6.2.2 Redirect类型
6.2.2.1 说明
Redirect属于重定向。如果用redirect类型,则在reuqest作用域的值不能传递到前台。
6.2.2.2 例子
页面:resulttype/testRedirect.jsp
Action:RedirectAction
配置文件:struts-resulttype.xml
6.2.3 redirectAction类型
6.2.3.1 说明
1、 把结果类型重新定向到action
2、 可以接受两种参数
a) actionName: action的名字
b) namespace:命名空间
6.2.3.2例子
第一种方式:
<result name="success" type="redirectAction">resulttype/redirectactionAction.action</result>
第二种方式:
<result name="success" type="redirectAction">
<!--
actionName:
请求的action的路径
namespace:
如果不写,默认就是请求的action的路径,如果写,路径将被重新赋值
-->
<param name="actionName">
resulttype/redirectactionAction.action
</param>
</result>
|
|
7 Action原型模式
7.1 回顾servlet
在servlet中,一个servlet只有一个对象,也就是说servlet是单例模式。如果把一个集合写在servlet属性中,则要考虑线程安全的问题。
7.2 Action多例模式
但是在struts2的框架中,并不存在这种情况,也就是说struts2中的action,只要访问一次就要实例化一个对象。这样不会存在数据共享的问题。这也是struts2框架的一个好处。
7.3 实验
可以写一个类,如下:
| |
| |
package cn.itcast.struts2.action.moreinstance;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class MoreInstanceAction extends ActionSupport{
public MoreInstanceAction(){
System.out.println("create new action");
}
public String execute(){
System.out.println("more instance action");
return SUCCESS;
}
}
|
|
配置文件为:
| |
| |
<struts>
<package name="moreinstance" namespace="/moreinstance">
<action name="moreinstanceAction"
class="cn.itcast.struts2.action.moreinstance.MoreInstanceAction">
</action>
</package>
</struts>
|
|
请求两次http://localhost:8080/struts2/moreinstance/moreinstanceAction.action路径,如果构造函数中的”create new action”输出两次,说明创建了两次对象。
8 通配符
8.1 Execute方法的弊端
假设有这样的需求:
1、 有一个action为PersonAction。
2、 在PersonAction中要实现增、删、改、查四个方法。
3、 但是在action中方法的入口只有一个execute方法
4、 所以要想完成这样的功能,有一种方法就是在url链接中加参数。
那么在action中的代码可能是这样的:
| |
| |
PatternAction
public class PatternAction extends ActionSupport{
private String method;
public String execute(){
if(method.equals("add")){
//增加操作
}else if(method.equals("update")){
//修改操作
}else if(method.equals("delete")){
//删除操作
}else{
//查询操作
}
return "";
}
}
|
|
可以看出这样写结构并不是很好。而通配符的出现解决了这个问题。
8.2 method属性
Pattern.jsp
访问PersonAction的add方法:<br>
<a href="${pageContext.request.contextPath}/pattern/patternAction.action">测试</a><br>
Struts-pattern.xml
<package name="pattern" namespace="/pattern"
extends="struts-default">
<action name="patternAction" method="add"
class="cn.itcast.struts2.action.pattern.PatternAction">
</action>
</package>
PatternAction
public String add(){
return "add";
}
|
|
说明:从上述代码可以看出只要配置文件中的method属性的值和方法名称一样就可以了。但是这种写法有弊端。如果aciton中需要5个方法。则在struts的配置文件中需要写5个配置,这样会导致配置文件的篇幅很大。而且重复性也很大
8.3 动态调用方法
在url中通过action名称!方法名称可以动态调用方法。
| |
| |
Pattern.jsp
动态调用PatternAction中的add方法:<br>
<a
href="${pageContext.request.contextPath}/pattern/patternAction!add.action">测试</a>
struts-pattern.xml
<action name="patternAction"
class="cn.itcast.struts2.action.pattern.PatternAction">
</action>
说明:这样的情况在配置文件中不需要method属性
|
|
8.4 通配符映射
8.4.1 映射一
需求:a_add.action、b_add.action、c_add.action全部请求PatternAction的add方法
Pattern.jsp
通配符映射实例1:<br>
<a href="${pageContext.request.contextPath}/pattern/a_add.action">测试</a>
<a href="${pageContext.request.contextPath}/pattern/b_add.action">测试</a>
<a href="${pageContext.request.contextPath}/pattern/c_add.action">测试</a>
说明:不管是a_add还是b_add还是c_add的路径都指向PatternAction的add方法。
struts-pattern.xml
<action name="a_add" method="add"
class="cn.itcast.struts2.action.pattern.PatternAction">
</action>
<action name="b_add" method="add"
class="cn.itcast.struts2.action.pattern.PatternAction">
</action>
<action name="c_add" method="add"
class="cn.itcast.struts2.action.pattern.PatternAction">
</action>
上述结构是很差的,经过改进如下:
<action name="*_add" method="add"
class="cn.itcast.struts2.action.pattern.PatternAction">
|
|
8.4.2 映射二
请求PersonAction和StudentAction的add方法
Pattern.jsp
通配符映射实例2:
<br>
<a
href="${pageContext.request.contextPath}/pattern/personAction_add.action">请求personAction的add方法</a>
<a
href="${pageContext.request.contextPath}/pattern/studentAction_add.action">请求studentAction的add方法</a>
|
|
| |
| |
Struts-pattern.xml
<action name="personAction_add" method="add" class="cn.itcast.struts2.action.pattern.PersonAction"></action>
<action name="studentAction_add" method="add" class="cn.itcast.struts2.action.pattern.StudentAction"></action>
改进如下:
<action name=”*_add” method=”add” class=” cn.itcast.struts2.action.pattern.{1}”/>
说明:*和{1}是相对应的关系。
|
|
8.4.3 映射三
需求:在TeacherAction中有增、删、改、查的方法。这个时候配置文件怎么写比较简单?
Pattern.jsp
通配符映射实例3:
<a
href="${pageContext.request.contextPath}/pattern/PersonAction_add.action">请求teacherAction的add方法</a>
<a
href="${pageContext.request.contextPath}/pattern/StudentAction_update.action">请求teacherAction的update方法</a>
<a
href="${pageContext.request.contextPath}/pattern/StudentAction_delete.action">请求teacherAction的delete方法</a>
<a
href="${pageContext.request.contextPath}/pattern/StudentAction_query.action">请求teacherAction的query方法</a>
struts-pattern.xml
<action name="teacherAction_*" method="{1}"
class="cn.itcast.struts2.action.pattern.TeacherAction">
</action>
说明:*和method的属性值保持一致。
延伸:
<action name="*_*" method="{2}"
class="cn.itcast.struts2.action.pattern.{1}">
</action>
第一个*匹配{1},第二个*匹配{2}
|
|
9 全局结果类型
9.1 说明
当很多提交请求跳转到相同的页面,这个时候,这个页面就可以成为全局的页面。在struts2中提供了全局页面的配置方法。
9.2 例子
Struts-pattern.xml
<global-results>
<result name="success">success.jsp</result>
</global-results>
注意:
* 这个配置必须写在action配置的上面。dtd约束的规定。
* 如果在action的result中的name属性也有success值,顺序为先局部后全局。
|
|
9.3 错误的统一处理
9.3.1 xml文件

9.3.2 Java文件

10 Struts2与serlvet接口
10.1 说明
通过前面的练习大家都知道,在action的方法中与servlet的所有的API是没有任何关系的。所以在struts2中做到了aciton与serlvet的松耦合,这点是非常强大的。但是如果没有HttpServletRequest,HttpServletSession,ServletContext有些功能是没有办法完成的。比如购物车程序,需要把购买的物品放入session中。所以就得找一些路径使得在struts2中和serlvet的API相结合。
10.2 实现一
Struts2中提供了ServletActionContext类访问servlet的api。
| |
| |
Servlet.jsp
通过SerlvetActionContext类访问servlet的API:<br>
<a href="${pageContext.request.contextPath}/servlet/servletAction_testServletAPI.action">测试struts2中访问servletAPI</a>
ServletAction
public String testServletAPI(){
//访问request
HttpServletRequest request = ServletActionContext.getRequest();
//访问session 对session进行了二次封装
Map sessionMap = ServletActionContext.getContext().getSession();
//访问application
ServletContext servletContext = ServletActionContext.getServletContext();
return "";
}
|
|
10.3
ServletAction
public class ServletAction extends ActionSupport implements ServletContextAware,
SessionAware,ServletRequestAware{
private HttpServletRequest request;
private Map sessionMap;
private ServletContext servletContext;
public String testServletAPI2(){
System.out.println(this.servletContext);
System.out.println(this.sessionMap);
System.out.println(this.request);
return "";
}
public void setServletContext(ServletContext context) {
// TODO Auto-generated method stub
this.servletContext = context;
}
public void setSession(Map<String, Object> session) {
// TODO Auto-generated method stub
this.sessionMap = session;
}
public void setServletRequest(HttpServletRequest request) {
// TODO Auto-generated method stub
this.request = request;
}
}
|
|
实现二
11 拦截器
假设有一种场景:
在action的一个方法中,要进行权限的控制。如果是admin用户登入,就执行该方法,如果不是admin用户登入,就不能执行该方法。
11.1 实现方案一
| |
| |
AccessAction
public String testAccess(){
if(this.username.equals("admin")){
//执行业务逻辑方法
return SUCCESS;
}else{
//不执行
return "failed";
}
}
|
|
说明:
5、 这样做,程序的结构不是很好。原因是权限的判断和业务逻辑的方法紧密耦合在了一起。如果权限的判断很复杂或者是业务逻辑很复杂会造成后期维护的非常困难。所以结构不是很好
6、 这种形式只能控制一个action中的一个方法。如果很多action中的很多方法都需要这种控制。会导致大量的重复代码的编写。
11.2 实现方案二
动态代理可以实现。请参见cn.itcast.struts.jdkproxy包下的类。
11.3 实现方案三
在struts2中,用拦截器(interceptor)完美的实现了这一需求。在struts2中,
内置了很多拦截器,在struts-default.xml文件中可以看出。用户还可以自定义 自己的拦截器。自定义拦截器需要以下几点:
1、 在配置文件中:
包括两个部分:声明拦截器栈和使用拦截器栈
| |
| |
struts-interceptor.xml
<!--
声明拦截器
-->
<interceptors>
<!--
定义自己的拦截器
-->
<interceptor name="accessInterceptor"
class="cn.itcast.struts2.action.interceptor.PrivilegeInterceptor">
</interceptor>
<!--
声明拦截器栈
-->
<interceptor-stack name="accessInterceptorStack">
<!--
引用自定义的拦截器
-->
<interceptor-ref name="accessInterceptor"></interceptor-ref>
<!--
引用struts2内部的拦截器栈
-->
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
说明:红色部分是struts2内部的拦截器。可以从struts-default.xml文件中得到内容。自己定义的栈必须引入struts2默认的栈。因为我们在访问action时,属性的赋值等一些工作都是由内部的栈完成的。如果不怎么写,struts2的功能将发挥不出来。可以用debug进行调试也能得出结论。
<!--
使用拦截器
-->
<default-interceptor-ref name="accessInterceptorStack"></default-interceptor-ref>
说明:使用拦截器栈。从上面声明部分可以看出,accessInterceptorStack栈既包括了自定义的拦截器,又包括了struts2内部的拦截器栈。
|
|
2、 在拦截器类中
一个类如果是拦截器,必须实现Interceptor接口。
| |
| |
public class PrivilegeInterceptor implements Interceptor{
public void destroy() {
// TODO Auto-generated method stub
}
public void init() {
// TODO Auto-generated method stub
}
public String intercept(ActionInvocation invocation) throws Exception {
// TODO Auto-generated method stub
System.out.println("aaaa");
//得到当前正在访问的action
System.out.println(invocation.getAction().toString());
//得到Ognl值栈
System.out.println(invocation.getInvocationContext().getValueStack());
//请求路径action的名称,包括方法
System.out.println(invocation.getProxy().getActionName());
//方法名称
System.out.println(invocation.getProxy().getMethod());
//命名空间
System.out.println(invocation.getProxy().getNamespace());
String method = invocation.invoke();
return null;
}
}
说明:
1、 这个类中init、intercept和destroy三个方法说明了一个拦截器的生命周期。
2、 在interceptor方法中的参数invocation是执行action的上下文,可以从
这里得到正在访问的action、Ognl值栈、请求路径、方法名称、命名空间
等信息。以帮助程序员在拦截器中做相应的处理工作。
3、 红色部分是关键部分,就是调用action的方法。这里可以成为目标类的
目标方法。
4、因为这里配置的拦截器是针对包的,所以只要是包的action都起作用。
|
|
12 验证
12.1 需求
验证用户名不能为空,密码也不能为空,并且长度不能小于6位数。
12.2 基本验证
ValidateAction.java
public class ValidateAction extends ActionSupport{
private String username;
private String password;
//set和get方法
public String testValidate(){
return "success";
}
public String aaa(){
return "success";
}
public void validate() {
if(this.username.equals("")){
this.addFieldError("username", "用户名不能为空");
}
if(this.password.equals("")){
this.addFieldError("password", "密码不能为空");
}else if(this.password.length()<6){
this.addFieldError("password", "密码不能小于6个字符");
}
}
}
说明:
l 在ActionSupport类中有这样的描述:
/**
* A default implementation that validates nothing.
* Subclasses should override this method to provide validations.
*/
public void validate() {
}
这是一个默认的实现,子类应该覆盖整个方法去完成验证逻辑。
|
|
所以我们只需要去重写validate这个方法就可以了。
l 从下面的图中可以看出错误信息的封装机制。

总的结构为Map<String,List<String>>
String为要验证的字段
List<String> 封装错误信息
l 错误信息会出现在input指向的页面。
l validate是针对action中所有的方法进行验证。如果想针对某一个方法进行验证,
应该把validate方法改为validate方法名称(方法名称的第一个字母大写)。
|
|
12.3 框架验证
1、 编写xml文件,名字的输出规则为:
<ActionClassName>-<aliasName_methodName>-validation.xml
其中alias为配置文件中action元素name属性的值。
2、 在该文件中填写需要校验的内容
13 国际化
13.1 说明
一个系统的国际化就是根据操作系统的语言,页面上的表现形式发生相应的变化。比如如果操作系统是英文,页面的文字应该用英语,如果操作系统是中文,页面的语言应该是中文。
13.2 步骤
13.2.1 建立资源文件
资源文件的命名规则:
默认的命名为:
文件名前缀.properties
根据语言的命名为:
文件名前缀.语言种类.properties
例如:
中文:
resource_zh_CN.properties
内容:
item.username=用户名
item.password=密码
英文:
resource_en_US.properties
内容:
item.username=username_en
item.password=password_en
默认:
resource.properties
内容:
item.username=username
item.password=password
13.2.2 配置文件中
需要在配置文件中加入:
<constant name="struts.custom.i18n.resources" value="cn.itcast.struts2.action.i18n.resource">
</constant>
说明:
1、 这样struts2就会去找你写的资源文件
2、 name的值可以在org/apache/struts2/default.properties中找到。
3、 如果放置的资源文件在src下,则value的值可以直接写,如果在
包中则可以写成包名.resource。
4、 在这里resource是个基名,也就是说可以加载以resource开头的文件。
13.2.3 页面中
利用<s:text name=”item.username”/>就可以把资源文件中的内容输出来。
| |
|
| |
I18n/login.jsp
<s:form action="i18n/loginAction_login.action" method="post">
<table border="1">
<tr>
<td><s:text name="item.username"/></td>
<td><s:textfield name="username"/></td>
</tr>
<tr>
<td><s:text name="item.password"/></td>
<td><s:password name="password"/></td>
</tr>
<tr>
<td><s:submit name="submit" value="%{getText(‘item.username‘)}"/></td>
</tr>
</table>
</s:form>
|
|
| |
| |
说明:
1、 标红色部分的是要从资源文件中找的内容。item.username和item.password代码key的值。
2、 也可以利用%{getText(‘item.username‘)}方式来获取资源。采取的是OGNL表达式的方式。
3、 getText的来源:
从源代码可以看出ActionSupport实现了TextProvider接口。
Provides access to {@link ResourceBundle}s and their underlying text messages.意思是说提供了访问资源文件的入口。而TextProvider中提供了getText方法,根据key可以得到value。
|
|
13.2.4 在action中
可以利用ActionSupport中的getText()方法获取资源文件的value值。
| |
| |
I18n/LoginAction
public class LoginAction extends ActionSupport{
public String login(){
String username = this.getText("item.username");
System.out.println(username);
String password = this.getText("item.password");
System.out.println(password);
return "";
}
}
说明:通过this.getText()方法可以获取资源文件的值。
|
|
14 OGNL
14.1 介绍
OGNL表达式是(Object-Graph Navigation Language)是对象图形化导航语言。OGNL是一个开源的项目,struts2中默认使用OGNL表达式语言来显示数据。与serlvet中的el表达式的作用是一样的。OGNL表达式有下面以下特点:
1、 支持对象方法调用,例如:objName.methodName();
2、 支持类静态的方法调用和值访问,表达式的格式为
@[类全名(包括包路经)]
@[方法名 | 值名]
例如:
@java.lang.String@format(‘foo%s‘,‘bar‘)
@tutorial.MyConstant@APP_NAME;
3、 支持赋值操作和表达式串联,例如:
price=100, discount=0.8, calculatePrice(),这个表达式会返回80;
4、 访问OGNL上下文(OGNL context)和ActionContext
5、 操作集合对象
14.2 回顾el表达式
在servlet中学习的el表达式实际上有两步操作:
1、 把需要表现出来的数据放入到相应的作用域中(req,res,session,application)。
2、 利用el表达式把作用域的值表现在页面上
14.3 ognl类图

14.4 ValueStack
14.4.1 说明
1、 ValueStack是一个接口,在struts2中使用OGNL表达式实际上是使用实现了ValueStack接口的类OgnlValueStack,这个类是OgnlValueStack的基础。
2、 ValueStack贯穿整个action的生命周期。每一个action实例都拥有一个ValueStack对象。其中保存了当前action对象和其他相关对象。
3、 Struts2把ValueStack对象保存中名为struts.valueStack的request域中。
14.4.2 ValueStack内存图

14.4.3 ValueStack的组织结构

从图上可以看出OgnlValueStack和我们有关的内容有两部分:即OgnlContext和CompoundRoot。所以把这两部分搞清楚很重要。
14.4.4 OgnlContext组织结构

14.4.4.1 _values
从上述可以看出,OgnlContext实际上有一部分功能是Map。所以可以看出_values就是一个Map属性。而运行一下下面的代码就可以看到:
//在request域中设置一个参数
ServletActionContext.getRequest().setAttribute("req_username","req_username");
//在request域中设置一个参数
ServletActionContext.getRequest().setAttribute("req_psw", "req_psw");
//在session域中设置一个参数
ActionContext.getContext().getSession().put("session_username", "session_username");
//在session域中设置一个参数
ActionContext.getContext().getSession().put("session_psw", "session_psw");
//获取OGNL值栈
ValueStack valueStack = ActionContext.getContext().getValueStack();
|
|
在_values的map中:
|
key
|
value
|
|
application
|
ApplicationMap
|
|
request
|
RequestMap
|
|
action
|
自己写的action
|
|
com.opensymphony.xwork2.ActionContext.session
|
SessionMap
|
而request中的值为:
{
req_psw=req_psw,
req_username=req_username,
__cleanup_recursion_counter=1,
struts.valueStack=com.opensymphony.xwork2.ognl.OgnlValueStack@3d58b2,
struts.actionMapping=org.apache.struts2.dispatcher.mapper.ActionMapping@1f713ed
}
|
|
可以看出在程序中存放在request作用域的值被放入到了_values的request域中。
而com.opensymphony.xwork2.ActionContext.session的值为:
| |
| |
{session_username=session_username, session_psw=session_psw}
|
|
从上图中可以看出在程序中被加入到session的值在_values中也体现出来。
14.4.4.2 _root

从图中可以看出_root实际上CompoundRoot类,从类的组织结构图中可以看出,这个类实际上是继承了ArrayList类,也就是说这个类具有集合的功能。而且在默认情况下,集合类的第一个为ValueStackAction,也就是我们自己写的action。
14.5 总图

说明:
1、 上图是ognl完整的数据结构图,可以清晰得看出数据的组成。
2、 Context中的_root和ValueStack中的root(对象栈)里的数据结构和值是一样的。
3、 这就意味着我们只需要操作OgnlContext就可以完成对数据的存和取的操作。
4、 ValueStack内部有两个逻辑的组成部分:
a) ObjectStack
Struts会把动作和相关的对象压入到ObjectStack中。
b) ContextMap
Struts会把一些映射关系压入到ContextMap中
14.6 存数据
14.6.1 Map中存数据
14.6.1.1 方法1
| |
| |
//向map中存放数据
ServletActionContext.getRequest().setAttribute("req_username","req_username");
ServletActionContext.getRequest().setAttribute("req_psw", "req_psw");
ActionContext.getContext().getSession().put("session_username", "session_username");
ActionContext.getContext().getSession().put("session_psw", "session_psw");
|
|
上面的代码都是往ContextMap中存放数据。因为这些值都是具有映射关系的。
14.6.1.2 方法2
| |
| |
ActionContext.getContext().put("msg", "msg_object");
|
|
通过执行上述代码把”msg”和”msg_object”放入到了ContextMap中。
14.6.2 值栈中存数据
14.6.2.1 方法1
| |
| |
/*
* 把对象放入到值栈中
*/
//方法一:先得到root,把一个对象压入到root中
ValueStack valueStack = ActionContext.getContext().getValueStack();
valueStack.getRoot().add(new Person());
valueStack.getRoot().add(new Student());
|
|
运行以后的值栈结构图:

从内存图中可以看出最后被压入到list中的对象在最下面。

图为ArrayList中的add方法的解释:
追加的指定的元素放到集合的最下面。
14.6.2.2
/*
* 方法二:先得到root,利用add(index,Object)把一个对象压入到root中
* 这里index指的是集合中的位置
*/
ValueStack valueStack = ActionContext.getContext().getValueStack();
valueStack.getRoot().add(new Person());
valueStack.getRoot().add(0, new Student());
|
|
方法2
运行后的结构图为:

从图中可以很明显看出新创建的Student对象被放到了第一个位置,因为
Index的值为0,所以是第一个位置。
14.6.2.3 方法3
把一个键值对存放在对象栈中,做法为:
| |
| |
/*
* 方法三:
* 把一个键值对存放在对象栈中
*/
ValueStack valueStack = ActionContext.getContext().getValueStack();
valueStack.set("msg","msg_object");
|
|
对象栈图为:

从图中可以看出上面的代码执行过程为:
1、 先把”msg”和”msg_object”两个字符串封装成Map
2、 再把封装以后的Map放入到对象栈中。
14.6.2.4 方法4
/*
* 方法4
* 利用ValueStack的push方法可以把一个对象直接压入对象栈的第一个位置
*/
ValueStack valueStack = ActionContext.getContext().getValueStack();
valueStack.push(new Person());
|
|
执行完push方法以后,对象栈的情况如下:

Push方法把新创建的Person对象放入到了对象栈的首个位置。
14.7 OGNL Context

14.7.1 说明
1、 上图为OGNL Context的结构图
2、 当struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action。然后把action压入到值栈中。所以action的实例变量可以被ognl访问。所以利用ognl表达式可以访问action。
14.8 ActionContext
/*
* ActionContext的作用
*/
ActionContext.getContext().getSession().put("session_username", "session_username");
ActionContext.getContext().getSession().put("session_psw", "session_psw");
ValueStack valueStack = ActionContext.getContext().getValueStack();
|
|
14.8.1 说明
从上面的代码中可以看出来,struts2中的ActionContext的作用是提供了对ognl数
据的操作。并且可以通过ActionContext获取到经过struts2封装了的session等参数。
14.9 ServletActionContext
/*
* 返回servlet中的request和servletcontext
*/
ServletActionContext.getRequest().setAttribute("req_username","req_username");
ServletActionContext.getRequest().setAttribute("req_psw", "req_psw");
ServletActionContext.getServletContext();
//得到ActionContext
ServletActionContext.getContext();
|
|
14.9.1 说明
1、 可以通过ServletActionContext得到servlet中的一些类,比如HttpServletRequest,ServletContext等
2、 可以通过ServletActionContext返回ActionContext
14.10 Ognl表达式
14.10.1 简述
从9.6到9.9讨论了ognl的结构、如何存数据。9.10重点讨论如何把ognl结构中的数据呈现在页面上。所以Ognl表达式的作用就是把OgnlContext中的数据输出到页面上。
14.10.2 el表达式例子
参照课堂演示例子
14.10.3 ognl表达式例子
14.10.3.1 用法1(#号用法)
说明:
1、 访问OGNL上下文和action上下文,#相当于ActionContext.getContext();
2、 如果访问的是map中的值而不是对象栈中的值,由于map中的数据不是根对象,所以在访问时需要添加#前缀。
|
名称
|
作用
|
例子
|
|
parameters
|
包含当前HTTP请求的Map
|
#parameters.id[0]=request.getParameter(“id”)
|
|
request
|
包含当前HttpServletRequest属性的Map
|
#request.username=request.getAttribute(“username”);
|
|
session
|
包含当前HttpSession属性的Map
|
#session.username=session.getAttribute(“username”);
|
|
application
|
包含当前ServletContext属性的Map
|
#application.username=application.getAttribute(“username”);
|
|
attr
|
用于按照request>session>application顺序访问其属性
|
#attr.username相当于按照顺序在以上三个范围内读取username的属性,直到找到为止。
|
注:也可以写为#request[‘username’] #sesssion[‘username’] #application[‘username’]
主要步骤:
在action中
public String testScope() {
// 把字符串"request_msg"放入到request域中
ServletActionContext.getRequest().setAttribute("request_username",
"request_username");
// 把字符串"session_msg"放入到session域中
ServletActionContext.getRequest().getSession().setAttribute(
"session_username", "session_username");
// 把字符串"application_msg"放入到application域中
ServletActionContext.getServletContext().setAttribute(
"application_username", "application_username");
return "ognl_scope";
}
|
|
在页面中:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%> //导入struts2的标签
ognl表达式关于scope(request,session,application)的输出<br>
request:<s:property value="#request.request_username"/><br>
session:<s:property value="#session.session_username"/><br>
application:<s:property value="#application.application_username"/><br>
|
|
14.10.3.2 用法2
OGNL会设定一个对象(root对象),在struts2中根对象就是CompoundRoot,或者为OgnlValueStack中的root,通常被叫做ValueStack(值栈或者对象栈),如果要访问根对象的属性,则可以省略去#,直接访问对象的属性即可。
在action中
| |
| |
public String testObjectValue(){
//得到ognl的上下文
ValueStack valueStack = ActionContext.getContext().getValueStack();
valueStack.set("msg", "object_msg");
return "ognl_objectstack";
}
|
|
在页面中:
对象栈的内容:<s:property value="msg"/>
//把对象栈中key值为msg的value的值输出
|
|
问题:如果把一个字符串push进对象栈,怎么样取?
在14.11.1中将解决这个问题。
14.10.3.3 用法3(深入理解对象栈)
有三个类:Person.java,Student.java,OgnlAction.java
Person类
package cn.itcast.struts2.valuestack.bean;
public class Person {
private Integer pid;
private String pname;
private String comment;
}
Student类
package cn.itcast.struts2.valuestack.bean;
public class Student {
private Integer sid;
private String sname;
private String comment;
}
OgnlAction类
public class OgnlAction extends ActionSupport {
private String comment="55";
private String id = "3";
}
把Person类和Student类创建出对象,然后放入到对象栈中,代码为:
ValueStack valueStack = ActionContext.getContext().getValueStack();
Person person = new Person();
person.setPid(1);
person.setPname("zz");
person.setComment("person");
valueStack.getRoot().add(0,person);
Student student = new Student();
student.setSid(2);
student.setSname("student");
student.setComment("student");
valueStack.getRoot().add(0,student);
|
|
从以前学过的OGNLContext结构可以看出,对象栈中的分布如图所示:

但是大家注意一个现象:在student对象中有comment属性,在person对象中
也有comment属性,在OgnlAction中还有comment属性,如果页面输出
Comment属性应该选择哪种呢?
结论:
对于对象栈中存放的属性,struts2会从最顶部开始寻找,如果找到则赋值,
如果找不到,则会依次往下寻找,直到找到为止。所以应该输出的是student
对象的comment的值。
14.10.3.4 用法4(构造map)
还可以利用ognl表达式构造Map,如#{‘foo1’:’bar1’,’foo2’:’bar2’};这种用法经常用
在给radio,checkbox等标签赋值上。
1、 在页面上可以这样输出:
<s:radio list="#{‘foo1‘:‘bar1‘,‘foo2‘:‘bar2‘}" name="sex" label="性别"></s:radio>
上面的代码相当于
性别:<input type=”radio” name=”sex” value=”foo1”/>bar1
<input type=”radio” name=”sex” value=”foo2”/>bar2
|
|
2、 也可以这样使用:
后台代码:
Map<String, String> map = new HashMap<String, String>();
map.put("male", "男");
map.put("female", "女");
ServletActionContext.getRequest().setAttribute("map", map);//1
ActionContext.getContext().put("map", map);//2
jsp页面:
如果执行的是1,则为
<s:property value="#request.map.male"/><br>
<s:property value="#request.map.female"/><br>
如果执行的是2,则为
<s:property value="map.male"/><br>
<s:property value="map.female"/><br>
|
|
14.10.3.5 用法5(%)
“%”符号的用途是在标签的属性值给理解为字符串类型时,执行环境%{}中添加的是OGNL表达式。
{}中用OGNL表达式
在代码中:
ServletActionContext.getRequest().setAttribute("request_username", "username");
在页面上:
<s:textfield name="name" label="%{#request.request_username}"></s:textfield>
|
|
用{‘’}来表示字符串
<s:textfield name="username" label="%{‘用户名‘}"></s:textfield>
相当于
用户名:<input type=”text” name=”username”>
|
|
14.10.3.6 用法6($)
$主要有两个用途:
用于在国际化资源文件中引用OGNL表达式
在struts2的配置文件中引用OGNL表达式
在action中:
ServletActionContext.getRequest().setAttribute("request_username", "username");
在配置文件中:
<result name="dollar">successOgnl.jsp?msg=%{#request.request_username}</result>
在页面中:
<s:property value="#parameters.msg[0]"/>
如果把action中的值放入到对象栈中呢?
|
|
14.11 标签用法
14.11.1 Property标签
14.11.1.1 说明
用于输出指定的值
14.11.1.2 属性
1、 default:
可选属性,如果输出的值为null,则显示该属性指定的值。
2、 escape
可选属性,指定是否格式化为html代码
3、 value
可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出ValueStack栈顶的值。
14.11.1.3 例1(默认值)
在testOgnlTag.jsp中
利用property标签获取栈顶的值:
<br>
<a
href="${pageContext.request.contextPath}/ognl/ognlTagAction_testProperty1.action">测试</a>
在OgnlTagAction中
public String testProperty1(){
return "ognlTag";
}
在successOgnlTag.jsp中:
利用property标签获取栈顶的值:<br>
<s:property/> //没有value属性,所以应该输出的是栈顶元素
结果:
cn.itcast.struts2.action.ognltag.OgnlTagAction@5a82b2
|
|
14.11.1.4 例2(default和value)
在testOgnlTag.jsp中
测试property中的default的值:<br>
<a
href="${pageContext.request.contextPath}/ognl/ognlTagAction_testDefault.action">测试</a><br>
在OgnlTagAction中
public String testDefault() {
ServletActionContext.getRequest().setAttribute("request_username",
"username");
return "ognlDefault";
}
在successOgnlTag.jsp中
当property的value没有值的情况下,输出default的值:<br>
<s:property value="#request.request_username11" default="default value"/>
<s:property value="#request.request_username" default="default value"/>
说明:因为在后台request中的key值为.request_username,而页面上的
输出为.request_username11,不对应,所以应该输出default的值。
|
|
14.11.1.5 例3(escape)
在testOgnlTag.jsp中:
测试property中的escape的值:<br>
<s:property value="%{‘<hr>hr的解析‘}"/>
说明:因为escapse默认的值为true,也就是说<hr>hr的解析会当作一个字符串处理。
<s:property value="%{‘<hr>hr的解析‘}" escape=”false”/>
说明:因为如果escapse为false,则把字符串中符合html标签的语法,会当作html
标签来进行处理。
|
|
14.11.1.6 例4(输出栈顶String的值)
在testOgnlTag.jsp中:
取出栈顶的值:<br>
<a
href="${pageContext.request.contextPath}/ognl/ognlTagAction_testString.action">测试</a><br>
在OgnlTagAction中
public String testString(){
ActionContext.getContext().getValueStack().push("msg");
return "ognlString";
}
在successOgnlTag中
取出栈顶的值:<br>
<s:property/>
|
|
14.11.2 Debug标签
14.11.2.1 说明
利用debug标签可以输出OGNLContext所有的值
14.11.2.2 例子
在testOgnlTag.jsp中:
利用debug标签输出ognl的所有的值:<br>
<a
href="${pageContext.request.contextPath}/ognl/ognlTagAction_testDebug.action">测试</a><br>
在OgnlTagAction中
public String testDebug(){
return "ognlDebug";
}
在successOgnlTag.jsp中:
利用debug标签输出OgnlContext中所有的值:<br>
<s:debug></s:debug>//利用这个标签可以输出ognlcontext中所有的值
|
|
14.11.3 Set标签
14.11.3.1 说明
用于将某个指定的值放入到指定的范围
14.11.3.2 属性
var:
变量的名字,name、id与var表达的含义是一样的。Var已经取代了name,id;
Scope:
指定变量被放置的范围。该属性可以接受application,session,request,page或
Action。如果没有设置该属性,则默认会放在action中。
Value:
赋值给变量的值。如果没有设置该属性,则将ValueStack栈顶的值赋给变量。
14.11.3.3 例1
在testOgnlTag.jsp中
测试set标签:<br>
<a
href="${pageContext.request.contextPath}/ognl/ognlTagAction_testSet.action">测试</a><br>
在OgnlTagAction中
public String testSet() {
ServletActionContext.getRequest().setAttribute("request_username",
"username");
return "ognlSet";
}
在successOgnlTag.jsp中
测试set标签:<br>
<s:set value="#request.request_username" var="aaaaa" scope="request"></s:set>
<s:property value="#request.aaaaa"/>
说明:这样的情况,aaaaa的key值会出现在request域中。
<s:set value="#request.request_username" var="aaaaa"></s:set>
<s:property value="#request.aaaaa"/>
说明:这种写法aaaaa会作为一个key值存在ognl的map中。所以利用
<s:property value="#aaaaa"/>也能输出值。
|
|
14.11.4 Push标签
14.11.4.1 说明
把对象放入到栈顶,不能放入到其他的范围,当标签结束时,会从栈顶删除。
14.11.4.2 例子
在testOgnlTag.jsp中
用push方法把一个对象放入到栈顶:<br>
<a
href="${pageContext.request.contextPath}/ognl/ognlTagAction_testPUSH.action">测试</a><br>
在OgnlTagAction中
public String testPUSH(){
ServletActionContext.getRequest().setAttribute("request_username", "username");
return "ognlPUSH";
}
在successOgnlTag.jsp中
利用push方法把对象放入到栈顶:<br>
<s:push value="#request.request_username">
<s:property/>
<s:debug></s:debug>//注意debug标签的位置,这个位置是正确的
</s:push>
<s:debug></s:debug>//这个位置的标签是错误的。因为s:push标签已经结束,所以栈顶元素已经被删除掉了。
|
|
14.11.5 Bean标签
14.11.5.1 说明
实例化一个符合javabean规范的class,标签体内可以包含几个param元素,可用于调用set方法,给class的属性赋值。
14.11.5.2 属性
Name:
要被实例化的class的名字,符合javabean规范。
Var:
赋值给变量的值。放置在request作用域中。如果没有设置该属性,对象被
设置到栈顶。
14.11.5.3 例子
在testOgnlTag.jsp中
利用bean标签实例化一个person对象:<br>
<a
href="${pageContext.request.contextPath}/ognl/ognlTagAction_testBean.action">测试</a><br>
在OgnlTagAction中
public String testBean(){
ServletActionContext.getRequest().setAttribute("pid", 1);
ServletActionContext.getRequest().setAttribute("request_username", "username");
ServletActionContext.getRequest().setAttribute("pname", "username");
ServletActionContext.getRequest().setAttribute("comment", "person");
return "ognlBean";
}
在successOgnlTag.jsp中
给一个person赋值,值来自于后台:<br>
<s:bean name="cn.itcast.struts2.valuestack.bean.Person" var="myperson">
<s:param name="pid" value="#request.pid"></s:param>
<s:param name="pname" value="#request.pname"></s:param>
<s:param name="comment" value="#request.comment"></s:param>
<!--
因为person在栈顶,所以输出栈顶
-->
<s:property value="pid"/>
<s:property value="pname"/>
<s:property value="comment"/>
<s:debug></s:debug>
</s:bean>
<s:property/>//值不再是Person对象
<!—
前提条件:不加var=”myperson”属性
由于bean标签在栈顶,所以当bean标签结束的时候,
栈顶的person对象就被删除掉了。所以在bean标签的外部输出栈顶的person
值是不行的。
-->
|
|
<s:property/>
<!--
当bean标签中加var属性值为myperson,用debug标签再观察其值的分布情况
在map中出现了"myperson":person对象
-->
<s:property value="#person.pid"/><br>
<s:property value="#person.pname"/><br>
<s:property value="#person.comment"/><br>
说明:当加上var=”myperson”属性的时候,myperson对象出现在了map中,这个时候就可以在bean标签的外部获取person的属性了。
|
|
14.11.6 Action标签
14.11.6.1 说明
通过指定命名空间和action的名称,可以直接调用后台的action.
14.11.6.2 属性
Name:
Aciton的名字
Namespace:
Action所在的命名空间(action的名称后不加.action)
executeResult:
Action的result是否需要被执行,默认值为false,不执行
14.11.6.3 例子
在testOgnlTag.jsp中
通过action标签访问后台的action:<br>
<s:action name="ognlTagAction_testBean" namespace="/ognl" executeResult="true"></s:action>
在OgnlTagAction中
public String testBean(){
ServletActionContext.getRequest().setAttribute("pid", 1);
ServletActionContext.getRequest().setAttribute("request_username", "username");
ServletActionContext.getRequest().setAttribute("pname", "username");
ServletActionContext.getRequest().setAttribute("comment", "person");
return "ognlBean";
}
说明:
如果executeResult的属性值为false,会执行到相应的action,但是action跳转后的页面
将不再执行。如果executeResult的属性值为true,则会在当前页面包含跳转后的页面
值。
|
|
14.11.7 Iterator标签
14.11.7.1 说明
该标签用于对集合进行迭代。这里的集合包括:list,set和数组
14.11.7.2 属性
Value:
可选属性,指定被迭代的集合。如果没有设置该属性,则使用对象栈顶的集合。
Var:
可选属性,引用变量的名称
Status:
可选属性,该属性指定迭代时的IteratorStatus实例。该实例包含如下的方法:
int getCount() 返回当前迭代的元素个数
int getIndex() 返回当前迭代元素的索引
boolean isEven() 返回当前迭代元素的索引是否是偶数
boolean isOdd() 返回当前迭代元素的索引是否是奇数
boolean isFirst() 返回当前迭代元素是否为第一个元素
boolean isLast() 返回当前迭代元素是否为最后一个元素
14.11.7.3 例1(push)
在testOgnlTag.jsp中
通过iterator标签把后台的list集合遍历(list通过push方法压入到栈顶)<br>
<a href="${pageContext.request.contextPath}/ognl/ognlTagAction_testList1.action">测试</a><br>
在OgnlTagAction中
/*
* 把生成的list利用push方法压入到栈顶
*/
public String testList1(){
ServletActionContext.getRequest().setAttribute("request_username", "username");
List<Person> personList = new ArrayList<Person>();
List<Person> person1List = new ArrayList<Person>();
for(int i=0;i<10;i++){
Person person = new Person();
person.setPid(i);
person.setPname("person"+i);
person.setComment("person"+i);
personList.add(person);
}
for(int i=11;i<15;i++){
Person person = new Person();
person.setPid(i);
person.setPname("person"+i);
person.setComment("person"+i);
person1List.add(person);
}
/*
* 说明:
* 1、执行完三次压栈以后,对象栈的栈顶存放的元素为String类型的aaa;
* 而这个时候如果页面用<s:iterator value="top"></s:iterator>
* top代表对象栈的栈顶的元素
* 进行迭代的时候,针对的元素是aaa;
|
|
* 2、执行第一次压栈和第二次压栈,对象栈的栈顶存放的元素为person1List;
* 页面上用
* <s:iterator value="top"></s:iterator>
* top代表对象栈的栈顶的元素
* 进行迭代的时候,针对的元素是person1List;
* 3、执行第一次压栈,对象栈的栈顶存放的元素为personList;
* 页面上用
* <s:iterator value="top"></s:iterator>
* top代表对象栈的栈顶的元素
* 进行迭代的时候,针对的元素是personList;
*/
//把personList压入到栈顶
ActionContext.getContext().getValueStack().push(personList);
/*
* 把perosn1List压入到栈顶
*/
ActionContext.getContext().getValueStack().push(person1List);
/*
* 把aaa压入到栈顶
*/
ActionContext.getContext().getValueStack().push("aaa");
return "ognlList1";
}
在successOgnlTag.jsp中
用iterator标签迭代后台list中的数据(把list通过push方法压入到栈顶 ):<br>
<!--
因为iterator是一个迭代器标签,value的属性值top代表栈顶的元素
-->
<s:iterator value="top">
<s:property value="pname"/>
<s:property/>//可以输出对象栈栈顶的元素
<s:debug></s:debug>//输出OgnlContext的结构
</s:iterator><br>
<s:debug></s:debug>//当iterator迭代完之后观察OgnlContext的结构
说明:当进行迭代的时候,当前的被迭代的对象会被放入到栈顶,所以在property元素输出的时候不用加#号。当iterator标签结束的时候,栈顶的对象会被删除掉。
|
|
14.11.7.4 例2(action属性)
在testOgnlTag.jsp中
通过iterator标签把后台的list集合遍历(list作为action中的属性)<br>
<a href="${pageContext.request.contextPath}/ognl/ognlTagAction_testList2.action">测试</a><br>
在OgnlTagAction中
private List<Person> pList;
public List<Person> getPList() {
return pList;
}
public void setPList(List<Person> list) {
pList = list;
}
public String testList2(){
ServletActionContext.getRequest().setAttribute("request_username", "username");//在successOgnlTag.jsp中
<s:push value="#request.request_username">标签需要用到后台的这个操作。
this.pList = new ArrayList<Person>();
for(int i=0;i<10;i++){
Person person = new Person();
person.setPid(i);
person.setPname("person"+i);
person.setComment("person"+i);
pList.add(person);
}
return "ognlList2";
}
说明:通过代码可以看出来,这个例子中把pList作为action的属性,然后给其赋值。
在successOgnlTag.jsp中
用iterator标签迭代后台list中的数据(list作为action中的属性 ):<br>
<s:iterator value="pList">
<s:property value="pname"/>
<s:property/>
<s:debug></s:debug>
</s:iterator><br>
<s:debug></s:debug>
说明:因为OgnlTagAction在对象栈,所以value中的pList可以不加#号。从第一个debug标签可以看出,这个标签把当前正在迭代的对象临时放入到了栈顶。如果iterator元素结束迭代时,栈顶的对象就消失了。 所以第一次的debug和第二次的
Debug内容是不一样的。
|
|
14.11.7.5 例3(Map中)
在testOgnlTag.jsp中
通过iterator标签把后台的list集合遍历(list通过ActionContext.getContext().put放入OgnlContext的Map中)<br>
<a
href="${pageContext.request.contextPath}/ognl/ognlTagAction_testList3.action">测试</a><br>
在OgnlTagAction中
/*
* 把personList通过put方法放入到OnglContext的map中
*/
public String testList3(){
ServletActionContext.getRequest().setAttribute("request_username", "username");
List<Person> personList = new ArrayList<Person>();
for(int i=0;i<10;i++){
Person person = new Person();
person.setPid(i);
person.setPname("person"+i);
person.setComment("person"+i);
personList.add(person);
}
ActionContext.getContext().put("personList", personList);
return "ognlList3";
}
在successOgnlTag.jsp中
用iterator标签迭代后台list中的数据(list经过ActionContext.getContext().put方法放入到OgnlContext中 ):<br>
<s:iterator value="personList">
<s:property value="pname"/>
<s:property/>
<s:debug></s:debug>
</s:iterator><br>
<s:debug></s:debug>
说明:用iterator进行迭代,迭代的集合如果来自map,value的值可以加#也可以不加#,这点需要注意。Debug的现象和例2一样。
|
|
14.11.7.6 例4(begin,end,step)
在testOgnlTag.jsp中
利用iterator的begin、end、step属性控制数据的显示:<br>
<a
href="${pageContext.request.contextPath}/ognl/ognlTagAction_testList4.action">测试</a><br>
在OgnlTagAction中
/*
* 在页面上通过iterator的begin,end,step属性控制页面的显示
*/
public String testList4(){
ServletActionContext.getRequest().setAttribute("request_username", "username");
List<Person> personList = new ArrayList<Person>();
for(int i=0;i<10;i++){
Person person = new Person();
person.setPid(i);
person.setPname("person"+i);
person.setComment("person"+i);
personList.add(person);
}
ServletActionContext.getRequest().setAttribute("first", 2);
ServletActionContext.getRequest().setAttribute("end", 8);
ServletActionContext.getRequest().setAttribute("step", 2);
ActionContext.getContext().put("personList", personList);
return "ognlList4";
}
在successOgnlTag.jsp中
用iterator标签属性begin,end,step控制数据的显示:<br>
<s:iterator value="personList" begin="%{#request.first}" end="%{#request.end}" step="%{#request.step}">
<s:property value="pname"/><br>
<s:property/><br>
</s:iterator><br>
说明:begin属性为开始位置,end属性为结束位置,step为步长。这里要注意begin的值是通过ognl表达式传递过来的。
|
|
14.11.7.7 例5(status)
在testOgnlTag.jsp中
测试iterator的status属性:<br>
<a
href="${pageContext.request.contextPath}/ognl/ognlTagAction_testList5.action">测试</a><br>
在OgnlTagAction中
/*
* 测试iterator的status属性
*/
public String testList5(){
ServletActionContext.getRequest().setAttribute("request_username", "username");
List<Person> personList = new ArrayList<Person>();
for(int i=0;i<10;i++){
Person person = new Person();
person.setPid(i);
person.setPname("person"+i);
person.setComment("person"+i);
personList.add(person);
}
ActionContext.getContext().put("personList", personList);
return "ognlList5";
}
在successOgnlTag.jsp中
测试iterator的status属性的值:<br>
<!--
如果提供status 每次迭代时候将生成一个IteratorStatus实例并放入堆栈中
用debug标签查看堆栈,在map中,存在一个st,类型为org.apache.struts2.views.jsp.IteratorStatus
st对应一个IteratorStatus对象,有如下属性
int getCount() 返回当前迭代的元素个数
int getIndex() 返回当前迭代元素的索引
boolean isEven() 返回当前迭代元素的索引是否是偶数
boolean isOdd() 返回当前迭代元素的索引是否是奇数
boolean isFirst() 返回当前迭代元素是否为第一个元素
boolean isLast() 返回当前迭代元素是否为最后一个元素-->
<table border="1">
<s:iterator value="personList" var="person" status="st">
<tr>
<td><s:property value="#st.count"/></td>
<td><s:property value="#st.getIndex()"/></td>
<td><s:property value="#person.pname"/></td>
</tr>
</s:iterator>
</table>
<br>
|
|
说明:上述标明status属性,就会在栈中的map中有一个key值为st,而st对应的值为org.apache.struts2.views.jsp.IteratorStatus的对象。在迭代每一个元素的时候,都存在这个对象,我们可以根据这个对象获取到该对象的属性。从而得到遍历中的相关信息。
|
|
14.11.7.8 例6(奇偶行变色)
testOgnlTag.jsp
利用iterator的status属性完成隔行变色:<br>
<a
href="${pageContext.request.contextPath}/ognl/ognlTagAction_testList6.action">测试</a><br>
OgnlTagAction
/*
* 利用iterator的status属性完成表格的隔行变色
*/
public String testList6(){
ServletActionContext.getRequest().setAttribute("request_username", "username");
List<Person> personList = new ArrayList<Person>();
for(int i=0;i<10;i++){
Person person = new Person();
person.setPid(i);
person.setPname("person"+i);
person.setComment("person"+i);
personList.add(person);
}
ActionContext.getContext().put("personList", personList);
return "ognlList6";
}
successOgnlTag.jsp
<style type="text/css">
.odd{
background-color:red;
}
.even{
background-color:blue;
}
</style>
|
|
14.11.8 If/elseif/else标签
14.11.8.1 说明
基本的流程控制标签。If标签可以单独使用,也可以结合elseif或else标签使用。
14.11.8.2 属性
test:后面跟判断表达式。
14.11.8.3 例子
testOgnlTag.jsp
测试if/elseif/else标签的使用:<br>
<a
href="${pageContext.request.contextPath}/ognl/ognlTagAction_testIF.action">测试</a><br>
OgnlTagAction
/*
* 测试if标签
*/
public String testIF(){
ServletActionContext.getRequest().setAttribute("request_username", "username");
List<Person> personList = new ArrayList<Person>();
for(int i=0;i<10;i++){
Person person = new Person();
person.setPid(i);
person.setPname("person"+i);
person.setComment("person"+i);
personList.add(person);
}
ActionContext.getContext().put("personList", personList);
return "ognlIF";
}
successOgnlTag.jsp
测试if/elseif/else标签:<br>
<table border="1">
<s:iterator value="personList" var="person" status="st">
<!--
value的值可以跟双目表达式
-->
<tr class="<s:property value="#st.even?‘even‘:‘odd‘"/>">
<td><s:property value="#person.pid"/></td>
<td><s:property value="#person.pname"/></td>
<td>
<s:if test="#person.pid<3">这个id号小于3</s:if>
<s:elseif test="#person.pid<5">这个id号大于等于3小于5</s:elseif>
<s:else>这个id号大于等于5</s:else>
</td>
</tr>
</s:iterator>
</table>
|
|
14.11.9 url标签
14.11.9.1 说明
该标签用于创建url,可以通过”param”标签提供request参数。
14.11.9.2 属性
Value:
如果没有值,就用当前的action,使用value后必须加.action.
Action:
用来生成url的action.如果没有使用则用value;
Namespace:
命名空间
Var:
引用变量的名称。
14.11.9.3 例子
testOgnlTag.jsp
测试url标签的使用:<br>
<a
href="${pageContext.request.contextPath}/ognl/ognlTagAction_testURL.action">测试</a><br>
OgnlTagAction
/*
* 测试标签url
*/
public String testURL(){
ServletActionContext.getRequest().setAttribute("request_username", "username");
return "ognlURL";
}
|
|
successOgnlTag.jsp
测试url标签的使用:<br>
<!--
作用:直接输出url地址
-->
<s:url action="ognlTagAction.action" namespace="/ognl" var="myurl">
<s:param name="pid" value="12"></s:param>
<s:param name="pname" value="%{‘zd‘}"></s:param>
</s:url>
<s:debug></s:debug>
<a href="<s:property value="#myurl"/>">test</a>
说明:如果url标签不写var属性,则url标签输出url的路径。如果写上var变量,
则会在OgnlContext中出现相应的key值:myurl。所以在程序的最后一行引用了
#myurl变量从而得到路径。Param是传递的参数。这里需要注意:
value="%{‘zd‘}可以,但是value=”zd”是不行的。后面的写法会去栈顶查找zd。
所以s:url标签就相当于声明一个url,然后在外部使用。
|
|
14.11.10 Ognl操作集合
|
Java代码
|
OGNL表达式
|
|
list.get(0)
|
List[0]
|
|
array[0]
|
array[0]
|
|
((User)list.get(0)).getName()
|
list[0].name
|
|
Array.length
|
Array.length
|
|
List.size()
|
List.size
|
|
List.isEmpty()
|
List.isEmpty
|
14.11.11 Ognl操作Map
|
Java代码
|
Ognl表达式
|
|
map.get(“foo”)
|
Map[‘foo’]
|
|
Map.get(new Integer(1));
|
Map[1]
|
|
User user = (User)map.get(“user”);
Return user.getName()
|
Map[‘user’].name
|
|
Map.size()
|
Map.size
|
|
Map.isEmpty()
|
Map.isEmpty
|
|
Map.get(“foo”)
|
Map.foo
|
|
Java代码
|
Ognl表达式
|
|
Map map = new HashMap();
Map.put(“foo”,”bar”);
Map.put(“1”,”2”);
Return map;
|
#{“foo”:”bar”,”1”:”2”}
|
|
Map map = new HashMap();
Map.put(new Integer(1),”a”);
Map.put(new Integer(2),”b”);
Map.put(new Integer(3),”c”);
|
#{1:”a”,2:”b”,3:”c”}
|
testOgnlTag.jsp
写一个map集合:<br>
<s:iterator value="#{1:‘a‘,2:‘b‘,3:‘c‘}">
<!--
获取map中的key和value
-->
<s:property value="value"/>
<s:property value="key"/>
<s:debug></s:debug>
</s:iterator>
<br>
<s:iterator value="#{1:‘a‘,2:‘b‘,3:‘c‘}" var="map">
<!--
获取map中的key和value
-->
<s:property value="value"/><br>
<s:property value="key"/><br>
<s:property value="#map.value"/><br>
</s:iterator>
|
|
15 UI标签
15.1 说明
1、 UI标签在html文件中表现为一个表单元素。
2、 使用struts2的ui标签有如下好处:
1、 可以进行表单的回显
2、 对页面进行布局和排版
3、 标签的属性可以被赋值为一个静态的值,或者一个OGNL表达式。
15.2 Form标签

15.2.1 说明
1、 id属性为s:form的唯一标识。可以用document.getElementById()获取。
2、 name属性为s:form的名字,可以用document.getElementsById()获取。
3、 在默认情况下,s:form将显示表格的形式。
15.3 Textfield标签

15.3.1 说明
实际上相当于在表格中多了一行,在这行中多了两列。其变化从上述图中可以很明显的看出。
15.4 Password标签

15.4.1 说明
如果不加showPassword属性,则密码不会显示,把showPassword属性的值设置为true,就能显示密码。
15.5 Hidden标签

15.5.1 说明
Hidden标签并没有加tr和td
15.6 Submit标签
15.6.1 情况一

15.6.2 说明一
这种情况为submit的type属性为submit类型。
15.6.3 情况二:

15.6.4 说明二
这种情况submit的type属性为button.
15.6.5 情况三

15.6.6 说明三
该type类型为image。
15.6.7 综合
以上三种情况说明,当type为submit、button、image都能完成提交。
15.7 Reset标签


15.8 Textarea标签

15.9 Checkbox标签

15.10 Checkboxlist标签
15.10.1 集合为list

15.10.2 集合为map

上述的checkboxlist标签也可以表示为:

listKey相当于<input type=”checkbox”>中的value,listValue相当于label的显示值。
15.10.3 List中是javabean

从图中可以看出:listKey的值对应<input type=”checkbox”>中的value;
而listValue的值对应label的内容。
s:select,s:radio的应用与checkbox相同。
15.11 标签回显
BackValueAction:
private String username = "aaa";
private String password = "bbb";
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;
}
|
|
Backvalue.jsp
<s:textfield name="username"></s:textfield>
<s:password name="password" showPassword="true"></s:password>
注意:这里标签中的username和action中的属性名称必须保持一致。
其他的标签也是根据name进行回显。
|
|
16 属性驱动
16.1 说明
在servlet中获取页面传递过来的数据的方式是:request.getParameter(“username”);这个代码可以获取到页面的username的数据。在action中可以通过属性驱动的方式来获取页面的值。
16.2 例子
Propertydriver/login.jsp
<form action="propertydriver/propertyDriverAction_testPropertyDriver.action" method="post">
用户名:<input type="text" name="username"/>
密码:<input type="password" name="password"/>
<input type="submit"/>
</form>
PropertyDriverAction
public class PropertyDriverAction extends ActionSupport{
private String username;
private String password;
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;
}
public String testPropertyDriver(){
System.out.println(this.username);
System.out.println(this.password);
return SUCCESS;
}
}
|
|
说明:
1、 页面中name的属性和action中的属性必须保持一致。
2、 Action中的属性必须有get和set方法。
3、 满足这两个条件就实现了属性驱动。
|
|
16.3 原理

1、 当执行所有的拦截器的时候,当前请求的action已经放在了栈顶。
2、 放在对象栈的对象的特点是其属性能够直接访问。
3、 也就是说当执行ParameterInterceptor拦截器的时候,action的所有的属性在栈顶。
4、 所以只需要给栈顶的action的属性赋值就可以了。
5、 而ParameterInterceptor拦截器正好完成了此功能。
如果属性中要求接受的不是String类型,而是其他类型呢?struts2将做自动的转化。
17 类型转化
17.1 问题
testConverter.jsp
<form action="converter/converterAction_testConverter.action" method="post">
年龄:<input type="text" name="age"><br>
姓名:<input type="text" name="name"><br>
出生日期:<input type="text" name="birthday"><br>
<input type="submit"/>
</form>
通过属性驱动可以得出,只要在action中有age和name属性,有set和
get方法就能得到页面上age和name的值。如果action中是日期类型呢?
在struts2中可以自动隐式得到转化。比如在struts2中可以把字符串类型转化为日期类型。但是必须要求是yyyy-mm-dd的格式。其他格式转换不了。
如果页面上传过来的是yyyyMMdd,应该怎么办呢?类型转化将解决这个问题。
|
|
17.2 解决方案
17.2.1 DateConverter类
public class DateConverter extends DefaultTypeConverter{
@Override
public Object convertValue(Map context, Object value, Class toType) {
// TODO Auto-generated method stub
String dateStr = ((String[])value)[0];
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
try {
return simpleDateFormat.parse(dateStr);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
|
|
说明:
6、 这个类必须继承DefaultTypeConverter或者实现TypeConverter接口。
7、 在TypeConverter接口中,声明的方法是这样的:
public Object convertValue(Map context, Object target,
Member member, String propertyName,
Object value, Class toType);
可以看出里面实际上有六个参数。
但是这个类中,只有三个参数。
在DefaultTypeConverter类中:
public class DefaultTypeConverter implements TypeConverter
{
public DefaultTypeConverter()
{
super();
}
public Object convertValue(Map context, Object value, Class toType)
{
return OgnlOps.convertValue(value, toType);
}
public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType)
{
return convertValue(context, value, toType);
}
}
|
|
可以看出在实现了converValue的方法中调用了convertValue有三个参数的方法,所以能够执行。这样做的好处是程序员即可以用三个参数的方法,也可以用6个参数的方法。
8、 在convertType方法中:
value为从页面上传递过来的值
toType为转换以后的类型
17.2.2 properties文件
做完17.2.1以后,DateConverter类仅仅是一般的类,struts2框架不能把这个类看作为数据结构的转换类。所以需要把DateConverter类用配置文件进行注册。
注意事项:
1、 这个文件必须和相应的action类放在同一个目录下。
2、 文件的名称为:类名-conversion.properties。
3、 配置文件中的内容为:
action中的属性名称=DateConverter全名
4、 在执行的时候,一定要让浏览器的环境是中文的执行环境。在默认情况下,yyyy-MM-dd只有在中文的浏览器环境下才能识别,如果是英文识别不了。
可以参照XWorkBasicConverter这个类说明问题。
17.2.3 全局properties文件
除了17.2.2的做法,还可以考虑全局的配置文件。
步骤:
1、 在src下新建一个properties文件,为xwork-conversion.properties。
2、 在文件中键值对是这样的:
java.util.Date= cn.itcast.struts2.action.converter.DateConverter
这样配置,只要是java.util.Date类型都会通过DateConverter这个类进行转化。适合于所有的action中的属性。
18 模型驱动
18.1 说明
假设你正在完成一个网站的注册功能。在后台需要得到20多个属性,才能完成注册。如果用action中的属性获取值,就是这样的情况:
1、 在action中会写20个属性
2、 这20个属性添加set和get方法。
这样会导致action中的代码结构不是很好。模型驱动很好的解决了这个问题。
18.2 例子
Modeldriver.jsp
<s:form action="modeldriver/modelDriverAction_modeldriver.action" method="post">
<s:textfield name="username"></s:textfield>
<s:password name="password"></s:password>
<s:submit></s:submit>
</s:form>
ModelDriverAction
public class ModelDriverAction extends ActionSupport implements ModelDriven<User>{
private User model = new User();
public User getModel() {
// TODO Auto-generated method stub
return this.model;
}
public String modeldriver(){
return "modeldriver";
}
}
Modeldriver_value.jsp
实现回显的功能
|
|
18.3 原理

过程为:当浏览器提交modelDriverAction_modeldriver.action请求时,先经过拦截器。其中有一个拦截器为ModelDrivenInterceptor,从这个源代码可以看出,这个拦截器的作用就是获取实现了ModelDriver接口的action的模型驱动。在这里为user。然后把模型驱动利用push方法压入到栈顶。这样我们就能直接通过属性进行回显和赋值了。

通过这个图也可以看出模型驱动的拦截器在参数拦截器前面,也就是先把模型驱动压入栈顶,再进行赋值。
19 文件上传
struts2对文件上传做了更进一步的封装,使得开发更加简单。
19.1 页面
testUpload.jsp
<form action="upload/uploadAction_testUpload.action" method="post"
enctype="multipart/form-data">
要上传的文件:
<input type="file" name="upload">
<br>
<input type="submit">
</form>
注意:这里enctype="multipart/form-data"是必须写的,以二进制的形式进行上传。
|
|
19.2 UploadAction中
public class UploadAction extends ActionSupport{
private File upload;
private String uploadContentType;
private String uploadFileName;
public File getUpload() {
return upload;
}
public String getUploadContentType() {
return uploadContentType;
}
public void setUploadContentType(String uploadContentType) {
this.uploadContentType = uploadContentType;
}
public String getUploadFileName() {
return uploadFileName;
}
public void setUploadFileName(String uploadFileName) {
this.uploadFileName = uploadFileName;
}
public void setUpload(File upload) {
this.upload = upload;
}
|
|
19.3 多文件上传
testUploads.jsp
<form action="upload/uploadsAction_testUpload.action" method="post"
enctype="multipart/form-data">
要上传的文件:
<input type="file" name="uploads">
<br>
要上传的文件:
<input type="file" name="uploads">
<br>
要上传的文件:
<input type="file" name="uploads">
<br>
<input type="submit">
</form>
注意:所有的type=”file”的name属性都为uploads,所以后台获取的uploads是一个数组。
UploadsAction.java
public class UploadsAction extends ActionSupport{
private File[] uploads;
private String[] uploadsContentType;
private String[] uploadsFileName;
public String testUpload() throws Exception{
String path = ServletActionContext.getServletContext().getRealPath("/upload");
for(int i=0;i<this.uploads.length;i++){
File destFile = new File(path,this.uploadsFileName[i]);
FileUtils.copyFile(uploads[i], destFile);
}
return SUCCESS;
}
}
注:这里的原理和上传一个文件的原理是一样的,不同的是uploads,uploadsContentType,
uploadsFileName为数组。
|
|
20 高级部分

1 1、struts2是一个mvc框架,是struts1与xwork的结合体
2 2、struts2的配置文件
3 1、文件类型
4 最常用的配置文件有
5 default.properties
6 struts.i18n.encoding=UTF-8 设置默认的编码字符集
7 struts.multipart.maxSize=2097152 默认文件上传的大小
8 struts.action.extension=action 默认的扩展名
9 struts.devMode = false 是否是开发模式
10 struts.ui.theme = simple 配置struts2的简单样式
11 struts.custom.i18n.resources 自定义资源文件
12 struts-default.xml
13 bean的配置用于静态注入
14 package
15 resulttype
16 interceptor
17 struts-plugin.xml
18
19 struts.xml
20 用户最常用的配置文件
21 2、详细描述
22 1、配置文件中package的机制
23 1、继承
24 在配置文件中,package元素有一个属性为name,name的值是唯一的,是包的名称
25 现在有如下的配置
26 <package name="a" extends="struts-default">
27 </package>
28 <package name="b" extends="a"></package>
29 这样包b中就有了包a的内容
30 所以这样也可以推出:
31 包a拥有struts-default包中的内容
32 2、命名空间
33 在package中有一个属性为namespace
34 <package name="c" namespace="/user" extends="struts-default">
35 <action name="userAction_*">
36 </package>
37 这个时候在浏览器发出的请求:
38 http://localhost:8080/struts2/user/userAction_a.action可以找到action
39 http://localhost:8080/struts2/user/a/b/userAction_b.action可以找到action
40 http://localhost:8080/struts2/userAction_c.action不能够找到action
41 struts2容器在找action时,会采用这样的策略:
42 第一个URL正好和命名空间匹配,所以能够找到
43 第二个URL,当查找user/a/b时,并没有找到相应的action,则会查找user/a,如果找不到则会找user,这样就能找到了
44 第三个URL,当查找userAction时,并没有找到,因为userAction最低层的路径是user
45 2、action的配置
46 在action中,可以采用通配符的配置方式进行
47 <action name="userAction_*" method="{1}" class=".....UserAction">
48 <action name="*_*" method="{2}" class="cn.ithema03.struts.{1}">
49 3、result的配置
50 1、每一个result代表一个结果集,result中属性name的值和action中方法的返回值保持一致
51 2、struts2中最常用的结果集是dispatcher,redirect,redirectAction(见struts-default.xml内容)
52 3、result元素中type属性的值为结果集类型
53 4、在action的配置中,class属性可以不写,如果不写默认执行ActionSupport中execute中的内容,从struts-default.xml文件中可以看出
54 <default-class-ref class="com.opensymphony.xwork2.ActionSupport" />
55 5、拦截器的配置
56 1、interceptor只能配置在package中,所以不能配置全局的拦截器
57 2、struts2中关于interceptor有两个概念:interceptor,interceptors
58 这两个内容的声明都放在interceptors标签中
59 <interceptors>
60 //声明一个interceptor
61 <interceptor name="imageInterceptor" class="cn.itheima03.struts2.interceptor.MyInterceptor"></interceptor>
62 //声明一个interceptor stack
63 <interceptor-stack name="myInterceptor">
64 //自定义的interceptor
65 <interceptor-ref name="imageInterceptor"></interceptor-ref>
66 //struts2默认的执行的interceptor stack
67 <interceptor-ref name="defaultStack"></interceptor-ref>
68 </interceptor-stack>
69 </interceptors>
70 //引用myInterceptor的interceptor stack
71 <default-interceptor-ref name="myInterceptor"></default-interceptor-ref>
72 3、从上述的配置可以说明:
73 1、在struts2内部有默认的执行的interceptor stack
74 <default-interceptor-ref name="defaultStack"/>
75 如果一个interceptor不在这个interceptor stack中,那么将不执行该interceptor
76 2、可以把默认的interceptor stack变成自己的interceptor stack
77 3、在interceptor stack中可以指向(ref)一个interceptor,也可以指向(ref)一个interceptor stack
78 4、如果一个package要用到另外一个package中的interceptor,只需要通过package的继承机制就可以了
79 3、属性驱动
80 1、概念
81 可以利用属性驱动获取页面表单元素的内容
82 2、步骤
83 1、在action中声明属性,属性的名称和页面元素中name属性的值保持一致
84 2、action中的属性必须有set和get方法
85 4、模型驱动
86 1、如果页面上元素内容太多,用属性驱动实现,action中代码就会很庞大,这个时候可以考虑用模型驱动来实现
87 2、步骤
88 1、action实现一个接口ModelDriver
89 2、在action中声明一个属性,该属性会封装页面中的数据,并且用new的方法给该属性创建对象
90 3、填充接口中的方法getModel,返回该属性的值
91
92 public class UserAction implements ModelDriver<User>{
93 private User model = new User();
94 public User getModel(){
95 return this.model;
96 }
97 }
98
99 5、关于action的说明
100 1、action是多实例的
101 2、action的方法如果以get开头,会带来安全性的隐患
102 6、valueStack
103 1、valueStack是struts2中存放数据的一种数据结构
104 2、valueStack分为两种内存结构:对象栈、map栈
105 3、可以通过以下几种方法获取valueStack
106 1、ValueStack valueStack = ActionContext.getContext().getValueStack();
107 2、ValueStack valueStack2 = ServletActionContext.getValueStack(ServletActionContext.getRequest());
108 3、ValueStack valueStack3 = (ValueStack)ServletActionContext.getRequest().getAttribute("struts.valueStack");
109 以上三种方法获取的valueStack都是同一个对象
110 4、对map栈的操作
111 1、request,response,parameters,application等都在map栈中,分别的数据结构为
112 {"request",RequestMap} {"session",SessionMap} {"application",ApplicationMap}
113 2、可以通过如下方法把数据存放在request,session,application域中
114 ServletActionContext.getRequest().setAttribute("msg_request", "msg_request");
115 ServletActionContext.getServletContext().setAttribute("msg_application", "msg_application");
116 ServletActionContext.getRequest().getSession().setAttribute("msg_session", "msg_session");
117 3、可以通过如下方法吧数据直接存放在map栈中
118 ActionContext.getContext().put("aaaa", "aaaaa");
119 valueStack.getContext().put("bbb", "bbb");
120 5、对对象栈的操作
121 1、把一个数据存放在对象栈的尾部
122 valueStack.getRoot().add("aaaaa");
123 2、把一个数据存放在对象栈的栈顶
124 valueStack.getRoot().add(0,"adsf");
125 valueStack.push("rtrt");
126 3、把一个数据封装成map,把map放入到栈顶
127 valueStack.set("aaasdf", "asfd");
128 4、获取对象栈栈顶的元素
129 valueStack.peek();
130 valueStack.getRoot().get(0);
131 5、移除对象栈栈顶的元素
132 valueStack.getRoot().remove(0);
133 valueStack.pop();
134 7、ognl表达式
135 1、把一个数据放入到valueStack中,在页面上可以利用ognl表达式把该数据提取出来
136 2、ognl表达式总体的规则:如果一个数据存放在map栈中,则加#访问,如果一个数据在对象栈中,则不用加#访问
137 3、详细说明
138 1、s:property标签
139 1、如果没有value属性,则默认输出对象栈的栈顶的元素
140 2、<s:property value="#session.aa">输出map栈中session域中aa的值
141 2、s:iterator标签
142 1、迭代遍历集合
143 1、集合的类型:List,Set,Map,Object[]
144 2、value属性的值能直接跟ognl表达式
145 3、当前正在迭代的元素在栈顶存放
146 4、要熟练使用该标签迭代二重或者三重集合
147 2、属性var="a"表示当前正在迭代的元素在map栈中存放一份,key值为"a"
148 3、当迭代map的时候,当前正在迭代的元素是Entry
149 <s:iterator value="#map">
150 <s:property value="key"/>,<s:property value="value.pid"/>
151 <s:property value="key"/>,<s:property value="value.username"/>
152 </s:iterator>
153 4、属性status,代表当前迭代元素的一些性质
154 int getCount() 返回当前迭代的元素个数
155 int getIndex() 返回当前迭代元素的索引
156 boolean isEven() 返回当前迭代元素的索引是否是偶数
157 boolean isOdd() 返回当前迭代元素的索引是否是奇数
158 boolean isFirst() 返回当前迭代元素是否为第一个元素
159 boolean isLast() 返回当前迭代元素是否为最后一个元素
160 3、s:debug标签可以输出valueStack的内存快照
161 8、struts2的校验机制
162 struts2提供了两种校验机制
163 代码校验
164 步骤:
165 1、在页面上
166 <s:textfield name="username" label="用户名"></s:textfield>
167 <s:textfield name="password" label="密码"></s:textfield>
168 <s:fielderror fieldName="username"></s:fielderror>
169
170 说明:
171 <s:fielderror fieldName="username"></s:fielderror>
172 在这里fileldName="username"会把后台username的报错信息显示到前台
173 <s:fielderror></s:fielderror>
174 把所有的字段的错误信息显示到前台的页面中
175 2、在action中(action必须继承ActionSupport类,该类实现了Validateable接口),可以有两种写法
176 1、重写validate方法就可以了
177 public void validate() {
178 /**
179 * 验证用户名和密码是否为空
180 */
181 if("".equals(this.getUsername())){
182 this.addFieldError("username", "用户名不能为空");
183 }
184 if("".equals(this.getPassword())){
185 this.addFieldError("password", "密码不能为空");
186 }
187 }
188 2、如果action中的几个方法都需要校验,在action中必须写这样的方法
189 例如:对如下方法进行校验
190 public String testValidate(){
191
192 }
193 那么校验方法为:(validate+被校验的方法名称(方法的第一个字母大写))
194 public void validateTestValidate(){
195 //校验逻辑
196 }
197 说明:
198 this.addFieldError("username", "用户名不能为空");该方法中username和页面上的fieldName字段对应,而value的值就是报错信息
199 3、在struts2的配置文件中
200 如果校验没有通过,则指向input所指向的页面
201 <result name="input">validate/validate.jsp</result>
202 而在这个页面中,<s:fielderror fieldName="username"></s:fielderror>该标签将显示
203 this.addFieldError("username", "用户名不能为空");的错误信息
204 xml校验
205 步骤
206 1、在页面上
207 <s:textfield name="username" label="用户名"></s:textfield>
208 <s:textfield name="password" label="密码"></s:textfield>
209 <s:fielderror fieldName="username"></s:fielderror>
210
211 说明:
212 <s:fielderror fieldName="username"></s:fielderror>
213 在这里fileldName="username"会把后台username的报错信息显示到前台
214 <s:fielderror></s:fielderror>
215 把所有的字段的错误信息显示到前台的页面中
216 2、编写xml文件
217 xml文件的名称的命名规则为:
218 <ActionClassName>-<aliasName_methodName>-validation.xml
219 其中alias为配置文件中action元素name属性的值
220 例如:
221 1、在com.itheima.struts2.validate.action包中写了一个类ValidateAction
222 2、在配置文件中作为如下的配置
223 <action name="validateAction_*" method="{1}" class="cn.itheima03.struts2.validate.action.ValidateAction">
224 <result name="success">validate/success.jsp</result>
225 <result name="input">validate/validate.jsp</result>
226 </action>
227 3、在该类的相同路径下,写一个xml文件
228 ValidateAction-validateAction_testXMLValidate-validation.xml
229 该xml文件和类在同一个路径下
230 4、在该xml文件中,就可以进行属性的校验了
231 <validators>
232 //要验证的属性,和页面上name的属性的值对应
233 <field name="username">
234 //type为校验器,是struts2内置的
235 <field-validator type="requiredstring">
236 <param name="trim">true</param>
237 //如果验证不成功,则输出的内容
238 <message><![CDATA[用户名不能为空]]></message>
239 </field-validator>
240 </field>
241 <field name="password">
242 <field-validator type="requiredstring">
243 <param name="trim">true</param>
244 <message><![CDATA[密码不能为空]]></message>
245 </field-validator>
246 //正则表达式验证
247 <field-validator type="regex">
248 <param name="trim">true</param>
249 <param name="expression"><![CDATA[^[a-zA-Z0-9]{6,12}$]]></param>
250 <message><![CDATA[密码长度应该在6到12之间]]></message>
251 </field-validator>
252 </field>
253 </validators>
254 3、在struts2的配置文件中
255 input所指向的页面就是验证不成功,输出的验证信息的页面
256 <result name="input">validate/validate.jsp</result>
257 9、防止表单重复提交
258 1、原理
259 利用了struts2内置的拦截器token实现的
260 <interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
261 2、步骤
262 1、在要提交的表单页面上,要写一个struts2自定义的标签:
263 <s:form action="../tokenAction_token.action">
264 <s:token></s:token>//生成一大堆字符串,用于往session中存放
265 用户名:<s:textfield name="username"></s:textfield>
266 密码:<s:password name="password"></s:password>
267 <s:submit></s:submit>
268 </s:form>
269 2、在配置文件中,由于在defaultStack中没有把token包括进来,所以
270 <interceptors>
271 <interceptor-stack name="tokenInterceptor">
272 <interceptor-ref name="token">
273 //哪些方法可以防止重复提交,如果有很多方法,则用,隔开
274 <param name="includeMethods">token</param>
275 </interceptor-ref>
276 <interceptor-ref name="defaultStack"></interceptor-ref>
277 </interceptor-stack>
278 </interceptors>
279 <default-interceptor-ref name="tokenInterceptor"></default-interceptor-ref>
280 说明:
281 1、上面的配置新定义了一个拦截器栈
282 2、在拦截器栈中把默认的拦截器栈加了进来
283 <interceptor-ref name="defaultStack"></interceptor-ref>
284 3、在这个拦截器栈中把token也加了进来,并且指明了哪些方法需要用到防止重复提交
285 <interceptor-ref name="token">
286 //哪些方法可以防止重复提交,如果有很多方法,则用,隔开
287 <param name="includeMethods">token</param>
288 </interceptor-ref>
289 4、把新定义的拦截器栈tokenInterceptor成了默认的拦截器栈
290 3、如果表单重复提交了,将指向invalid.token的错误页面
291 <result name="invalid.token">token/error.jsp</result>
292 4、如果只执行了上述的内容,报错信息将是一个英文信息, 变成中文信息,步骤如下:
293 1、 在struts2的配置文件中,加入自定义资源文件的配置
294 <constant name="struts.custom.i18n.resources" value="com.itheima.struts2.resource.token"></constant>
295 说明:引入了一个资源文件:为com/itheima/struts2/resource/token.properties
296 2、在该资源文件中
297 struts.messages.invalid.token=\u4E0D\u80FD\u91CD\u590D\u63D0\u4EA4\u8868\u5355
298 说明:key值保持不变,value是输出的错误信息
299 10、文件的上传
300 1、原理:
301 在struts2中,利用拦截器fileUpload实现了文件上传
302 2、实现步骤
303 1、在页面上
304 <s:form action="uploadAction_upload.action" enctype="multipart/form-data">
305 <s:file name="resource"></s:file>
306 <s:submit></s:submit>
307 </s:form>
308 说明:enctype="multipart/form-data"是必须的,说明要上传的文件是以二进制流的形式传递的
309 2、在文件上传的action中,要有一个属性resource,属性的类型为File
310 public class UploadFileAction{
311 private File resource;
312 //set和get方法
313 }
314 3、写一个帮助类,可以把resource文件上传到服务器上的某一个地方
315 4、详情:
316 1、可以控制文件上传的大小
317 1、在default.properties文件中,有这样一个配置
318 struts.multipart.maxSize=2097152
319 说明文件上传的默认的最大的值为2M
320 2、如果要改变其默认值,在配置文件中,设置如下的配置
321 <constant name="struts.multipart.maxSize" value="8097152"></constant>
322 把文件上传的最大值改为8M
323 2、可以控制文件的类型和扩展名
324 1、在action中,可以显示文件的类型和扩展名
325 1、在action中可以设置如下的属性
326 resourceContentType 该属性的名称的组成为{name}ContentType
327 2、在action中可以设置如下的属性
328 resourceFileName 该属性的名称的组成为{name}FileName
329 2、在配置文件中
330 引入拦截器fileUpload
331 <interceptor-ref name="defaultStack">
332 <!--
333 允许上传的文件的最大size
334 -->
335 <param name="fileUpload.maximumSize">8097152</param>
336 <!--
337 允许上传文件的扩展名
338 -->
339 <param name="fileUpload.allowedExtensions">txt,doc,pdf,jpg</param>
340 <!--
341 允许上传文件的类型
342 -->
343 <param name="fileUpload.allowedTypes">application/msword</param>
344 </interceptor-ref>
345 说明:
346 1、因为拦截器在默认的栈中,所以只需要引入默认的栈就可以了
347 2、在这里可以通过param给默认栈的fileUpload拦截器的属性赋值
348 所以maximumSize,allowedExtensions,allowedTypes都是拦截器fileUpload的属性
349 3、如果大小、后缀名、扩展名不符合要求,则会报错,系统会跳转到input指向的页面
350 <result name="input">error.jsp</result>,在error.jsp中编写<s:fielderror></s:fielderror>,就可以看到错误信息
351 但是,报错的信息是英文的信息,如上传的时候类型错误的信息:
352 Content-Type not allowed: upload "xwork-2.1.6-src.zip" "upload_6fff0830_13174e12471__8000_00000000.tmp" application/x-zip-compressed
353 该信息的组成(org.apache.struts2):struts-messages.properties
354 struts.messages.error.file.too.large=File too large: {0} "{1}" "{2}" {3}
355 struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} "{1}" "{2}" {3}
356 struts.messages.error.file.extension.not.allowed=File extension not allowed: {0} "{1}" "{2}" {3}
357 {0}:代表页面上<input name="upload" type="file"/>中的name的值
358 {1}:代表文件上传的名称
359 {2}:文件保存在临时目录的名称。临时目录为work\Catalina\localhost\struts2
360 {3}:代表文件上传类型,或者文件上传大小。上面的错误代表文件上传类型。如果报第一个错误,则代表文件上传大小。
361 怎么样变成中文的信息呢?
362 步骤:
363 1、 建立一个properties文件。这个文件的名称可以任意取。
364 2、 在这个配置文件中,添入如下的内容:
365 struts.messages.error.file.too.large=文件超过了规定的大小: {0} "{1}" "{2}" {3}
366 struts.messages.error.content.type.not.allowed=该类型不允许被上传: {0} "{1}" "{2}" {3}
367 struts.messages.error.file.extension.not.allowed=不能上传该扩展名类型的文件: {0} "{1}" "{2}" {3}
368 3、 在struts-upload.xml中,指定配置文件的位置
369 <constant name="struts.custom.i18n.resources" value="com.itheima.struts2.action.upload.fileuploadmessage"></constant>
370 如果配置文件放在src下,则这样指定:
371 <constant name="struts.custom.i18n.resources" value="fileuploadmessage"></constant>
372 11、类型转化
373 1、应用场景
374 在实际的开发中有可能存在这样的情况,例如:在页面上选择自己的爱好,是一个多选,在action中获取选择的爱好,而获取选择爱好的属性为List,
375 一般情况下,页面表单的元素在后台用list是获取不到值的,这个时候需要类型转化
376 2、步骤
377 1、在页面上
378 <s:form action="../converterAction_aihaoss.action">
379 <s:checkboxlist list="{‘aa‘,‘bb‘,‘cc‘,‘dd‘}" name="aihaos"></s:checkboxlist>
380 <s:submit></s:submit>
381 </s:form>
382 2、在action中,用List接受数据
383 public class ConverterAction extends ActionSupport{
384 private List<String> aihaos;
385 }
386 3、建立一个转化器类ListConverter
387 //把页面上的选择的内容填充到List<String>中
388 public Object convertFromString(Map context, String[] values, Class toClass) {
389 List<String> aihaos = new ArrayList<String>();
390 for(String s:values){
391 aihaos.add(s);
392 }
393 return aihaos;
394 }
395 4、建立一个配置文件xwork-conversion.properties,该配置文件放在src下
396 java.util.List=cn.itheima03.struts2.converter.action.ListConverter
397 key值为转化后的类型,value值为转化器
398
基础部分

1 1、struts2的错误处理
2 1、java的错误处理
3 如果方法有异常,两种解决方案
4 try
5 抛出异常
6 2、UserAction(saveUser throws Exception)--->DefaultActionInvocation(invoke)--->DefaultActionProxy(execute)
7 ---->Dispatcher(serviceAction)
8 3、在serviceAction方法中:
9 if (devMode) {
10 catch (ConfigurationException e) {
11 // WW-2874 Only log error if in devMode
12 if(devMode) {
13 LOG.error("Could not find action or result", e);
14 }
15 else {
16 LOG.warn("Could not find action or result", e);
17 }
18 sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);
19 } catch (Exception e) {
20 sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
21 } finally {
22 UtilTimerStack.pop(timerKey);
23 }
24 }
25 如果devMode为true(<constant name="struts.devMode" value="true"/>)
26 如果抛出的异常是ConfigurationException或者及子类,第一个catch就可以捕捉到,如果是其他的异常类,第二个catch可以捕获到,
27 从上述的代码可以看出:struts2处理异常的方法为sendError方法
28
29 在sendError方法中准备了一个模板页面:/org/apache/struts2/dispatcher/error.ftl
30 需要把错误信息的值填充到里面
31 如果devMode为false,不会跳转到错误模板页面
32
33 4、全局的错误处理
34 在struts2的配置文件中:
35 <global-results>
36 <result name="errHandler" type="chain">
37 <param name="actionName">errorProcessor</param>
38 </result>
39 </global-results>
40 <global-exception-mappings>
41 <exception-mapping exception="java.lang.Exception"
42 result="errHandler" />
43 </global-exception-mappings>
44
45 <action name="errorProcessor" class="com.itheima03.oa.exception.ErrorPrcessor">
46 <result>error.jsp</result>
47 </action>
48 说明:
49 1、所有的action请求都会执行默认的拦截器栈defaultStack,而在默认的拦截器栈中有一个拦截器为exception
50 2、当执行action的时候,该拦截器会执行两次
51 3、在该exception拦截器中
52 try {
53 //执行拦截器或者action
54 result = invocation.invoke();
55 } catch (Exception e) {//捕获异常
56 if (isLogEnabled()) {
57 handleLogging(e);
58 }
59 //把global-exception-mappings的值获取到了
60 List<ExceptionMappingConfig> exceptionMappings = invocation.getProxy().getConfig().getExceptionMappings();
61 //获取到exception-mapping中的result属性
62 String mappedResult = this.findResultFromExceptions(exceptionMappings, e);
63 if (mappedResult != null) {
64 result = mappedResult;
65 //把捕获到的错误的信息存放在栈顶
66 publishException(invocation, new ExceptionHolder(e));
67 } else {
68 throw e;
69 }
70 }
71
72 return result;//代表的是error.jsp
73 4、在error.jsp中可以输出栈顶元素的值即报错信息
74 5、struts2的错误处理在整个过程中没有添加错误的状态码
高级部分

1 struts2与json的整合
2 1、步骤
3 1、导入jar包
4 struts2-json-plugin-2.1.8.1
5 该jar包的根目录有一个struts-plugin.xml文件
6 <package name="json-default" extends="struts-default">
7 <result-types>
8 <result-type name="json" class="org.apache.struts2.json.JSONResult"/>
9 </result-types>
10 <interceptors>
11 <interceptor name="json" class="org.apache.struts2.json.JSONInterceptor"/>
12 </interceptors>
13 </package>
14 从该配置文件可以看出:
15 1、自定义了一个结果集json-default
16 2、有一个拦截器json
17 2、在struts2的配置文件中
18 <result type="json"></result>
19 注意:
20 1、结果集的类型是json
21 2、因为是ajax请求,所以不需要返回任何页面,所以result中间没有内容
22 3、在action中,声明一个属性,该属性有get方法,属性有返回值,例如:
23 public String getXxx(){
24 return "aaa";
25 }
26 那么将以如下的值返回到客户端:‘xxx‘:‘aaa‘
27 4、在action中,如果不需要返回客户端值,那么方法最好别以get方法开头
28 5、在客户端就能接收到服务器端返回的数据
Struts2与json整合
Struts2.0笔记二
原文:https://www.cnblogs.com/biaogejiushibiao/p/9393507.html