一、 Introduction
Struts2是一个基于MVC设计模式的web应用框架,本质上是一个servlet,发挥了controller的作用来建立模型与视图的数据交互。它是struts的下一代产品,是在struts和webwork的技术基础上进行了合并的全新的struts2框架。在javaweb三层的开发中,web层一般采用servlet,一般的web系统中包含一定量的客户请求,如果使用单个单个的servlet来出理与客户请求的交互,将产生大量的开发量和增大维护的难度。web层框架的出现就是为了解决这个问题,以达到低耦合,高内聚,简开发,易维护的效果。实际上,在web层框架中,采用了控制器模式,将单个单个的servlet的处理交给了框架处理,而在编程上,只需要处理好前端控制器的实现。Struts2框架的前端控制器是过滤器。这个不同于springmvc所采用的是一个总的servlet来控制请求的流程,依次通过处理器映射器、处理器适配器、控制器、视图解析器等实现请求和响应。
二、 基本步骤
1. 创建web项目。
2. 引入struts2的jar包。
3. 编写或引入jsp文件,注意jsp中访问action的写法,后缀名必须为action。
4. 在web.xml中配置前端控制器。
5. 编写action类和方法,方法必须有返回值,且为String类型。如果要转发或重定向,可以设置返回值的字符串,然后在struts.xml中配置result标签。
6. 配置struts.xml文件,必须名字为struts.xml。
7. 测试
8. 例子
创建项目,导入jar包略
Jsp文件
<body>
<h3>哈哈哈</h3>
<a href="${pageContext.request.contextPath }/haha.action" >哈哈哈</a>
</body>
web.xml中配置前端控制器的过滤器
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
编写action类
public class HahaAction {
/**
* action类中方法的要求
* 1,必须public
* 2,返回值必须为字符串,必须有返回类型
* 3,方法名称任意,但是不能有参数
*/
public String haha(){
System.out.println("哈哈");
return "haah";
}
}
配置struts.xml
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE strutsPUBLIC
"-//ApacheSoftware Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<!-- 配置包结构
使用默认的包管理action
-->
<package name="default"namespace="/" extends="struts-default">
<!-- 配置action
name:代表访问源的.action的名字,如haha.action,则name="haha"
class:代表要访问的action类的全路径
method:代表要访问的action类的方法的名字
-->
<action name="haha" class="com.struts2.action.HahaAction"method="haha" />
<!-- 配置跳转页面,在这个action里面配置,result,底层通过反射实现,路径的写法,不管是转发还是重定向,都不要写项目名 -->
<result name="haah">/jsp/haha.jsp</result>
</package>
</struts>
测试略。
三、 Struts2的执行流程
在B/S框架中,当启动服务器的时候,会加载web.xml文件,文件中配置的struts2的核心过滤器就会被创建,init()方法执行,加载相关的配置文件,其中就包括struts.xml。
当请求达到服务器端后,在web.xml中的执行顺序是:listener---》filter---》struts拦截器---》servlet。当请求执行到struts拦截器的时候,实际上也是一个filter,会经过核心过滤器,也可以认为是前端控制器,StrutsPrepareAndExecuteFilter的类。然后,会执行struts2框架提供的很多拦截器filter,等这些拦截器执行完毕,就执行struts.xml,找到请求的路径,进而找到类,找到方法,通过反射的方式执行,如果return不为空,则根据strut.xml中的配置执行result跳转页面。
Struts2不同于springmvc中,是通过一个前端控制器的servlet,根据映射器处理器,找到controller处理器中映射的具体的方法,进而处理请求。可以认为二者的实现机制不同,struts2依赖于拦截器,而springmvc则依靠一个总的前端控制器的servlet。不同的文件配置也使得在具体的请求处理上,struts2每次都要访问action类,然后找到具体的方法,这使得同一个请求可以共用一些类级别的全局变量,然后也削弱了性能。而springmvc的url映射直接对应某个方法,在粒度上更细。
四、 Struts2框架配置文件的加载顺序
Struts2框架的核心是StrutsPrepareAndExecuteFilter过滤器,主要有两个功能:prepare,预处理,加载核心的配置文件;execute,执行,执行一部分拦截器。
1. StrutsPrepareAndExecuteFilter过滤器加载的配置文件和顺序。
顺序 | 方法 | 加载的文件 |
1 | init_DefaultProperties() | org/apache/struts2/default.properties |
2 | init_TraditionalXmlConfigurations() | struts-default.xml,struts-plugin.xml,struts.xml |
3 | init_LegacyStrutsProperties() | struts.properties |
4 | init_CustomConfigurationProviders() | 用户自定义的配置提供者 |
5 | init_FilterInitParameters() | web.xml |
2. 重点配置文件的说明
default.properties | 在org/apache/struts/目录下,代表配置的是struts2的常量的值 |
struts-default.xml | 在struts2的核心包下,代表struts2核心功能的配置,如bean,拦截器,结果类型等 |
struts.xml | 非常重要,代表web应用的默认配置,开发中,基本靠配置这个文件实现功能。可用来配置常量。 |
web.xml | 配置前端控制器,可以用来配置常量 |
注意:在所有的配置文件中,前3个配置文件(default.properties,struts-default.xml,struts-plugin.xml)是struts2框架的默认配置文件,基本不需要修改。后3个配置文件(struts.xml,struts.properties,web.xml)可以修改struts2的常量,而且对于同样的常量,后加载的会覆盖先加载的。在开发中,基本上是在struts.xml中配置相关常量。
五、 struts.xml配置文件的配置
1. <package>标签
name | 包的名称,需要是唯一的,用来管理这个包的action配置 |
extends | 继承,可以继承其他的包,就包含了继承的包的功能,一般使用struts-default。 |
namespace | 名称空间用来约束action的访问路径,和<action>标签中的name一起形成访问路径。 如:namespace=”/”,表示根名称空间,即从项目名开始;namespace=”/haha”带有名称的,即表示项目名/haha/。 |
abstract | 抽象的。表示被继承的。在java的接口有类似的概念。这个属性很少使用,值如果是true,表示包可以被继承。 |
2. <action>标签
name | 和<package>标签的namespace属性一起形成访问路径。 |
class | 配置action类的全路径。默认值是ActionSupport类。 |
method | Action类中执行的方法,如果不指定,默认值是execute。 |
关于method默认值的例子:
如果在struts.xml中的action中没有配置method的值,则默认就是method=“execute”。
在action类中编写一个execute方法,当发起访问这个没有设置method值的action访问路径的请求时,就会执行这个action类中自编写的execute方法。
<action name="haha"class="com.struts2.action.HahaAction" >
<!-- 配置跳转页面,在这个action里面配置,result,底层通过反射实现,路径的写法,不管是转发还是重定向,都不要写项目名 -->
<result name="haah">/jsp/haha.jsp</result>
</action>
public String execute(){
System.out.println("默认值");
return null;
}
3. <result>标签
name | 返回结果的跳转视图的名称。 |
type | 结果类型。默认值是转发,可以设置其他值。 |
注意:在一个action中可以配置多个result。
六、 配置struts2的常量
可以配置struts2常量的三个文件:struts.xml,struts.properties,web.xml。一般在struts.xml和web.xml中配置。相同的配置,后加载的覆盖先加载的。
1. 在struts.xml中配置常量
使用<constant name=”key” value=”value”></constant>
例子:
<!-- 配置常量 -->
<constant name="struts.action.extension" value="haha,,"></constant>
2. 在web.xml中配置常量
方式是在前端控制器的过滤器中配置初始化参数。
例子:
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
<init-param>
<param-name>struts.action.extension</param-name>
<param-value>haha,,</param-value>
</init-param>
</filter>
3. 常用的常量
struts.i18n.encoding=UTF-8 | 指定post请求的默认编码集,相当于request.setCharacterEncoding()方法。 |
struts.action.extension=action,, | 指定struts2处理的请求的后缀名,默认为action,即匹配*.action。如果需要指定多个请求后缀,则多个后缀之间用,隔开,如=action,haha,hei, |
struts.objectFactory.spring.autWire=true | 设置整合到spring时用。 |
struts.multipart.maxSize=2097152 | 文件上传最大尺寸。 |
struts.multipart.saveDir= | 临时文件的默认存储目录。 |
struts.configuration.browserCache=true | 指定浏览器是否缓存静态内容,默认是true。开发时一般关闭。 |
struts.enable.DynamicMethodInvocation=false | 指定是否采用动态方法方式。 |
struts.devMode=false | 开发模式是否开启。 |
struts.configuration.xml.reload=false | 指定当struts配置文件修改后,是否重新加载该文件,默认是false。 |
七、 分解struts.xml文件的方式
在有规模的应用开发中,为了避免struts.xml文件过于庞大,臃肿,提高可读性,可以将struts.xml分解为多个配置文件。然后在struts.xml中使用<include>标签引入其他struts_xx.xml文件。
例子:
<struts>
<include file="com/struts/user/struts_haha.xml" />
</struts>
八、 Action类的三种编写方式
(一) Action类就是一个pojo类
Pojo类,plain ordinary java object,简单的java对象。没有继承某个类,没有实现接口的类。开发中不常用。
例子:
public class HahaAction {
/**
* action类中方法的要求
* 1,必须public
* 2,返回值必须为字符串,必须有返回类型
* 3,方法名称任意,但是不能有参数
*/
public String haha(){
System.out.println("哈哈");
return "haah";
}
public String execute(){
System.out.println("默认值");
return null;
}
}
(二) Action类实现Action接口
Action定义了5个常量,5个常量对应5个逻辑常量视图跳转页面,还定义了一个execute方法。
static java.lang.String | The action execution was a failure. |
static java.lang.String | The action execution require more input in order to succeed. |
static java.lang.String | The action could not execute, since the user most was not logged in. |
static java.lang.String | The action execution was successful but do not show a view. |
static java.lang.String | The action execution was successful. |
例子:
编写action类实现Action接口
public class ActionTest1 implements Action{
public String execute() throws Exception {
System.out.println("实现了action的接口");
//returnSUCCESS,相当于return "success";
return SUCCESS;
}
}
配置action标签
<action name="haha2" class="com.struts2.action.ActionTest1">
<result name="success">/jsp/haha.jsp</result>
</action>
(三) Action类可以去继承ActionSupport类
开发中使用这个方法最多。它既有Action接口的方法,也有自己的一些方法。
例子:
编写action类继承actionsupport类。
public class ActionTest2 extends ActionSupport{
@Override
public String execute() throws Exception {
System.out.println("继承了actionSupport类");
return NONE;
}
}
配置action
<!-- 继承actionSupport类的方式 -->
<action name="haha3" class="com.struts2.action.ActionTest2">
<result name="none">/jsp/haha.jsp</result>
</action>
九、 Action的访问方式
1. 传统的配置文件的方法。通过<action>标签中的method属性,访问Action类中具体的方法。
例子:
编写jsp页面
<body>
<a href="${pageContext.request.contextPath }/queryUser.haha">查询用户</a><br />
<a href="${pageContext.request.contextPath }/addUser.haha">添加用户</a>
</body>
编写action类
public class UserAction extends ActionSupport{
public String query(){
System.out.println("查询用户");
return null;
}
public String add(){
System.out.println("添加客户");
return null;
}
}
配置action
<package name="haha"namespace="/" extends="struts-default">
<action name="queryUser" class="com.struts2.action.user.UserAction"method="query"></action>
<action name="addUser" class="com.struts2.action.user.UserAction"method="add"></action>
</package>
使用传统的方式,配置action。每次访问一个method,都要配置这个method的类,这繁琐了开发,困难了维护。
2. 通配符的方式
访问的路径和方法的名称有某种联系。通配符就是用*代表任意的字符。
使用通配符可以简化配置文件的代码编写,而且易于扩展和维护。
例子:
编写jsp页面,访问路径请使用:共用的名称_区分名称.后缀名
<a href="${pageContext.request.contextPath }/user_query.haha">查询用户</a><br />
<a href="${pageContext.request.contextPath }/user_add.haha">添加用户</a>
编写action类
public class UserAction2 extends ActionSupport{
public String query(){
System.out.println("通配符查询用户");
return "queryHaha";
}
public String add(){
System.out.println("通配符添加客户");
return "addHaha";
}
}
配置action
<!-- 通配符的方式
{1}代表请求过来的路径中的第一个*对应的值,依次类推
-->
<action name="user_*" class="com.struts2.action.user.UserAction2"method="{1}">
<result name="queryHaha">/jsp/haha.jsp</result>
<result name="addHaha">/jsp/haha.jsp</result>
</action>
3. 动态方法访问的方式
有的开发中使用。如果使用这个方式,需要开启一个常量,struts.enable.DynamicMethodInvocation= false,把值设置为true。
在struts.xml中开启该常量。<constant name=”struts.enable.DynamicMethodInvocation” value=”true”></constant>
例子:
开启动态方法的常量。
<constantname=”struts.enable.DynamicMethodInvocation” value=”true”></constant>
Jsp页面,访问路径,请使用action中name!action类中的方法.后缀名
<a href="${pageContext.request.contextPath }/user!query.haha">查询用户</a><br />
<a href="${pageContext.request.contextPath }/user!add.haha">添加用户</a>
编写action类
public class UserAction3 extends ActionSupport{
public String query(){
System.out.println("动态方法查询用户");
return "queryHaha";
}
public String add(){
System.out.println("动态方法添加客户");
return "addHaha";
}
}
配置action
<!--
动态方法访问的方式
只需要配置访问路径中共用的名称。
-->
<action name="user" class="com.struts2.action.user.UserAction3">
<result name="queryHaha">/jsp/haha.jsp</result>
<result name="addHaha">/jsp/haha.jsp</result>
</action>
十、 Struts2初步整合Hibernate
具体步骤:
1. 创建项目
2. 导入struts2和hibernate的jar包
3. 创建用于orm的pojo类
4. 创建hibernateUtil工具类
5. 配置hibernate的核心配置文件hibernate.cfg.xml
6. 配置orm文件
7. 配置web.xml的struts2的前端控制器
8. 依次编写jsp,action类,service层和dao层。
9. 配置struts.xml文件
10. 测试
十一、 Struts2中使用servlet的API
在Action类中可以获取到Servlet的一些常用的API。Struts2提供了两种方式。
(一) 完全解耦合的方式
Struts2提供了一个类,ActionContext类,该类提供了一些方法可以获取Servlet的API。
一些方法的列表
static ActionContext getContext() | 获取ActionContext对象实例 |
java.util.Map<java.lang.String,java.lang.Object> getParameters() | 获取请求参数,相当于request.getParameterMap(); |
java.util.Map<java.lang.String,java.lang.Object> getSession() | 获取代表session域的Map集合,相当于操作session。 |
java.util.Map<java.lang.String,java.lang.Object> getApplication() | 获取代表application域的Map集合。 |
void put(java.lang.String key, java.lang.Object value) | 向request域中存入值。 |
例子:
Jsp页面
<body>
<form action="${ pageContext.request.contextPath }/regist.haha" method="post">
姓名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
<input type="submit"value="注册" />
</form>
</body>
Action类
public class RegistAction extends ActionSupport{
public String regist(){
System.out.println("haha");
ActionContext context=ActionContext.getContext();
Map<String,Object> map=context.getParameters();
Set<String> keys=map.keySet();
for(String key:keys){
String[] v=(String[])map.get(key);
System.out.println(key+":"+Arrays.toString(v));
}
context.getSession().put("hh", "会话");
context.getApplication().put("hk", "后k");
//向request域存值
context.put("kk","kkk");
return SUCCESS;
}
}
struts.xml
<package name="haha2" namespace="/"extends="struts-default">
<action name="regist"class="com.struts2.action.RegistAction" method="regist">
<result name="success">/jsp/haha.jsp</result>
</action>
</package>
(二) 使用原生的servlet的API的方式
Struts2框架提供了一个类,ServletActionContext,有一些静态的方法。
static ActionContext | getActionContext(javax.servlet.http.HttpServletRequest req) Gets the current action context |
static ActionMapping | Gets the action mapping for this context |
static javax.servlet.jsp.PageContext | Returns the HTTP page context. |
static javax.servlet.http.HttpServletRequest | Gets the HTTP servlet request object. |
static javax.servlet.http.HttpServletResponse | Gets the HTTP servlet response object. |
static javax.servlet.ServletContext | Gets the servlet context. |
static ValueStack | getValueStack(javax.servlet.http.HttpServletRequest req) Gets the current value stack for this request |
static void | setRequest(javax.servlet.http.HttpServletRequest request) Sets the HTTP servlet request object. |
static void | setResponse(javax.servlet.http.HttpServletResponse response) Sets the HTTP servlet response object. |
static void | setServletContext(javax.servlet.ServletContext servletContext) Sets the current servlet context object |
例子:
Jsp和struts.xml同上,略
Action类
public class RegistServletAction extends ActionSupport{
public String regist(){
HttpServletRequest request=ServletActionContext.getRequest();
request.getSession().setAttribute("hh", "看看");
ServletActionContext.getServletContext().setAttribute("kl", "看看了");
HttpServletResponse response=ServletActionContext.getResponse();
return SUCCESS;
}
}
十二、 Struts2处理结果页面跳转
1. 结果页面的两种形式
根据action返回的结果的一致性与否,将结构页面分为全局结果页面和局部结果页面。
1) 全局结果页面:如果<package>包中的一些Action都返回同一个值,并且这个值都指向同一个JSP页面,这个jsp页面就是全局结果页面。
全局结果页面针对当前的包中的所有action,如果存在有局部页面跳转的,则使用局部跳转结果不使用全局跳转结果。
例子:
<global-results>
<result name="success">/jsp/haha.jsp</result>
<result name="fail">/jsp/regist.jsp</result>
</global-results>
2) 局部结果页面:即只有一个action跳转到这个结果页面。局部结果页面的优先级高于全局结果页面。
例子:
<package name="haha2" namespace="/"extends="struts-default">
<global-results>
<result name="success">/jsp/haha.jsp</result>
<result name="fail">/jsp/regist.jsp</result>
</global-results>
<action name="user_*"class="com.struts2.action.RegistServletAction" method="{1}">
</action>
<action name="user_regist"class="com.struts2.action.RegistServletAction"method="regist">
<result name="success">/jsp/heihei.jsp</result>
</action>
</package>
2. 结果页面的类型
结果页面使用<result>标签,具有两个属性。name,逻辑视图的名称,即action类中return的值。type,跳转类型。可以到struts-default.xml中查找全部类型。
常用结果页面类型有:
dispatcher | 转发。type的默认值即为dispatcher。可以从action类转到jsp。 |
redirect | 重定向。从action定向到jsp。 |
chain | 多个action之间的跳转,从一个action转发到另一个action。 |
redirectAction | 多个action之间的跳转,从一个action重定向到另一个action。 |
stream | stream。用于下载文件。 |
重定向例子:
<global-results>
<result name="success" type="redirect">/jsp/haha.jsp</result>
<result name="fail">/jsp/regist.jsp</result>
</global-results>
重定向action的例子:
Action类
public class RedirectActionTest extends ActionSupport{
public String haha(){
System.out.println("haha");
return SUCCESS;
}
public String redirect(){
System.out.println("重定向过来");
return NONE;
}
}
配置文件
<action name="redirectAction_*"class="com.struts2.action.RedirectActionTest"method="{1}">
<result name="success"type="redirectAction">redirectAction_redirect</result>
</action>
十三、 Struts2的数据封装
(一) Struts2封装数据的两种方式
在struts2框架,请求的处理流程中,当请求到达struts2的过滤器后就执行一些拦截器,这些拦截器负责各种处理,如类型转换,字符编码。其中有一个拦截器负责封装请求的数据,如果在action类中设置set和get方法,那么这个拦截器可以通过调用set、get方法将参数封装进去。
按照struts2要求的编写action类的set和get的方法,通过拦截器就可以实现数据的封装。Struts2提供了两种数据封装的方式。
1. 属性驱动
属性驱动分为两种方式:
1) 提供对应属性的set方法进行数据的封装。
表单中的哪些数据需要封装,那么就在对应的action类中提供该属性的set方法。当表单的数据提交到action类,通过action中的setXxx方法,赋值给作为全局变量的属性。
这种方式的特点:
A. 采用拦截器帮助封装数据。
B. 有缺点,如果属性很多,需要提供很多set方法,而且要手动将数据存入对象。
C. Action类,兼具了javabean的功能,违背了mvc思想模式,有抬高耦合性的风险。
例子:
编写jsp页面
<body>
<form action="${ pageContext.request.contextPath }/user_registField1.haha" method="post">
姓名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
年龄:<input type="text" name="age"/><br/>
<input type="submit"value="注册" />
</form>
</body>
创建action类,将表单中需要传入的name的值作为类的属性并设置set方法。
public class RegistAction1 extends ActionSupport{
private String username;
private String password;
private Integer age;
public voidsetUsername(String username){
this.username = username;
}
public voidsetPassword(String password){
this.password = password;
}
public voidsetAge(Integer age){
this.age = age;
}
public String registField1(){
System.out.println("执行了"+username+"--"+password+"--"+age);
return null;
}
}
2) 在jsp页面上,使用OGNL表达式进行封装数据
在jsp页面中使用OGNL表达式,可以直接将属性封装到一个javaBean的对象中。需要编写一个javabean,并提供set和get方法。Jsp页面的编写,使用OGNL表达式,形式如:<input type=”text” name=”user.username”/>,注意javabean的属性与表单name的一一对应。
特点:
A. 除了javabean需要提供set方法外,在action类中需要提供这个javabean类型的属性的get和set方法。
B. 执行action类时,会调用属性的get方法,判断是否有javabean类的属性的实例对象,如果没有,则调用set方法把拦截器创建的对象注入action类。依次将表单的值放入javabean对象。
例子:
编写jsp页面,使用OGNL表达式为表单的name赋值
<body>
<form action="${ pageContext.request.contextPath }/user_registField2.haha" method="post">
姓名:<input type="text" name="user.username"/><br/>
密码:<input type="password" name="user.password"/><br/>
年龄:<input type="text" name="user.age"/><br/>
<input type="submit"value="注册" />
</form>
</body>
创建action类
public class RegistAction2 extends ActionSupport{
private User user;
public User getUser() {
return user;
}
public voidsetUser(User user){
this.user = user;
}
public String registField2(){
System.out.println("执行了"+user);
return null;
}
}
2. 模型驱动
使用模型驱动,也是将表单中的数据直接封装到一个javabean的对象中,表单的写法不需要改变。实例化javabean类作为action类的属性。需要创建action的时候实现ModelDriven<T>接口,实现getModel()方法,在getModel()方法中返回javabean的实例对象。
例子:
编写jsp页面
<body>
<form action="${ pageContext.request.contextPath }/user_registModel.haha" method="post">
姓名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
年龄:<input type="text" name="age"/><br/>
<input type="submit"value="注册" />
</form>
</body>
创建action类
public class RegistAction3 extends ActionSupport implementsModelDriven<User>{
private User user=new User();
public User getModel() {
return user;
}
public String registModel(){
System.out.println("执行了"+user);
return null;
}
}
(二) 将数据封装到集合中的方法
封装数据到集合中采用的是属性驱动中OGNL表达式的方式。
1. 将数据封装到List集合中
在action类中声明一个以数据pojo类为泛型的list集合的属性,创建set和get方法。在jsp页面,利用action类中集合属性的下标和泛型的属性为name赋值。
例子:
Jsp页面
<body>
<form action="${ pageContext.request.contextPath }/user_registFieldList.haha" method="post">
姓名:<input type="text" name="ulist[0].username"/><br/>
密码:<input type="password" name="ulist[0].password"/><br/>
年龄:<input type="text" name="ulist[0].age"/><br/>
姓名:<input type="text" name="ulist[1].username"/><br/>
密码:<input type="password" name="ulist[1].password"/><br/>
年龄:<input type="text" name="ulist[1].age"/><br/>
<input type="submit"value="注册" />
</form>
</body>
创建action类
public class RegistAction4 extends ActionSupport{
private List<User> ulist;
public List<User> getUlist() {
return ulist;
}
public voidsetUlist(List<User> ulist) {
this.ulist = ulist;
}
public String registFieldList(){
for(User u:ulist){
System.out.println(u);
}
return null;
}
}
2. 将数据封装到Map集合中
在action类中声明一个以数据pojo类为泛型的Map属性,创建set和get方法。在jsp页面,利用action类中集合属性的下标(属性的下标可以自定义,主要作为区分的作用)和泛型的属性为name赋值。
Jsp页面
<body>
<form action="${ pageContext.request.contextPath }/user_registFieldMap.haha" method="post">
姓名:<input type="text" name="umap['1'].username"/><br/>
密码:<input type="password" name="umap['1'].password"/><br/>
年龄:<input type="text" name="umap['1'].age"/><br/>
姓名:<input type="text" name="umap['2'].username"/><br/>
密码:<input type="password" name="umap['2'].password"/><br/>
年龄:<input type="text" name="umap['2'].age"/><br/>
<input type="submit"value="注册" />
</form>
</body>
Action类
public class RegistAction5 extends ActionSupport{
private Map<String,User> umap;
public Map<String, User> getUmap() {
return umap;
}
public voidsetUmap(Map<String, User> umap) {
this.umap = umap;
}
public String registFieldMap(){
System.out.println(umap);
return null;
}
}
十四、 Struts2的拦截器技术
1. 拦截器简介
拦截器,interceptor,是AOP的一种实现。AOP,面向切面编程。拦截器指某个方法或者字段在被访问之前,加入某些操作。
在struts2中,拦截器对action类的某些方法进行拦截,不能拦截jsp。
拦截器与过滤器的区别:
1) 过滤器基于函数回调。过滤器过滤的是从客服端发送的请求,在请求的流程中,相关的资源都可以进行过滤,如jsp、servlet、url、js、css等。过滤器依赖于servlet容器。
2) 拦截器基于反射机制,拦截器只对action其作用。
拦截器运行模式:拦截器采用责任链模式,在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。责任链的每一个节点,都可以继续调用下一个节点,也可以阻止流程继续执行。在struts2中可以定义很多个拦截器,将多个拦截器按照特定顺序组成拦截器栈。
Struts2的运行流程:当服务器开启的时候,struts2相关的配置文件解析加载完毕,如struts.xml等。并将配置文件交给Configuration Manager类管理。当客户端发起请求后,请求到达服务器,形成一个HttpServletRequest对象,然后根据struts2的过滤器经由ActionMapper,结合配置文件管理类生成ActionProxy对象。接下来,就开始经由ActionInvocation对象来管理流程经过拦截器栈,当拦截器执行完毕,然后执行委托对象,即真正的action对象,结束后形成结果视图,再经由拦截器后,最后,形成响应。
2. 自定义拦截器和配置拦截器
Struts2提供了Interceptor接口。
Interceptor接口的方法列表。
void | destroy() Called to let an interceptor clean up any resources it has allocated. |
void | init() Called after an interceptor is created, but before any requests are processed using intercept , giving the Interceptor a chance to initialize any needed resources. |
java.lang.String | intercept(ActionInvocation invocation) Allows the Interceptor to do some processing on the request before and/or after the rest of the processing of the request by the ActionInvocation or to short-circuit the processing and just return a String return code. |
一般通过继承Interceptor接口的实现类的方式自定义拦截器。常用的实现类有:AbstractInterceptor,MethodFilterInterceptor等。
配置拦截器有两种方式,第一种,在struts.xml中声明一个拦截器,使用<interceptor>标签,然后在需要拦截的action标签中引入这个拦截器。第二种,自定义配置拦截器栈,引入自定义的拦截器。如果使用了自定义的拦截器或者拦截器栈,需要手动引入默认的拦截器栈,否则不执行默认的拦截器栈,导致失去很多重要的功能,如封装数据,设置编码等。
自定义拦截器的例子:
编写Action类
public class InterceptorAction1 extends ActionSupport{
public String interceptor1(){
System.out.println("测试拦截器");
return NONE;
}
}
编写自定义拦截器
public class Interceptor1 extends AbstractInterceptor{
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("action方法之前");
String result=invocation.invoke();
System.out.println("action方法之后");
return result;
}
}
配置拦截器
<package name="haha3" namespace="/"extends="struts-default">
<!-- 声明一个拦截器 -->
<interceptors>
<interceptor name="interceptor1"class="com.struts2.interceptor.Interceptor1"></interceptor>
</interceptors>
<action name="inter_*"class="com.struts2.action2.InterceptorAction1"method="{1}">
<!-- 引入拦截器 -->
<interceptor-ref name="interceptor1"></interceptor-ref>
<!-- 引入默认的拦截器栈 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
</action>
</package>
使用配置自定义拦截器栈的例子:
Action类和拦截器类同上。
配置拦截器栈。
<package name="haha3" namespace="/"extends="struts-default">
<!-- 声明一个拦截器 -->
<!--<interceptors>
<interceptorname="interceptor1"class="com.struts2.interceptor.Interceptor1"></interceptor>
</interceptors> -->
<!-- 自定义一个拦截器栈 -->
<interceptors>
<interceptor name="interceptor1"class="com.struts2.interceptor.Interceptor1"/>
<interceptor-stack name="interceptorStack1">
<interceptor-ref name="interceptor1"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<action name="inter_*"class="com.struts2.action2.InterceptorAction1"method="{1}">
<!-- 引入拦截器栈 -->
<interceptor-ref name="interceptorStack1"></interceptor-ref>
</action>
</package>
3. 使用拦截器实现登录认证
自定义拦截器
/**
* 判断应用系统是否登录,如果登录调到主页,没有登录,跳到登录页面。
* @author Administrator
*
*/
public class UserInterceptor extendsMethodFilterInterceptor{
protected String doIntercept(ActionInvocation invocation) throws Exception {
User user=(User)ServletActionContext.getRequest().getSession().getAttribute("user");
if(user==null){
//如果为空,没有登录,返回字符串,则不会继续执行请求。
return "login";
}
return invocation.invoke();
}
}
编写登录action
public class UserAction extends ActionSupport{
UserService us=new UserServiceImpl();
public String login(){
HttpServletRequest request=ServletActionContext.getRequest();
Map<String,String[]>map=request.getParameterMap();
User user=new User();
User userExist=null;
try {
BeanUtils.populate(user, map);
userExist=us.login(user);
if(userExist!=null){
request.getSession().setAttribute("user", userExist);
return "index";
}
request.setAttribute("msg", "用户名或密码错误");
return "login";
} catch (Exception e) {
e.printStackTrace();
}
request.setAttribute("msg", "系统繁忙,请稍后再试");
return "login";
}
}
配置拦截器
<package name="hhh" namespace="/"extends="struts-default">
<!-- package中的标签请按照一定的顺序配置 -->
<interceptors>
<interceptor name="login"class="com.haha.interceptor.UserInterceptor"></interceptor>
</interceptors>
<global-results>
<result name="login">/login.jsp</result>
</global-results>
<action name="user_*"class="com.haha.action.UserAction" method="{1}">
<result name="index">/index.jsp</result>
<!-- <result name="login">/login.jsp</result>-->
<!-- 引入拦截器,请手动配置默认拦截器栈 -->
<interceptor-ref name="login">
<!-- 配置不拦截的方法,为登录action放行 -->
<param name="excludeMethods">login</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</action>
<action name="customer_*"class="com.haha.action.CustomerAction" method="{1}">
<result name="saveSuccess"type="redirectAction">customer_list</result>
<result name="listSuccess">/jsp/customer/list.jsp</result>
<!-- 任何的系统操作都进行登录认证 -->
<interceptor-ref name="login">
<!-- 配置不拦截的方法,为登录action放行 -->
<param name="excludeMethods">login</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</action>
</package>
十五、 Struts2的OGNL表达式
1. OGNL概述
Struts2提供了值栈,把查询的数据存入到值栈中,转发到JSP页面上,在jsp页面上就可以从值栈中获取值。
OGNL是Object Graphic Navigation Language的缩写。所谓对象图,即以任意一个对象为根,通过OGNL可以访问这个对象相关联的其他对象。
OGNL是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。它比EL强大。它本来也是一种单独的语言。Struts2觉得它好用,就把它搞成了自己的一个部分。
OGNL的五大功能:支持对象方法调用;支持类静态的方法调用和值访问;访问OGNL上下文(OGNL context)和ActionContext;支持赋值操作和表达式串联;操作值对象。
存取值的例子:
public voidtest1() throwsOgnlException{
//ognl上下文对象
OgnlContext context=new OgnlContext();
//获取根对象
Object root=context.getRoot();
//存储数据
context.put("hah","hah好");
//获取值
Object value=Ognl.getValue("#hah",context,root);
System.out.println(value);
}
使用OGNL调用方法的例子:
public voidtest2() throwsOgnlException{
//ognl上下文对象
OgnlContext context=new OgnlContext();
//获取根对象
Object root=context.getRoot();
//存储数据
context.put("hah","hah好");
//获取值
Object value=Ognl.getValue("'hehi看看'.length()",context,root);
System.out.println(value);
}
使用OGNL获取root栈的值
public voidtest3() throwsOgnlException{
//ognl上下文对象
OgnlContext context=new OgnlContext();
User u=new User();
u.setUsername("hhaa");
context.setRoot(u);
String name=(String) Ognl.getValue("username",context,context.getRoot());
System.out.println(name);
}
2. Jsp页面使用OGNL表达式
步骤:
1) 引入struts2的标签库
<%@taglib prefix="s" uri="/struts-tags" %>
2) 使用struts2的标签库
例子:
<%@ page language="java" contentType="text/html;charset=utf-8"
pageEncoding="utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<meta http-equiv="Content-Type"content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
<h2>从jsp页面中获取值栈中已经存的值</h2>
<!-- 使用property标签从值栈中取值 -->
<s:property value="username"/>
</body>
</html>
十六、 Struts2的值栈
1. 什么是值栈
值栈相当于struts2框架的数据的中转站,可以向值栈中存入一些数据,从值栈中获取这些数据。Struts2提供了ValueStack接口,及其实现类OgnlValueStack,来操作值栈。在struts2中Action是多例的,有一个请求,就创建一个Action实例,创建一个ActionContext对象,代表的是Action的上下文对象,还会创建一个ValueStack对象。Struts2框架把ValueStack对象保存在名为”struts.valueStack”的请求属性中,Request中(值栈对象是request的一个属性)。比如:ValueStackvs=(ValueStack)request.getAttribute(“struts.valueStack”);
获取valueStack实例对象的例子:
public class ValueStackAction extends ActionSupport{
public String execute() throws Exception {
HttpServletRequest request=ServletActionContext.getRequest();
ValueStack vs1=(ValueStack)request.getAttribute("struts.valueStack");
System.out.println(vs1);
return NONE;
}
}
2. 值栈的内部结构
值栈由两部分组成:root栈和context栈。root,struts2通过root把动作和相关对象压入ObjectStack中。Root本质上是一个list集合。Context,struts2通过context把各种各样的映射关系(一些map类型的对象)压入contextMap中。
默认情况下,struts2会把以下映射压入ContextMap中。
parameters | 该Map中包含当前请求的参数。比如name=xxx&password=xxx |
request | 其中包含当前request对象的所有属性 |
session | 其中包含session对象中的所有属性 |
appli cation | 其中包含application对象中的所有属性 |
attr | 全域查找,该Map按如下顺序来检索某个属性:request,session,application |
注意:request代表的Map集合的key值,value的值其实也是一个Map集合。
ValueStack中存在root属性(CompoundRoot)、context属性(OgnlContext)。CompoundRoot就是ArrayList。OnglContext就是Map。
OGNL表达式访问值栈中的数据:访问root栈中数据时不需要加#,访问context栈中的数据时需要加#。一般讲操作值栈,默认情况下就是操作root元素。
3. 值栈的创建以及值栈和ActionContext对象的关系
值栈对象是请求时创建的。
ActionContext是绑定到当前的线程上的,在每个拦截器或者Action中获取到的ActionContext是同一个。
ActionContext中存在一个Map集合,该Map集合和ValueStack的context是同一个地址。
ActionContext中可以获取到ValueStack的引用,以后再开发,使用ActionContext来获取到值栈对象。
请查看源代码理解ValueStack和ActionContext的关系。
当执行struts2的核心过滤器的时候,在doFilter方法中,有一个prepare.createActionContext(request,response);的方法。
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,ServletException { try {
if (excludedPatterns != null&& prepare.isUrlExcluded(request,excludedPatterns)){
chain.doFilter(request, response);
} else {
prepare.setEncodingAndLocale(request, response);
prepare.createActionContext(request, response);
……
}
} finally {
prepare.cleanupRequest(request);
}
}
在createActionContext的方法中,如果没有老的context对象,则获取值栈对象,然后获取context栈,并压入默认map。然后,有一个new ActionContext(stack.getContext());的方法。将context栈中的数据传入ActionContext中。那么,以后就可以用ActionContext来操作栈。方便了。
public ActionContextcreateActionContext(HttpServletRequest request, HttpServletResponse response) {
ActionContext ctx;
Integer counter = 1;
Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
if (oldCounter != null){
counter = oldCounter + 1;
}
ActionContext oldContext = ActionContext.getContext();
if (oldContext != null){
// detected existing context, so we are probably in a forward
ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
} else {
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
ctx = new ActionContext(stack.getContext());
}
request.setAttribute(CLEANUP_RECURSION_COUNTER,counter);
ActionContext.setContext(ctx);
return ctx;
}
在ActionContext类中,有一个有参的构造函数。
public ActionContext(Map<String, Object> context) {
this.context = context;
}
4. 使用ActionContext来获取值栈对象
public class ValueStackAction extends ActionSupport{
public String execute() throws Exception {
/*HttpServletRequestrequest=ServletActionContext.getRequest();
ValueStackvs1=(ValueStack)request.getAttribute("struts.valueStack");
System.out.println(vs1);*/
ValueStack vs=ActionContext.getContext().getValueStack();
System.out.println(vs);
return NONE;
}
}
5. 向值栈中保存数据
Struts2主要提供了2个方法向值栈中存数据。
1) valueStack.push(Object obj);
push方法的底层盗用root对象的push方法,将元素添加到root栈的0位置,以后添加元素,从0位置依次压入栈内。
例子:
ValueStack vs=ActionContext.getContext().getValueStack();
System.out.println(vs);
vs.push("haha");
vs.push("h看看");
2) valueStack.set(String key, Object obj);
在源码中获取map集合(已经存在的,或者新创建的),把map集合push到栈顶,再把数据存入到map集合中。
例子:
ValueStack vs=ActionContext.getContext().getValueStack();
System.out.println(vs);
vs.push("haha");
vs.push("h看看");
vs.set("hh", "hhhkk看看");
6. 从值栈获取值
1) 使用push方法存入数据时
创建action类和save方法
public class ValueStackAction extends ActionSupport{
public String save() throws Exception{
//获取值栈对象
ValueStackvs=ActionContext.getContext().getValueStack();
vs.push("hah好");
return SUCCESS;
}
}
配置struts.xml文件
<package name="valueStack" namespace="/" extends="struts-default">
<action name="save" class="com.struts2.action.ValueStackAction"method="save">
<result name="success">/jsp/vsSuccess.jsp</result>
</action>
</package>
编写jsp页面
<%@ page language="java" contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE htmlPUBLIC "-//W3C//DTDHTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type"content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<span>从值栈中获取值</span><br />
<s:property value="[0].top" />
<!-- s:debug的作用是在jsp页面查看值栈的内部结构 -->
<s:debug></s:debug>
</body>
</html>
2) 使用set方法存入数据时
创建action类的方法
public String save() throws Exception{
//获取值栈对象
ValueStack vs=ActionContext.getContext().getValueStack();
//vs.push("hah好");
vs.set("msg","会话");
return SUCCESS;
}
配置struts.xml文件略
编写jsp页面
<body>
<span>从值栈中获取值</span><br />
<!--
获取使用push方法放入的栈顶的字符串值
<s:propertyvalue="[0].top" />
-->
<s:property value="[0].top.msg"/>
<!-- s:debug的作用是在jsp页面查看值栈的内部结构 -->
<s:debug></s:debug>
</body>
3) 获取对象的值
A. 使用push方式存入对象时
创建action类的方法
public String save()throwsException{
//获取值栈对象
ValueStack vs=ActionContext.getContext().getValueStack();
//vs.push("hah好");
//vs.set("msg","会话");
User user=new User();
user.setUsername("哈哈");
user.setPassword("1234");
user.setAge(123);
vs.push(user);
returnSUCCESS;
}
配置struts.xml略,创建pojo类略
编写jsp页面
<body>
<span>从值栈中获取值</span><br />
<!--
获取使用push方法放入的栈顶的字符串值
<s:propertyvalue="[0].top" />
-->
<!--
获取使用set方法放入的栈顶的map集合的值
<s:propertyvalue="[0].top.msg" />
-->
<!--
获取使用push方法放入的栈顶的对象的值
-->
<s:property value="[0].top.username"/>
<!-- s:debug的作用是在jsp页面查看值栈的内部结构 -->
<s:debug></s:debug>
</body>
注意:在jsp页面中获取push方式存放的对象时,可以省略[0].top,直接写需要的属性,如:<s:property value="username"/>
struts2提供了findValue的方法会直接查找属性名对应的值,直到找到就不找了。也就是重名的属性值取先找到的。
B. 使用set方式存入对象时
创建action类的方法
public String save() throws Exception{
//获取值栈对象
ValueStack vs=ActionContext.getContext().getValueStack();
//vs.push("hah好");
//vs.set("msg","会话");
User user=new User();
user.setUsername("哈哈");
user.setPassword("1234");
user.setAge(123);
//vs.push(user);
vs.set("user",user);
return SUCCESS;
}
配置struts.xml文件略,创建pojo类略
编写jsp页面
<body>
<span>从值栈中获取值</span><br />
<!--
获取使用push方法放入的栈顶的字符串值
<s:propertyvalue="[0].top" />
-->
<!--
获取使用set方法放入的栈顶的map集合的值
<s:propertyvalue="[0].top.msg" />
-->
<!--
获取使用push方法放入的栈顶的对象的值
<s:propertyvalue="[0].top.username" />
-->
<s:property value="[0].top.user.username"/>
<!-- s:debug的作用是在jsp页面查看值栈的内部结构 -->
<s:debug></s:debug>
</body>
同样的,在jsp页面可以省略[0].top。struts2会自动查找key值。如:<s:propertyvalue="user.username" />。
4) 获取使用action类的属性对象的值
在action类中创建自定义类的成员属性,并提供set、get方法
public class ValueStackAction extends ActionSupport{
User user=new User("action","1234",1223);
public User getUser() {
return user;
}
public voidsetUser(User user){
this.user = user;
}
public String save() throws Exception{
//获取值栈对象
ValueStack vs=ActionContext.getContext().getValueStack();
//vs.push("hah好");
//vs.set("msg","会话");
User user=new User("set","1234",13);
//vs.push(user);
vs.set("user",user);
return SUCCESS;
}
}
配置struts.xml和创建pojo略
编写jsp页面
<body>
<span>从值栈中获取值</span><br />
<!--
获取使用push方法放入的栈顶的字符串值
<s:propertyvalue="[0].top" />
-->
<!--
获取使用set方法放入的栈顶的map集合的值
<s:propertyvalue="[0].top.msg" />
-->
<!--
获取使用push方法放入的栈顶的对象的值
<s:propertyvalue="[0].top.username" />
-->
<!--
获取使用set方法存入的栈顶的对象的值
<s:propertyvalue="[0].top.user.username" />
或者
<s:propertyvalue="user.username" />
-->
<s:property value="[1].top.user.username"/>
<!-- s:debug的作用是在jsp页面查看值栈的内部结构 -->
<s:debug></s:debug>
</body>
5) 获取list集合的值和遍历list集合
A. 使用push方法存入list时
创建action类的方法
public String save() throws Exception{
//获取值栈对象
ValueStack vs=ActionContext.getContext().getValueStack();
//vs.push("hah好");
//vs.set("msg","会话");
//User user=newUser("set","1234",13);
//vs.push(user);
//vs.set("user",user);
List<User> ulist=new ArrayList<User>();
ulist.add(new User("user1","12",123));
ulist.add(new User("user2","13",63));
ulist.add(new User("user3","14",133));
ulist.add(new User("user4","16",83));
vs.push(ulist);
return SUCCESS;
}
编写jsp页面
<body>
<span>从值栈中获取值</span><br />
<!--
获取使用push方法放入的栈顶的字符串值
<s:propertyvalue="[0].top" />
-->
<!--
获取使用set方法放入的栈顶的map集合的值
<s:propertyvalue="[0].top.msg" />
-->
<!--
获取使用push方法放入的栈顶的对象的值
<s:propertyvalue="[0].top.username" />
-->
<!--
获取使用set方法存入的栈顶的对象的值
<s:property value="[0].top.user.username"/>
或者
<s:propertyvalue="user.username" />
-->
<!--
获取action类中成员属性的值的方法
<s:propertyvalue="[1].top.user.username" />
-->
<!-- -->
<s:property value="[0].top[0].username"/>
<s:property value="[0].top[1].username"/>
<!-- s:debug的作用是在jsp页面查看值栈的内部结构 -->
<s:debug></s:debug>
</body>
B. 使用set方法存入数据时
创建action类的方法
public String save() throws Exception{
//获取值栈对象
ValueStack vs=ActionContext.getContext().getValueStack();
//vs.push("hah好");
//vs.set("msg","会话");
//User user=newUser("set","1234",13);
//vs.push(user);
//vs.set("user",user);
List<User> ulist=newArrayList<User>();
ulist.add(new User("user1","12",123));
ulist.add(new User("user2","13",63));
ulist.add(new User("user3","14",133));
ulist.add(new User("user4","16",83));
//vs.push(ulist);
vs.set("ulist",ulist);
return SUCCESS;
}
编写jsp页面
<body>
<span>从值栈中获取值</span><br />
<!--
获取使用push方法放入的栈顶的字符串值
<s:propertyvalue="[0].top" />
-->
<!--
获取使用set方法放入的栈顶的map集合的值
<s:property value="[0].top.msg"/>
-->
<!--
获取使用push方法放入的栈顶的对象的值
<s:propertyvalue="[0].top.username" />
-->
<!--
获取使用set方法存入的栈顶的对象的值
<s:propertyvalue="[0].top.user.username" />
或者
<s:propertyvalue="user.username" />
-->
<!--
获取action类中成员属性的值的方法
<s:propertyvalue="[1].top.user.username" />
-->
<!--
获取使用push方法存入的栈顶的list集合的值
<s:propertyvalue="[0].top[0].username" />
<s:propertyvalue="[0].top[1].username" />
-->
<s:property value="ulist[0].username"/>
<s:property value="ulist[1].username"/>
<!-- s:debug的作用是在jsp页面查看值栈的内部结构 -->
<s:debug></s:debug>
</body>
C. 迭代list集合的方法
Struts2提供了s:iterator标签。value:从值栈中获取的迭代的集合。var:迭代的对象。如果写var,则将迭代产生的对象压入context栈,这时需要写#。如果不写var,则将迭代的对象压入root栈。
a) 写var时
Jsp页面
<!--
s:iterator标签
value:从值栈中获取的迭代的集合
var:迭代的对象。如果写var,则将迭代产生的对象压入context栈,这时需要写#。如果不写var,则将迭代的对象压入root栈。
-->
<s:iterator value="ulist"var="user">
<s:property value="#user.username"/>
<s:property value="#user.password"/>
</s:iterator>
b) 不写var时
<s:iterator value="ulist">
<!--
不省略书写栈顶的方式
<s:propertyvalue="[0].top.username" />
<s:propertyvalue="[0].top.password" />
-->
<s:property value="username"/>
<s:property value="password"/><br />
</s:iterator>
6) 从context栈中获取值
从context栈中获取值时,记得要加#。
Context栈底层已经封装request,session,application,parameters,attr等对象。从context栈取值,需要先手动向request,session,application等域中存入值。
A. 从request,session,application等域中取值
创建action类并向request等域中存入值
public String save() throws Exception{
//获取值栈对象
ValueStack vs=ActionContext.getContext().getValueStack();
//vs.push("hah好");
//vs.set("msg","会话");
//User user=newUser("set","1234",13);
//vs.push(user);
//vs.set("user",user);
List<User> ulist=new ArrayList<User>();
ulist.add(newUser("user1","12",123));
ulist.add(newUser("user2","13",63));
ulist.add(newUser("user3","14",133));
ulist.add(newUser("user4","16",83));
//vs.push(ulist);
vs.set("ulist",ulist);
HttpServletRequest request=ServletActionContext.getRequest();
request.setAttribute("haha", "会话");
request.getSession().setAttribute("haha", "看看");
return SUCCESS;
}
编写jsp页面
<!-- 从context栈中取值 -->
<s:property value="#request.haha"/>
<s:property value="#session.haha"/>
B. 从parameters对象中取值
向页面传入parameters参数
http://127.0.0.1:8080/struts01/save.haha?haha=参数
jsp页面
<s:propertyvalue="#parameters.haha" />
C. 从attr对象中取值
<s:propertyvalue="#attr.haha" />
7) 使用EL表达式从值栈中取值
使用el表达式结合jstl标签使用,在eclipse开发下注意导入jstl的jar包。
创建action类的方法
public String save() throws Exception{
//获取值栈对象
ValueStack vs=ActionContext.getContext().getValueStack();
List<User> ulist=new ArrayList<User>();
ulist.add(newUser("user1","12",123));
ulist.add(newUser("user2","13",63));
ulist.add(newUser("user3","14",133));
ulist.add(newUser("user4","16",83));
//vs.push(ulist);
vs.set("ulist",ulist);
return SUCCESS;
}
编写jsp页面,使用el表达式取值
<c:forEach items="${ulist }"var="user">
${user.username }====${user.password }<br />
</c:forEach>
为什么EL也能访问值栈中的内容
采用了装饰者模式对封装进行了增强。在StrutsPrepareAndExecuterFilter的doFilter代码中request=prepare.wrapRequest(request);对request 对象进行了包装,StrutsRequestWrapper增强了request的getAttribute方法。
Objectattribute = super.getAttribute(s);
if(attribute == null) {
attribute =stack.findValue(s);
}
当使用el表达式访问request范围的数据时,如果数据找不到,就去值栈中找。这就使el表达式具备了访问值栈数据的能力(查找root栈数据)。
十七、 Struts2的标签指引
Struts2提供了通用的标签和UI标签等。
Generic Tag Reference
Struts Generic Tags control the executionflow as pages render.
Control Tags |
| Data Tags |
|
UI Tags
UI Tag Reference
Struts UI Tags display data in rich andreusable HTML.
Form Tags |
| Non-Form UI Tags |
| Ajax Tags |
|
十八、 OGNL表达式的特殊符号
1. #号的用法
1) 获得contextMap中的数据
如:
<!-- 从context栈中取值 -->
<s:property value="#request.haha"/>
<s:property value="#session.haha"/>
2) 构建一个Map集合
例子:
<s:form action=""method="post">
<%--<s:radio name="sex" list="{'男','女'}" /> --%>
<%--使用#构建Map集合 --%>
<s:radio name="sex"list="#{'0':'男','1':'女' }" />
</s:form>
2. %号的用法
强制将字符串解析成OGNL表达式
例子:
<%--使用%{''}将OGNL强制转换成字符串输出而不是从值栈取值 --%>
<s:property value="%{'aaa'}"/>
<%--直接以单引号表示的字符串也是被强制转换成字符串输出而不是OGNL --%>
<s:property value="'aaa'"/>
3. $号的用法
在jsp页面中使用$可以使用EL表达式时使用。
在配置文件中可以使用使用$号引用OGNL表达式。在配置文件下载时就会用到$号。
例子:
<action name="download1" class="com.struts2.haha.DownloadAction">
<result name="success" type="stream">
<param name="contentType">${contentType}</param>
<param name="contentDisposition">attachment;filename=${downFilename}</param>
</result>
</action>
十九、 Struts2下使用ajax
Struts2没有提供封装的ajax功能。在struts2下使用ajax,基本同单个servlet使用ajax的方式。如果使用eclipse请导入相关jar包。
例子使用ajax实现页面加载数据字典功能:
在jsp页面使用jquery的ajax
function loadSelect(typecode,positionId,selectname,selectId){
var $select = $("<select name="+selectname+"></select>");
$select.append($("<option value=''>--请选择--</option>"));
$("#"+positionId).append($select);
$.post("${pageContext.request.contextPath}/BaseDictAction",{dict_type_code:typecode},function(data){
//alert("Data loaded:"+data);
$.each(data,function(i,json){
//alert(json["dict_item_name"]);
var $option=$("<option value='"+json['dict_id']+"'>"+json['dict_item_name']+"</option>");
if(json["dict_id"] == selectId){
$option.attr("selected","selected");
}
$select.append($option);
});
},"json");
}
编写ajax的url路径的专门用于加载数据字典的action类的方法,在方法中,return为null。
public class BaseDictAction extends ActionSupport{
private String dict_type_code;
private BaseDictService baseDictService;
@Override
public String execute() throws Exception {
List<BaseDict> list=baseDictService.getListByTypeCode(dict_type_code);
String json=JSONArray.fromObject(list).toString();
ServletActionContext.getResponse().setContentType("application/json;charset=utf-8");
ServletActionContext.getResponse().getWriter().write(json);
return null;
}
public String getDict_type_code() {
return dict_type_code;
}
public voidsetDict_type_code(String dict_type_code) {
this.dict_type_code = dict_type_code;
}
public voidsetBaseDictService(BaseDictService baseDictService) {
this.baseDictService = baseDictService;
}
}
在struts.xml中配置action
<action name="BaseDictAction" class="baseDictAction" method="execute"></action>
二十、 Struts2实现文件上传
Struts2封装了用于文件上传的拦截器,在实现上相对比较简单。以上传图片为例。
在jsp页面做文件上传,需要满足3个条件,1,表单传输方式为post,2,表单提交类型enctype必须为多段式,即enctype=”multipart/form-data”,3,文件上传使用<input type=”file” name=”filename” />标签。
在action类中,需要声明以上传文件的name为名字的File类属性,如果需要使用源文件的名字和类型,则声明以name开头的字符串FileName和ContentType属性。提供这些属性的setter和getter方法。然后,在需要上传的地方使用这些属性。使用file类的renameTo(new File());方法。
例子:
Jsp页面
<form action=”” method=”post” enctype=”multipart/form-data” >
<input type="file" name="photo"/>
</form>
Action类中
private File photo;
private String photoFileName;
private String photoContentType;
set和get方法略
public String add()throws Exception{
photo.renameTo(newFile("f:/upload/"+photoFileName));
cs.save(customer);
return "toList";
}