您的位置:首页 > 编程语言 > Java开发

Struts2学习总结(3)--值栈,Ognl表达式,Struts标签库,国际化,数据校验,拦截器

2016-03-28 10:56 826 查看
本文包括以下五个部分:

值栈。
Ognl表达式。

struts2标签库。
国际化。
表单数据校验
拦截器。
struts2的执行过程。

一、值栈

采用servlet和JSP开发时,servlet通过域对象保存数据,在JSP页面通过jstl标签+el表达式获取数据。

采用struts2和JSP框架进行开发时,Action通过值栈对象保存数据,在JSP页面通过struts标签+ognl表达式获取数据。

1.1 值栈(ValueStack)的概念

值栈就是存放action的堆栈,用来存储请求响应数据,是Struts2存取数据的核心。值栈将数据统一管理起来,方便Action、Result、Interceptor的使用。值栈有一个标准接口ValueStack,而在实际的项目开发中,是通过一个实现类OgnlValueStack来存储数据的。

1.2 值栈的结构

值栈分为值栈分为两个逻辑结构(数据结构):

Object Stack(对象栈):ArrayList (CompoundRoot),底层数据结构为ArrayList集合+栈的结构(先进后出)。

对象栈主要存储Action对象和Provider代理对象。

Context Map(映射栈): HashMap (OgnlContext),底层的数据结构为Map集合的结构。

a. 映射栈主要存放各个域存放的数据和用户的参数信息。

b. 对象栈主要有五个对象:

KeyValue
requestRequestMap
sessionSessionMap
applicationApplicationMap
parametersParaemterMap
attrAttributeMap (封装了三个Map(request,session,application))
1.3 操作值栈对象

1.3.1 操作Object Stack

//得到ActionContext对象
ActionContext ac = ActionContext.getContext();
//得到值栈对象
ValueStack vs = ac.getValueStack();


push(Object o):压入栈顶

Object pop():推出栈顶元素

1.3.2 操作Context Map

Map<String,Object> getContext(): 得到Context Map对象

ActionContxt.get("request").put("name",Object): 操作Context Map中的reqest元素

ActionContxt.getSession().put("name",Object): 操作Context Map中的session元素

ActionContxt.getApplication().put("name",Object): 操作Context Map中的application元素

package edu.scut.d_valuestack;

import java.util.Date;
import java.util.Map;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
//演示值栈
public class BookAction extends ActionSupport {
public String list(){
//值栈是由struts2框架在每次访问Action的方法时创建的,然后将其存入ActionContext

//1 得到ActionContext对象
ActionContext ac = ActionContext.getContext();
//2 得到值栈对象
ValueStack vs = ac.getValueStack();
System.out.println(vs);

//3 操作值栈
//Object Stack(对象栈)
Book book = new Book();
book.setName("java编程思想");
book.setPrice(58);
book.setPublishtime(new Date());

//3.1 压入元素(栈定)
vs.push(book);
System.out.println(vs);

//3.2 推出元素
vs.pop();
System.out.println(vs);

//4 映射栈(Context Map)
//4.1 操作request属性
Map requestMap = (Map) ac.get("request");
requestMap.put("r_book", book);

//4.2 操作session属性
ac.getSession().put("s_book", book);

//4.3 操作application属性
ac.getApplication().put("c_book", book);

System.out.println(vs);
return SUCCESS;
}
}


二、Ognl表达式

2.1 Ognl表达式简介

OGNL是Object GraphicNavigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts2框架使用OGNL作为默认的表达式语言。在struts2项目中导入ognl.jar 包来实现支持ognl表达式。

2.2 Ognl表达式和EL表达式的比较

Ognl表达式EL表达式
获取域对象的数据。可以存放数据,可以调用方法。

获取域对象的数据。不能存放数据,不能调用方法

2.3 Ognl表达式的优势

支持对象方法调用,如xxx.doSomeSpecial();

