Struts2学习记录

          Struts2学习记录

创建动态java工程

一路next。

引入lib包过程略

配置web.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">

	<!-- 配置 Struts2 的 Filter -->
	<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>

</web-app>

然后再配置struts.xml文件略

目录结构

语法介绍

struts2-2---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: 包. struts2 使用 package 来组织模块. 
		name 属性: 必须. 用于其它的包应用当前包. 
		extends: 当前包继承哪个包, 继承的, 即可以继承其中的所有的配置. 通常情况下继承 struts-default
		         struts-default 这个包在 struts-default.xml 文件中定义.
		namespace 可选, 如果它没有给出, 则以 / 为默认值. 
		                        若 namespace 有一个非默认值, 则要想调用这个包里的Action, 
		                        就必须把这个属性所定义的命名空间添加到有关的 URI 字符串里
		                        
		          http://localhost:8080/contextPath/namespace/actionName.action
	-->
    <package name="helloWorld" extends="struts-default">
    	
    	<!-- 
    		配置一个 action: 一个 struts2 的请求就是一个 action 
    		action里面的name: 对应一个 struts2 的请求的名字(或对一个 servletPath, 但去除 / 和扩展名), 不包含扩展名
    		class 的默认值为: com.opensymphony.xwork2.ActionSupport
    		method 的默认值为: execute
    		result: 结果. 
    	-->
    	<action name="product-input" 
    		class="com.opensymphony.xwork2.ActionSupport"
    		method="execute">
    		<!--  
    			result: 结果. 表示 action 方法执行后可能返回的一个结果. 所以一个 action 节点可能会有多个 result 子节点.
    			多个 result 子节点使用 name 来区分
    			name: 标识一个 result. 和 action 方法的返回值对应. 默认值为 success
    			type: 表示结果的类型. 默认值为 dispatcher(转发到结果.)
    			
    		-->
    		<result name="success" type="dispatcher">/WEB-INF/pages/input.jsp</result>
    	</action>
    	<!--
    	action里面的class和method分别对应到指定类里面的method属性值的那个方法
    	result里面的name 代表上面方法的return 的返回的名字
    	对下面这句话解释:
    	
       由 input.jsp 页面的 action: product-save.action 到 Product's save, 再到  /WEB-INF/pages/details.jsp
	<action name="product-save" class="com.atguigu.struts2.helloworld.Product"
		method="save">
		<result name="details">/WEB-INF/pages/details.jsp</result>	
	</action>
	
	在 Prodcut 中定义一个 save 方法, 且返回值为 details
    	 	-->
    	<action name="product-save" class="com.atguigu.struts2.helloworld.Product"
    		method="save">
    		<result name="details">/WEB-INF/pages/details.jsp</result>	
    	</action>
    	
    	<action name="test" class="com.atguigu.struts2.helloworld.Product" method="test">
    		<result>/index.jsp</result>
    	</action>
    	
    </package>

</struts>

详解

1,

1). action: 代表一个  Struts2 的请求.

2). Action 类: 能够处理 Struts2 请求的类.

> 属性的名字必须遵守与 JavaBeans 属性名相同的命名规则.

    属性的类型可以是任意类型. 从字符串到非字符串(基本数据库类型)之间的数据转换可以自动发生

> 必须有一个不带参的构造器: 通过反射创建实例

> 至少有一个供 struts 在执行这个 action 时调用的方法

> 同一个 Action 类可以包含多个 action 方法.

> Struts2 会为每一个 HTTP 请求创建一个新的 Action 实例, 即 Action 不是单例的, 是线程安全的.

I. 和 Servlet API 解耦的方式: 只能访问有限的 Servlet API 对象, 且只能访问其有限的方法(读取请求参数, 读写域对象的属性, 使 session 失效...).

> 使用 ActionContext

> 实现 XxxAware 接口

> 选用的建议: 若一个 Action 类中有多个 action 方法, 且多个方法都需要使用域对象的 Map 或 parameters, 则建议使用

Aware 接口的方式

> session 对应的 Map 实际上是 SessionMap 类型的! 强转后若调用其 invalidate() 方法, 可以使其 session 失效!

II. 和 Servlet API 耦合的方式: 可以访问更多的 Servlet API 对象, 且可以调用其原生的方法.  

> 使用 ServletActionContext

> 实现 ServletXxxAware 接口.

> 使用 ActionContext

com.atguigu.struts2.action.TestActionContextAction.class
public String execute(){
		//0. 获取 ActionContext 对象
		//ActionContext 是 Action 的上下文对象. 可以从中获取到当往 Action 需要的一切信息
		ActionContext actionContext = ActionContext.getContext();
		//1. 获取 application 对应的 Map, 并向其中添加一个属性
		//通过调用 ActionContext 对象的 getApplication() 方法来获取 application 对象的 Map 对象
		Map<String, Object> applicationMap = actionContext.getApplication();
		//设置属性
		applicationMap.put("applicationKey", "applicationValue");
		//获取属性
		Object date = applicationMap.get("date");
		System.out.println("date: " + date);
		//2. session
		Map<String, Object> sessionMap = actionContext.getSession();
		sessionMap.put("sessionKey", "sessionValue");
		System.out.println(sessionMap.getClass()); 
		if(sessionMap instanceof SessionMap){
			SessionMap sm = (SessionMap) sessionMap;
			sm.invalidate();
			System.out.println("session 失效了. ");
		}
		//3. request* 
		//ActionContext 中并没有提供 getRequest 方法来获取 request 对应的 Map
		//需要手工调用 get() 方法, 传入 request 字符串来获取. 
		Map<String, Object> requestMap = (Map<String, Object>) actionContext.get("request");
		requestMap.put("requestKey", "requestValue");
		//4. 获取请求参数对应的 Map, 并获取指定的参数值. 
		//键: 请求参数的名字, 值: 请求参数的值对应的字符串数组
		//注意: 1. getParameters 的返回值为在 Map<String, Object>, 而不是 Map<String, String[]>
		//     2. parameters 这个 Map 只能读, 不能写入数据, 如果写入, 但不出错, 但也不起作用!
		Map<String, Object> parameters = actionContext.getParameters();
		System.out.println(((String[])parameters.get("name"))[0]);
		parameters.put("age", 100);
		return "success";
	}

Jsp

<body>
	<h4>Test ActionContext Page</h4>
	application : ${applicationScope.applicationKey }
	<br><br>
	session: ${sessionScope.sessionKey }
	<br><br>
	request: ${requestScope.requestKey }
	<br><br>
	age: ${parameters.age }
</body>

> 实现 XxxAware 接口

TestAwareAction.class

package com.atguigu.struts2.action;

import java.util.Map;

