我是靠谱客的博主 朴实小刺猬,这篇文章主要介绍JAVAWEB开发之Struts2详解(二)——Action接受请求参数、类型转换器、使用Struts2的输入校验、以及遵守约定规则实现Struts2的零配置...,现在分享给大家,希望可以做个参考。

Action接受请求参数

作为MVC框架,必须要负责解析HTTP请求参数,并将其封装到Model对象中
Struts2提供了非常强大的类型转换机制用于请求数据 到 model对象的封装
Struts2和MVC定义关系
StrutsPrepareAndExecuteFilter:控制器
在Struts2中action是什么?(Struts2是一个MVC框架)
V:jsp
M:action
C:action StrutsPrepareAndExecuteFilter

Struts2提供了三种数据封装方式:
  • Action本身作为model对象,通过成员setter封装。(属于属性驱动)
  • 创建独立model对象,页面通过ognl表达式封装。(属于属性驱动)
  • 使用ModelDrivern接口,对请求数据进行封装。(属于模型驱动)
具体使用如下:

1.属性驱动

属性驱动方式(一):直接将action作为一个model,就可以得到请求参数
action类成员变量setter接收参数如下图所示:

问题1:action封装请求参数,会不会存在线程安全问题?
是不会的,因为每一次请求,都是一个新的action
优点:使用简单
缺点:需要单独定义JavaBean,将action中属性copy到JavaBean中(不能将Action作为model传给service层)
这种方式,底层是通过反射进行实现的。

属性驱动方式(二):创建一个单独的Model类,在action类中引用model作为成员变量。(页面使用ognl)
具体操作:
在action类中声明一个mdoel,private User user;
提供对应的setter和getter方法。
在页面上使用ognl来进行描述
<input type="text" name="user.username"> 如下图所示:

这种方式的优点:简单易使用,解决了第一种封装的问题。
缺点:在页面上使用了ognl表达式,页面不通用了。
问题:这种方式,数据是怎样封装的?
是通过Struts2中name为params的interceptor拦截器进行的数据封装(Struts的核心core包下struts-default.xml中定义)<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>

2.模型驱动(在开发中应用比较多)