支持类静态的方法调用和值访问,表达式的格式:

@[类全名(包括包路径)]@[方法名 | 值名],例如:

@java.lang.String@format('foo %s', 'bar')或者

@tutorial.MyConstant@APP_NAME。

支持赋值操作和表达式串联,如(price=10,discount=0.6,calculatePrice()),这个表达式会返回6.0;

可以访问OGNL上下文(OGNL context)和ActionContext;

可以操作集合对象。

2.4 Ognl表达式的核心对象(OgnlContext)

2.4.1 Ognl表达式的核心对象OgnlContext对象的使用

package edu.scut.a_ognl;

import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import org.junit.Test;

//演示Ognl
public class OgnlDemo {
//1 学习了解Ognl表达式的核心对象OgnlContext对象的使用
@Test
public void test1(){
User user = new User();
user.setId(1);
user.setUserName("乔峰");
user.setUserPsw("666666");

//1 创建一个OgnlContext对象
OgnlContext context = new OgnlContext();

//2 把user对象存入OgnlContext对象
context.put("user", user);
//3 从OgnlContext对象取出数据
User user2 = (User) context.get("user");
System.out.println(user2.getId()+"\t"+user2.getUserName()+"\t"+user2.getUserPsw());
}
}


2.4.2 使用Ognl表达式取出OgnlContext的数据,根对象不需要有key,取值不需要#

package edu.scut.a_ognl;

import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import org.junit.Test;

//演示Ognl
public class OgnlDemo {

//2 使用Ognl表达式取出OgnlContext的数据,根对象不需要有key,取值不需要#
@Test
public void test2() throws Exception{
User user = new User();
user.setId(3);
user.setUserName("段誉");
user.setUserPsw("88888888");

//1 创建一个OgnlContext对象
OgnlContext context = new OgnlContext();

//2 把user对象存入OgnlContext对象
//根对象要存储数据需要key
context.setRoot(user);
//3 从OgnlContext对象取出数据,使用Ognl表达式取数据
Object ognlObj = Ognl.parseExpression("userName");
Object result = Ognl.getValue(ognlObj, context, context.getRoot());

System.out.println(result);
}

}


2.4.3 使用Ognl表达式取出OgnlContext的数据,非根对象需要有key,取值需要#

package edu.scut.a_ognl;

import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import org.junit.Test;

//演示Ognl
public class OgnlDemo {

<pre name="code" class="java">        //3 使用Ognl表达式取出OgnlContext的数据,非根对象需要有key,取值需要#
@Test
public void test3() throws Exception{
User user = new User();
user.setId(2);
user.setUserName("虚竹");
user.setUserPsw("7777777");

//1 创建一个OgnlContext对象
OgnlContext context = new OgnlContext();

//2 把user对象存入OgnlContext对象
//非根对象要存储数据需要key
context.put("user", user);
//3 从OgnlContext对象取出数据,使用Ognl表达式取数据
Object ognlObj = Ognl.parseExpression("#user.userName");
Object result = Ognl.getValue(ognlObj, context, context.getRoot());

System.out.println(result);
}
}



2.4.4 Ognl表达式调用静态方法

package edu.scut.a_ognl;

import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import org.junit.Test;

//演示Ognl
public class OgnlDemo {

//4 Ognl表达式调用静态方法
@Test
public void test4() throws Exception{
System.out.println(Math.round(12.55));
//1 创建一个OgnlContext对象
OgnlContext context = new OgnlContext();
//使用Ognl表达式取数据
Object ognlObject = Ognl.parseExpression("@Math@round(10.5)");
Object result = Ognl.getValue(ognlObject, context, context.getRoot());
System.out.println(result);
}
}


结论:

a. 从OgnlContext对象的根对象取出数据,不需要#号。

b. 从OgnlContext对象的非根对象取出数据,需要#号。

2.5 值栈如何共享和获取数据