import org.apache.struts2.interceptor.ApplicationAware;
import org.apache.struts2.interceptor.ParameterAware;
import org.apache.struts2.interceptor.RequestAware;
import org.apache.struts2.interceptor.SessionAware;

public class TestAwareAction implements ApplicationAware, SessionAware, RequestAware,
	ParameterAware{
	
	public String execute(){
		
		//1. 向 application 中加入一个属性: applicationKey2 - applicationValue2
		application.put("applicationKey2", "applicationValue2");
		
		//2. 从 application 中读取一个属性 date, 并打印. 
		System.out.println(application.get("date"));
		
		return "success";
	}
	
	public String save(){
		
		return null;
	}

	private Map<String, Object> application;
	
	@Override
	public void setApplication(Map<String, Object> application) {
		this.application = application;
	}

	@Override
	public void setParameters(Map<String, String[]> parameters) {
		
	}

	@Override
	public void setRequest(Map<String, Object> request) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void setSession(Map<String, Object> session) {
		// TODO Auto-generated method stub
		
	}
	
}
Jsp
<body>
	<h4>Test Aware Page</h4>
	application: ${applicationScope.applicationKey2 }
</body>

> 使用 ServletActionContext

> 实现 ServletXxxAware 接口.

session存储用户信息

Login.jsp

userAction.php

 ActionSupport

1). ActionSupport 是默认的 Action 类: 若某个 action 节点没有配置 class 属性, 则 ActionSupport 即为

待执行的 Action 类. 而 execute 方法即为要默认执行的 action 方法

/testActionSupport.jsp

等同于

class="com.opensymphony.xwork2.ActionSupport"

method="execute">

/testActionSupport.jsp

2). 在手工完成字段验证, 显示错误消息, 国际化等情况下, 推荐继承 ActionSupport.

result:

1). result 是 action 节点的子节点

2). result 代表 action 方法执行后, 可能去的一个目的地

3). 一个 action 节点可以配置多个 result 子节点.

4). result 的 name 属性值对应着 action 方法可能有的一个返回值.

/index.jsp

5). result 一共有 2 个属性, 还有一个是 type: 表示结果的响应类型

6). result 的 type 属性值在 struts-default 包的 result-types 节点的 name 属性中定义.

         常用的有

    > dispatcher(默认的): 转发. 同 Servlet 中的转发.

    > redirect: 重定向

    > redirectAction: 重定向到一个 Action

     注意: 通过 redirect 的响应类型也可以便捷的实现 redirectAction 的功能!

    

testAction

namespace">/atguigu

OR

/atguigu/testAction.do

    > chain: 转发到一个 Action

     注意: 不能通过 type=dispatcher 的方式转发到一个 Action

         只能是:

    

testAction

namespace">/atguigu

不能是:

/atguigu/testAction.do

dispatcher

dispatcher 结果类型是最常用的结果类型, 也是 struts 框架默认的结果类型

该结果类型有一个 location 参数, 它是一个默认参数

redirect

redirectAction

redirectAction 结果类型把响应重定向到另一个 Action

 chain

struts.xml

<struts>

	<!-- 配置 Struts 可以受理的请求的扩展名 -->
	<constant name="struts.action.extension" value="action,do,"></constant>
	
	<!-- 打开允许动态方法调用的开关, 默认是 false -->
	<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
	
    <package name="default" namespace="/" extends="struts-default">

		<action name="TestActionContext" 
			class="com.atguigu.struts2.action.TestActionContextAction">
			<result  name="success">/test-actionContext.jsp</result>	
		</action>
		
		<action name="TestAware" 
			class="com.atguigu.struts2.action.TestAwareAction">
			<result>/test-aware.jsp</result>	
		</action>
		
		<action name="testActionSupport">
			<result>/testActionSupport.jsp</result>
		</action>

		<action name="testResult" class="com.atguigu.struts2.action.TestResultAction">
			<result name="success" type="dispatcher">/success.jsp</result>
			<result name="login" type="redirect">/login.jsp</result>
			
			<!-- 重定向到一个 Action -->
			<!--  
			<result name="index" type="redirectAction">
				<param name="actionName">testAction</param>
				<param name="namespace">/atguigu</param>
			</result>
			-->
			
			<!-- 通过 redirect 的响应类型也可以便捷的实现 redirectAction 的功能! -->
			<result name="index" type="redirect">/atguigu/testAction.do</result>
			
			<!-- 转发到一个 Action -->
			<!--  
			<result name="test" type="chain">
				<param name="actionName">testAction</param>
				<param name="namespace">/atguigu</param>
			</result>
			-->
			
			<!-- 不能通过 type=dispatcher 的方式转发到一个 Action -->
			<result name="test">/atguigu/testAction.do</result>
			
			
		</action>
		
		<action name="testDynamicMethodInvocation"
			class="com.atguigu.struts2.action.TestDynamicMethodInvocationActoin"
			method="save">
			<result>/success.jsp</result>	
		</action>

    </package>
   
    <package name="testPackage" namespace="/atguigu" extends="struts-default">
    	<action name="testAction" 
    		class="com.atguigu.struts2.action.TestAction">
    		<result>/success.jsp</result>
    	</action>
    </package>
</struts>

通配符映射

动态方法调用

1. 关于值栈:

1). helloWorld 时, ${productName} 读取 productName 值, 实际上该属性并不在 request 等域对象中, 而是从值栈中获取的.

2). ValueStack:

I.  可以从 ActionContext 中获取值栈对象

II. 值栈分为两个逻辑部分

> Map 栈: 实际上是 OgnlContext 类型, 是个 Map, 也是对 ActionContext 的一个引用. 里边保存着各种 Map:

         requestMap, sessionMap, applicationMap, parametersMap, attr

         

> 对象栈: 实际上是 CompoundRoot 类型, 是一个使用 ArrayList 定义的栈. 里边保存各种和当前 Action 实例相关的对象.

                   是一个数据结构意义的栈.

                   

 2. Struts2 利用 s:property 标签和 OGNL 表达式来读取值栈中的属性值

 1). 值栈中的属性值:

  > 对于对象栈: 对象栈中某一个对象的属性值

 

  > Map 栈: request, session, application 的一个属性值 或 一个请求参数的值.

 

 2). 读取对象栈中对象的属性:

  > 若想访问 Object Stack 里的某个对象的属性. 可以使用以下几种形式之一:

  object.propertyName ; object['propertyName'] ; object["propertyName"]

> ObjectStack 里的对象可以通过一个从零开始的下标来引用. ObjectStack 里的栈顶对象可以用 [0] 来引用,

     它下面的那个对象可以用 [1] 引用.

   

  [0].message   

     

> [n] 的含义是从第 n 个开始搜索, 而不是只搜索第 n 个对象

