一、开发Struts2应用依赖的jar文件(http://download.csdn.net/detail/huangzebiao007/6358105)
struts2-core-2.x.x.jar :Struts 2框架的核心类库
xwork-core-2.x.x.jar :XWork类库,Struts 2在其上构建
ognl-2.6.x.jar :对象图导航语言(Object Graph Navigation Language),struts2框架通过其读写对象的属性
freemarker-2.3.x.jar :Struts 2的UI标签的模板使用FreeMarker编写
commons-logging-1.x.x.jar :ASF出品的日志包,Struts 2框架使用这个日志包来支持Log4J和JDK 1.4+的日志记录。
commons-fileupload-1.2.1.jar 文件上传组件,2.1.6版本后必须加入此文件
二、struts2开发流程
1、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> <!-- 自从Struts 2.1.3以后,下面的FilterDispatcher已经标注为过时 <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> -->
在StrutsPrepareAndExecuteFilter的init()方法中将会读取类路径下默认的配置文件struts.xml完成初始化操作。struts2读取到struts.xml的内容后,以javabean形式存放在内存中,以后struts2对用户的每次请求处理将使用内存中的数据,而不是每次都读取struts.xml文件
如果struts.xml文件放置到别的文件夹下,例如com.hzb.xml下,则在xml配置文件中要做如下设置:
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
<init-param>
<param-name>config</param-name>
<param-value>
struts-default.xml,
struts-plugin.xml,
/com/hzb/xml/struts.xml
</param-value>
</init-param>
</filter>
2
、配置文件
struts.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="hzb" namespace="/test" extends="struts-default">
<action name="helloworld" class="com.hzb.action.HelloWorldAction" method="execute" >
<result name="success">/hello.jsp</result>
</action>
</package>
</struts>
配置包时必须指定name属性,该name属性值可以任意取名,但必须唯一,他不对应java的类包,如果其他包要继承该包,必须通过该属性进行引用。包的namespace属性用于定义该包的命名空间,命名空间作为访问该包下Action的路径的一部分,如访问上面例子的Action,访问路径为:http://localhost:8080/struts2/test/helloworld.action。 namespace属性可以不配置,对本例而言,如果不指定该属性,默认的命名空间为“”(空字符串)。http://localhost:8080/struts2/helloworld.action(后缀“.action”可不加)
通常每个包都继承struts-default包, 因为Struts2很多核心的功能都是拦截器来实现。如:从请求中把请求参数封装到action、文件上传和数据验证等等都是通过拦截器实现的。 struts-default定义了这些拦截器和Result类型。可以这么说:当包继承了struts-default才能使用struts2提供的核心功能。 struts-default包是在struts2-core-2.x.x.jar文件中的struts-default.xml中定义。 struts-default.xml也是Struts2默认配置文件。 Struts2每次都会自动加载 struts-default.xml文件。包还可以通过abstract=“true”定义为抽象包,抽象包中不能包含action。
3、创建Action类,通常继承了ActionSupport 类
package com.hzb.action;
import com.opensymphony.xwork2.ActionSupport;
public class HelloWorldAction extends ActionSupport{
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String execute() {
this.message = "我的第一个struts2应用";
return "success";
}
}
4、返回视图页面hello.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'hello.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
${message } <br>
</body>
</html>
三、struts2常量
1、常量可以在struts.xml或struts.properties中配置,两种配置方式如下:
在struts.xml文件中配置常量:
<struts>
<constant name="struts.action.extension" value="do"/>
</struts>
在struts.properties中配置常量:
struts.action.extension=do
2、常用常量
<!-- 指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法 和freemarker 、velocity的输出 -->
<constant name="struts.i18n.encoding" value="UTF-8"/>
<!-- 该属性指定需要Struts 2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts2处理。如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。 -->
<constant name="struts.action.extension" value="do"/>
<!-- 设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭 -->
<constant name="struts.serve.static.browserCache" value="false"/>
<!-- 当struts的配置文件修改后,系统是否自动重新加载该文件,默认值为false(生产环境下使用),开发阶段最好打开 -->
<constant name="struts.configuration.xml.reload" value="true"/>
<!-- 开发模式下使用,这样可以打印出更详细的错误信息 -->
<constant name="struts.devMode" value="true" />
<!-- 默认的视图主题 -->
<constant name="struts.ui.theme" value="simple" />
<!– 与spring集成时,指定由spring负责action对象的创建 -->
<constant name="struts.objectFactory" value="spring" />
<!–该属性设置Struts 2是否支持动态方法调用,该属性的默认值是true。如果需要关闭动态方法调用,则可设置该属性为false。 -->
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
<!--上传文件的大小限制(字节为单位)-->
<constant name="struts.multipart.maxSize" value=“10701096"/>
四、struts.xml通过<include>元素包含配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<include file="/com/hzb/xml/struts-user.xml"/>
<include file="struts-order.xml"/>
</struts>
五、配置文件说明
1、Action配置中的各项默认值
如果没有为action指定class,默认是ActionSupport。
如果没有为action指定method,默认执行action中的execute() 方法。
如果没有指定result的name属性,默认值为success。
2、Action中result的各种转发类型
<action name="helloworld" class="com.hzb.action.HelloWorldAction">
<result type= "dispatcher" name="success">/hello.jsp</result>
</action>
rstruts2中提供了多种结果类型,常用的类型有: dispatcher(默认值--forward)、 redirect 、 redirectAction 、 plainText。
在result中还可以使用${属性名}表达式访问action中的属性,表达式里的属性名对应action中的属性。如下:
<result type="redirect">/view.jsp?id=${id}</result>
下面是redirectAction 结果类型的例子,如果重定向的action中同一个包下:
<result type="redirectAction">helloworld</result>
如果重定向的action在别的命名空间下:
<result type="redirectAction">
<param name="actionName">helloworld</param>
<param name="namespace">/test</param>
</result>
plaintext:显示原始文件内容,例如:当我们需要原样显示jsp文件源代码 的时候,我们可以使用此类型。
<result name="source" type="plainText ">
<param name="location">/xxx.jsp</param>
<param name="charSet">UTF-8</param><!-- 指定读取文件的编码 -->
</result>
3、多个Action共享一个视图--全局result配置
<package ....>
<global-results>
<result name="message">/message.jsp</result>
</global-results>
</package>
4、为Action的属性注入值
Struts2为Action中的属性提供了依赖注入功能,在struts2的配置文件中,我们可以很方便地为Action中的属性注入值。注意:属性必须提供setter方法。
public class HelloWorldAction{
private String savePath;
public String getSavePath() {
return savePath;
}
public void setSavePath(String savePath) {
this.savePath = savePath;
}
}
<package name="hzb" namespace="/test" extends="struts-default">
<action name="helloworld" class="com.hzb.action.HelloWorldAction" >
<param name="savePath">/images</param>
<result name="success">/hello.jsp</result>
</action>
</package>
上面通过<param>节点为action的savePath属性注入“/images”
六、动态方法调用
如果Action中存在多个方法时,我们可以使用!+方法名调用指定方法。
public class HelloWorldAction{
private String message;
....
public String execute() throws Exception{
this.message = "我的第一个struts2应用";
return "success";
}
public String other() throws Exception{
this.message = "第二个方法";
return "success";
}
}
http://localhost:8080/struts2/test/helloworld!other.action
七、使用通配符定义 action
<package name="hzb" namespace="/test" extends="struts-default">
<action name="helloworld_*" class="com.hzb.action.HelloWorldAction" method="{1}">
<result name="success">/hello.jsp</result>
</action>
</package>
public class HelloWorldAction{
private String message;
....
public String execute() throws Exception{
this.message = "我的第一个struts2应用";
return "success";
}
public String other() throws Exception{
this.message = "第二个方法";
return "success";
}
}
要访问other()方法,可以通过这样的URL访问:/test/helloworld_other.action
八、自定义类型转换器
1、自定义类型转换器,即定义一个类继承DefaultTypeConverter
为了实现双向转换,判断toType的类型即可判断转换的方向。toType类型是需要转换的目标类型,当toType类型是User类型时,表明需要将字符串转换成对象实例;当toType类型是String类型时,表明需要把对象实例转换为字符串类型。
public class UserConverter extends DefaultTypeConverter{
public Object convertValue(Map context, Object value,Class toType) {
//当需要将字符串向User类型转换时,即浏览器请求->Action
if(toType == UserBean.class){
//系统的请求参数是一个字符串数组
String[] params = (String[]) value;
//创建一个User实例
UserBean user = new UserBean();
//只处理请求参数数组第一个数组元素,并将该字符串以英文逗号分割成两个字符串(因为客户端是user,password格式提交上来给user属性的)
String[] userValue = params[0].split(",");
//为user实例赋值
user.setName(userValue[0]);
user.setPassword(userValue[1]);
return user;
}
if(toType == String.class){
//将需要转换的值强制类型转换成User类型,即Action->浏览器
UserBean user = (UserBean) value;
return "<" +user.getName()+","+user.getPassword()+">";
}
return null;
}
}
2、注册类型转换器
若注册为局部类型转换器,则:
在Action类所在的包下放置ActionClassName-conversion.properties文件,ActionClassName是Action的类名(不含包名),后面的-conversion.properties是固定写法,在properties文件中的内容为:
属性名称=类型转换器的全类名
如例:UserLoginAction-conversion.properties文件中的内容为:
user= com.hzb.conversion.UserConverter
若将上面的类型转换器注册为全局类型转换器,则:
在WEB-INF/classes下放置xwork-conversion.properties文件 。在properties文件中的内容为:
待转换的类型=类型转换器的全类名
对于本例而言, xwork-conversion.properties文件中的内容为:
com.hzb.po.UserBean= com.hzb.conversion.UserConverter
九、HttpServletRequest / HttpSession / ServletContext / HttpServletResponse对象
获取方法:
<body>
${requestScope.req}<br>
${sessionScope.ses}<br>
${applicationScope.app} <br>
</body>
设值方法:
//方法一,通过ServletActionContext.类直接获取:
public String rsa() throws Exception{
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession() ;
ServletContext servletContext = ServletActionContext.getServletContext();
HttpServletResponse response = ServletActionContext.getResponse();
request.setAttribute("req","请求范围属性" );
session .setAttribute("ses", "会话范围属性");
servletContext.setAttribute("app", "应用范围属性");
return "scope";
}
//方法二,实现指定接口,由struts框架运行时注入(一般不用):
public class HelloWorldAction implements ServletRequestAware, ServletResponseAware, ServletContextAware{
private HttpServletRequest request;
private ServletContext servletContext;
private HttpServletResponse response;
public void setServletRequest(HttpServletRequest req) {
this.request=req;
}
public void setServletResponse(HttpServletResponse res) {
this.response=res;
}
public void setServletContext(ServletContext ser) {
this.servletContext=ser;
}
}
//方法三:通过ActionContext 类
public String scope() throws Exception{//这种方式比较好
ActionContext ctx = ActionContext.getContext();
ctx.getApplication().put("app", "应用范围");//往ServletContext里放入app
ctx.getSession().put("ses", "session范围");//往session里放入ses
ctx.put("req", "request范围");//往request里放入req
return "scope";
}
十、文件上传
<form enctype="multipart/form-data"
action="${pageContext.request.contextPath}/fileUpload.action" method="post">
<input type="file" name="uploadImage">
<input type = "submit" value="提交"/>
</form>
public class FileUploadAction extends ActionSupport {
private File uploadImage;// 得到上传的文件
private String uploadImageContentType;// 得到文件的类型
private String uploadImageFileName;// 得到文件的名称
public String execute() throws Exception {
String realpath = ServletActionContext.getServletContext().getRealPath(
"/images");
System.out.println(uploadImageContentType);
System.out.println(realpath);
System.out.println(uploadImageFileName);
File file = new File(realpath);
if (!file.exists())
file.mkdirs();
FileUtils.copyFile(uploadImage, new File(file, uploadImageFileName));
return "success";
}
public File getUploadImage() {
return uploadImage;
}
public void setUploadImage(File uploadImage) {
this.uploadImage = uploadImage;
}
public String getUploadImageContentType() {
return uploadImageContentType;
}
public void setUploadImageContentType(String uploadImageContentType) {
this.uploadImageContentType = uploadImageContentType;
}
public String getUploadImageFileName() {
return uploadImageFileName;
}
public void setUploadImageFileName(String uploadImageFileName) {
this.uploadImageFileName = uploadImageFileName;
}
}
十一、多文件上传
<form enctype="multipart/form-data" action="${pageContext.request.contextPath}/fileUpload.action" method="post">
<input type="file" name="uploadImages">
<input type="file" name="uploadImages">
<input type = "submit" value="提交"/>
</form>
private File[] uploadImages;// 得到上传的文件
private String[] uploadImagesContentType;//得到文件的类型
private String[] uploadImagesFileName;//得到文件的名称
public String execute() throws Exception {
String realpath = ServletActionContext.getServletContext().getRealPath("/images");
File file = new File(realpath);
if(!file.exists()) file.mkdirs();
for(int i=0 ;i<uploadImages.length; i++){
File uploadImage = uploadImages[i];
FileUtils.copyFile(uploadImage, new File(file, uploadImagesFileName[i]));
}
return "success";
}
十二、自定义拦截器
1、<http://localhost:8080/struts2/user.jsp> 表示用户已登陆,存放session对象
2、<http://localhost:8080/struts2/quit.jsp> 表示用户已退出,移除session对象
3、<http://localhost:8080/struts2/login/addUIHelloWorld.action> 如果session存在则往下执行,否则提示“你没有权限执行该操作”
4、<http://localhost:8080/struts2/login/executeHelloWorld.action> 如果session存在则往下执行,否则提示“你没有权限执行该操作”
代码
user.jsp页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
request.getSession().setAttribute("user", "hzb");
%>
用户已经登录
quit.jsp页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
request.getSession().removeAttribute("user");
%>
用户已经退出登录
Action类
package com.hzb.action;
public class HelloWorldAction {
private String message;
public String addUI() {
this.message = "addUI";
return "success";
}
public String execute() throws Exception {
this.message = "execute";
return "success";
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
权限类
package com.hzb.interceptor;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
/**
* 限制没有登录的用户进入访问页面
*/
public class PermissionInterceptor implements Interceptor {
public void destroy() {
}
public void init() {
}
public String intercept(ActionInvocation invocation) throws Exception {
Object user = ActionContext.getContext().getSession().get("user");
// 如果user不为null,代表用户已经登录,允许执行action中的方法
if (user != null){
return invocation.invoke();
}
ActionContext.getContext().put("msg", "你没有权限执行该操作");
return "success";
}
}
struts.xml
配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="login" namespace="/login" extends="struts-default">
<interceptors>
<!-- 自定义拦截器 -->
<interceptor name="permission" class="com.hzb.interceptor.PermissionInterceptor"/>
<!-- 配制默许的拦截器到拦截器栈 -->
<interceptor-stack name="permissionStack">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="permission" />
</interceptor-stack>
</interceptors>
<!-- 配置默认的拦截器 -->
<default-interceptor-ref name="permissionStack" />
<!-- 全局变量 -->
<global-results>
<result name="success">/WEB-INF/page/message.jsp</result>
</global-results>
<action name="helloWorld_*" class="com.hzb.action.HelloWorldAction"
method="{1}">
<!-- <interceptor-ref name="permissionStack" /> -->
</action>
</package>
</struts>
因为struts2中如文件上传,数据验证,封装请求参数到action等功能都是由系统默认的defaultStack中的拦截器实现的,所以我们定义的拦截器需要引用系统默认的defaultStack,这样应用才可以使用struts2框架提供的众多功能。
如果希望包下的所有action都使用自定义的拦截器,可以通过<default-interceptor-ref name=“permissionStack”/>把拦截器定义为默认拦截器。注意:每个包只能指定一个默认拦截器。另外,一旦我们为该包中的某个action显式指定了某个拦截器,则默认拦截器不会起作用。
十三、输入校验
输入校验的流程:
1。类型转换器对请求参数执行类型转换,并把转换后的值赋给action中的属性。
2。如果在执行类型转换的过程中出现异常,系统会将异常信息保存到ActionContext,conversionError拦截器将异常信息添加到fieldErrors里。不管类型转换是否出现异常,都会进入第3步。
3。系统通过反射技术先调用action中的validateXxx()方法,Xxx为方法名。
4。再调用action中的validate()方法。
5。经过上面4步,如果系统中的fieldErrors存在错误信息(即存放错误信息的集合的size大于0),系统自动将请求转发至名称为input的视图。如果系统中的fieldErrors没有任何错误信息,系统将执行action中的处理方法。
1. 采用手工编写代码实现。
通过重写validate() 方法实现, validate()方法会校验action中execute方法。当某个数据校验失败时,我们应该调用addFieldError()方法往系统的fieldErrors添加校验失败信息(为了使用addFieldError()方法,action可以继承ActionSupport ),如果系统的fieldErrors包含失败信息,struts2会将请求转发到名为input的result。在input视图中可以通过<s:fielderror/>显示失败信息。
public void validate() {
if(this.mobile==null || "".equals(this.mobile.trim())){
this.addFieldError("username", "手机号不能为空");
}else{
if(!Pattern.compile("^1[358]\\d{9}").matcher(this.mobile.trim()).matches()){
this.addFieldError(“mobile", "手机号的格式不正确");
}
}
}
//验证失败后,请求转发至input视图:
<result name="input">/addUser.jsp</result>
在addUser.jsp页面中使用<s:fielderror/>显示失败信息。
validateXxx()方法:validateXxx()只会校验action中方法名为Xxx的方法。其中Xxx的第一个字母要大写。
validateXxx()方法使用例子:
public String add() throws Exception{
return "success";
}
public void validateAdd(){
if(username==null && "".equals(username.trim()))
this.addFieldError("username", "用户名不能为空");
}
假设现在有多个业务逻辑方法,应该针对每个业务逻辑进行校验。但现在只有一个validate方法,不可能把所有业务逻辑的验证都放在validate方法里。所以需要针对每个业务逻辑方法增加一个validateXxx()方法。这里要特别注意的是,如果action中重写了validate方法,那么validate方法一定会被调用(参看上面的执行流程)。
以newReport为例,action方法的调用顺序如下:validateNewReport()->validate()->newReport
因此在这种情况下,不要重写validate方法。但是这有牵涉到一个问题,如果不写validate方法,那execute方法如何校验呢?按照上面的规则,只要编写一个validateExecute方法进行校验就可以了。
2、基于XML配置方式实现
使用基于XML配置方式实现输入校验时,Action也需要继承ActionSupport,并且提供校验文件,校验文件和action类放在同一个包下,文件的取名格式为:ActionClassName-validation.xml,其中ActionClassName为action的简单类名,-validation为固定写法。如果Action类为com.hzb.UserAction,那么该文件的取名应为:UserAction-validation.xml。下面是校验文件的模版:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.3//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">
<validators>
<field name="username">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>用户名不能为空!</message>
</field-validator>
</field>
</validators>
<field>指定action中要校验的属性,<field-validator>指定校验器
系统提供的校验器如下:
required (必填校验器,要求field的值不能为null)
requiredstring (必填字符串校验器,要求field的值不能为null,并且长度大于0,默认情况下会对字符串去前后空格)
stringlength(字符串长度校验器,要求field的值必须在指定的范围内,否则校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field之前是否去除字符串前后的空格)
regex(正则表达式校验器,检查被校验的field是否匹配一个正则表达式.expression参数指定正则表达式,caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true)
int(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)
double(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)
fieldexpression(字段OGNL表达式校验器,要求field满足一个ognl表达式,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过)
email(邮件地址校验器,要求如果field的值非空,则必须是合法的邮件地址)
url(网址校验器,要求如果field的值非空,则必须是合法的url地址)
date(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)
conversion(转换校验器,指定在类型转换失败时,提示的错误信息)
visitor(用于校验action中的复合属性,它指定一个校验文件用于校验复合属性中的属性)
expression(OGNL表达式校验器,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过,该校验器不可用在字段校验器风格的配置中)
required 必填校验器
<field-validator type="required">
<message>性别不能为空!</message>
</field-validator>
requiredstring 必填字符串校验器
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>用户名不能为空!</message>
</field-validator>
stringlength:字符串长度校验器
<field-validator type="stringlength">
<param name="maxLength">10</param>
<param name="minLength">2</param>
<param name="trim">true</param>
<message><![CDATA[产品名称应在2-10个字符之间]]></message>
</field-validator>
email:邮件地址校验器
<field-validator type="email">
<message>电子邮件地址无效</message>
</field-validator>
regex:正则表达式校验器
<field-validator type="regex">
<param name="expression"><![CDATA[^1[358]\d{9}$]]></param>
<message>手机号格式不正确!</message>
</field-validator>
int:整数校验器
<field-validator type="int">
<param name="min">1</param>
<param name="max">150</param>
<message>年龄必须在1-150之间</message>
</field-validator>
字段OGNL表达式校验器
<field name="imagefile">
<field-validator type="fieldexpression">
<param name="expression"><![CDATA[imagefile.length() <= 0]]></param>
<message>文件不能为空</message>
</field-validator>
</field>
当校验文件的取名为ActionClassName-validation.xml时,会对 action中的所有处理方法实施输入验证。如果你只需要对action中的某个action方法实施校验,那么,校验文件的取名应为:ActionClassName-ActionName-validation.xml,其中ActionName为struts.xml中action的名称。例如:在实际应用中,常有以下配置:
<action name="user_*" class="com.hzb.action.UserAction" method="{1}“ >
<result name="success">/WEB-INF/page/message.jsp</result>
<result name="input">/WEB-INF/page/addUser.jsp</result>
</action>
UserAction中有以下两个处理方法:
public String add() throws Exception{
}
public String update() throws Exception{
}
要对add()方法实施验证,校验文件的取名为: UserAction-user_add-validation.xml
要对update()方法实施验证,校验文件的取名为: UserAction-user_update-validation.xml
当为某个action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml两种规则的校验文件时,系统按下面顺序寻找校验文件:
1。AconClassName-validation.xml
2。ActionClassName-ActionName-validation.xml
系统寻找到第一个校验文件时还会继续搜索后面的校验文件,当搜索到所有校验文件时,会把校验文件里的所有校验规则汇总,然后全部应用于action方法的校验。如果两个校验文件中指定的校验规则冲突,则只使用后面文件中的校验规则。
当action继承了另一个action,父类action的校验文件会先被搜索到。
十四、OGNL与struts2标签1、ognl的结构
ActionContext(ValueStack、parameters、request、session、application、attr)
当struts2接受一个请求时,会迅速创建ActionContext、ValueStack、action的实例,然后把action实例存入ValueStack中,所以action的实例变量可以被ognl直接访问。
ValueStack中的对象不用加#可以直接访问;如果访问其他Context中的对象,由于他们不是根对象,所以在访问时,需要添加#前缀。
application对象:用于访问ServletContext,例如#application.userName或者#application['userName'],相当于调用ServletContext的getAttribute("username")。
session对象:用来访问HttpSession,例如#session.userName或者#session['userName'],相当于调用session.getAttribute("userName")。
request对象:用来访问HttpServletRequest,例如#request.userName或者#request['userName'],相当于调用request.getAttribute("userName")。
parameters对象:用于访问HTTP的请求参数,例如#parameters.userName或者#parameters['userName'],相当于调用request.getParameter("username")。
attr对象:用于按page->request->session->application顺序访问其属性。
2、采用OGNL表达式创建List/Map集合对象:
Set标签用于将某个值放入指定范围。
scope:指定变量被放置的范围,该属性可以接受application、session、request、 page或action。如果没有设置该属性,则默认放置在OGNL Context(不是值栈)中。
value:赋给变量的值.如果没有设置该属性,则将ValueStack栈顶的值赋给变量。
使用如下代码直接生成一个List对象:
<s:set name="list" value="{'zhangming','xiaoi','liming'}" scope="request"/>
//遍历的对象会被放入值栈的栈顶
<s:iterator value="#list">
<s:property/><br>
</s:iterator>
生成一个Map对象:
<s:set name="foobar" value="#{'foo1':'bar1', 'foo2':'bar2'}" scope="request"/>
<s:iterator value="#foobar" >
<s:property value="key"/>=<s:property value="value"/><br>
</s:iterator>
3、OGNL表达式的投影功能
?:获得所有符合逻辑的元素。
^:获得符合逻辑的第一个元素。
$:获得符合逻辑的最后一个元素。
例如代码:
<s:iterator value="books.{?#this.price > 35}">//遍历的对象会被放入值栈的栈顶
<s:property value="title" /> - $<s:property value="price" /><br>
</s:iterator>
在上面代码中,直接在集合后紧跟.{}运算符表明用于取出该集合的子集,{}内的表达式用于获取符合条件的元素,this指的是为了从大集合books筛选数据到小集合,需要对大集合books进行迭代,this代表当前迭代的元素。本例的表达式用于获取集合中价格大于35的书集合。
public class BookAction extends ActionSupport {
private List<Book> books;
public String execute() {
books = new LinkedList<Book>();
books.add(new Book("A735619678", "spring", 67));
books.add(new Book("B435555322", "ejb3.0",15));
}
}
4、ognl中“%”、“#”、“$”三个符号的使用
1)“#”符号有三种用途:
(1)、访问非根对象(struts中值栈为根对象)
(2)、用于过滤和投影集合books.{?#this.price>35} , #books.{?#this.price>35}(如果books不在值栈范围要加#)
(3)、构造Map, #{'foo1':'bar1', 'foo2':'bar2'}
2)“%”符号的用途是在标签的属性值被理解为字符串类型时,告诉执行环境%{}里的是OGNL表达式。
<s:set name="myMap" value="#{'key1':'value1','key2':'value2'}"/>
<s:property value="#myMap['key1']"/>
<s:url value="#myMap['key1']" />
<s:set name="myMap" value="#{'key1':'value1','key2':'value2'}"/>
<s:property value="#myMap['key1']"/>
<s:url value="#myMap['key1']"/>
上面的代码第2行会在页面上输出“value1”,而第3行则会输出"#myMap['key1']"这么一个字符串。 如果将第3行改写成这样:
<s:url value="%{#myMap['key1']}"/>
则输出为“value1”。
这说明struts2里不同的标签对ognl的表达式的理解是不一样的。如果当有的标签“看不懂”类似“#myMap['key1']”的语句时,就要用%{}来把这括进去,“翻译”一下了。
一般像property,if test,iterator这种类型的可以看懂,而hidden,input,url这种表单类型的不能看懂。
3)、“$”有两种用途
(1)、在国际化资源文件中,引用OGNL表达式。
(2)、在Struts 2配置文件中,引用OGNL表达式:
<action name="saveUser" class="userAction" method="save">
<result type="redirect">listUser.action?msg=${msg}</result>
</action>
1)加入struts2标签:<%@ taglib uri="/struts-tags" prefix="s"%>
2)iterator标签:用于对集合进行迭代,这里的集合包含List、Set和数组。
<s:set name="list" value="{'zhangming','xiaoi','liming'}" />
<s:iterator value="#list" status="st">
<font color=<s:if test="#st.odd">red</s:if><s:else>blue</s:else>>
<s:property />
</font>
<br>
</s:iterator>
value:可选属性,指定被迭代的集合,如果没有设置该属性,则使用ValueStack栈顶的集合。
id:可选属性,指定集合里元素的id。
status:可选属性,该属性指定迭代时的IteratorStatus实例。
该实例包含如下几个方法:
int getCount(),返回当前迭代了几个元素。
int getIndex(), 返回当前迭代元素的索引。
boolean isEven(),返回当前被迭代元素的索引是否是偶数
boolean isOdd(),返回当前被迭代元素的索引是否是奇数
boolean isFirst(),返回当前被迭代元素是否是第一个元素。
boolean isLast(),返回当前被迭代元素是否是最后一个元素。
3)date日期标签:<
s:date
name
=
"#request.time"
format
=
"yyyy-MM-dd日"
/>