2.5.1 值栈如何传递到jsp页面

struts2框架在Action中最后把值栈对象传递到jsp页面,是通过request域对象的struts.valueStack名称的属性传递到jsp页面的。

class Action{
1.创建OgnlValueStack对象

public String list(){
2.得到OgnlValueStack对象,然后操作OgnlValueStack对象
}

3.把OgnlValueStack对象放入request域对象
request.setAttribute("struts.valueStack",OgnlValueStack对象);
}


2.5.2 查看值栈对象所有内容

<s:debug/>


2.5.3 获取值栈数据

2.5.3.1 获取值栈数据的条件

a. 必须使用ognl表达式获取,需要学习ognl表达式语法。

b. ognl表达式必须依赖struts2的标签。

struts2获取数据的标签:
<s:property value="ognl表达式"/>


2.5.3.1 获取值栈数据的规则

a. 获取Object Stack: 不带#号直接获取

(a) 获取对象(对象栈): [下标] 下标从0开始 。

规则:从对象栈的指定下标的元素开始查找,直到最后一个元素为止!

[0] 从第一个元素开始查找

[1] 从第二个元素开始查找

<s:property value="[1]"/>

(b) 获取对象属性: [下标].属性 注意:下标可以不写,默认从第一个元素开始查找。

规则:从对象栈的指定下标的元素开始查找指定的属性,如果找到了就直接返回了,如果没有继续查找,直到最后一个元素为止!

<s:property value="[1].name"/>


b. 获取Context Map(映射栈):带#号获取

<s:property value="#name"/>
<s:property value="#request.r_book.price"/><br/>
<s:property value='#session.s_book.price'/>
<s:property value='#application.c_book.price'/>
<s:property value="#parameters.email"/>
<s:property value="#attr.request.r_book.price"/>


三、struts2标签库

3.1 逻辑类标签: 类似于jstl里面的的条件判断,循环等,用于处理jsp页面的逻辑

<s:set/> 保存数据

<s:property/> 获取数据

<s:if/>+<s:elseif/>+<s:else> 条件判断

<s:iterator/> 循环

<body>
<%-- <s:set> 设置数据, <s:property> 获取数据--%>
<s:set var="message" value="'itcast'" scope="request"/>
<s:set var="message" value="'itcast'" scope="session"/>
<s:set var="message" value="'itcast'" scope="application"/>
<s:property value="#request.message"/>
<hr>

<%--条件判断 <s:if>  <s:elseif>  <s:else> --%>
<%--默认登录数据放在ContextMapd的session中 --%>
<s:set var="loginInfo" value="'rose'" scope="session" ></s:set>
<s:if test="#session.loginInfo==null">
请先登录
</s:if>
<s:else>
              欢迎你,用户名:<s:property value="#session.loginInfo"/>
</s:else>
<hr>

<%--<S:iterator>循环迭代  --%>
<s:iterator value="#request.books" status="vs" id="book">
序号:<s:property value="#vs.count"/>
书名:<s:property value="#book.name"/>
价格:<s:property value="#book.price"/><br>
</s:iterator>
</body>


3.2 UI标签:用于简化html标签(表单)使用

<s:form>

<s:textfield>

<s:password>

<s:submit>

<s:reset>

<s:checkbox>

*<s:checkboxlist>

*<s:radio>

*<s:select>

<body>
<s:form action="book_list" namespace="/ognl">
<%--Struts2的UI部分不是代表ognl表达式,如果要让他们成为OGNL表达式,要加上%{}
%{ognl表达式}
--%>
<s:textfield label="用户名" name="curUser.userName" cssClass="style2"></s:textfield>
<s:password label="密码" name="curUser.userPsw" ></s:password>
<s:textarea label="简介" name="info"></s:textarea>
<s:submit value="注册" align="left"></s:submit>
<s:checkbox name="hobby" label="篮球" value="打篮球"></s:checkbox>
<s:checkbox name="hobby" label="足球" value="足球"></s:checkbox>
<s:checkbox name="hobby" label="网球" value="网球"></s:checkbox>