模型驱动的步骤:
1.让action 类实现ModelDrivern接口。
2.重写getModel方法
3.在action中实例化一个model对象,让getModel方法返回这个对象。
public class Login3Action extends ActionSupport implements ModelDriven<User> {
private User user = new User();
public User getModel() {
return user;
}

如下图所示:

优点:解决了属性驱动存在的问题
缺点:一次只能封装一个model对象
Struts2有很多围绕模型驱动的属性(在struts-default.xml中的拦截器中定义)
<interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
扩展:
(1)将数据封装到List集合。类型转换与Collection配合使用。
Struts2还允许填充Collection里的对象,这常见于需要快速录入批量数据的场合。

页面:
username1:<input type="text" name="users[0].username"><br>
password1:<input type="password" name="users[0].password"><br>
username2:<input type="text" name="users[1].username"><br>
password2:<input type="password" name="users[1].password"><br>

action类:
private List<User> users;
get/set方法
(2)将数据封装到Map集合(类型转换与Map配合使用)

页面:
username1:<input type="text" name="map['aaa'].username"><br>
password1:<input type="password" name="map['aaa'].password"><br>
username2:<input type="text" name="map['bbb'].username"><br>
password2:<input type="password" name="map['bbb'].password"><br>

action类:
private Map<String, User> map;
提供get/set
示例:
1.将action作为model(属性驱动方式一)
Login1Action
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package cn.itcast.action; import javax.servlet.http.HttpServletRequest; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; // 获取请求参数 属性驱动 第一种,直接将action作为model public class Login1Action extends ActionSupport { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String execute() throws Exception { HttpServletRequest request = ServletActionContext.getRequest(); // 2.判断用户名与密码是否正确 if ("tom".equals(username) && "123".equals(password)) { request.getSession().setAttribute("username", username); return SUCCESS; } else { request.setAttribute("login.message", "用户名或密码错误"); return "failer"; } } }
login1.jsp
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 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> ${requestScope["login.message"]} <br> <form action="${pageContext.request.contextPath}/login1" method="POST"> username:<input type="text" name="username"><br> password:<input type="password" name="password"><br> <input type="submit" value="登录"> </form> </body> </html>
2.将model对象作为Action类成员(属性驱动模式二)
封装Model类
User
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package cn.itcast.domain; public class User { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User [username=" + username + ", password=" + password + "]"; } }

Login2Action
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package cn.itcast.action; import javax.servlet.http.HttpServletRequest; import org.apache.struts2.ServletActionContext; import cn.itcast.domain.User; import com.opensymphony.xwork2.ActionSupport; // 获取请求参数 属性驱动 第二种,直接在action上声明一个model public class Login2Action extends ActionSupport { private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public String execute() throws Exception { HttpServletRequest request = ServletActionContext.getRequest(); // 2.判断用户名与密码是否正确 if ("tom".equals(user.getUsername()) && "123".equals(user.getPassword())) { request.getSession().setAttribute("username", user.getUsername()); return SUCCESS; } else { request.setAttribute("login.message", "用户名或密码错误"); return "failer"; } } }
login2.jsp
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 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> ${requestScope["login.message"]} <br> <form action="${pageContext.request.contextPath}/login2" method="POST"> username:<input type="text" name="user.username"><br> password:<input type="password" name="user.password"><br> <input type="submit" value="登录"> </form> </body> </html>
3.模型驱动(action类实现ModelDrivern接口)
Login3Action
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package cn.itcast.action; import javax.servlet.http.HttpServletRequest; import org.apache.struts2.ServletActionContext; import cn.itcast.domain.User; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; // 获取请求参数 模型驱动 public class Login3Action extends ActionSupport implements ModelDriven<User> { private User user=new User(); @Override public User getModel() { return user; } @Override public String execute() throws Exception { HttpServletRequest request = ServletActionContext.getRequest(); // 2.判断用户名与密码是否正确 if ("tom".equals(user.getUsername()) && "123".equals(user.getPassword())) { request.getSession().setAttribute("username", user.getUsername()); return SUCCESS; } else { request.setAttribute("login.message", "用户名或密码错误"); return "failer"; } } }
login3.jsp
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 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> ${requestScope["login.message"]} <br> <form action="${pageContext.request.contextPath}/login3" method="POST"> username:<input type="text" name="username"><br> password:<input type="password" name="password"><br> <input type="submit" value="登录"> </form> </body> </html>

success.jsp
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 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> ${username } </body> </html>

4.将数据封装到List集合
list.jsp
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 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> <form action="${pageContext.request.contextPath}/list" method="POST"> username:<input type="text" name="users[0].username"><br> password:<input type="password" name="users[0].password"><br> username:<input type="text" name="users[1].username"><br> password:<input type="password" name="users[1].password"><br> <input type="submit" value="登录"> </form> </body> </html>
ListAction
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package cn.itcast.action; import java.util.List; import cn.itcast.domain.User; import com.opensymphony.xwork2.ActionSupport; public class ListAction extends ActionSupport { private List<User> users; public List<User> getUsers() { return users; } public void setUsers(List<User> users) { this.users = users; } @Override public String execute() throws Exception { System.out.println(users); return null; } }
5.将数据封装到Map集合
map.jsp
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 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> <form action="${pageContext.request.contextPath}/map" method="POST"> username:<input type="text" name="map['aaa'].username"><br> password:<input type="password" name="map['aaa'].password"><br> username:<input type="text" name="map['bbb'].username"><br> password:<input type="password" name="map['bbb'].password"><br> <input type="submit" value="登录"> </form> </body> </html>
MapAction
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package cn.itcast.action; import java.util.Map; import cn.itcast.domain.User; import com.opensymphony.xwork2.ActionSupport; public class MapAction extends ActionSupport { private Map<String, User> map; public Map<String, User> getMap() { return map; } public void setMap(Map<String, User> map) { this.map = map; } @Override public String execute() throws Exception { System.out.println(map); return null; } }
配置struts.xml
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="default" namespace="/" extends="struts-default"> <action name="login1" class="cn.itcast.action.Login1Action"> <result name="failer">/login1.jsp</result> <result type="redirect">/success.jsp</result> </action> <action name="login2" class="cn.itcast.action.Login2Action"> <result name="failer">/login2.jsp</result> <result type="redirect">/success.jsp</result> </action> <action name="login3" class="cn.itcast.action.Login3Action"> <result name="failer">/login3.jsp</result> <result type="redirect">/success.jsp</result> </action> <action name="list" class="cn.itcast.action.ListAction"></action> <action name="map" class="cn.itcast.action.MapAction"></action> </package> </struts>

验证属性驱动方式一:

验证属性驱动方式二:


验证模型驱动:

验证封装数据到List集合

验证封装数据到Map集合


2自定义类深入剖析属性驱动模式一和模型驱动

首先导入DOM4J、XPATH、BeanUtils的相关jar包

在src下新建struts.xml
新建自定义的ModelDrivern接口MyModelDrivern
复制代码
1
2
3
4
5
package cn.itcast; public interface MyModelDriven<T> { public T getModel(); }
创建User类(和上面的一样)
......
创建Action类
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package cn.itcast.action; import cn.itcast.MyModelDriven; import cn.itcast.User; public class HelloAction implements MyModelDriven<User> { // 剖析模型驱动 private User user = new User(); public User getModel() { return user; } /* 属性驱动模式一 private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } */ public String say() { System.out.println(user.getUsername() + " " + user.getPassword()); return "good"; } }
自定义拦截器(模仿Struts中的params拦截器)
StrutsFilter
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package cn.itcast.filter; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.beanutils.BeanUtils; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import cn.itcast.MyModelDriven; public class StrutsFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { // 1.强转 HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; // 2.操作 // 2.1 得到请求资源路径 String uri = request.getRequestURI(); String contextPath = request.getContextPath(); String path = uri.substring(contextPath.length() + 1); // System.out.println(path); // hello // 2.2 使用path去struts.xml文件中查找某一个<action name=path>这个标签 SAXReader reader = new SAXReader(); try { // 得到struts.xml文件的document对象。 Document document = reader.read(new File(this.getClass() .getResource("/struts.xml").getPath())); Element actionElement = (Element) document .selectSingleNode("//action[@name='" + path + "']"); // 查找<action // name='hello'>这样的标签 if (actionElement != null) { // 得到<action>标签上的class属性以及method属性 String className = actionElement.attributeValue("class"); // 得到了action类的名称 String methodName = actionElement.attributeValue("method");// 得到action类中的方法名称。 // 2.3通过反射,得到Class对象,得到Method对象 Class actionClass = Class.forName(className); Method method = actionClass.getDeclaredMethod(methodName); // 处理请求参数封装: Object actionObj = actionClass.newInstance(); // 2.模型驱动 if (actionObj instanceof MyModelDriven) { MyModelDriven mmd = (MyModelDriven) actionObj; BeanUtils.populate(mmd.getModel(), request.getParameterMap()); } else { // 1.属性驱动 BeanUtils.populate(actionObj, request.getParameterMap());// } // 2.4 让method执行. String returnValue = (String) method.invoke(actionObj); // 是让action类中的方法执行,并获取方法的返回值。 // 2.5 // 使用returnValue去action下查找其子元素result的name属性值,与returnValue做对比。 Element resultElement = actionElement.element("result"); String nameValue = resultElement.attributeValue("name"); if (returnValue.equals(nameValue)) { // 2.6得到了要跳转的路径。 String skipPath = resultElement.getText(); // System.out.println(skipPath); request.getRequestDispatcher(skipPath).forward(request, response); return; } } } catch (DocumentException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 3.放行 chain.doFilter(request, response); } public void destroy() { } }
编辑自定义的struts.xml
复制代码
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8" ?> <struts> <action name="hello" class="cn.itcast.action.HelloAction" method="say"> <result name="good">/hello.jsp</result> </action> </struts>
在web.xml中配置拦截器
创建jsp进行测试:
hello.jsp
复制代码
1
2
3
4
5
6
7
8
9
10
11
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'index.jsp' starting page</title> </head> <body> <h1>hello Struts2</h1> </body> </html>
index.jsp
复制代码
1
2
3
4
5
6
7
8
9
10
11
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'index.jsp' starting page</title> </head> <body> <a href="${pageContext.request.contextPath}/hello?username=tome&password=123">第一次使用struts2</a> </body> </html>

测试结果如下:

Struts2内置的类型转换器

类型转换:即在web中 使用Servlet时,使用BeanUtils直接将表单数据封装到JavaBean中。
对于大部分常用类型,开发者根本无需创建自己的转换器,Struts2内置了常见数据类型的多种转换器。
  • boolean和Boolean
  • char和Character
  • int和Integer
  • long和Long
  • float和Float
  • double和Double
  • Date可以接收yyyy-MM-dd格式字符串(浏览器语言首选项为中文时)
  • 数组 可以将多个同名参数转换到数组中
  • 集合 支持将数据保存到List或者Map集合
例如:日期类型,我们传递yyyy-MM-dd yyyy年MM月dd日 格式都可以,但是如果是yyyy/MM/dd就会出现问题。
关于Struts2中的类型转换器:
Struts2中的类型转换器根接口是:com.opensymphony.xwork.conversion.TypeConverter

自定义类型转换器

Struts2提供常规类型转换器,可以实现常用类型的转换,但是如果目标类型是一个特殊类型,则需要自定义转换器
Struts2 类型转换器实际上都是基于OGNL实现的,在OGNL项目中,有一个TypeConverter接口
自定义类型转换器必须实现 ongl.TypeConverter接口

相关的关键源代码如下:
TypeConverter接口关键源代码:

DefaultTypeConverter类实现了TypeConverter接口:


StrutsTypeConverter直接继承自DefaultTypeConverter:

自定义转换器的的步骤:
1.创建一个类实现TypeConverter接口
2.重写接口中方法,实现类型转换操作
3.注册类型转换器
详细说明:
1.创建一个自定义类型转换器(有三种方式)
方式一:实现TypeConverter需要重写
public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName, Object value, Class toType);
如果实现接口,这个方法参数太多(6个)
方式二:不推荐实现接口,可以继承DefaultTypeConverter类
优点:重写的方法参数没有那么多
public Object convertValue(Map<String, Object> context, Object value, Class toType) {
return convertValue(value, toType);
}
方式三:(推荐使用)继承DefaultTypeConverter类的一个子类StrutsTypeConverter
public abstract Object convertFromString(Map context, String[] values, Class toClass);——请求封装
public abstract String convertToString(Map context, Object o);——数据回显
类型转换器一直都是双向转换:
页面提交的请求参数,封装到model——需要转换
model数据 需要在页面上回显——需要转换
2.怎样注册一个自定义类型转换器
注册方式有三种类型:
方式一:局部—针对于action
配置文件所在的位置及名称:在Action类所在包下,创建 Action类名-conversion.properties
配置文件书写:格式 属性名称=类型转换器的全类名
方式二:局部—针对于Model(属性驱动模式二)
配置文件所在位置以及名称:在Model类所在包,创建 Model类名-conversion.properties
配置文件书写:格式 属性名称=类型转换器的全类名
方式三:全局
配置文件所在位置以及名称:在src下创建一个 xwork-conversion.properties
配置文件书写 格式:要转换的类型全名=类型转换器的全类名

注意:
对于struts2中类型转换器,如果表单数据提交时,将数据向model封装,出现了问题,会报错:
No result defined for action cn.itcast.action.RegistAction and result input
上面的意思是说,在RegistAction的配置中没有配置input结果视图.
<action name="regist" class="cn.itcast.action.RegistAction">
<result name="input">/success.jsp</result>
</action>
如果配置了,出现类型转换问题,就会跳转到input指定的视图。

问题:为什么会向input视图跳转?

是因为struts2中的拦截器(interceptor).
在struts2中的conversionError拦截器用于记录类型转换问题
<interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
在struts2中的workflow拦截器用于得到问题,向input视图跳转。
<interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>

类型转换中的错误处理:
  • Struts2提供了一个名为conversionError的拦截器,查看struts-default.xml
  • <interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
  • 如果Struts2的类型转换器执行类型转换时出现错误,该拦截器将负责将对应错误封装成表单域错误(FieldError),并将这些错误信息放入ActionContext中
  • 使用类型转换中的错误处理用户定义Action必须继承ActionSupport
  • 在自定义类型转换器中,异常必须抛出不能捕获,conversionError会处理该异常,然后转入名为input的逻辑视图
  • 在Action所在包中,创建 ActionName.properties,在局部资源文件中配置提示信息 :invalid.fieldvalue.属性名= 错误信息
  • 在input逻辑视图所对应jsp页面中,通过<s:fielderror/> 输出类型转换信息
程序示例如下:


首先新建自定义转换类MyDateConverter (通过继承自DefaultTypeConverter)
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package cn.itcast.utils; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import ognl.DefaultTypeConverter; public class MyDateConverter extends DefaultTypeConverter { @Override public Object convertValue(Map context, Object value, Class toType) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd"); try { if (toType == Date.class) {// 当字符串向Date类型转换时 String[] params = (String[]) value; return dateFormat.parse(params[0]); } else if (toType == String.class) {// 当Date转换成字符串时 Date date = (Date) value; return dateFormat.format(date); } } catch (ParseException e) { throw new RuntimeException("日期转换错误"); } return null; } }
再新建一个日期转换类(使用另外一种方式继承自StrutsTypeConverter)
MyDateConverter2
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package cn.itcast.utils; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import org.apache.struts2.util.StrutsTypeConverter; public class MyDateConverter2 extends StrutsTypeConverter { // 接收页面传递的数据封装到JavaBean @Override public Object convertFromString(Map context, String[] values, Class toClass) { String value = values[0]; SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); Date date = null; try { date = sdf.parse(value); } catch (ParseException e) { // e.printStackTrace(); throw new RuntimeException(); } return date; } @Override public String convertToString(Map context, Object o) { return null; } }
具体代码如下:
RegistAction
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package cn.itcast.action1; import cn.itcast.domain.User; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class RegistAction extends ActionSupport implements ModelDriven<User> { private User user = new User(); @Override public User getModel() { return user; } @Override public String execute() throws Exception { System.out.println(user); return null; } }

Regist2Action
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package cn.itcast.action2; import java.util.Arrays; import java.util.Date; import com.opensymphony.xwork2.ActionSupport; public class Regist2Action extends ActionSupport { private String username; private String password; private int age; private Date birthday; private String[] hobby; @Override public String toString() { return "Regist2Action [username=" + username + ", password=" + password + ", age=" + age + ", birthday=" + birthday + ", hobby=" + Arrays.toString(hobby) + "]"; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String[] getHobby() { return hobby; } public void setHobby(String[] hobby) { this.hobby = hobby; } @Override public String execute() throws Exception { System.out.println(this); return null; } }

Regist2Action-conversion.properties
复制代码
1
2
birthday=cn.itcast.utils.MyDateConverter2
User
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package cn.itcast.domain; import java.util.Arrays; import java.util.Date; public class User { private String username; private String password; private int age; private Date birthday; private String[] hobby; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String[] getHobby() { return hobby; } public void setHobby(String[] hobby) { this.hobby = hobby; } @Override public String toString() { return "User [username=" + username + ", password=" + password + ", age=" + age + ", birthday=" + birthday + ", hobby=" + Arrays.toString(hobby) + "]"; } }
User-conversion.properties
复制代码
1
2
birthday=cn.itcast.utils.MyDateConverter

struts.xml
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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="default" namespace="/" extends="struts-default"> <action name="regist" class="cn.itcast.action1.RegistAction"> <result name="input">/success.jsp</result> </action> <action name="regist2" class="cn.itcast.action2.Regist2Action"> <result name="input">/success.jsp</result> </action> </package> </struts>

regist.jsp
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 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> <form action="${pageContext.request.contextPath}/regist" method="POST"> username:<input type="text" name="username"><br> password:<input type="password" name="password"><br> hobby:<input type="checkbox" name="hobby" value="eat">吃 <input type="checkbox" name="hobby" value="drink">喝 <input type="checkbox" name="hobby" value="play">玩<br> age:<input type="text" name="age"><br> birthday:<input type="text" name="birthday"><br> <input type="submit" value="注册"> </form> </body> </html>
regist2.jsp
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 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> <form action="${pageContext.request.contextPath}/regist2" method="POST"> username:<input type="text" name="username"><br> password:<input type="password" name="password"><br> hobby:<input type="checkbox" name="hobby" value="eat">吃 <input type="checkbox" name="hobby" value="drink">喝 <input type="checkbox" name="hobby" value="play">玩<br> age:<input type="text" name="age"><br> birthday:<input type="text" name="birthday"><br> <input type="submit" value="注册"> </form> </body> </html>
success.jsp
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Insert title here</title> </head> <body> <s:fielderror/> </body> </html>



Struts2的输入校验

校验的分类 : 客户端数据校验 和 服务器端数据校验
客户端数据校验 ,通过JavaScript 完成校验 (改善用户体验,使用户减少出错 )
服务器数据校验 ,使用框架内置校验功能 (struts2 内置校验功能 ) ----- 必须的

struts2 支持校验方式
代码校验 :在服务器端通过编写java代码,完成数据校验
配置校验 :XML配置校验(主流) 和 注解配置校验

代码校验(手动校验)了解

要求:action类必须继承自ActionSupport。需要重写一个validate方法
通过测试发现在action中的validate方法执行了。并且是在请求处理方法(execute)之前执行的。
对于struts2提供的校验,它也是通过拦截器实现的。

问题1:在validate方法中怎样存储错误校验信息?
在validate方法中 this.addFieldError(String name,String value);
问题2:在页面上怎样获取错误信息?(在Input视图上)
<s:fielderror> 展示所有的错误信息
<s:fielderror fieldName=""> 展示特定名称的错误信息
问题3:在同一个Action中有多个请求处理方法(login,regist) ,那么有些方法是需要校验的,有些是不需要的,如何处理?
解决方案:创建一个名称叫 validate+请求方法处理名(首字母大写) 例如:请求处理方法叫 regist() 校验 的方法名 validateRegist().会发现不管定义多少validateXxx方法 执行时都会走validate方法,所以可以将一些公共的校验放在validate方法中,可以把针对于特定方法的检验放在validateXxx方法下(xxx请求方法处理名)

手动校验的流程如下:
第一步:类型转换器对请求参数执行类型转换,并将转换后的值赋给action中的属性。
第二步:如果在执行转换的过程中出现异常,系统会将异常信息保存到ActionContext,conversionError拦截器将异常信息封装到fieldErrors里,然后执行第三步。如果类型转换没有出现异常,则直接进入第三步。
第三步:系统通过反射技术调用action中的ValidateXxx()方法,xxx为请求方法处理名。
第四步:调用action中的validate方法。
第五步:经过上面4步,如果系统中的fieldErrors存在错误信息(即存放错误信息的集合的size大于0),系统自动将请求转发至名称为input的视图。如果系统中的fieldErrors没有任何错误信息,系统将执行action中的处理方法。
手动校验示例如下:
导入Strutsjar包 配置好struts.xml
在WebRoot下新建login.jsp和regist.jsp
login.jsp
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'index.jsp' starting page</title> </head> <body> <s:fielderror/> <form action="${pageContext.request.contextPath}/login" method="post"> username:<input type="text" name="username"><s:fielderror fieldName="username.message"/><br> password:<input type="password" name="password"><s:fielderror fieldName="password.message"/><br> repassword:<input type="password" name="repassword"><br> hobby:<input type="checkbox" name="hobby" value="eat">吃<input type="checkbox" name="hobby" value="drink">喝<input type="checkbox" name="hobby" value="play">玩<br> age:<input type="text" name="age"><br> birthday:<input type="text" name="birthday"><br> email:<input type="text" name="email"><br> url:<input type="text" name="url"><br> telphone:<input type="text" name="telphone"><br> <input type="submit" value="注册"> </form> </body> </html>

regist.jsp
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'index.jsp' starting page</title> </head> <body> <s:fielderror/> <form action="${pageContext.request.contextPath}/regist" method="post"> username:<input type="text" name="username"><s:fielderror fieldName="username.message"/><br> password:<input type="password" name="password"><s:fielderror fieldName="password.message"/><br> repassword:<input type="password" name="repassword"><br> hobby:<input type="checkbox" name="hobby" value="eat">吃<input type="checkbox" name="hobby" value="drink">喝<input type="checkbox" name="hobby" value="play">玩<br> age:<input type="text" name="age"><br> birthday:<input type="text" name="birthday"><br> email:<input type="text" name="email"><br> url:<input type="text" name="url"><br> telphone:<input type="text" name="telphone"><br> <input type="submit" value="注册"> </form> </body> </html>
新建model类User
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package cn.itcast.domain; import java.util.Arrays; import java.util.Date; public class User { private String username; private String password; private int age; private Date birthday; private String[] hobby; private String url; private String email; private String telphone; private String repassword; public String getRepassword() { return repassword; } public void setRepassword(String repassword) { this.repassword = repassword; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getTelphone() { return telphone; } public void setTelphone(String telphone) { this.telphone = telphone; } public String[] getHobby() { return hobby; } public void setHobby(String[] hobby) { this.hobby = hobby; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } @Override public String toString() { return "User [username=" + username + ", password=" + password + ", age=" + age + ", birthday=" + birthday + ", hobby=" + Arrays.toString(hobby) + "]"; } }

接着使用手动校验创建Action类UserAction
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package cn.itcast.action; import cn.itcast.domain.User; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; // 采用手动的方式 public class UserAction extends ActionSupport implements ModelDriven<User> { private User user = new User(); @Override public User getModel() { return user; } @Override public String execute() throws Exception { System.out.println(user); return null; } public String login() throws Exception { System.out.println("login method ......"); return null; } public String regist() throws Exception { System.out.println("regist method ......"); return null; } @Override public void validate() { System.out.println("validate method ......"); } public void validateRegist() { if (user.getUsername() == null || user.getUsername().trim().length() == 0) { // 说明用户名为空 this.addFieldError("username.message", "用户名不能为空"); } if (user.getPassword() == null || user.getPassword().trim().length() == 0) { this.addFieldError("password.message", "密码不能为空"); } if (!(user.getAge() >= 10 && user.getAge() <= 40)) { this.addFieldError("age.message", "年龄必须在10-40之间"); } System.out.println("validateRegist......"); } }

配置struts.xml
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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="default" namespace="/" extends="struts-default"> <action name="regist" class="cn.itcast.action.UserAction" method="regist"> <result name="input">/regist.jsp</result> </action> <action name="login" class="cn.itcast.action.UserAction" method="login"> <result name="input">/login.jsp</result> </action> </package> </struts>
输入http://localhost/mystruts2_day02_3/login.jsp 直接提交

发现首先调用了validate方法载调用了login请求处理方法

接着调试注册方法(提供了注册校验)
本地调试 http://localhost/mystruts2_day02_3/regist.jsp 直接提交

发现首先调用了validateRegist方法 再调用了validate方法
因此 只要重写了validate方法,不管怎样都会执行校验。所以可以把公共部分抽取到validate方法内。

配置校验

Struts2的校验框架已经完成了校验操作(做了很多校验法)而我们在使用时只需要调用它们即可(通过配置文件)
基于XML配置方式实现输入校验:
要求:Action类必须继承自ActionSupport或实现了Validateable接口。
在struts.xml中添加type为input的视图结果集。
添加配置文件。
关于配置文件的配置方式:
配置文件位置: xml文件要与Action类在同一包下。
配置文件名称:Action简单类名-validation.xml
配置文件约束:xwork-core-2.3.7.jar 中 xwork-validator-1.0.3.dtd 下
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">

配置文件的书写:
1.根元素:<validators>
2.子元素:<field name="属性名称"></field>
指定action中要校验的属性,name属性指定将被验证的表单字段的名称。
3.<field>的子元素
<field-validator type="校验器"> 是用于指定校验器
问题:校验器有哪些?
xwork-core-2.3.7.jar 中 /com/opensymphony/xwork2/validator/validators/default.xml下
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/> <validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/> <validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/> <validator name="long" class="com.opensymphony.xwork2.validator.validators.LongRangeFieldValidator"/> <validator name="short" class="com.opensymphony.xwork2.validator.validators.ShortRangeFieldValidator"/> <validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/> <validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/> <validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/> <validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/> <validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/> <validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/> <validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/> <validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/> <validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/> <validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/> <validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/>

4.<field-validator>的子元素
<message>错误信息</message>
如果需要国际化,可以为message指定key属性,key的值为属性文件中的key。
5.<field-validator>的子元素
<param name="">值</param>
用于指定校验器中的参数
关于配置校验中的校验器:
  • required (必填校验器,要求被校验的属性值不能为null)
  • requiredstring (必填字符串校验器,要求被校验的属性值不能为null,并且长度大于0,默认情况下会对字符串去前后空格)
  • stringlength (字符串长度校验器,要求被校验的属性值必须在指定的范围内,否则校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field之前是否去除字符串前后的空格)
  • regex (正则表达式校验器,检查被校验的属性值是否匹配一个正则表达式,expression参数指定正则表达式,caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true)
  • int(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)
  • double(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)
  • fieldexpression (字段OGNL表达式校验器,要求field满足一个ognl表达式,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过)
  • email(邮件地址校验器,要求如果被校验的属性值非空,则必须是合法的邮件地址)
  • url(网址校验器,要求如果被校验的属性值非空,则必须是合法的url地址)
  • date(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)
注意:关于param的name属性 要到指定校验器对应的类中找到属性的setter方法setXxx 其xxx就是要设置的name属性

问题配置校验,怎样处理在同一个action中存在多个请求处理方法校验问题?
只需要将校验xml文件名称修改即可:
Action简单类名-valication.xml 现在要对action类西红某一个方法校验,
就改为:Action简单类名-action名称-validation.xml
将上面手动配置校验的代码进行修改。
屏蔽UserAction中的所有校验的方法
在同一包下新建UserAction-validation.xml
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?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> <!-- 对username属性进行校验 --> <field name="username"> <!-- 指定username不能为空 --> <field-validator type="requiredstring"> <!-- 错误信息 --> <message>用户名不能为空---</message> </field-validator> <!-- 长度校验,规定用户名必须在6-10位之间 --> <field-validator type="stringlength"> <param name="maxLength">10</param> <param name="minLength">6</param> <message>用户名必须在${minLength}-${maxLength}位之间</message> </field-validator> </field> <!-- 对age进行校验,规定年龄必须在10-40之间 --> <field name="age"> <field-validator type="int"> <param name="min">10</param> <param name="max">40</param> <message>年龄必须在${min}--${max}之间</message> </field-validator> </field> <!-- 对birthday进行校验 --> <field name="birthday"> <field-validator type="date"> <param name="min">1974-01-01</param> <param name="max">2004-12-31</param> <message>生日必须在${min}--${max}年之间</message> </field-validator> </field> <!-- 校验邮箱 --> <field name="email"> <field-validator type="email"> <message>邮箱格式不正确</message> </field-validator> </field> <!-- url校验 --> <field name="url"> <field-validator type="url"> <message>url不合法,应类似于http://www.baidu.com</message> </field-validator> </field> <!-- 使用正则 --> <field name="telphone"> <field-validator type="regex"> <param name="regexExpression"><![CDATA[^135[0-9]{8}$]]></param> <message>电话号码必须是135xxxxxxxx</message> </field-validator> </field> <field name="repassword"> <field-validator type="fieldexpression"> <param name="expression"><![CDATA[(password==repassword)]]></param> <message>两次密码输入不一致</message> </field-validator> </field> </validators>
调试结果如下:

登录/login,会有同样的校验
如果要只对regist的action方法进行校验,需要做如下修改
UserAction-validation.xml修改为UserAction-regist-validation.xml即可
修改后调试如下:



发现login方法没有做校验,而regist方法做了校验
当然 也可以自定义校验器,不过很少自定义校验器

基于XML校验的一些特点:

当为某个action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml两种规则的校验文件时,系统按下面顺序寻找校验文件:
1。AconClassName-validation.xml
2。ActionClassName-ActionName-validation.xml
系统寻找到第一个校验文件时还会继续搜索后面的校验文件,当搜索到所有校验文件时,会把校验文件里的所有校验规则汇总,然后全部应用于处理方法的校验。如果两个校验文件中指定的校验规则冲突,则只使用后面文件中的校验规则。

当action继承了另一个action,父类action的校验文件会先被搜索到。假设UserAction继承BaseAction, UserAction在struts.xml的配置如下:
<action name="user" class="cn.itcast.action.UserAction" method="{1}">
.....
</action>
访问上面名为user的action,系统先搜索到BaseAction-validation.xml, BaseAction-user-validation.xml,接着搜索到UserAction-validation.xml, UserAction-user-validation.xml。校验规则是这四个文件的总和。

编写校验文件时,不能出现提示(帮助信息)解决方案:
在编写ActionClassName-validation.xml校验文件时,如果出现不了帮助信息,可以按下面方式解决:
windwos->preferences->myeclipse->files and editors->xml->xmlcatalog
点“add”,在出现的窗口中的location中选“File system”,然后在xwork-2.1.2解压目录的srcjava目录中选择xwork-validator-1.0.3.dtd,回到设置窗口的时候不要急着关闭窗口,应把窗口中的Key Type改为URI 。Key改为http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd

约定访问提供Struts2的零配置

从struts2.1开始,struts2 引入了Convention插件来支持零配置
使用约定无需struts.xml或者Annotation配置
需要 struts2-convention-plugin-2.3.7.jar 、asm-*.jar(三个)
插件会自动搜索action、actions、struts、struts2包下所有Java类
所有实现了com.opensymphony.xwork2.Action的Java类
所有类名以Action结尾的Java类
下面类名都符合Convention插件
cn.itcast.struts2.HelloAction
cn.itcast.actions.books.BookSearchAction
cn.itcast.struts.user.UserAction
cn.itcast.estore.action.test.LoginAction

struts2-convention-plugin-2.3.7.jar 中struts-plugin.xml重要常量
<constant name="struts.convention.package.locators" value="action,actions,struts,struts2"/> 默认扫描包
<constant name="struts.convention.exclude.packages" value="org.apache.struts.*,org.apache.struts2.*,org.springframework.web.struts.*,org.springframework.web.struts2.*,org.hibernate.*"/> 不扫描
<constant name="struts.convention.action.suffix" value="Action"/> 默认扫描以Action结尾的类
<constant name="struts.convention.result.path" value="/WEB-INF/content/"/> 结果result页面存放位置
<constant name="struts.convention.classes.reload" value="false" /> Action类文件重新自动加载

如果Action类名包含Action后缀,将Action后缀去掉
将Action类名的驼峰写法,转成中划线写法
例如:
cn.itcast.struts2.HelloAction 映射到 /hello.action
cn.itcast.actions.books.BookSearchAction 映射到 /books/book-search.action
cn.itcast.struts.user.UserAction 映射到 /user/user.action
cn.itcast.estore.action.test.LoginAction 映射到 /test/login.action

默认情况下,Convention总会到Web应用的WEB-INF/content路径下定位结果资源
<constant name="struts.convention.result.path" value="/WEB-INF/content/"/>
约定: actionName + resultCode + suffix
例如:
访问cn.itcast.struts.user.UserAction返回success
Convention优先使用 WEB-INF/content/user/user-success.jsp
如果user-success.jsp不存在,会使用user-success.html
如果user-success.html不存在,会使用user.jsp


最后

以上就是朴实小刺猬最近收集整理的关于JAVAWEB开发之Struts2详解(二)——Action接受请求参数、类型转换器、使用Struts2的输入校验、以及遵守约定规则实现Struts2的零配置...的全部内容,更多相关JAVAWEB开发之Struts2详解(二)——Action接受请求参数、类型转换器、使用Struts2内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(65)

评论列表共有 0 条评论

立即
投稿
返回
顶部