> 若从栈顶对象开始搜索, 则可以省略下标部分: message

> 结合 s:property 标签:  

  1. . 默认情况下, Action 对象会被 Struts2 自动的放到值栈的栈顶.

<body>

	<s:debug></s:debug>

	ProductName: ^<s:property value="[0].productName"/>
	<br><br>
	
	ProductDesc: <s:property value="[1].productDesc"/>
	<br><br>
	
	ProductPrice: ${productPrice }
	<br><br>
	
	ProductPrice: ^<s:property value="[0].productPrice"/>
	<br><br>
	
	ProductPrice: ^^<s:property value="productPrice"/>
	<br><br>
	
</body>

访问 Map 类型的属性

异常处理: exception-mapping 元素

exception-mapping 元素: 配置当前 action 的声明式异常处理

exception-mapping 元素中有 2 个属性

exception: 指定需要捕获的的异常类型。异常的全类名

result: 指定一个响应结果, 该结果将在捕获到指定异常时被执行, 既可以来自当前 action 的声明, 也可以来自 global-results 声明.

可以通过 global-exception-mappings 元素为应用程序提供一个全局性的异常捕获映射. 但在 global-exception-mappings 元素下声明的任何 exception-mapping 元素只能引用在 global-results 元素下声明的某个 result 元素

声明式异常处理机制由  ExceptionMappingInterceptor 拦截器负责处理, 当某个 exception-mapping 元素声明的异常被捕获到时, ExceptionMappingInterceptor 拦截器就会向 ValueStack 中添加两个对象:

exception: 表示被捕获异常的 Exception 对象

exceptionStack: 包含着被捕获异常的栈

可以在视图上通过 标签显示异常消息

标签讲解

property 标签

url 标签用来动态地创建一个 URL

set 标签

表单标签

Struts2注册模块

emp-input.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>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	
	<% 
		request.setAttribute("theme", "xhtml");
	%>
	<!--struts2用户注册模块  -->
	<s:form action="emp-save">
		
		<s:textfield name="name" label="Name"></s:textfield>
		<s:password name="password" label="Password"></s:password>
	
		<s:radio name="gender" list="#{'1':'Male','0':'Female' }" label="Gender"></s:radio>
	
		<s:select list="#request.depts" listKey="deptId"
			listValue="deptName" name="dept" label="Department"></s:select>
		<s:checkboxlist list="#request.roles" listKey="roleId"
			listValue="roleName" name="roles" label="Role"></s:checkboxlist>
	
		<s:textarea name="desc" label="Desc"></s:textarea>
		<s:submit></s:submit>
		
	</s:form>
	
</body>
</html>

emp-save.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>
	
	Name: ${name }
	<br><br>

	Password: ${password }
	<br><br>
	
	Gender: ${gender }
	<br><br>
	
	Dept: ${dept }
	<br><br>
	
	Roles: ${roles }
	<br><br>
	
	Desc: ${desc }
	<br><br>
	
</body>
</html>

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>

	<!-- 修改当前 Struts 应用的主题 -->
	<constant name="struts.ui.theme" value="simple"></constant>

    <package name="default" namespace="/" extends="struts-default">

		
		
		<action name="emp-*"
			class="com.atguigu.struts.valuestack.app.Employee"
			method="{1}">
			<result name="{1}">/emp-{1}.jsp</result>	
		</action>
	
    </package>


</struts>

Dao.Java

package com.atguigu.struts.valuestack.app;

import java.util.ArrayList;
import java.util.List;

public class Dao {

	public List<Department> getDepartments(){
		
		List<Department> depts = new ArrayList<>();
		
		depts.add(new Department(1001, "AAA"));
		depts.add(new Department(1002, "BBB"));
		depts.add(new Department(1003, "CCC"));
		depts.add(new Department(1004, "DDD"));
		depts.add(new Department(1005, "EEE"));
		
		return depts;
	}
	
	public List<Role> getRoles(){
		
		List<Role> roles = new ArrayList<>();
		
		roles.add(new Role(2001, "XXX"));
		roles.add(new Role(2002, "YYY"));
		roles.add(new Role(2003, "ZZZ"));
		
		return roles;
	}
	
}

Department.Java

package com.atguigu.struts.valuestack.app;

public class Department {

	private Integer deptId;
	
	private String deptName;

	public Integer getDeptId() {
		return deptId;
	}

	public void setDeptId(Integer deptId) {
		this.deptId = deptId;
	}

	public String getDeptName() {
		return deptName;
	}

	public void setDeptName(String deptName) {
		this.deptName = deptName;
	}

	public Department(Integer deptId, String deptName) {
		super();
		this.deptId = deptId;
		this.deptName = deptName;
	}
	
	public Department() {
		// TODO Auto-generated constructor stub
	}
	
}

Employee.Java

package com.atguigu.struts.valuestack.app;

import java.util.List;
import java.util.Map;

import org.apache.struts2.interceptor.RequestAware;

public class Employee implements RequestAware{
	
	private Map<String, Object> requestMap = null;
	
	private Dao dao = new Dao();
	
	private String name;
	private String password;
	
	private String gender;
	private String dept;
	
	private List<String> roles;
	private String desc;
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPassword() {
		return password;
	}

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

	public String getGender() {
		return gender;
	}

	public void setGender(String gender) {
		this.gender = gender;
	}

	public String getDept() {
		return dept;
	}

	public void setDept(String dept) {
		this.dept = dept;
	}

	public List<String> getRoles() {
		return roles;
	}

	public void setRoles(List<String> roles) {
		this.roles = roles;
	}

	public String getDesc() {
		return desc;
	}

	public void setDesc(String desc) {
		this.desc = desc;
	}

	public String save(){
		System.out.println("save: " + this);
		return "save";
	}
	
	public String input(){
		requestMap.put("depts", dao.getDepartments());
		requestMap.put("roles", dao.getRoles());
		return "input";
	}

	@Override
	public void setRequest(Map<String, Object> request) {
		this.requestMap = request;
	}

	@Override
	public String toString() {
		return "Employee [name=" + name + ", password=" + password
				+ ", gender=" + gender + ", dept=" + dept + ", roles=" + roles
				+ ", desc=" + desc + "]";
	}
	
}

Role.Java

package com.atguigu.struts.valuestack.app;

public class Role {

	private Integer roleId;
	
	private String roleName;

	public Integer getRoleId() {
		return roleId;
	}

	public void setRoleId(Integer roleId) {
		this.roleId = roleId;
	}

	public String getRoleName() {
		return roleName;
	}

	public void setRoleName(String roleName) {
		this.roleName = roleName;
	}

	public Role(Integer roleId, String roleName) {
		super();
		this.roleId = roleId;
		this.roleName = roleName;
	}
	