<%-- 可以用ognllist进行数据的展示
ognl表达式创建List集合
ognl表达式创建Map集合
--%>
<%--label和value一致的情况 --%>
<s:checkboxlist name="hobby" list="{'篮球','足球','网球'}"></s:checkboxlist>
<s:checkboxlist name="types" list="#request.types"></s:checkboxlist>

<%--lable和value不一致的情况 --%>
<s:checkboxlist name="hobby" list="#{'eat':'吃','drink':'喝','play':'玩'}"></s:checkboxlist>

<%--list:所以checkbox的值
value:默认选中的checkbox的值 --%>
<s:checkboxlist name="types" list="#request.typesMap" value="curTypes" ></s:checkboxlist>
<s:select name="types" list="#request.typesMap" value="curTypes"></s:select>
<s:radio list="#request.typesMap" value="%{curTypes}"></s:radio>
<s:reset value="重置" align="left" theme="simple"></s:reset>
</s:form>
</body>


四、国际化

国际化步骤:

1. 在src目录下创建resource包,在resource包下创建国际化的properties文件:

message_en_US.properties:

user=USERNAME
password=PASSWORD
login=LOGIN


message_zh_CN.properties:

user=\u7528\u6237\u540D
password=\u5BC6\u7801
login=\u767B\u5F55


2. 统一绑定资源包。

在struts.xml配置文件中添加国际化的常量配置。

<!-- 修改国际化资源包的路径 -->
<constant name="struts.custom.i18n.resources" value="resource.message"></constant>


3. 使用国际化

a. Action:注意action类要继承ActionSupport类,用getText()方法获取需要国际化的属性。

package edu.scut.a_ognl;

import com.opensymphony.xwork2.ActionSupport;

public class BookAction extends ActionSupport{
public String i18n(){
//获取国际化内容
String user = getText("user");
String password = getText("password");
String login = getText("login");
System.out.println(user);
System.out.println(password);
System.out.println(login);

return "i18n";
}
}
b. JSP页面:注意用key接收需要国际化的属性。

<body>
<%--在标签中使用国际化内容 --%>
<s:form>
<s:textfield key="user"/>
<s:password key="password"/>
<s:submit key="login"/>
</s:form>
</body>


五、表单数据校验

采用javascript页面前端进行验证安全性不够,容易被绕过去,为了提高安全性,一般要进行后台验证。

5.1 使用代码验证

使用代码进行验证的方式灵活太差,一般比较少用。

5.1.1 全局验证(所有的方法都验证)

a. 编写一个Action类,继承ActionSupport(为了实现Valiateable接口)。b. 覆盖validate()方法。

b. 覆盖validate()方法。

下面的代码中对reg()和list()两个方均进行了验证。

package edu.scut.b_validate;

