Java Web SSH-Struts 2框架复习
一、 自定义MVC框架
1. Struts2和MVC
Struts2是一种基于MVC的Web应用框架,下面看看Struts2和MVC的关系。这里只是先讲一下Struts2是如何跟MVC对应的,其中一些名词所代表的具体功能,比如前端控制器(FilterDispatcher)、动作(Action)、结果(Result)等。
控制器——FilterDispatcher【MVC的核心】
用户请求首先到达前端控制器FilterDispatcher。FilterDispatcher负责根据用户提交的URL和struts.xml中的配置,来选择合适的动作(Action),让这个Action来处理用户的请求。FilterDispatcher其实是一个过滤器(Filter,servlet规范中的一种web组件),它是Struts2核心包里已经做好的类,不需要我们去开发,只是要在项目的web.xml中配置一下即可。FilterDispatcher体现了J2EE核心设计模式中的前端控制器模式。
动作——Action
在用户请求经过FilterDispatcher之后,被分发到了合适的动作Action对象。Action负责把用户请求中的参数组装成合适的数据模型,并调用相应的业务逻辑进行真正的功能处理,获取下一个视图展示所需要的数据。Struts2 的Action,相比于别的web框架的动作处理,它实现了与Servlet API的解耦,使得Action里面不需要再直接去引用和使用HttpServletRequest与HttpServletResponse等接口。因而使得Action的单元测试更加简单,而且强大的类型转换也使得我们少做了很多重复的工作。
视图——Result
视图结果用来把动作中获取到的数据展现给用户。在Struts2中有多种优秀的结果展示方式,常规的jsp,模板 freemarker、velocity,还有各种其它专业的展示方式,如图表jfreechart、报表JasperReports、将XML转化为 HTML的XSLT等等。而且各种视图结果在同一个工程里面可以混合出现。
2. MVC开发的历史
起初:
采用Model I模式进行开发,模式有纯JSP开发和JSP+JavaBean混合模式开发。
缺点:大量代码的重复和高耦合,后期难以维护和扩展。
进化:
采用Model II模式进行开发,采用JSP+Servlet+JavaBean共同开发应用程序,体现了基于MVC的设计模式,把数据显示、流程控制和业务逻辑进行分离,降低耦合度。
3.
MVC模式示意图
4. DTD验证
DTD即文档类型定义,可以用来描述XML文档的结构,对XML文件的节点元素进行约束。
引用外部DTD文件验证XML文档合法性的示例:
外部DTD文档:
<?xmlversion="1.0" encoding="UTF-8"?>
<!-- 根元素为students;student后的星号表示student子节点会出现0次到多次 -->
<!ELEMENTstudents (student*)>
<!--ELEMENT关键字声明了student子节点其下有name,college,telephone,notes这几个属性节点 -->
<!ELEMENTstudent (name,college,telephone,notes)>
<!--ATTLIST声明各个属性节点下的DTD属性 -->
<!ATTLISTstudent age CDATA "20" >
<!ELEMENTname (#PCDATA)>
<!--college属性节点下可以放置任何字符数据,但也可以有属性leader -->
<!ELEMENTcollege (#PCDATA)>
<!-- 元素名称为college;属性名称为leader;属性的数据类型为CDATA(字符数据);默认值为leader-->
<!ATTLISTcollege leader CDATA "leader" >
<!ELEMENTtelephone (#PCDATA)>
<!ELEMENT notes (#PCDATA)>
要验证的XML文档:
<?xmlversion="1.0" encoding="UTF-8"?>
<!-- 在此处引用了外部的DTD文件 -->
<!DOCTYPEstudents SYSTEM "Student.dtd">
<students>
<student age="25">
<name>张三</name>
<!-- college属性节点中可以包含leader属性,这里赋值了自定义的值-->
<collegeleader="123">牛津大学</college>
<telephone>12345678901</telephone>
<notes>Microsoft Office365</notes>
</student>
<student>
<name>李四</name>
<!-- college属性节点中可以包含leader属性,这里虽然没有写,其实leader的值为leader-->
<college>复旦大学</college>
<telephone>98765432110</telephone>
<notes>MicrosoftOneNote</notes>
</student>
</students>
5. 反射机制
概念:在运行时,动态获取类、对象及动态调用对象方法的功能。
动态性质:
A. 运行时生成对象实例
B. 运行时调用方法
C. 运行时更改属性
6. Java反射常用API:
A. Class类 – 反射核心类
B. Field类 – 类的属性
C. Method类 – 类的方法
D. Constructor类 – 类的构造方法
7. 使用反射的基本步骤:
A. 导入java.lang.reflect包
B. 获得要操作类的java.lang.Class对象
C. 使用反射API进行操作
8. 反射类的示例
//加载类:使用Class类的forName方法根据类的全限定名加载类到对象d上
Class d =Class.forName("java.util.Date");
//获取方法:使用Java的反射API中的Method类的getMethod()方法获得其中的某一个方法getHours(),并设定参数为null
Method date1 =d.getMethod("getHours", null);
//调用方法:实例化一个基类类型的对象,使用invoke()方法调用上面获取到的方法
// 因为getHours()方法不需要提供输入参数,所以参数赋值为null即可
Object resu =date1.invoke(new Date(), null);
//输出调用结果
System.out.println(resu.toString());
二、 Struts 2入门
1. Struts 1模式的缺点:
A. 表现层支持单一– Struts 1仅支持JSP显示数据
B. 严重依赖Servlet API – Servlet必须依赖Web容器,一旦脱离Web容器Servlet将难以完成初始化
C. 不利于代码重用– Servlet中高耦合度的代码和网页元素混杂在一起,难以重用和维护扩展
2. Struts 2的由来
3. Struts 2的包定义
4. Action处理后返回的结果类型
常量 | 含义 |
SUCCESS | 程序处理正常 |
NONE | 程序处理正常结束且不做任何用户界面的显示 |
ERROR | 程序处理结果失败 |
INPUT | 表单验证失败,需要继续输入才可正常处理 |
LOGIN | 需要用户登录后才可继续操作 |
*这些值仅仅是Action类中定义的静态字符串常量,还可以自己自定义返回的字符串,然后在struts.xml中根据自定义的结果字符串进行不同的操作。
5. Action可以接收请求的数据,也可以用来输出处理的结果。
Action处理并输出的过程图示:
此外,新建并编码好一个继承了ActionSupport类或者实现了Action接口的Action类后,需要在struts.xml文件中配置,配置文件的示例如下:
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//ApacheSoftware Foundation//DTD Struts Configuration 2.1//EN""http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<!--package定义处理请求的逻辑单元,继承自Struts 2框架中已经写好的默认包;namespace属性定义Action在哪个命名空间中,是可选属性 -->
<!--name属性必需且唯一 -->
<packagename="default" extends="struts-default"namespace="/">
<!--action元素用于配置此“工作单元”的类,name属性可以任意定义,访问此Action时以name属性.action路径访问,即name属性就是class属性中对应的Action的名字 -->
<actionname="loginUser"class="com.renthousesys.action.LoginAction">
<!--设置了“成功”的结果类型,值为处理结果成功后要跳转的页面 -->
<!--name属性表示result的逻辑视图名称 -->
<resultname="SUCCESS">success.jsp</result>
<!--设置了“错误”的结果类型,值为处理结果失败后要跳转的页面 -->
<!--name属性表示result的逻辑视图名称 -->
<resultname="ERROR">failed.jsp</result>
</action>
</package>
</struts>
*Struts 2的控制器在web.xml文件中配置而非struts.xml中配置。因为web.xml中包含控制全部Action跳转的过滤器,都是因为这个过滤器才使得Action能够正常的工作。
6. Struts 2访问Servlet API对象
i. 与Servlet API解耦的方式
域 | 获取方式 |
Request | ActionContext ac = ActionContext.getContext(); Map request = (Map)ac.get(“request”); |
Session | ActionContext ac = ActionContext.getContext(); Map session = ac.getSession(); |
Application | ActionContext ac = ActionContext.getContext(); Map application = ac.getApplication(); |
ii. 与Servlet API耦合的方式
使用org.apache.struts2.ServletActionContext类来获取ServletAPI对象。
ServletActionContext类的方法 | 可获取的Servlet API对象 |
public static HttpServletRequest getRequest() | HttpServletRequest对象、HttpSession对象 |
public static ServletContext getServletContext() | ServletContext()对象 |
public static HttpServletResponse getResponse() | HttpServletResponse()对象 |
*在与Servlet API耦合的方式中,没有单独获取Session的方法,可以通过public static HttpServletRequest getRequest()方法获取HttpServletRequest对象和HttpSession对象。
7. Struts 2数据校验
数据校验的步骤:
A. 在页面中使用OGNL表达式 <s:fielderror />标签在页面显示服务器返回的校验错误信息
B. 在Action类中重写validate()方法,将已知addFieldError(“[封装的属性名称]”,”[错误提示]”);
8. Struts 2的标签
两种标签:UI标签(表单标签)、通用标签。
前提条件:在页面顶部导入类库:
<%@ tagliburi=”/strut-tags” prefix=”s” %>
<s:iterator value=”[Action类中封装的集合属性名称]”>
<s:propertyvalue=”[Action类中封装的集合的单个属性名]” />
</s:iterator>
三、 Struts 2配置详解
1.
Struts 2执行登录过程的执行流程:
*Struts 2框架需要在web.xml中配置核心控制器StrutsPrepareAndExecuteFilter,用于框架的初始化、处理所有的请求。
*Action的作用:
A. 封装工作单元
B. 数据转移的场所
2. 带核心控制器的web.xml文件配置:
web.xml文件配置如下:
<?xmlversion="1.0" encoding="UTF-8"?>
<web-appversion="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<!-- welcome-file-list元素是指定欢迎页面的根元素-->
<welcome-file-list>
<!-- welcome-file元素定义单个的欢迎页面-->
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 第一个filter元素配置了Struts 2的核心控制器(过滤器),用于拦截所有的请求 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<!-- 第二个filter元素配置了Struts 2所有的Action类的action请求 -->
<!-- 如果请求的资源以.action结尾,则该请求将被输入到Struts2框架中进行处理 -->
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
<!-- 配置这个过滤器的目的是当要直接访问带OGNL表达式的JSP页面时防止因OGNL表达式造成的页面错误-->
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
</web-app>
3. struts.xml文件配置如下:
<?xmlversion="1.0" encoding="UTF-8" ?>
<!DOCTYPEstruts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<!-- 解决Struts 2框架处理中文乱码-->
<constant name="struts.i18n.encoding"value="UTF-8"></constant>
<!-- 定义包并继承默认包 -->
<package name="default"namespace="/" extends="struts-default">
<!-- 配置带通配符的Action-->
<actionname="login_*" class="com.hw.action.LoginAction"method="{1}">
<result name="success">page/{1}_success.jsp</result>
<resultname="error">page/{1}_failed.jsp</result>
<resultname="input">page/{1}_{1}.jsp</result>
</action>
</package>
</struts>
*method属性用于多个请求在同一个Action上,可以指定不同的执行方法,进行不同的操作。
*配置文件的加载顺序:
最先加载:struts.default.xml – 其后加载:struts-plugin.xml – 最后加载:struts.xml
4. Action动态方法的调用
在访问Action时不直接指定是哪一个方法,即在struts.xml中<action>元素中不指定method属性,而是以形如 actionName!methodName.action 的方式在URL上指定要调用当前Action中的某一个方法。
5. Result的配置
常用的结果类型
类型名称 | 作用 |
dispatcher | 转发形式,源数据依然存在,Struts 2的默认结果类型 |
redirect | 重定向形式,源数据会丢失 |
redirectAction | 重定向到新的Action上,源数据会丢失 |
6. Action中通配符的使用
四、 Struts 2深入
1. Struts 2框架
构成:
Struts 2框架的体系结构:
2. Struts 2的几个重要对象:
对象名 | 作用 | 备注 |
ActionMapper | 在请求和Action之间形成映射关系 |
|
ActionMapping | 保存了调用Action的映射信息 |
|
ActionProxy | 在XWork和真正的Action间充当代理的角色 | 因为操纵非实际对象,所以可以在Action之前之后各执行不同的操作 |
ActionInvocation | 表示Action的执行状态 | 保存了拦截器和Action的实例。ActionProxy创建ActionInvocation之后使用invoke()方法反射加载Action类并按顺序执行拦截器,执行完毕并返回字符串后再次以相反的顺序执行拦截器 |
Interceptor | 在请求处理之前之后执行的组件 | 是Struts 2框架的重要特性 |
*ActionInvocation通过intercept()方法将一个拦截器的控制权转交给另一个符合条件的拦截器。
3. Struts 2的拦截器
拦截器名称 | 作用 |
param拦截器 | 将请求中的数据设置到Action的属性上 |
staticParams拦截器 | 将配置的Action的param元素参数设置到Action的属性上 |
servletConfig拦截器 | 便于向Struts 2中的Action注入Servlet API |
fileUpload拦截器 | 将multipart-form/data转换为常规数据并设置到Action的属性上 |
validation拦截器 | 执行数据校验 |
workflow拦截器 | 数据校验错误时终止执行流程 |
exception拦截器 | 捕获异常(应位于所有拦截器中的首位) |
自定义拦截器 | 当内置拦截器不能满足需要时可以自行配置拦截器 |
4. 拦截器的执行周期:
A. 做Action执行前的预处理
B. 调用ActionInvocation的invoke()方法继续执行后续拦截器或不调用invoke()方法直接返回字符串终止执行
C. 做Action执行后的处理
5. 自定义拦截器的配置
配置步骤:
A. 建立针对某个Action的拦截器类,要继承AbstractInterceptor类,重写intercept()方法,再此方法中根据业务需求返回结果字符串进行重定向以达到拦截的目的
B. 在struts.xml配置文件中,使用<interceptor>元素定义拦截器,使用<interceptor-ref>元素引用拦截器使其生效
示例:
拦截器类:
packagecom.hw.interceptor;
importjava.util.Map;
importcom.hw.tool.AccountType;
importcom.opensymphony.xwork2.ActionInvocation;
importcom.opensymphony.xwork2.interceptor.AbstractInterceptor;
/**
* 房屋管理权限的拦截器
*
* 项目名称:Chapt04_HomeWork1类名称:HouseManageInterceptor 类描述: 创建人:晶 创建时间:2015-10-18
* 上午9:47:32 修改人:晶 修改时间:2015-10-18 上午9:47:32 修改备注:
*
* @version 1.0 Alpha
*
*/
public classHouseManageInterceptor extends AbstractInterceptor {
@Override
public Stringintercept(ActionInvocation invocation) throws Exception {
// TODO Auto-generated methodstub
// 尝试获取session中登录的用户类型
Map<String, Object>session = invocation.getInvocationContext()
.getSession();
String loginAccountType =(String) session.get("login");
if(loginAccountType.contains(AccountType.ADMIN)) {
return"manage";
} else {
return"normal";
}
}
}
struts.xml配置文件:
<?xmlversion="1.0" encoding="UTF-8" ?>
<!DOCTYPEstruts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<constantname="struts.i18n.encoding" value="UTF-8"></constant>
<package name="default"namespace="/" extends="struts-default">
<!-- 定义拦截器元素-->
<interceptors>
<!-- 定义一个用于检测是否登录的拦截器 -->
<interceptorname="permissionControllerOfLogin"class="com.hw.interceptor.LoginInterceptor" />
<!-- 定义一个用于控制权限的拦截器 -->
<interceptorname="permissionControllerOfManager"
class="com.hw.interceptor.HouseManageInterceptor"/>
<!-- 为了方便,定义一个拦截器栈-->
<interceptor-stackname="myStack">
<interceptor-refname="permissionControllerOfLogin" />
<interceptor-refname="permissionControllerOfManager" />
<interceptor-refname="defaultStack" />
</interceptor-stack>
</interceptors>
<!-- 定义全局结果 -->
<global-results>
<resultname="login">login_login.jsp</result>
<resultname="manage">house_manager.jsp</result>
<resultname="normal">forbidden_manage.jsp</result>
</global-results>
<!-- 配置管理页面相关的Action-->
<actionname="running" class="com.hw.action.ManageAction"
method="running">
<resultname="success">house_manager.jsp</result>
<!-- 要拦截的是管理页面,因为具有管理员权限的用户(即管理员用户)才可以访问管理页面 -->
<interceptor-refname="myStack" />
</action>
</package>
</struts>
6. Struts 2的文件上传
注意事项:
A. 提交页面的提交数据传输类型enctype必须设为multipart/form-data,提交方式必须为post
B. 在处理的Action中,要至少设置3个属性,分别为File类型的upload属性、String类型的uploadContentType属性和String类型的uploadFileName属性,其中要注意页面type为file的表单的name属性要和Action中定义的第一个File类型的属性名一致,且后两个属性都要以这个File类型的属性名开头
示例:
JSP上传页面:
<divstyle="margin: 0px auto; width: 450px;">
<s:formaction="upload.action" enctype="multipart/form-data"method="post">
<s:filename="upload" label="选择一个文件" />
<s:submitvalue="上传头像" />
</s:form>
</div>
处理文件上传的Action:
packagecom.hw.action;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
importorg.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.Action;
importcom.opensymphony.xwork2.ActionSupport;
/**
* 上传文件的Action
*
* 项目名称:Chapt04_HomeWork2类名称:UploadAction 类描述:创建人:晶 创建时间:2015-10-18下午1:56:14
* 修改人:晶 修改时间:2015-10-18 下午1:56:14 修改备注:
*
*@version 1.0 Alpha
*
*/
public class UploadAction extendsActionSupport {
//和页面文件表单的name属性一致的File类型的upload属性
privateFile upload;
//文件上传的类型属性
privateString uploadContentType;
//文件的名称属性
privateString uploadFileName;
//保存路径属性【这个savePath已经在struts.xml中以param参数的形式配置好】
privateString savePath;
publicFile getUpload() {
returnupload;
}
publicvoid setUpload(File upload) {
this.upload= upload;
}
publicString getUploadContentType() {
returnuploadContentType;
}
publicvoid setUploadContentType(String uploadContentType) {
this.uploadContentType= uploadContentType;
}
publicString getUploadFileName() {
returnuploadFileName;
}
publicvoid setUploadFileName(String uploadFileName) {
this.uploadFileName= uploadFileName;
}
publicString getSavePath() {
savePath= ServletActionContext.getServletContext().getRealPath(
savePath);
returnsavePath;
}
publicvoid setSavePath(String savePath) {
this.savePath= savePath;
}
@Override
publicString execute() throws Exception {
//TODO Auto-generated method stub
//提交时enctype指定为二进制数据传输,那么一定要实例化一个二进制数组循环写入到服务器上
byte[]buffer = new byte[1024];
//使用FileInputStream从上传的路径中读取文件,参数即从客户机上传的绝对路径
FileInputStreamfis = new FileInputStream(getUpload());
//FileOutputStream写入到Tomcat服务器的指定路径下
FileOutputStreamfos = new FileOutputStream(getSavePath() + "\\"
+this.getUploadFileName());
intlength = fis.read(buffer);
//循环写入
while(length > 0) {
fos.write(buffer,0, length);
length= fis.read(buffer);
}
//关闭流,刷新缓冲区之后关闭写入器释放资源
fis.close();
fos.flush();
fos.close();
//返回SUCCESS表示上传成功
returnAction.SUCCESS;
}
}
struts.xml配置文件:
<?xmlversion="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//ApacheSoftware Foundation//DTD Struts Configuration 2.1//EN""http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<constantname="struts.i18n.encoding"value="UTF-8"></constant>
<packagename="default" namespace="/"extends="struts-default">
<actionname="upload" class="com.hw.action.UploadAction">
<!--在Action属性中定义的savePath属性的值就会从这里读取并加载到对象中 -->
<paramname="savePath">/upload</param>
<resultname="success">success.jsp</result>
<resultname="input">upload.jsp</result>
</action>
</package>
</struts>
7. Struts 2的文件下载
stream结果类型的配置参数
参数名称 | 作用 |
contentType | 设置发送到浏览器的MIME类型 |
contentLength | 设置文件大小 |
contentDisposition | 设置响应的HTTP头信息中Content-Disposition参数的值 |
inputName | 指定Action中提供的inputStream类型的属性名 |
bufferSize | 设置读取和下载时的缓冲区大小 |
*重点是在struts.xml文件中配置下载的Action时,<result>元素的type设置为stream。
示例:
JSP页面:
<body>
<ahref="download.action?fileName=334.gif">点击此处下载文档</a>
</body>
*334.gif表示要下载的文件名,这里通过struts.xml中配置的contentDisposition参数的值来实现。
文件上传的Action:
packagecn.jbit.houserent.action;
importjava.io.BufferedInputStream;
importjava.io.FileInputStream;
importjava.io.FileNotFoundException;
importjava.io.InputStream;
importorg.apache.struts2.ServletActionContext;
importcom.opensymphony.xwork2.ActionSupport;
publicclass FileDownAction extends ActionSupport {
//读取下载文件的目录
private String inputPath;
//下载文件的文件名
private String fileName;
//读取下载文件的输入流
private InputStream inputStream;
//下载文件的类型
private String conetntType;
//创建InputStream输入流
public InputStream getInputStream() throws FileNotFoundException{
Stringpath=ServletActionContext.getServletContext().
getRealPath(inputPath);
return newBufferedInputStream(new FileInputStream(path+"\\"+
fileName));
}
@Override
public String execute() throwsException {
return SUCCESS;
}
public void setInputStream(InputStreaminputStream) {
this.inputStream =inputStream;
}
public String getFileName() {
return fileName;
}
public void setFileName(StringfileName) {
this.fileName = fileName;
}
public String getConetntType() {
return conetntType;
}
public String getInputPath() {
return inputPath;
}
public void setInputPath(StringinputPath) {
this.inputPath = inputPath;
}
public void setConetntType(StringconetntType) {
this.conetntType =conetntType;
}
}
struts.xml配置文件:
<?xmlversion="1.0" encoding="UTF-8" ?>
<!DOCTYPEstruts PUBLIC
"-//Apache Software Foundation//DTDStruts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constantname="struts.i18n.encoding" value="UTF-8"/>
<constantname="struts.ui.theme" value="simple"/>
<package name="default"namespace="/" extends="struts-default">
<!-- 配置下载的Action-->
<action name="download"class="cn.jbit.houserent.action.FileDownAction">
<!-- 设置Action中读取下载文件的目录属性参数 -->
<paramname="inputPath">/upload</param>
<!-- 设置结果类型为stream -->
<result name="success"type="stream">
<!-- 设置contentType参数为application/octet-stream,表示下载的文件类型为可执行文件 -->
<paramname="contentType">application/octet-stream</param>
<!-- 设置inputName参数为inputStream,对应Action类中inputStream类型的属性名 -->
<paramname="inputName">inputStream</param>
<!-- 设置contentDisposition参数 -->
<paramname="contentDisposition">attachment;filename="${fileName}"</param>
<!-- 设置缓冲区大小-->
<paramname="bufferSize">4096</param>
</result>
</action>
</package>
</struts>
五、 OGNL对象导航图语言
1. 基本概念
工作在视图层,用于取代JSP页面的小脚本,简化数据访问,是EL表达式的加强。
2. OGNL的完整执行过程原理
3. OGNL的内置转换器
转换器名称 | 作用 |
String | 将int、long、double、boolean、String、java.util.Date类型转换为字符串 |
Boolean/boolean | 在字符串和布尔值间进行转换 |
Character/char | 在字符串和字符间进行转换 |
Date | 在字符串和日期间进行转换 |
int/Integer、float/Float、long/Long、double/Double | 在字符串和数值数据类型间进行转换 |
4. 自定义转换器
配置自定义转换器的步骤如下:
A. 创建继承于org.apache.struts2.util.StrutsTypeConventer抽象类的类,重写publicObject convertFromString(Map arg0, String[] arg1, Class arg2)方法和publicString convertToString(Map arg0, Object arg1)方法,前者用于从字符串转换为指定对象,后者用于从对象转换为字符串
B. 创建属性文件:
i. 全局转换器
在src根目录下创建名为xwork-conversion.properties的属性文件,内容为转换类的全限定名=类型转换器类的全限定名
ii. 特定类型转换器
在与特定类相同目录下创建ClassName-conversion.properties 属性文件,内容为 特定类的属性名=类型转换器类的全限定名
示例:
转换器类:
package converter;
import java.text.DateFormat;
importjava.text.SimpleDateFormat;
importjava.util.Date;
importjava.util.Map;
importorg.apache.struts2.util.StrutsTypeConverter;
importcom.opensymphony.xwork2.conversion.TypeConversionException;
public classDateConverter extends StrutsTypeConverter {
//定义时间格式的枚举,其中写到的日期格式都可以被成功转换
private final DateFormat[]dfs = { new SimpleDateFormat("yyyy年MM月dd日"),
newSimpleDateFormat("yyyy-MM-dd"),
newSimpleDateFormat("yyyy/MM/dd"),
newSimpleDateFormat("yyyyMMdd") };
//重写了从字符串转换为日期的方法
@Override
public ObjectconvertFromString(Map arg0, String[] arg1, Class arg2) {
// TODOAuto-generated method stub
String d = arg1[0];
for (int i = 0; i< dfs.length; i++) {
try {
returndfs[i].parse(d);
} catch(Exception e) {
//TODO: handle exception
//注意:这里不中断循环执行,这里表示当第一个格式不能转换那么就继续枚举其它格式看能否转换
continue;
}
}
//如果枚举了所有可以转换的格式后都没能转换,那么就抛出异常
throw newTypeConversionException();
}
//重写了从日期转换为字符串的方法
@Override
public StringconvertToString(Map arg0, Object arg1) {
// TODOAuto-generated method stub
Date d = (Date)arg1;
return newSimpleDateFormat("yyyy-MM-dd").format(d);
}
}
全局配置文件xwork-conversion.properties内容:
java.util.Date=converter.DateConverter
5. 处理类型转换错误
前提条件:
A. 启动StrutsConversionErrorInterceptor拦截器(struts.xml中要继承struts-default包)
B. 实现ValidationAware接口,ActionSupport已实现该接口
C. 配置input结果映射
D. 页面中使用OGNL表达式的<s:fielderror>标签输出转换错误
配置两种范围的错误转换信息:
i. 修改所有类型的转换错误信息
a) 在struts.xml文件中配置一个常量:
<constant name="struts.custom.i18n.resources"value="message"></constant>
b) 在src根目录下创建 message.properties 属性文件,内容为:
xwork.default.invalid.fieldvalue=\u5B57\u6BB5"{0}"\u7684\u503C\u65E0\u6548
ii. 定制特定字段的类型转换错误信息
a) 在和自定义转换器的Action同包下创建ClassName.properties配置文件,其内容为 invalid.fieldvalue.timeDate=\u65E5\u671F\u683C\u5F0F\u9519\u8BEF,其中invalid.fieldvalue为固定语法,timeDate为要验证的字段名称
示例:
设置所有类型的转换错误信息:
struts.xml配置文件:
<?xmlversion="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//ApacheSoftware Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<!--配置message属性文件 -->
<constantname="struts.custom.i18n.resources" value="message"/>
<constantname="struts.i18n.encoding" value="UTF-8"/>
<constantname="struts.ui.theme" value="simple"/>
<packagename="renthouse" extends="struts-default">
<default-action-refname="defaultAction" />
<actionname="dateConvert"class="cn.jbit.action.DateConvertAction">
<resultname="input">index.jsp</result>
<resultname="success">success.jsp</result>
</action>
</package>
</struts>
src根目录下的message.properties配置文件:
xwork.default.invalid.fieldvalue=field"{0}"'svalue is invalid
设置特定字段的类型转换错误信息:
自定义转换器类和错误信息提示的属性文件目录结构:
自定义转换器类:
packagecn.jbit.action;
import java.util.Date;
import com.opensymphony.xwork2.ActionSupport;
public class DateConvertAction extends ActionSupport {
privateDate timeDate;
publicString execute(){
returnSUCCESS;
}
publicDate getTimeDate() {
returntimeDate;
}
publicvoid setTimeDate(Date timeDate) {
this.timeDate= timeDate;
}
}
错误提示属性文件:
invalid.fieldvalue.timeDate=\u65E5\u671F\u683C\u5F0F\u9519\u8BEF
6. ActionContext
ActionContext(运行上下文)所包含的对象:
*attr按照pageContext – request –session – application 的顺序依次遍历查找、访问属性;
*访问值栈中的属性无需加“#”号就可直接访问到;若访问非值栈中的属性必须加“#”号写域名来取出属性;
*若定义变量时没有指定变量的作用域(scope),则会放入Action中的同时也放入request域中。
注意:
<s:set name=”country1” value=”China” /> 这里会把China当作变量【未加单引号】,表示将China这个变量的值赋值给country1这个变量,若China没有值当输出country1时将什么也不输出
<s:set name=”country1” value=”’China’ ”/> 这里会把China当作字符串【加了单引号】,表示将China这个字符串赋值给country1这个变量
7. URL标签
示例:
使用URL标签生成URL链接的JSP页面:
<%@ pagelanguage="java" import="java.util.*"pageEncoding="UTF-8"%>
<%@ tagliburi="/struts-tags" prefix="s"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":"+ request.getServerPort()
+ path + "/";
%>
<!DOCTYPE HTML PUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<basehref="<%=basePath%>">
<title>查询结果</title>
<metahttp-equiv="pragma" content="no-cache">
<metahttp-equiv="cache-control" content="no-cache">
<metahttp-equiv="expires" content="0">
<metahttp-equiv="keywords" content="keyword1,keyword2,keyword3">
<metahttp-equiv="description" content="This is my page">
</head>
<body>
<div style="margin: 0px auto; width:500px;">
<b> 房屋标题 房屋位置 联系人 房屋类型 房屋价格 访问地址 </b> <br />
<div style="margin: 0px auto; width:95%;">
<s:iteratorvalue="houses">
<a
href="
<s:urlvalue="detail.jsp">
<s:paramname="houseId" value="id" />
</s:url>
"><s:property
value="title"/> </a>
<s:propertyvalue="street.district.name" />
<s:property value="street.name"/>
<s:propertyvalue="contact" />
<s:propertyvalue="types.name" />
<s:propertyvalue="price" />
<s:urlvalue="detail.jsp">
<s:paramname="houseId" value="id" />
</s:url>
<br />
</s:iterator>
</div>
<a href="#">上一页</a><ahref="#">下一页</a><a href="#">首页</a><ahref="#">末页</a>
</div>
</body>
</html>
六、 Struts 2数据校验和国际化
1. 使用编写Java代码的方式进行数据校验
重写validate()方法进行逻辑判断,将可能出现的错误使用addFieldError()方法进行添加,之后在页面使用<s:fielderror>标签对错误进行输出显示。
还可针对Action中特定的方法进行数据校验,方法名为validateXxx(),如Action中有一个执行方法为login(),则验证方法为validateLogin()
调用顺序为:先调用特定的validateXxx()方法,最后调用validate()方法,且validate()方法无论怎样始终都会被执行。
2. 类型转换和数据校验
原理:
当用户提交数据时,即使转换失败了,也会执行validate()方法进行验证,因为Struts 2框架对转换失败的变量会赋值为null或0,同时错误信息也会被添加到fieldError中去。
当类型转换失败时,并不是直接返回表单页面,而会继续执行校验方法进行数据校验。
3. Struts 2验证框架
Struts 2内置校验器(常用)
校验器类型 | 校验器名称 | 作用 |
必填校验器 | required | 指定字段必须有值不能为空 |
必填字符串校验器 | requiredstring | 指定字段不为空且长度大于0 |
整数校验器 | int | 指定字段的值在一定范围内 |
字符串长度校验器 | stringlength | 指定字段的长度在一定范围内 |
正则表达式校验器 | regex | 指定字段匹配一个正则表达式 |
*使用验证框架前提要验证的Action一定要继承ActionSupport类。
4. 验证框架的使用步骤:
A. 创建继承于ActionSupport类的Action类用于处理业务
B. 在struts.xml中配置好此Action
C. 创建含表单的提交页面供用户输入
D. 编写验证文件和校验规则
验证文件要和要验证的Action放于同包下,采用 ClassName-validation.xml或 ClassName-alias-validation.xml的命名方式(前者命名方式常用)(alias表示Action在struts.xml中配置的name属性的值,即Action的名字),
示例:
Action类:
packagecn.jbit.houserent.action;
importcn.jbit.houserent.bean.User;
importcom.opensymphony.xwork2.ActionSupport;
publicclass RegisterAction extends ActionSupport {
private User user; //用户实体属性
private String repassword; //确认密码属性
public String getRepassword() {
return repassword;
}
public void setRepassword(Stringrepassword) {
this.repassword = repassword;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String execute() {
return SUCCESS;
}
}
struts.xml配置文件:
<?xmlversion="1.0" encoding="UTF-8" ?>
<!DOCTYPEstruts PUBLIC
"-//Apache SoftwareFoundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constantname="struts.custom.i18n.resources" value="message"/>
<constantname="struts.i18n.encoding" value="UTF-8"/>
<constantname="struts.ui.theme" value="simple"/>
<package name="struts2"extends="struts-default">
<action name="register"class="cn.jbit.houserent.action.RegisterAction">
<resultname="success">success.jsp</result>
<resultname="input">register.jsp</result>
</action>
</package>
</struts>
JSP页面:
<body>
<h2>
<s:textname="register.title" />
</h2>
<s:fielderror></s:fielderror>
<s:formaction="register">
<table>
<tr><td><s:textname="name" />:</td><td><s:textfieldname="user.name" /></td></tr>
<tr><td><s:textname="password" />:</td><td><s:textfieldname="user.password" /></td></tr>
<tr><td><s:textname="repassword" />:</td><td><s:textfieldname="repassword" /></td></tr>
<tr><td><s:textname="telephone" />:</td><td><s:textfieldname="user.telephone" /></td></tr>
<tr><td><s:textname="username" />:</td><td><s:textfield name="user.username"/></td></tr>
<tr><tdcolspan="2"><s:submit value="%{getText('submit')}"/></td></tr>
</table>
</s:form>
</body>
校验文件和Action类的关系:
校验文件:
<?xmlversion="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//ApacheStruts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<fieldname="user.name">
<field-validatortype="requiredstring">
<paramname="trim">true</param>
<messagekey="name.null"/>
</field-validator>
<field-validatortype="stringlength">
<paramname="maxLength">10</param>
<paramname="minLength">6</param>
<messagekey="name.length"/>
</field-validator>
</field>
<fieldname="user.password">
<field-validatortype="requiredstring">
<paramname="trim">true</param>
<messagekey="password.null"/>
</field-validator>
<field-validatortype="stringlength">
<paramname="minLength">6</param>
<messagekey="password.length"/>
</field-validator>
</field>
<field name="repassword">
<field-validatortype="requiredstring">
<messagekey="repassword.null"/>
</field-validator>
<field-validatortype="fieldexpression">
<paramname="expression">user.password==repassword</param>
<messagekey="repassword.same"/>
</field-validator>
</field>
<fieldname="user.telephone">
<field-validatortype="requiredstring">
<messagekey="telephone.null"/>
</field-validator>
<field-validatortype="regex">
<paramname="regex">^(\d{3,4}-){0,1}(\d{7,8})$</param>
<message>电话号码格式不正确</message>
</field-validator>
</field>
<fieldname="user.username">
<field-validatortype="requiredstring">
<paramname="trim">true</param>
<messagekey="username.null"/>
</field-validator>
<field-validatortype="stringlength">
<paramname="maxLength">4</param>
<paramname="minLength">2</param>
<message>用户姓名长度必须在 ${minLength}和 ${maxLength}之间 </message>
</field-validator>
</field>
</validators>
5. 数据校验总结
数据校验要经过以下几个步骤:
A. 对输入的数据进行类型转换(Struts 2内置转换器),并封装成Action的属性
B. 若转换错误,则将错误信息放在ActionContext上,同时封装到fieldError中
C. 类型转换完毕后就会执行校验方法进行数据校验
D. 若到此步等候没有出现错误,则调用Action中要执行的方法进行业务处理,成功后返回字符串跳转到指定页面呈现视图,展示数据;若出现错误,则由workflow拦截器控制返回输入页面
当既使用了框架验证,又使用了Java代码验证的方式时,先执行框架验证,再执行代码验证。
6. 国际化和本地化
实现国际化的步骤:
A. 在struts.xml配置文件中配置常量:
<constant name="struts.custom.i18n.resources"value="message"/>
此处的message表示资源文件的基名,即文件开头的字符,可以随意定义
B. 在src根目录下创建针对不同国家语言的资源文件
C. 更改JSP页面的元素值,实现国际化
注意:
示例:
struts.xml配置文件:
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//ApacheSoftware Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constantname="struts.custom.i18n.resources" value="message"/>
<constantname="struts.i18n.encoding" value="UTF-8"/>
<constantname="struts.ui.theme" value="simple"/>
<packagename="struts2" extends="struts-default">
<actionname="register"class="cn.jbit.houserent.action.RegisterAction">
<resultname="success">success.jsp</result>
<resultname="input">register.jsp</result>
</action>
</package>
</struts>
国际化属性文件的目录结构:
message.properties属性文件:
register.page=\u65B0\u7528\u6237\u6CE8\u518C
register.title=\u65B0\u7528\u6237\u6CE8\u518C
name=\u7528\u6237\u540D
password=\u5BC6\u7801
repassword=\u786E\u8BA4\u5BC6\u7801
telephone=\u7535\u8BDD
username=\u7528\u6237\u59D3\u540D
submit=\u7ACB\u5373\u6CE8\u518C
message_en.properties属性文件:
register.page=User Register
register.title=New User Register
name=Name
password=Password
repassword=RePassword
telephone=Telephone
username=UserName
submit=Register Now
message_zh_HK.properties属性文件:
register.page=\u65B0\u7528\u6236\u8A3B\u518A
register.title=\u65B0\u7528\u6236\u8A3B\u518A
name=\u7528\u6236\u540D
password=\u5BC6\u78BC
repassword=\u78BA\u8A8D\u5BC6\u78BC
telephone=\u96FB\u8A71
username=\u7528\u6236\u59D3\u540D
submit=\u7ACB\u5373\u8A3B\u518A
JSP页面:
<body>
<h2>
<s:textname="register.title" />
</h2>
<s:fielderror></s:fielderror>
<s:formaction="register">
<table>
<tr><td><s:textname="name" />:</td><td><s:textfield name="user.name"/></td></tr>
<tr><td><s:textname="password" />:</td><td><s:textfieldname="user.password" /></td></tr>
<tr><td><s:textname="repassword" />:</td><td><s:textfieldname="repassword" /></td></tr>
<tr><td><s:textname="telephone" />:</td><td><s:textfieldname="user.telephone" /></td></tr>
<tr><td><s:textname="username" />:</td><td><s:textfieldname="user.username" /></td></tr>
<tr><tdcolspan="2"><s:submit value="%{getText('submit')}"/></td></tr>
</table>
</s:form>
</body>
若要浏览器模拟不同地区,则设置到Internet选项 – 语言 – 语言首选项窗口,将语言上移或下移即可改变当前的区域。
7. 国际化错误信息显示
步骤:
A. 在src根目录下的若干国际化属性文件中加入针对表单验证的错误信息表达式
B. 将与Action同包下的XML验证文件中的<message>标签的提示改为属性文件表达式左边的变量名即可
示例:
修改后的message.properties属性文件:
register.page=\u65B0\u7528\u6237\u6CE8\u518C
register.title=\u65B0\u7528\u6237\u6CE8\u518C
name=\u7528\u6237\u540D
password=\u5BC6\u7801
repassword=\u786E\u8BA4\u5BC6\u7801
telephone=\u7535\u8BDD
username=\u7528\u6237\u59D3\u540D
submit=\u7ACB\u5373\u6CE8\u518C
name.null=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A
name.length=\u7528\u6237\u540D\u957F\u5EA6\u5FC5\u987B\u5728${minLength}\u548C ${maxLength}\u4E4B\u95F4
password.null=\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A
password.length=\u5BC6\u7801\u957F\u5EA6\u5FC5\u987B\u5927\u4E8E\u7B49\u4E8E${minLength}
repassword.null=\u786E\u8BA4\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A
repassword.same=\u5BC6\u7801\u548C\u786E\u8BA4\u5BC6\u7801\u5FC5\u987B\u76F8\u540C
telephone.null=\u7535\u8BDD\u4E0D\u80FD\u4E3A\u7A7A
username.null=\u7528\u6237\u59D3\u540D\u4E0D\u80FD\u4E3A\u7A7A
修改后的message_en.properties属性文件:
register.page=User Register
register.title=New User Register
name=Name
password=Password
repassword=RePassword
telephone=Telephone
username=UserName
submit=Register Now
name.null=Name cannot be null
name.length=Name should be between${minLength} and ${maxLength}
password.null=Password cannot be null
password.length=Mininum passwordlength is ${minLength}
repassword.null=Repassword cannot benull
repassword.same=Repassword should besame with password
telephone.null= Telephone cannot be null
username.null=UserName cannot be null
修改后的message_zh_HK.properties属性文件:
register.page=\u65B0\u7528\u6236\u8A3B\u518A
register.title=\u65B0\u7528\u6236\u8A3B\u518A
name=\u7528\u6236\u540D
password=\u5BC6\u78BC
repassword=\u78BA\u8A8D\u5BC6\u78BC
telephone=\u96FB\u8A71
username=\u7528\u6236\u59D3\u540D
submit=\u7ACB\u5373\u8A3B\u518A
name.null=\u7528\u6236\u540D\u4E0D\u80FD\u70BA\u7A7A
name.length=\u7528\u6236\u540D\u9577\u5EA6\u5FC5\u9808\u5728${minLength}\u548C ${maxLength}\u4E4B\u9593
password.null=\u5BC6\u78BC\u9577\u5EA6\u4E0D\u80FD\u70BA\u7A7A
password.length=\u5BC6\u78BC\u9577\u5EA6\u5FC5\u9808\u5927\u65BC\u7B49\u65BC${minLength}
repassword.null=\u78BA\u8A8D\u5BC6\u78BC\u4E0D\u80FD\u70BA\u7A7A
repassword.same=\u5BC6\u78BC\u548C\u78BA\u8A8D\u5BC6\u78BC\u5FC5\u9808\u76F8\u540C
telephone.null=\u96FB\u8A71\u4E0D\u80FD\u70BA\u7A7A
username.null=\u7528\u6236\u59D3\u540D\u4E0D\u80FD\u70BA\u7A7A
修改后的XML验证文件:
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//ApacheStruts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<fieldname="user.name">
<field-validatortype="requiredstring">
<paramname="trim">true</param>
<messagekey="name.null"/>
</field-validator>
<field-validatortype="stringlength">
<paramname="maxLength">10</param>
<paramname="minLength">6</param>
<messagekey="name.length"/>
</field-validator>
</field>
<fieldname="user.password">
<field-validatortype="requiredstring">
<paramname="trim">true</param>
<messagekey="password.null"/>
</field-validator>
<field-validatortype="stringlength">
<paramname="minLength">6</param>
<messagekey="password.length"/>
</field-validator>
</field>
<fieldname="repassword">
<field-validatortype="requiredstring">
<messagekey="repassword.null"/>
</field-validator>
<field-validatortype="fieldexpression">
<paramname="expression">user.password==repassword</param>
<messagekey="repassword.same"/>
</field-validator>
</field>
<fieldname="user.telephone">
<field-validatortype="requiredstring">
<messagekey="telephone.null"/>
</field-validator>
<field-validatortype="regex">
<paramname="regex">^(\d{3,4}-){0,1}(\d{7,8})$</param>
<message>电话号码格式不正确</message>
</field-validator>
</field>
<fieldname="user.username">
<field-validatortype="requiredstring">
<paramname="trim">true</param>
<messagekey="username.null"/>
</field-validator>
<field-validatortype="stringlength">
<paramname="maxLength">4</param>
<paramname="minLength">2</param>
<message>用户姓名长度必须在 ${minLength}和 ${maxLength}之间 </message>
</field-validator>
</field>
</validators>
8. 资源文件的范围
在查找资源文件时,首先查找当前包下的Action范围内的资源文件,若没有找到,会沿着当前包逐级向上查找以基名为准的资源文件,直到顶级包,若仍未找到,则从struts.xml中配置的常量中的包查找全局的资源配置文件。【从小范围到大范围查找的顺序】