	public Role() {
		// TODO Auto-generated constructor stub
	}
	
}

值栈

1. 关于值栈:

1). helloWorld 时, ${productName} 读取 productName 值, 实际上该属性并不在 request 等域对象中, 而是从值栈中获取的.

2). ValueStack:

I.  可以从 ActionContext 中获取值栈对象

II. 值栈分为两个逻辑部分

> Map 栈: 实际上是 OgnlContext 类型, 是个 Map, 也是对 ActionContext 的一个引用. 里边保存着各种 Map:

         requestMap, sessionMap, applicationMap, parametersMap, attr

         

> 对象栈: 实际上是 CompoundRoot 类型, 是一个使用 ArrayList 定义的栈. 里边保存各种和当前 Action 实例相关的对象.

                   是一个数据结构意义的栈.

                   

 2. Struts2 利用 s:property 标签和 OGNL 表达式来读取值栈中的属性值

 1). 值栈中的属性值:

  > 对于对象栈: 对象栈中某一个对象的属性值

 

  > Map 栈: request, session, application 的一个属性值 或 一个请求参数的值.

 

 2). 读取对象栈中对象的属性:

  > 若想访问 Object Stack 里的某个对象的属性. 可以使用以下几种形式之一:

  object.propertyName ; object['propertyName'] ; object["propertyName"]

> ObjectStack 里的对象可以通过一个从零开始的下标来引用. ObjectStack 里的栈顶对象可以用 [0] 来引用,

     它下面的那个对象可以用 [1] 引用.

   

  [0].message   

     

> [n] 的含义是从第 n 个开始搜索, 而不是只搜索第 n 个对象

> 若从栈顶对象开始搜索, 则可以省略下标部分: message

> 结合 s:property 标签:  

. 默认情况下, Action 对象会被 Struts2 自动的放到值栈的栈顶.

PrepareInterceptor 和 Preparable 接口.

2. 使用 paramsPrepareParamsStack 拦截器栈后的运行流程

1). paramsPrepareParamsStack 和 defaultStack 一样都是拦截器栈. 而 struts-default 包默认使用的是

defaultStack

2). 可以在 Struts 配置文件中通过以下方式修改使用的默认的拦截器栈

interceptor-ref name="paramsPrepareParamsStack">interceptor-ref>

3). paramsPrepareParamsStack 拦截器在于

params -> modelDriven -> params

所以可以先把请求参数赋给 Action 对应的属性, 再根据赋给 Action 的那个属性值决定压到值栈栈顶的对象, 最后再为栈顶对象的属性赋值.

对于 edit 操作而言:

I.   先为 EmployeeAction 的 employeeId 赋值

II.  根据 employeeId 从数据库中加载对应的对象, 并放入到值栈的栈顶

III. 再为栈顶对象的 employeeId 赋值(实际上此时 employeeId 属性值已经存在)

IV.  把栈顶对象的属性回显在表单中.

4). 关于回显: Struts2 表单标签会从值栈中获取对应的属性值进行回显.

5). 存在的问题:

getModel 方法

public Employee getModel() {

if(employeeId == null)

employee = new Employee();

else

employee = dao.get(employeeId);

return employee;

}

I.   在执行删除的时候, employeeId 不为 null, 但 getModel 方法却从数据库加载了一个对象. 不该加载!

II.  指向查询全部信息时, 也 new Employee() 对象. 浪费!

6). 解决方案: 使用 PrepareInterceptor 和 Preparable 接口.

7). 关于 PrepareInterceptor

[分析后得到的结论]

若 Action 实现了 Preparable 接口, 则 Struts 将尝试执行 prepare[ActionMethodName] 方法,

若 prepare[ActionMethodName] 不存在, 则将尝试执行 prepareDo[ActionMethodName] 方法.

若都不存在, 就都不执行.

若 PrepareInterceptor  的 alwaysInvokePrepare 属性为 false,

则 Struts2 将不会调用实现了 Preparable 接口的  Action 的 prepare() 方法

[能解决 5) 的问题的方案]

可以为每一个 ActionMethod 准备 prepare[ActionMethdName] 方法, 而抛弃掉原来的 prepare() 方法

将 PrepareInterceptor  的 alwaysInvokePrepare 属性置为 false, 以避免 Struts2 框架再调用 prepare() 方法.

如何在配置文件中为拦截器栈的属性赋值: 参看 /struts-2.3.15.3/docs/WW/docs/interceptors.html

    

        ref name="defaultStack">

            token

        ref>

    

interceptor-ref name="parentStack"/>

----------------------------------源代码解析---------------------------------

public String doIntercept(ActionInvocation invocation) throws Exception {

//获取 Action 实例

    Object action = invocation.getAction();

//判断 Action 是否实现了 Preparable 接口

    if (action instanceof Preparable) {

        try {

            String[] prefixes;

            //根据当前拦截器的 firstCallPrepareDo(默认为 false) 属性确定 prefixes

            if (firstCallPrepareDo) {

                prefixes = new String[] {ALT_PREPARE_PREFIX, PREPARE_PREFIX};

            } else {

                prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX};

            }

            //若为 false, 则 prefixes: prepare, prepareDo

            //调用前缀方法.

            PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes);

        }

        catch (InvocationTargetException e) {

            Throwable cause = e.getCause();

            if (cause instanceof Exception) {

                throw (Exception) cause;

            } else if(cause instanceof Error) {

                throw (Error) cause;

            } else {

                throw e;

            }

        }

//根据当前拦截器的 alwaysInvokePrepare(默认是 true) 决定是否调用 Action 的 prepare 方法

        if (alwaysInvokePrepare) {

            ((Preparable) action).prepare();

        }

    }

    return invocation.invoke();

}

PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes) 方法:

public static void invokePrefixMethod(ActionInvocation actionInvocation, String[] prefixes) throws InvocationTargetException, IllegalAccessException {

//获取 Action 实例

Object action = actionInvocation.getAction();

//获取要调用的 Action 方法的名字(update)

String methodName = actionInvocation.getProxy().getMethod();

if (methodName == null) {

// if null returns (possible according to the docs), use the default execute

        methodName = DEFAULT_INVOCATION_METHODNAME;

}

//获取前缀方法

Method method = getPrefixedMethod(prefixes, methodName, action);

//若方法不为 null, 则通过反射调用前缀方法

if (method != null) {

method.invoke(action, new Object[0]);

}

}

PrefixMethodInvocationUtil.getPrefixedMethod 方法:

public static Method getPrefixedMethod(String[] prefixes, String methodName, Object action) {

assert(prefixes != null);

//把方法的首字母变为大写

String capitalizedMethodName = capitalizeMethodName(methodName);

    

    //遍历前缀数组

    for (String prefixe : prefixes) {

        //通过拼接的方式, 得到前缀方法名: 第一次 prepareUpdate, 第二次 prepareDoUpdate

        String prefixedMethodName = prefixe + capitalizedMethodName;

        try {

         //利用反射获从 action 中获取对应的方法, 若有直接返回. 并结束循环.

            return action.getClass().getMethod(prefixedMethodName, EMPTY_CLASS_ARRAY);

        }

        catch (NoSuchMethodException e) {

            // hmm -- OK, try next prefix

            if (LOG.isDebugEnabled()) {

                LOG.debug("cannot find method [#0] in action [#1]", prefixedMethodName, action.toString());

            }

        }

    }

return null;

}

1. Action 实现 ModelDriven 接口后的运行流程

1). 先会执行 ModelDrivenInterceptor 的 intercept 方法.

    public String intercept(ActionInvocation invocation) throws Exception {

     //获取 Action 对象: EmployeeAction 对象, 此时该 Action 已经实现了 ModelDriven 接口

     //public class EmployeeAction implements RequestAware, ModelDriven

        Object action = invocation.getAction();

//判断 action 是否是 ModelDriven 的实例

        if (action instanceof ModelDriven) {

         //强制转换为 ModelDriven 类型

            ModelDriven modelDriven = (ModelDriven) action;

            //获取值栈

            ValueStack stack = invocation.getStack();

            //调用 ModelDriven 接口的 getModel() 方法

            //即调用 EmployeeAction 的 getModel() 方法

            /*

            public Employee getModel() {

employee = new Employee();

return employee;

}

            */

            Object model = modelDriven.getModel();

            if (model !=  null) {

             //把 getModel() 方法的返回值压入到值栈的栈顶. 实际压入的是 EmployeeAction 的 employee 成员变量

             stack.push(model);

            }

            if (refreshModelBeforeResult) {

                invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model));

            }

        }

        return invocation.invoke();

    }

   

2). 执行 ParametersInterceptor 的 intercept 方法: 把请求参数的值赋给栈顶对象对应的属性. 若栈顶对象没有对应的属性, 则查询

值栈中下一个对象对应的属性...

3). 注意: getModel 方法不能提供以下实现. 的确会返回一个 Employee 对象到值栈的栈顶. 但当前 Action

的 employee 成员变量却是 null.

public Employee getModel() {

return new Employee();

}    

struts2完成员工CRUD

目录截图

Dao.java

package com.atguigu.struts2.app;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class Dao {
	//Dao是帮助Employee提供增删改查用的  
	//LinkedHashMap有序     HashMap无序
	private static Map<Integer, Employee> emps = new LinkedHashMap<Integer, Employee>();
	
	static{
		emps.put(1001, new Employee(1001, "AA", "aa", "aa@atguigu.com"));
		emps.put(1002, new Employee(1002, "BB", "bb", "bb@atguigu.com"));
		emps.put(1003, new Employee(1003, "CC", "cc", "cc@atguigu.com"));
		emps.put(1004, new Employee(1004, "DD", "dd", "dd@atguigu.com"));
		emps.put(1005, new Employee(1005, "EE", "ee", "ee@atguigu.com"));
	}
	
	public List<Employee> getEmployees(){
		return new ArrayList<>(emps.values());
	}
	
	public void delete(Integer empId){
		emps.remove(empId);
	}
	
	public void save(Employee emp){
		long time = System.currentTimeMillis();
		emp.setEmployeeId((int)time);
		
		emps.put(emp.getEmployeeId(), emp);
	}
	
	public Employee get(Integer empId){
		return emps.get(empId);
	}
	
	public void update(Employee emp){
		emps.put(emp.getEmployeeId(), emp);
	}
	
}

Employee.java

package com.atguigu.struts2.app;

public class Employee {

	private Integer employeeId;
	private String firstName;
	private String lastName;

	private String email;

	public Integer getEmployeeId() {
		return employeeId;
	}

	public void setEmployeeId(Integer employeeId) {
		this.employeeId = employeeId;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public Employee(Integer employeeId, String firstName, String lastName,
			String email) {
		super();
		this.employeeId = employeeId;
		this.firstName = firstName;
		this.lastName = lastName;
		this.email = email;
	}

	public Employee() {
		// TODO Auto-generated constructor stub
	}

	@Override
	public String toString() {
		return "Employee [employeeId=" + employeeId + ", firstName="
				+ firstName + ", lastName=" + lastName + ", email=" + email
				+ "]";
	}
	
	

}

EmployeeAction.java

package com.atguigu.struts2.app;

import java.util.Map;

import org.apache.struts2.interceptor.RequestAware;

import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;

public class EmployeeAction implements RequestAware, ModelDriven<Employee>, Preparable{
	//ModelDriven 拦截器
	//当用户触发 add 请求时, ModelDriven 拦截器将调用 EmployeeAction 对象的 getModel() 方法, 并把返回的模型(Employee实例)压入到 ValueStack 栈. 
	//接下来 Parameters 拦截器将把表单字段映射到 ValueStack 栈的栈顶对象的各个属性中. 因为此时 ValueStack 栈的栈顶元素是刚被压入的模型(Employee)对象, 所以该模型将被填充. 如果某个字段在模型里没有匹配的属性, Param 拦截器将尝试 ValueStack 栈中的下一个对象
	
	//Preparable 拦截器
    //Struts 2.0 中的 modelDriven 拦截器负责把 Action 类以外的一个对象压入到值栈栈顶.而 prepare 拦截器负责准备为 getModel() 方法准备 model

	
	private Dao dao = new Dao();
	
	private Employee employee;
	
	public String update(){
		dao.update(employee);
		return "success";
	}
	
	public void prepareUpdate(){
		employee = new Employee();
	}

	public String edit(){	
		//1获取传入的employeeID:employee.getEmployeeID()
		//2根据employeeID获取employee对象
		//3把栈顶对象的属性装配好
		return "edit";
	}
	
	public void prepareEdit(){
		employee = dao.get(employeeId);
	}
	
	public String save(){
		//1,获取请求参数:通过定义属性的方式(如private Integer employeeId;)
		//2,调用dao的save方法
		//3,通过redirectAction的方式响应结果给emp-list
		dao.save(employee);
		return "success";
	}
	
	public void prepareSave(){
		employee = new Employee();
	}
	
	public String delete(){
		dao.delete(employeeId);
		//返回结果类型为EmployeeAction
		return "success";
	}
	
	public String list(){
		request.put("emps", dao.getEmployees());
		return "list";
	}
	
	private Map<String, Object> request;

	@Override
	public void setRequest(Map<String, Object> arg0) {
		this.request = arg0;
	}
	//需要在当前的EmployeeAction中定义employeeId属性,以应用在删除的时候接受请求参数
	private Integer employeeId;
	
	public void setEmployeeId(Integer employeeId) {
		this.employeeId = employeeId;
	}
	
	@Override
	public Employee getModel() {
		return employee;
	}

	@Override
	public void prepare() throws Exception {
		System.out.println("prepare...");
	}
	
}

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

		<!-- 配置使用 paramsPrepareParamsStack 作为默认的拦截器栈 -->
		<!-- 修改 PrepareInterceptor 拦截器的 alwaysInvokePrepare 属性值为 false -->
		<interceptors>
		    <interceptor-stack name="atguigustack">
		        <interceptor-ref name="paramsPrepareParamsStack">
		            <param name="prepare.alwaysInvokePrepare">false</param>
		        </interceptor-ref>
		    </interceptor-stack>
		</interceptors>
 
		<default-interceptor-ref name="atguigustack"/>

		<action name="emp-*" 
			class="com.atguigu.struts2.app.EmployeeAction"
			method="{1}">
			<result name="{1}">/emp-{1}.jsp</result>
			<result name="success" type="redirectAction">emp-list</result>	
		</action>
		
		<action name="emp-list"
			class="com.atguigu.struts2.app.EmployeeAction"
			method="list">
			<result name="list">/emp-list.jsp</result>	
		</action>
			
    </package>


</struts>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">
	<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>
</web-app>

index.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>
	
	<a href="emp-list">List All Employees</a>
	
</body>
</html>

emp-list.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>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

	<s:form action="emp-save">
		
		<s:textfield name="firstName" label="FirstName"></s:textfield>
		<s:textfield name="lastName" label="LastName"></s:textfield>
		<s:textfield name="email" label="Email"></s:textfield>
		
		<s:submit></s:submit>		
	</s:form>

	<br>
	<hr>
	<br>
	
	<table cellpadding="10" cellspacing="0" border="1">
		<thead>
			<tr>
				<td>ID</td>
				<td>FirstName</td>
				<td>LastName</td>
				<td>Email</td>
				<td>Edit</td>
				<td>Delete</td>
			</tr>
		</thead>
		
		<tbody>
			<s:iterator value="#request.emps">
				<tr>
					<td>${employeeId }</td>
					<td>${firstName }</td>
					<td>${lastName }</td>
					<td>${email }</td>
					<td><a href="emp-edit?employeeId=${employeeId }">Edit</a></td>
					<td><a href="emp-delete?employeeId=${employeeId }">Delete</a></td>
				</tr>
			</s:iterator>
		</tbody>
		
	</table>

</body>
</html>

<%
	System.out.println("now: " + new java.util.Date());
%>

emp-edit.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>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	
	<s:debug></s:debug>
	
	<br>
	<br>
	
	<s:form action="emp-update">
		
		<s:hidden name="employeeId"></s:hidden>
		
		<s:textfield name="firstName" label="FirstName"></s:textfield>
		<s:textfield name="lastName" label="LastName"></s:textfield>
		<s:textfield name="email" label="Email"></s:textfield>
		
		<s:submit></s:submit>		
	</s:form>
	
</body>
</html>

类型转换错误

基于字段的配置

Index.php

<s:form action="testConversion" theme="simple">
		Age: <s:textfield name="age" label="Age"></s:textfield>
		${fieldErrors.age[0] }
		^<s:fielderror fieldName="age"></s:fielderror>
		<br><br>
		
		Birth: <s:textfield name="birth"></s:textfield>
		<s:fielderror fieldName="birth"></s:fielderror>
		<br><br>
		
		<s:submit></s:submit>
	</s:form>

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="testConversion" class="com.atguigu.struts2.app.ConversionAction">
			<result>/success.jsp</result>
			<result name="input">/index.jsp</result>
		</action>
  </package>
</struts>

ConversionAction.java

package com.atguigu.struts2.app;

import java.util.Date;

import com.atguigu.struts2.model.Customer;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

public class ConversionAction extends ActionSupport implements ModelDriven<Customer>{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	public String execute(){
		System.out.println("model: " + model);
		return "success";
	}
	
	private Customer model;

	@Override
	public Customer getModel() {
		model = new Customer();
		return model;
	}
	
}

ConversionAction.properties

invalid.fieldvalue.age=\u9519\u8BEF\u7684\u5E74\u9F84\u683C\u5F0F.

基于类型的配置:这里略

国际化资源文件

1. 国际化的目标

1). 如何配置国际化资源文件

I.   Action 范围资源文件: 在Action类文件所在的路径建立名为 ActionName_language_country.properties 的文件

II.  包范围资源文件: 在包的根路径下建立文件名为 package_language_country.properties 的属性文件,

一旦建立,处于该包下的所有 Action 都可以访问该资源文件。注意:包范围资源文件的 baseName 就是package,不是Action所在的包名。

III. 全局资源文件

> 命名方式: basename_language_country.properties

> struts.xml

IV.  国际化资源文件加载的顺序如何呢 ? 离当前 Action 较近的将被优先加载.

假设我们在某个 ChildAction 中调用了getText("username"):

(1) 加载和 ChildAction 的类文件在同一个包下的系列资源文件 ChildAction.properties

(2) 加载  ChildAction 实现的接口 IChild,且和 IChildn 在同一个包下 IChild.properties 系列资源文件。

(3) 加载 ChildAction 父类 Parent,且和 Parent 在同一个包下的 baseName 为 Parent.properties 系列资源文件。

(4) 若 ChildAction 实现 ModelDriven 接口,则对于getModel()方法返回的model 对象,重新执行第(1)步操作。

(5) 查找当前包下 package.properties 系列资源文件。

(6) 沿着当前包上溯,直到最顶层包来查找 package.properties 的系列资源文件。

(7) 查找 struts.custom.i18n.resources 常量指定 baseName 的系列资源文件。

(8) 直接输出该key的字符串值。

2). 如何在页面上 和 Action 类中访问国际化资源文件的  value 值

I. 在 Action 类中. 若 Action 实现了 TextProvider 接口, 则可以调用其 getText() 方法获取 value 值

> 通过继承 ActionSupport 的方式。

II. 页面上可以使用 s:text 标签; 对于表单标签可以使用表单标签的 key 属性值

> 若有占位符, 则可以使用 s:text 标签的 s:param 子标签来填充占位符

> 可以利用标签和 OGNL 表达式直接访问值栈中的属性值(对象栈 和 Map 栈)

time=Time:{0}

------------------------------------

time2=Time:${date}

3). 实现通过超链接切换语言.

I.  关键之处在于知道 Struts2 框架是如何确定 Local 对象的 !

II. 可以通过阅读 I18N 拦截器知道.