import org.hibernate.validator.constraints.Email;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
//采用方式进行验证,必须继承actionSupport
public class UserAction extends ActionSupport implements ModelDriven<User> {
//接受页面数据
private User user = new User();
@Override
public User getModel() {
// TODO Auto-generated method stub
return user;
}

/**
* 1)用户名不能为空,必须为数字或字母,长度4-16位
* 2)密码不能为空,必须为数字,长度为6-16位
*/
@Override
public void validate() {
System.out.println("执行数据校验!");
System.out.println(user);
if(user!=null){
//进行数据校验
//用户名
if(user.getName()==null || user.getName().equals("")){
//用户名不能为空
//将错误信息带入注册页面
addFieldError("username", "用户名不能为空!");
}else{
//必须是字母或者数字。并且长度为4-16位
if(!user.getName().matches("[0-9a-zA-Z]{4,16}")){
addFieldError("username", "用户名格式错误!");
};
}

//密码
if(user.getPassword()==null || user.getPassword().equals("")){
//密码不能为空
addFieldError("password", "密码不能为空!");
}else{
//必须为数字,4-16位
if(!user.getPassword().matches("[0-9]{6,16}")){
addFieldError("password", "密码格式有误!");
}
}
}

public String reg(){
System.out.println(user);
return SUCCESS;
}
<span style="color:#FF0000;"><span style="color:#000000;"></span></span><pre name="code" class="java">        public String list(){
System.out.println(user);
return "list";
}
}



c. 在struts.xml文件中对应的action配置加上input视图,然后struts2就会自动把错误信息转发到input视图的页面上去。

<result name="input">/reg.jsp</result>
d.在input视图页面上,打印出错误信息。

<s:fielderror></s:fielderror>


5.1.2 局部验证(对一个方法验证)

局部校验时,只需要把需要验证的方法名改成固定格式(validate+需要验证的方法名称)即可。

例如,要对只对reg()方法验证,而不对其他方法验证,只需将reg()方法名称改成validateReg()即可。

//代码方式局部验证,编写validate+需要验证的方法名称!!!
public void validateReg(){
//有错误信息,struts2会自动跳转到input页面
if(user!=null){
//用户名验证
if(user.getName()==null || user.getName().equals("")){
addFieldError("name", "用户名不能为空!");
}else{
if(!user.getName().matches("[0-9a-zA-Z]{4,16}")){
addFieldError("name", "用户名格式错误!");
}
}

}
}


5.2 使用配置文件验证

为了提高数据校验的灵活性,可以使用配置文件的方式完成校验。

5.2.1 全局验证(所有的方法都验证)

这种配置方式对action的所有方法都生效。

a. 编写一个xml文件,名称: Action文件名-validation.xml。

b. 该xml文件必须放在Action文件的同一目录。

例如:UserAction-validation.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<!-- 验证一个属性 name是属性名 -->
<field name="name">
<!-- 属性验证器 -->
<field-validator type="requiredstring">
<!-- 错误信息 -->
<message key="name.requried"></message>
</field-validator>
<field-validator type="regex">
<!-- 给验证器注入一个参数 -->
<param name="expression">[0-9a-zA-Z]{4,16}</param>
<!-- 错误信息 -->
<message key="name.formaterror"></message>
</field-validator>
</field>

<field name="email">
<field-validator type="requiredstring">
<message key="email.requried"></message>
</field-validator>
<field-validator type="email">
<message key="email.formaterror"></message>
</field-validator>
</field>
</validators>


5.2.2 局部验证(对一个方法验证)

这种配置方式对action的指定方法都生效。

a. 编写一个xml文件,名称: Action文件名-访问方法路径-validation.xml。

b. 该xml文件必须放在Action文件的同一目录。

例如:UserAction-user_reg-validation.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<!-- 验证一个属性 name是属性名 -->
<field name="name">
<!-- 属性验证器 -->
<field-validator type="requiredstring">
<!-- 错误信息 -->
<message key="name.requried"></message>
</field-validator>
<field-validator type="regex">
<!-- 给验证器注入一个参数 -->
<param name="expression">[0-9a-zA-Z]{4,16}</param>
<!-- 错误信息 -->
<message key="name.formaterror"></message>
</field-validator>
</field>

<field name="email">
<field-validator type="requiredstring">
<message key="email.requried"></message>
</field-validator>
<field-validator type="email">
<message key="email.formaterror"></message>
</field-validator>
</field>
</validators>


六、拦截器

6.1 拦截器简介

拦截器的功能类似于过滤器,但是过滤器可以过滤项目的任何请求(servlet/jsp/html/img),拦截器只能拦截Action资源。拦截器是struts2框架的核心,因为struts2的核心功能都是通过拦截器来实现的。

例如:

参数的接收拦截器:com.opensymphony.xwork2.interceptor.ParametersInterceptor

文件上传拦截器:org.apache.struts2.interceptor.FileUploadInterceptor

国际化拦截器:com.opensymphony.xwork2.interceptor.I18nInterceptor

6.2 拦截器和过滤器的比较

拦截器过滤器
可以拦截代码可以拦截代码
struts2的组件之一,主要是拦截的action(方法)servlet的三大组件之一,主要是拦截请求(静态内容和动态内容)和响应
6.3 自定义拦截器的开发步骤

struts2框架有很多拦截器,来支持其核心功能。但是,有时候不能满足实际开发的需求,用户需要自定义具有特殊功能的拦截器。struts2支持自定义拦截器。 自定义拦 截器的开发步骤如下:

a. 编写java类,实现Intercepor接口。

Example

编写两个拦截器:

MyInterceptor1

package edu.scut.d_Interceptor;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;

public class MyInterceptor1 implements Interceptor {

@Override
public void destroy() {
}

@Override
public void init() {
}

@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("1 执行interceptor1的action前面的代码!");
String result = invocation.invoke();
System.out.println("5 执行interceptor1的action后面的代码!");
return result;
}
}


