【Java EE 学习 36】【struts2】【struts2系统验证】【struts2 ognl值栈】【struts2 ongl标签】【struts2 UI标签】【struts2模型驱动和令牌机制】
一、struts2系统验证
1.基于struts2系统验证的方式实际上就是通过配置xml文件的方式达到验证的目的。
2.实际上系统校验的方法和手工校验的方法在底层的基本实现是相同的。但是使用系统校验的时候对实现过程进行了封装,用起来比较方便。
3.实现过程
(1)在Action文件中定义好相关属性,属性名字和表单中的name属性值要相同。
(2)在Action同目录下新建一个XML文件,文件命名规则如下
* 如果是针对整个Action文件的,则文件名为:ActionClassName-validation.xml
* 如果是针对Action中的某个方法,则文件名为:ActionClassName-ActionName-validation.xml(ActionName为struts.xml文件中)
(3)书写配置文件
(4)不需要在struts.xml文件中进行相关的注册。
4.配置文件的书写
(1)首先,dtd文件规范是:xwork-core-2.3.24.jar包下xwork-validator-x.x.x.dtd文件,将该文件引入配置文件中,如果没有相关提示,则提示方法参考:
http://kuangdaoyizhimei.blog.163.com/blog/static/22055721120158582251764/
(2)按照提示一步一步写就可以了,但是注意有一个field-validator标签,该标签有一个type属性,该属性的值应当参考
com.opensymphony.xwork2.validator.validators.default.xml文件中的相关定义
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE validators PUBLIC 3 "-//Apache Struts//XWork Validator Definition 1.0//EN" 4 "http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd"> 5 6 <!-- START SNIPPET: validators-default --> 7 <validators> 8 <validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/> 9 <validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/> 10 <validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/> 11 <validator name="long" class="com.opensymphony.xwork2.validator.validators.LongRangeFieldValidator"/> 12 <validator name="short" class="com.opensymphony.xwork2.validator.validators.ShortRangeFieldValidator"/> 13 <validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/> 14 <validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/> 15 <validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/> 16 <validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/> 17 <validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/> 18 <validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/> 19 <validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/> 20 <validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/> 21 <validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/> 22 <validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/> 23 <validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/> 24 </validators> 25 <!-- END SNIPPET: validators-default -->
(3)field-validator下的param标签中的参数参考com.opensymphony.xwork2.validator.validators.default.xml文件中的类源文件,查看源代码即可得到需要传递的参数的相关信息,如参数名,参数类型等。
5.将手动验证的案例使用系统验证重新写一遍,只列出配置文件:
* 文件名:ValidateAction-validateAction-validation.xml
*文件内容:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.3//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd"> <validators> <field name="username"> <field-validator type="requiredstring"> <param name="trim">true</param> <message><![CDATA[用户名不能为空!]]></message> </field-validator> </field> <field name="password"> <field-validator type="requiredstring"> <message><![CDATA[密码不能为空]]></message> </field-validator> <field-validator type="regex"> <param name="trim">true</param> <param name="regex"><![CDATA[^[0-9a-zA-Z]{6,10}$]]></param> <message><![CDATA[密码为6-10位的字母和数字的组合!]]></message> </field-validator> </field> </validators>
6.两种配置文件的搜索顺序:ActionClassName-validation.xml,ActionClassName-ActionName-validation.xml,系统找到第一个校验文件的时候会继续搜索下一个校验文件,当找到所有校验文件的时候,会把校验文件里面的所有校验规则汇总,然后全部应用于处理方法的校验。如果两个校验文件中指定的校验规则冲突,则只是用后面文件中的校验规则。
二、ognl值栈
1.ognl是Object Graphic Navigation Language的缩写,中文名为对象导航语言,它是一个开源项目。struts2使用ognl作为默认的表达式语言(EL)。
2.使用ognl表达式的优点
(1)支持对象方法调用,如xxx.doSomething();
(2)支持类静态的方法调用和值访问,表达式的格式:@全类名@方法名
举例:@java.lang.String@format('foo %s','bar')
(3)支持赋值操作和表达式串联,如price=100,discount=0.8,calculate(),这个表达式会返回80
(4)访问ognl上下文(OGNL Context)和ActionContext
(5)操作集合对象
3.OGNL有一个上下文的概念(Context),说白了就是一个Map结构,它实现了java.utils.Map接口
4.OGNL表达式中的ValueStack接口
(1)ValueStack是一个接口,在OGNL中使用ValueStack的时候,实际上使用的是OGNLValueStack类,该类实现了ValueStack接口。
(2)ValueStack(值栈)贯穿于整个Action生命周期,每个Action都拥有一个ValueStack对象的实例。它相当于一个中转站,再其中保存当前Action对象和其它相关对象。
(3)ValueStack对象的保存位置:struts2框架将ValueStack对象保存到request对象中的名为"struts.valueStack"的请求属性 中。
(4)ValueStack对象内部有两个逻辑的组成部分。
* ObjectStack:对象栈,struts2将动作和相关对象压入ObjectStack中,实际上是一个List集合。
* ContextMap:struts把各种各样的映射关系(一些Map类型的对象)压入ContextMap中。
(5)ContextMap中常用的一些映射
* parameters:该Map中包含当前请求中的请求参数
* request:该Map中包含当前request中的所有属性
* session:该Map中包含当前session的所有属性。
* application:该Map中包含当前application对象中的所有属性。
* attr:该Map对象会按照如下顺序检索某个属性:request、session、application
5.OgnlValueStack类中对应着对象栈和ContextMap的两个属性
源代码:
public class OgnlValueStack implements Serializable, ValueStack, ClearableValueStack, MemberAccessValueStack { CompoundRoot root; transient Map<String, Object> context; }
其中root继承了ArrayList,是用于实现对象栈的关键。
context就是一个Map对象,它是实现ContextMap的关键。
6.在jsp页面中可以通过<s:debug/>标签获取ValueStack对象中的详细信息。
7.可以通过ValueStack对象的getRoot方法获取对象栈,因为对象栈实际上是一个ArrayList,所以可以通过add方法在最后插入一个对象,也可以在指定的位置插入一个指定的对象。
浏览器通过debug输出可以得到的信息:
8.通过jsp页面获取对象栈中的值的时候不需要加上#,但是如果想想要访问ContextMap中的值,需要加上#。可以使用<s:property value=""/>访问ValueStack对象中的属性值。
三、OGNL表达式
1.课件超链接:http://www.doc88.com/p-9059093413348.html
2.#符号的使用方法
(1)#相当于ActionServlet.getContext()
(2)如果访问“根对象”,即对象栈中的对象,可以不加上#,但是如果要访问ContextMap中的对象,则需要加上#,比如访问request、session、application、attr、parameters对象中的值,都要加上#符号。
(3)实例:
获取对象栈中保存的username的值和password的值:<br/> username:<s:property value="username"/> <br/> password:<s:property value="password"/> <br/> 获取request对象中的username值:<s:property value="#request.username"/><br/> 获取session对象中的username值:<s:property value="#session.username"/><br/> 获取application对象中的username值:<s:property value="#application.username"/><br/> 获取attr中的username值:<s:property value="#attr.username"/><br/> 获取parameters中的username值:<s:property value="#parameters.username[0]"/><br/> <!-- 这里直接获取就可以吗?因为放在了对象栈中 --> 获取使用ValueStack的set方法设置的键值对:<s:property value="msg"/>
(4)如果使用了ValueStack对象中的set(key,value)方法,则不需要加上#也可以直接访问。
通过查看底层代码可以知道,使用该方法的时候会先判断对象栈中是否有已经存在的HashMap对象,如果有,则直接调用该对象的put方法,如果没有,则先会创建一个Map对象,然后再调用put方法添加相应的键值对;该HashMap对象最终被添加到对象栈中。
疑问:使用<s:property>标签尅直接访问到该对象,但是使用<s:debug>标签不能访问到该对象,即对象栈中不存在使用set方法设置的值。
3.集合的投影(过滤)
(1)什么是集合的投影?集合的投影是只输出部分值的方法。
(2)首先使用List集合存储十个人的信息,然后将list对象保存到request中,在jsp页面中遍历访问该对象,使用的标签是:<s:iterator>
<s:iterator value="#request.persons" var="person"> <s:property value="#person"/><br/> </s:iterator>
结果是:
(4)投影的使用方法:collectionName.{expression}
* 输出id号大于12的所有Peson的信息
<s:iterator value="#request.persons.{?#this.id>12}" var="person"> <s:property value="#person"/><br/> </s:iterator>
* 只输出所有人的名字信息
<s:iterator value="#request.persons.{name}" var="person"> <s:property value="#person"/><br/> </s:iterator>
4.集合的投影(过滤)续
(1)集合的投影(过滤)有以下三种方式
* a."?#",这种方式过滤所有符合条件的集合,如users.{?#this.id>13}
* b."^#",这种方式过滤第一个符合条件的元素,如users.{^this.age>13}
* c."$.#",这种方式过滤掉最后一个符合条件的元素,如users.{$this.age>13}
(2)this在上述的例子中代表什么?
this在上述的实例中代表集合中的单个元素
(3)可以使用带有下标的表达式获取指定的集合中的元素,如#request.pesons.{?#this.id>13}[0],代表id大于12的人中的第一个人。
<s:property value="#request.persons.{?#this.id>13}[0]"/>
运行结果:
5.#符号的用法:构造Map
构造Map的举例:#{'id1':'name1','id2':'name2'}
举例:
jsp代码:
<s:radio list="#{'male':'男','female':'女'}" name="sex" label="性别"/>
翻译成的HTML代码:
<tr> <td class="tdLabel"><label for="sex" class="label">性别:</label></td> <td>
<input type="radio" name="sex" id="sexmale" value="male"/><label for="sexmale">男</label> <input type="radio" name="sex" id="sexfemale" value="female"/><label for="sexfemale">女</label> </td> </tr>
效果图:
构造出来的Map对象和radio中的标签中的各个对应关系看看即可。
6.OGNL表达式语言$的用法
(1)$的用途
* 用于在国际化资源中,引用OGNL表达式
* 在struts2配置文件中,引用OGNL表达式
(2)在struts.xml配置文件中,引用ognl表达式,引用request等作用域中的值。如:
<result name="success">/orgl/success.jsp?msg=$(#request.msgxx)</result>
jsp页面只需要使用<s:property>标签就可以获取到该值。
<s:property value="#parameters.msg[0]"/>
7.OGNL表达式%的用法
(1)"%"的作用是当标签的属性值被认为是字符串类型的时候,告诉执行环境%{}里的是ognl表达式。
(2)用法示例:
<s:textfield name="name" label="%(#request.username)"/>
四、OGNL标签
1.课件链接:http://wenku.baidu.com/view/b806982dcfc789eb172dc808.html?re=view
2.略,自行研究即可
五、struts2 UI标签
1.课件链接:http://wenku.baidu.com/view/a62ce2c089eb172ded63b708?fr=prin
2.略,自行研究即可。
六、struts2 模型驱动和令牌机制
1.在使用struts2作为前端的企业级应用程序的时候吧动作和模型清洗的隔离开来是十分有必要的;有些动作类不代表任何模型对象,它们的功能仅仅是提供图文显示服务。
比如在Action动作类中提供了两个属性username和password,但是该动作类不应当提供这两个属性,这两个属性应当放在JavaBean中。
2.实现模型驱动的原理
情景设置:有一个处理客户的CuscomAction类,该动作类实现了ModelDriven接口。
当用户触发CustomerAction动作的时候,ModelDriven拦截器将会调用相关CustomerAction对象的getModel()方法,并把返回的模型(Customer)实例压入到ValueStack栈。接下来Parameters拦截器将会把表单字段映射到ValueStack栈的栈顶对象的各个属性中。因为此时ValueStack栈的栈顶元素是刚刚被压入的模型对象,所以该模型将会被填充。如果某个字段在模型中没有匹配的属性,Param拦截器将会尝试ValueStack栈中的下一个对象。
一个模型类必须拥有一个不带任何参数的构造器。
3.小案例,使用JavaBean获取并封装页面中提交的表单数据
只提供Action的写法,其它都和之前的相同,也就是说只需要改动Action即可。
改动之处只有两处:首先提供JavaBean,再者Action需要实现ModelDriver接口,泛型写上javaBean的类名。
package com.kdyzm.model; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class UserModelAction extends ActionSupport implements ModelDriven<User>{ private static final long serialVersionUID = 5929526165758504719L; User user=new User(); @Override public User getModel() { return user; } public String login() throws Exception{ System.out.println(user); return "success"; } }
4.实现自动回显
自动回显的原理是使用struts标签库,<s:textfield />该标签在被加载的时候会自动查找ValueStack的对象栈,如果找到了相同名字的属性,则会其对应的值作为表单
中的默认值;如果找不到,则直接使用“”作为表单的默认值。
1.方法一:直接改变Action中定义的Model对象中的属性值。
2.方法二:由于查找的是对象栈,那么直接将对象栈中的响应Model对象删除掉,然后增加新的对象到栈顶即可。
方法示例代码:
package com.kdyzm.model; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; import com.opensymphony.xwork2.util.ValueStack; public class UserModelAction extends ActionSupport implements ModelDriven<User>{ private static final long serialVersionUID = 5929526165758504719L; User user=new User(); @Override public User getModel() { return user; } //自动回显示例 public String show() throws Exception{ //假设数据是从数据库中读取出来的 User newUser=new User("zhangsan","zhangsan_password"); //第一种方法: /* user.setUsername(newUser.getUsername()); user.setPassword(newUser.getPassword());*/ //第二种方法:先删掉之前的,再的到现在的 ValueStack vs=ServletActionContext.getContext().getValueStack(); vs.pop(); vs.push(newUser); return "success"; } }
5.处理表单重复提交
(1)首先建立表单:
<%@ page isELIgnored="false" language="java" import="java.util.*" pageEncoding="utf-8" contentType="text/html; charset=utf-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="/struts-tags" prefix="s" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Insert title here !</title> <meta http-equiv="content-type" content="text/html;charset=utf-8"> </head> <body> <s:form action="tokenAction" method="post" name="form1" namespace="/tokenSpace"> <s:textfield label="账号" name="username"/> <s:textfield label="密码" name="password"/> <s:token/> <s:submit type="submit" value="注册"/> </s:form> </body> </html>
(2)创建struts.xml自定义配置文件并将其include到struts.xml文件中
<?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="tokenpackage" namespace="/tokenSpace" extends="struts-default"> <interceptors> <interceptor-stack name="token_stack"> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="token"> <param name="includeMethods">register</param> </interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="token_stack"></default-interceptor-ref> <action name="tokenAction" class="com.kdyzm.token.TokenAction" method="register"> <result name="success"> /04_token/success.jsp </result> <result name="invalid.token"> /04_token/fail.jsp </result> </action> </package> </struts>
(3)在失败的页面中使用标签<s:actionerror/>打印错误提示信息。默认打印是中文,国际化问题略。
(4)注意这时候web.xml文件中配置的过滤器其匹配规则为:/*,否则报错。
(5)注意拦截器token,查看对应的拦截器源代码可发现更多问题。