III. 具体确定 Locale 对象的过程:

> Struts2 使用 i18n 拦截器 处理国际化,并且将其注册在默认的拦截器栈中

> i18n拦截器在执行Action方法前,自动查找请求中一个名为 request_locale 的参数。

      如果该参数存在,拦截器就将其作为参数,转换成Locale对象,并将其设为用户默认的Locale(代表国家/语言环境)。

      并把其设置为 session 的 WW_TRANS_I18N_LOCALE 属性

> 若 request 没有名为request_locale 的参数,则 i18n 拦截器会从 Session 中获取 WW_TRANS_I18N_LOCALE 的属性值,

     若该值不为空,则将该属性值设置为浏览者的默认Locale

> 若 session 中的 WW_TRANS_I18N_LOCALE 的属性值为空,则从 ActionContext 中获取 Locale 对象。

IV.  具体实现: 只需要在超连接的后面附着  request_locale 的请求参数, 值是 语言国家 代码.

href="testI18n.action?request_locale=en_US">English

href="testI18n.action?request_locale=zh_CN">中文

> 注意: 超链接必须是一个 Struts2 的请求, 即使 i18n 拦截器工作!

1. Struts2 的验证

1). 验证分为两种:

> 声明式验证*

>> 对哪个 Action 或 Model 的那个字段进行验证

>> 使用什么验证规则

>> 如果验证失败, 转向哪一个页面, 显示是什么错误消息

> 编程式验证

2). 声明式验证的 helloworld

I.  先明确对哪一个 Action 的哪一个字段进行验证: age

II. 编写配置文件:

> 把 struts-2.3.15.3\apps\struts2-blank\WEB-INF\classes\example 下的 Login-validation.xml 文件复制到

当前 Action 所在的包下.

> 把该配置文件改为: 把  Login 改为当前 Action 的名字.

> 编写验证规则: 参见 struts-2.3.15.3/docs/WW/docs/validation.html 文档即可.

> 在配置文件中可以定义错误消息:

         validator type="int">

             min">20

             50

             ^^Age needs to be between ${min} and ${max}

         validator>

     

     

     > 该错误消息可以国际化吗. 可以

     

     .

     

            再在 国际化资源文件 中加入一个键值对: error.int=^^^Age needs to be between ${min} and ${max}

III. 若验证失败, 则转向 input 的那个 result. 所以需要配置 name=input 的 result

 /validation.jsp

IV. 如何显示错误消息呢 ?  

> 若使用的是非 simple, 则自动显示错误消息.

> 若使用的是 simple 主题, 则需要 s:fielderror 标签或直接使用 EL 表达式(使用 OGNL)

${fieldErrors.age[0] }

OR

*

3). 注意: 若一个 Action 类可以应答多个 action 请求, 多个 action 请求使用不同的验证规则, 怎么办 ?

> 为每一个不同的 action 请求定义其对应的验证文件: ActionClassName-AliasName-validation.xml

> 不带别名的配置文件: ActionClassName-validation.xml 中的验证规则依然会发生作用. 可以把各个 action 公有的验证规则

配置在其中. 但需要注意的是, 只适用于某一个 action 的请求的验证规则就不要这里再配置了.

4). 声明式验证框架的原理:

> Struts2 默认的拦截器栈中提供了一个 validation 拦截器

> 每个具体的验证规则都会对应具体的一个验证器. 有一个配置文件把验证规则名称和验证器关联起来了. 而实际上验证的是那个验证器.

该文件位于 com.opensymphony.xwork2.validator.validators 下的 default.xml

5). 短路验证: 若对一个字段使用多个验证器, 默认情况下会执行所有的验证. 若希望前面的验证器验证没有通过, 后面的就不再验证, 可以使用短路验证

validator type="conversion" short-circuit="true">

^Conversion Error Occurred

validator>

validator type="int">

min">20

60

validator>

6). 若类型转换失败, 默认情况下还会执行后面的拦截器, 还会进行 验证. 可以通过修改 ConversionErrorInterceptor 源代码的方式使

当类型转换失败时, 不再执行后续的验证拦截器, 而直接返回 input 的 result

Object action = invocation.getAction();

        if (action instanceof ValidationAware) {

            ValidationAware va = (ValidationAware) action;

            if(va.hasFieldErrors() || va.hasActionErrors()){

             return "input";

            }

        }

        

7). 关于非字段验证: 不是针对于某一个字段的验证.

        password==password2

        Password is not equals to password2

    

     

          显示非字段验证的错误消息, 使用 s:actionerror 标签:  

          

8). 不同的字段使用同样的验证规则, 而且使用同样的响应消息 ?

error.int=${getText(fieldName)} needs to be between ${min} and ${max}

age=\u5E74\u9F84

count=\u6570\u91CF       

详细分析参见  PPT 159.  

9). 自定义验证器:

I.   定义一个验证器的类

> 自定义的验证器都需要实现 Validator.

> 可以选择继承 ValidatorSupport 或 FieldValidatorSupport 类

> 若希望实现一个一般的验证器, 则可以继承 ValidatorSupport

> 若希望实现一个字段验证器, 则可以继承 FieldValidatorSupport

> 具体实现可以参考目前已经有的验证器.

> 若验证程序需要接受一个输入参数, 需要为这个参数增加一个相应的属性

II.  在配置文件中配置验证器

> 默认情况下下, Struts2 会在 类路径的根目录下加载 validators.xml 文件. 在该文件中加载验证器.

     该文件的定义方式同默认的验证器的那个配置文件: 位于 com.opensymphony.xwork2.validator.validators 下的 default.xml

     

> 若类路径下没有指定的验证器, 则从 com.opensymphony.xwork2.validator.validators 下的 default.xml 中的验证器加载     

III. 使用: 和目前的验证器一样.

IV. 示例代码: 自定义一个 18 位身份证验证器   

文件上传和下载

1. 文件的上传:

1). 表单需要注意的 3 点

2). Struts2 的文件上传实际上使用的是 Commons FileUpload 组件, 所以需要导入

commons-fileupload-1.3.jar

commons-io-2.0.1.jar

3). Struts2 进行文件上传需要使用 FileUpload 拦截器

4). 基本的文件的上传: 直接在 Action 中定义如下 3 个属性, 并提供对应的 getter 和 setter

//文件对应的 File 对象

private File [fileFieldName];

//文件类型

private String [fileFieldName]ContentType;

//文件名

private String [fileFieldName]FileName;

5). 使用 IO 流进行文件的上传即可.

核心代码