MyInterceptor2

package edu.scut.d_Interceptor;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;

public class MyInterceptor2 implements Interceptor {

@Override
public void destroy() {
}

@Override
public void init() {
}

@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("2 执行interceptor2的action前面的代码!");
String result = invocation.invoke();
System.out.println("4 执行interceptor2的action后面的代码!");
return result;
}
}


action类

package edu.scut.d_Interceptor;

import com.opensymphony.xwork2.ActionSupport;
//图书操作类
public class BookAction extends ActionSupport {
//接收页面参数
private String name;
public void setName(String name) {
this.name = name;
}

private String password;
public void setPassword(String password) {
this.password = password;
}

 public String list(){
System.out.println("3 执行了图书的list方法!");
System.out.println(name);
System.out.println(password);
return SUCCESS;
}
}


b. 在struts.xml文件中配置拦截器,并定义拦截器栈。。

注意:在配置action时,引用了自定义的拦截器栈后,默认的拦截器栈(defaultStack)也要引用,并且放在配置的第一位置。否则,struts2的很多核心功能将实效。例如不能接收页面传过来的参数。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="interceptor" extends="struts-default" namespace="/interceptor">
<!-- 配置自定义拦截器 -->
<interceptors>
<!-- 定义拦截器 -->
<interceptor name="interceptor1" class="edu.scut.d_Interceptor.MyInterceptor1"/>
<interceptor name="interceptor2" class="edu.scut.d_Interceptor.MyInterceptor2"/>
<!-- 定义拦截器栈 -->
<interceptor-stack name="myStack">
<!-- 一个拦截器栈包含多个拦截器 -->
<!-- 注意:struts2的默认拦截器一定要配置在第一位!否则会被覆盖! -->
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="interceptor1"></interceptor-ref>
<interceptor-ref name="interceptor2"></interceptor-ref>
</interceptor-stack>
</interceptors>

<action name="book_*" class="edu.scut.d_Interceptor.BookAction" method="{1}" >
<!-- 引入拦截器 -->
<interceptor-ref name="myStack"></interceptor-ref>
<result >/succ.jsp</result>
</action>
</package>
</struts>


以上Example的运行结果:

1 执行interceptor1的action前面的代码!
2 执行interceptor2的action前面的代码!
3 执行了图书的list方法!
4 执行interceptor2的action后面的代码!
5 执行interceptor1的action后面的代码!


可以看出,页面发送的请求,必须经过拦截器才能访问action类,而且通过拦截器的顺序是根据struts2里面的配置顺序进行的。执行了第一个拦截器的(String result = invocation.invoke();)才能放行,依次执行后面的拦截器或者方法。

七、struts2的执行过程

下面这个图不是我自己画的,但是为了说明问题,我引用一下。跟着步骤仔细看一遍,会加深理解。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: