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

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

<%@ 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

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

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

<%@ 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

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

<%@ 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

<%@ 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

<%@ 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

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

<%@ 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

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

<?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

package cn.itcast;

public interface MyModelDriven<T> {

	public T getModel();
}

创建User类(和上面的一样)

......

创建Action类

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

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

<?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

<%@ 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

<%@ 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)

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

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

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

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

birthday=cn.itcast.utils.MyDateConverter2

User

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

birthday=cn.itcast.utils.MyDateConverter

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="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

<%@ 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

<%@ 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

<%@ 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

<%@ 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

<%@ 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

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

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

<?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下

<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

<?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解压目录的src\java目录中选择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

时间: 2024-08-02 02:48:57

JAVAWEB开发之Struts2详解(二)——Action接受请求参数、类型转换器、使用Struts2的输入校验、以及遵守约定规则实现Struts2的零配置的相关文章

JAVAWEB开发之Hibernate详解(一)——Hibernate的框架概述、开发流程、CURD操作和核心配置与API以及Hibernate日志的使用

Hibernate框架概述 什么是Hibernate? 框架:软件的半成品,完成部分代码的功能. Hibernate:Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思想来操作数据库.Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序中使用,也可以在Servlet/JSP的web应用程序中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成

JAVAWEB开发之Spring详解之——Spring的入门以及IOC容器装配Bean(xml和注解的方式)、Spring整合web开发、整合Junit4测试

Spring框架学习路线 Spring的IOC Spring的AOP,AspectJ Spring的事务管理,三大框架的整合 Spring框架概述 什么是Spring? Spring是分层的JavaSE/EE full-stack(一站式)轻量级开源框架. 所谓分层: SUN提供的EE的三层结构:web层.业务层.数据访问层(也称持久层,集成层). Struts2是web层基于MVC设计模式框架. Hibernate是持久的一个ORM的框架. 所谓一站式:Spring框架有对三层的每层解决方案.

Android开发之InstanceState详解

Android开发之InstanceState详解 本文介绍Android中关于Activity的两个神秘方法:onSaveInstanceState() 和 onRestoreInstanceState(),并且在介绍这两个方法之后,再分别来实现使用InstanceState保存和恢复数据功能.Android实现屏幕旋转异步下载效果这样两个示例. 首先来介绍onSaveInstanceState() 和 onRestoreInstanceState() .关于这两个方法,一些朋友可能在Andr

Android开发之BroadcastReceiver详解

BroadcastReceiver,顾名思义就是"广播接收者"的意思,它是Android四大基本组件之一,这种组件本质上是一种全局的监听器,用于监听系统全局的广播消息.它可以接收来自系统和应用的的广播. 由于BroadcastReceiver是一种全局的监听器,因此它可以非常方便地实现系统不同组件之间的通信.比如Activity与通过startService()方法启动的Service之间通信,就可以借助于BroadcastReceiver来实现. BroadcastReceiver简

Android开发之InstanceState详解(转)---利用其保存Activity状态

Android开发之InstanceState详解 本文介绍Android中关于Activity的两个神秘方法:onSaveInstanceState() 和 onRestoreInstanceState(),并且在介绍这两个方法之后,再分别来实现使用InstanceState保存和恢复数据功能.Android实现屏幕旋转异步下载效果这样两个示例. 首先来介绍onSaveInstanceState() 和 onRestoreInstanceState() .关于这两个方法,一些朋友可能在Andr

Android开发之SpannableString详解

在实际的应用开发过程中经常会遇到,在文本的不同部分显示一些不同的字体风格的信息如:文本的字体.大小.颜色.样式.以及超级链接等.一般情况下,TextView中的文本都是一个样式,对于类似的情况,可以借助SpannableString或SpannableStringBuilder对象来实现以上设置. SpannableString与SpannableStringBuilder都可以将某段文本设置成一个Span,在Android中,Span表示一段文本的效果,例如,链接形式.图像.带背景的文本等.只

Android开发之WebView详解

概述: 一个显示网页的视图.这个类是你可以滚动自己的Web浏览器或在你的Activity中简单地显示一些在线内容的基础.它使用了WebKit渲染引擎来显示网页,包括向前和向后导航的方法(通过历史记录),放大和缩小,执行文本搜索等. 需要注意的是:为了让你的应用能够使用WebView访问互联网和加载网页,你必须添加Internet的权限在Android Manifest文件中: <uses-permission android:name="android.permission.INTERNE

【Android】Android软件开发之ListView 详解

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://xys289187120.blog.51cto.com/3361352/657171 ListView的使用方法 ListView是Android软件开发中非常重要组件之一,基本上是个软件基本都会使用ListView ,今天我通过一个demo来教大家怎么样使用ListView组件 绘制出漂亮的列表,说道ListView就不得不说Adapter适配器,因为只有通过Adapter才可

Android软件开发之TextView详解

Android软件开发之TextView详解<IGNORE_JS_OP> TextView的API 中文文档中 说明它的结构 结构java.lang.Object   android.view.View        android.widget.TextView直接子类:    Button, CheckedTextView, Chronometer, DigitalClock, EditText间接子类:     AutoCompleteTextView, CheckBox, Compou