Html

	<s:form action="testUpload" 
		method="post" enctype="multipart/form-data" theme="simple">
        <s:fielderror name="ppt"></s:fielderror>
		<s:actionerror/>
				
		PPTFile: <s:file name="ppt" label="PPTFile"></s:file>
		PPTDesc: <s:textfield name="pptDesc[0]" label="PPTDesc"></s:textfield>
	
		<br><br>
		PPTFile:<s:file name="ppt" label="PPTFile"></s:file>
		PPTDesc:<s:textfield name="pptDesc[1]" label="PPTDesc"></s:textfield>

		<br><br>
		PPTFile:<s:file name="ppt" label="PPTFile"></s:file>
		PPTDesc:<s:textfield name="pptDesc[2]" label="PPTDesc"></s:textfield>
		
		<br><br>
		<s:submit></s:submit>
	</s:form>

Action

package com.atguigu.struts2.upload.app;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.List;

import javax.servlet.ServletContext;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;

public class UploadAction extends ActionSupport {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private List<File> ppt;
	private List<String> pptContentType;
	private List<String> pptFileName;
	
	private List<String> pptDesc;
	
	
	public List<File> getPpt() {
		return ppt;
	}


	public void setPpt(List<File> ppt) {
		this.ppt = ppt;
	}


	public List<String> getPptContentType() {
		return pptContentType;
	}


	public void setPptContentType(List<String> pptContentType) {
		this.pptContentType = pptContentType;
	}


	public List<String> getPptFileName() {
		return pptFileName;
	}


	public void setPptFileName(List<String> pptFileName) {
		this.pptFileName = pptFileName;
	}


	public List<String> getPptDesc() {
		return pptDesc;
	}


	public void setPptDesc(List<String> pptDesc) {
		this.pptDesc = pptDesc;
	}


	@Override
	public String execute() throws Exception {
		
		System.out.println(ppt);
		System.out.println(pptContentType);
		System.out.println(pptFileName);
		System.out.println(pptDesc);
		//主要是是下面注释的这些
//		ServletContext servletContext = ServletActionContext.getServletContext();
//		String dir = servletContext.getRealPath("/files/" + pptFileName);
//		System.out.println(dir);
//		
//		FileOutputStream out = new FileOutputStream(dir);
//		FileInputStream in = new FileInputStream(ppt);
//		
//		byte [] buffer = new byte[1024];
//		int len = 0;
//		
//		while((len = in.read(buffer)) != -1){
//			out.write(buffer, 0, len);
//		}
//		
//		out.close();
//		in.close();
		
		return "input";
	}
}

文件下载

Action

package com.atguigu.struts2.download.app;

import java.io.FileInputStream;
import java.io.InputStream;

import javax.servlet.ServletContext;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;

public class DownLoadAction extends ActionSupport {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private String contentType;
	private long contentLength;
	private String contentDisposition;
	private InputStream inputStream;
	
	public String getContentType() {
		return contentType;
	}

	public long getContentLength() {
		return contentLength;
	}

	public String getContentDisposition() {
		return contentDisposition;
	}
	
	public InputStream getInputStream() {
		return inputStream;
	}

	@Override
	public String execute() throws Exception {

		//确定各个成员变量的值
		contentType = "text/html";
		contentDisposition = "attachment;filename=hidden.html";
		
		ServletContext servletContext = 
				ServletActionContext.getServletContext();
		String fileName = servletContext.getRealPath("/files/hidden.html");
		inputStream = new FileInputStream(fileName);
		contentLength = inputStream.available();
		
		return SUCCESS;
	}
}

表单的重复提交问题

1). 什么是表单的重复提交

> 在不刷新表单页面的前提下: 

>> 多次点击提交按钮

>> 已经提交成功, 按 "回退" 之后, 再点击 "提交按钮".

>> 在控制器响应页面的形式为转发情况下,若已经提交成功, 然后点击 "刷新(F5)"

> 注意:

>> 若刷新表单页面, 再提交表单不算重复提交

>> 若使用的是 redirect 的响应类型, 已经提交成功后, 再点击 "刷新", 不是表单的重复提交

2). 表单重复提交的危害:  

3). Struts2 解决表单的重复提交问题:

I. 在 s:form 中添加 s:token 子标签

> 生成一个隐藏域

> 在 session 添加一个属性值

> 隐藏域的值和 session 的属性值是一致的.

II. 使用 Token 或 TokenSession 拦截器.

> 这两个拦截器均不在默认的拦截器栈中, 所以需要手工配置一下

> 若使用 Token 拦截器, 则需要配置一个 token.valid 的 result

> 若使用 TokenSession 拦截器, 则不需要配置任何其它的 result

III. Token VS TokenSession

> 都是解决表单重复提交问题的

> 使用 token 拦截器会转到 token.valid 这个 result

> 使用 tokenSession 拦截器则还会响应那个目标页面, 但不会执行 tokenSession 的后续拦截器. 就像什么都没发生过一样!

IV. 可以使用 s:actionerror 标签来显示重复提交的错误消息.

该错误消息可以在国际化资源文件中覆盖. 该消息可以在 struts-messages.properties 文件中找到

struts.messages.invalid.token=^^The form has already been processed or no token was supplied, please try again.

Html

<body>
	
	<s:form action="testToken">
		<s:token></s:token>
		<s:textfield name="username" label="Username"></s:textfield>
		<s:submit></s:submit>
	</s:form>
	
</body>

Struts.xml

	<action name="testToken" class="com.atguigu.struts2.token.app.TokenAction">
			<interceptor-ref name="hello"></interceptor-ref>
			<interceptor-ref name="tokenSession"></interceptor-ref>
			<interceptor-ref name="defaultStack"></interceptor-ref>
			<result>/success.jsp</result>
			<result name="invalid.token">/token-error.jsp</result>
		</action>

Action

package com.atguigu.struts2.token.app;

import com.opensymphony.xwork2.ActionSupport;

public class TokenAction extends ActionSupport{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private String username;
	
	public void setUsername(String username) {
		this.username = username;
	}
	
	public String getUsername() {
		return username;
	}
	
	@Override
	public String execute() throws Exception {
		Thread.sleep(2000); 
		System.out.println(username);
		return SUCCESS;
	}
	
}

自定义拦截器

1). 具体步骤

I. 定义一个拦截器的类

> 可以实现 Interceptor 接口

> 继承 AbstractInterceptor 抽象类

II. 在 struts.xml 文件配置.

ref name="hello">ref>

ref name="defaultStack">ref>

/success.jsp

/token-error.jsp

III. 注意: 在自定义的拦截器中可以选择不调用 ActionInvocation 的 invoke() 方法. 那么后续的拦截器和 Action 方法将不会被调用.

Struts 会渲染自定义拦截器 intercept 方法返回值对应的 result

用户权限验证,用户是否登录

===========================================================

暂时关于struts知识点记了一下,以后学了再添加。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值