Struts2学习笔记
1. Struts2文件上传
1.1表单三要素
1)表单必须以POST方式提交,因为GET方式提交会有大小的限制(大约2000个字符),对POST,无大小限制要求。
2)表单必须加入enctype="multipart/form-data"表示上传的文件,以随着请求体,经过编码后,一同传入服务器。
3)每个表单项,必须取一个名字,因为服务端便于能过名字收集信息。
这里使用Struts2的标签:
<s:form action="upload"method="post"enctype="multipart/form-data">
<s:textfieldlabel="用户名"name="username"/>
<s:filelabel="上传文件"name="upload"/>
<s:submitvalue="提交"/>
</s:form>
1.2框架自身有fileUpload文件上传拦截器
1)在默认栈中,有一个fileUpload文件上传拦截器,无文件下载拦截器(要程序员自己处理)。org.apache.struts2.interceptor.FileUploadInterceptor(框架的拦截器)
2)在默认情况下,fileUpload拦截器只能上传不超过2M(默认)的数据,而且struts2框架的上传拦截器底层就是使用jakarta common fileupload开源组件来完成上传的,和传统Web应用上传一样。
1.3 fileUpload拦截器,自动注入三个参数
privateFile upload;//上传临时文件(fileUpload拦截器传入的参数)
privateString uploadContentType;//上传文件的类型(fileUpload拦截器传入的参数)
privateString uploadFileName;//上传文件的真实文件名(fileUpload拦截器传入的参数)
注意:upload和表单项<s:file>中的name属性一致;upload拼上 contentType 首字母c要大写。
1.4上传文件细节
1)上传成功的时候临时文件要删除
//关闭资源,并删除临时文件,应在finally中处理的
if(is !=null) is.close();
if(os !=null) os.close();
if(upload.exists())upload.delete();
2)上传文件总大小限制(上传多个文件时加起来的大小)(框架默认是2M)
在struts.xml中配置:<constantname="struts.multipart.maxSize" value="20971520"/>
或在struts.properties中配置struts.multipart.maxSize=20971520
超过限制抛异常:
org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException
3)上传文件单个大小限制
<!-- 设置[单]个上传文件不得超过5M -->
<param name="maximumSize">5242880</param>
4)上传文件扩展名限制
<!--设置上传文件的扩展名-->
<paramname="allowedExtensions">.jpg,.txt</param>
5)上传文件类型限制
<!--设置上传文件的类型-->
<paramname="allowedTypes">image/pjpeg,text/plain</param>
<action name="UploadAction"class="com.maple.upload.action.UploadAction"method="execute">
<resultname="success"type="dispatcher">/WEB-INF/upload_success.jsp</result>
<resultname="input"type="dispatcher">upload.jsp</result><!--有错时转到该页面 -->
<!-- 给框架默认文件上传拦截器(FileUploadInterceptor)设置值 -->
<interceptor-refname="fileUpload"><!-- name的值固定,可以在struts-default.xml文件中找到 -->
<!-- 设置上传单个文件大小为5M -->
<param name="maximumSize">5242880</param>
<!-- 设置上传文件的后缀名 -->
<param name="allowedExtensions">jpg,txt,doc,avi</param>
<!-- 设置上传文件的内容类型 -->
<param name="allowedTypes">image/jpeg,text/plain,application/msword,video/x-msvideo</param>
</interceptor-ref>
<interceptor-refname="defaultStack"/><!--这个是默认的拦截器,如果申明了其他拦截器,必须显式申明引用默认的 -->
</action>(如果没有显式引用则会报空指针,和JavaBean中必须有空参构造器类似)
1.5覆盖系统的提示信息
写一个任意.properies文件,存放的位置也是任意,在里面覆盖系统指定的key=value.
即覆盖struts-messages.properties文件。
在struts.xml文件中,加入以下代码,以至于让框架在错误提示时,找用户自定义的properties文件
<constant name="struts.custom.i18n.resources"value="配置文件的路径 "/>
<!-- value值不需要后缀名,可以放多个,用英文逗号隔开 -->
<constant name="struts.custom.i18n.resources"value="my_message"/>value值的就是资源文件的路径。
1.6上传多个文件
private File[]upload;
private String[]uploadContentType;
private String[]uploadFileName;
收集参数时,可以使用数组,也可以使用集合List。
表单中的<s:file>标签的name属性值要一样,这里全为upload。
<s:form action="UploadAction"method="post"enctype="multipart/form-data">
<s:textfieldlabel="用户名"name="username"/>
<s:filelabel="上传文件1"name="upload"/>
<s:filelabel="上传文件2"name="upload"/>
<s:filelabel="上传文件3"name="upload"/>
<s:submitvalue="提交"/>
</s:form>
1.6将参数注入Action中
在XxxAction中,有个字段叫uploadPath,提供setter和getter方法。在struts.xml文件中可以为其赋值,在对应的action节点中添加param节点。如:
<action name="UploadAction"class="com.maple.upload.action.UploadAction"method="execute">
<resultname="success"type="dispatcher">/WEB-INF/upload_success.jsp</result>
<!-- 为该action的uploadPath属性注入值 -->
<paramname="uploadPath">D:/Java_Develop/My_Projects/MDay32/WebContent/WEB-INF/upload</param>
</action>
2.拦截器Interceptor
2.1概念
Struts2 拦截器在访问某个 Action 方法之前或之后实施拦截, Struts2 拦截器是可插拔的。
拦截器栈(InterceptorStack): 将拦截器按一定的顺序联结成一条链. 在访问被拦截的方法时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被依次调用,类似于Filter在web.xml文件中的配置顺序调用。
每个拦截器都是实现了 com.opensymphony.xwork2.interceptor.Interceptor接口的Java 类。
init: 该方法将在拦截器被创建后立即被调用, 它在拦截器的生命周期内只被调用一次. 可以在该方法中对相关资源进行必要的初始化。
interecept: 每拦截一个动作请求,该方法就会被调用一次。
destroy: 该方法将在拦截器被销毁之前被调用, 它在拦截器的生命周期内也只被调用一次。
2.2拦截器的作用
在struts2中,拦截器能够对Action前后进行拦截,当请求某个action时执行拦截。
2.3自定义拦截器
1) 写一个类,实现Interceptor接口
2) 在struts.xml文件中配置(注册)栏截器
在package节点下注册:
<interceptors>
<!-- 注册自定义拦截器 -->
<interceptor name="LoginInterceptor"class="com.maple.interceptor.action.LoginInterceptor"/>
</interceptors>
在action节点下引用:(谁引用就拦截谁)
<!-- 引用自定义拦截器-->
<interceptor-refname="LoginInterceptor"/>
拦截器中各个方法的执行顺序:
默认构造器(服务器启动的时候执行,且在init方法之前)
init()方法(服务器启动的时候执行,在构造方法之后执行)
destory()方法(服务器关闭的时候执行)
intercept()方法(每次请求被拦截的Action时都会执行)
publicclass LoginInterceptorimplements Interceptor {
public LoginInterceptor(){
System.out.println("默认构造器");
}
@Override
publicvoid init() {
System.out.println("init()方法");
}
@Override
public Stringintercept(ActionInvocation invocation)throws Exception {
System.out.println("intercept()方法");
//调用invoke方法放行请求
return invocation.invoke();
}
@Override
publicvoid destroy() {
System.out.println("destroy()方法");
}
}
2.4栏截器的执行顺序
在struts.xml文件中,<interceptor-ref/>的引用顺序一致,先引用的先执行,后引用的后执行。
如果某个拦截器出错或不允许通过,那么下一个拦截器无法执行
需要拦截哪个Action,就在哪个Action对应的<action>标签中配置即可
在部署web应用时,拦截器的空参构造方法和init()方法各执行一次,每次请求时,intercept()都会执行一次 。
2.5拦截器栈
<interceptors>
<!-- 注册自定义拦截器 -->
<interceptorname="LoginInterceptor"class="com.maple.interceptor.action.LoginInterceptor"/>
<!-- 申明自定义拦截器栈 -->
<interceptor-stackname="myStack">
<!-- 这个是默认的拦截器,如果申明了其他拦截器,必须显式申明引用默认的 -->
<interceptor-refname="defaultStack"/>
<!-- 引用自定义的拦截器 -->
<interceptor-refname="LoginInterceptor"/>
</interceptor-stack>
</interceptors>
在Action节点中引用自定的拦截器栈:
<!-- 引用自定义的拦截器栈 -->
<interceptor-refname="myStack"/>
3.国际化
3.1国际化的概念
国际化(Internationalization由于在I和n之间有18个字母,所以也简称作I18N)是指对程序的某种处理使之能够运行于不同的区域和语言环境中。一个国际化的软件应该具有以下一些特征:
1、 能够运行于不同区域和语言环境中;
2、 如果提供资源文件,可以即时增加对某种语言的支持,而且可以通过修改资源文件内容来定制自己的界面显示;
3、 界面布局以及其它一些地域相关的信息显示能够符合该区域使用者的习惯;
4、 最重要的一点就是,你的应用程序必须能够实实在在支持国际化使用者的需求,不仅仅是显示的正确性,还包括数据的正确处理,操作的正确性。
提到国际化就不得不提本地化。本地化(Localization简称作L10N),就是调整软件以适应于特定区域和语言环境的一个过程。可以说,国际化是本地化的一个前奏,一个良好国际化的软件可以使其本地化工作事半功倍。比如,现在Windows中英文版本的发布几乎能够做到完全同步就归功于最初设计时的国际化考虑。
不同国家的客户,访问同一张Web页面,看到的文字效果不一样。
3.2对JSP页面国际化
国际化资源文件定义的格式:基名_语言_国家(可省略).properties
如:message_zh_CN.propreties message_en_US.propreties
默认资源文件 基名.properties
如:message.properties
步骤:
1. 创建login.jsp页面
2. 创建资源文件
message_zh_CN.propreties message_en_US.propreties
2. 在struts.xml文件中package节点下配置:
<constantname="struts.custom.i18n.resources" value="message"/>
value的值只要写资源的基名就可以了,不用后缀。
在页面中使用<s:textfieldkey="login.username(资源文件中的key)"/>
<s:submit key="login.submit(资源文件中的key)"/>
为{0},{1}占位符动态设置值
<s:text name="资源文件的key"/>
<s:param>为{0}设置值的真实值</s:param>
<s:param>为{1}设置值的真实值</s:param>
<s:form action="LoginAction"method="post">
<s:textfieldkey="login.username"name="username"/>
<s:submitkey="login.submit"/>
</s:form>
<!--
login.hello=IAM {0}, YOU ARE {1}
login.jack=JACK
login.marry=MARRY
-->
<s:text name="login.hello">
<s:param><s:textname="login.jack"/></s:param><!--为占位符赋值 -->
<s:param><s:textname="login.marry"/></s:param>
</s:text>
3.3对验证错误消息提取
写死的错误提示信息:
publicvoid validate() {
if(StringUtils.isBlank(username)) {
this.addFieldError("username","用户名不能为空");
}else {
//指定用户名必须为中文
if(!username.matches("[/u4E00-/uFA29]+")) {
this.addFieldError("username","用户名必须为中文");
}
}
}
配置的错误提示信息:
1)在国际化的资源文件中配置以下key–value
login.username.required =USERNAMEREQUIRED
login.username.mustchinese =USERNAMEMUSTBECHINESE
publicvoid validate() {
if(StringUtils.isBlank(username)) {
// this.addFieldError("username","用户名不能为空");
/*
* getText方法会根据key到资源文件中找对应的值
* 是TextProvider接口的中方法
* ActionSupport实现了它
*/
this.addFieldError("username",this.getText("login.username.required"));
}else {
//指定用户名必须为中文
if(!username.matches("[/u4E00-/uFA29]+")) {
// this.addFieldError("username","用户名必须为中文");
this.addFieldError("username",this.getText("login.username.mustchinese"));
}
}
}
可以将出错后的消息,绑定到资源文件中,有利于解耦
this.getText("result.username.mustchinese")
其中result.username.mustchinese是资源文件中的键,自动找对应的值。
4.结果类型
4.1常用结果集类型
Struts2的结果集类型有10种,在struts2-core-xxxx.jar下的struts-defalut.xml文件中约111行可以看到定义的结果集类型。
常用的有:
1) dispatcher 转发 和servlet中的转发一样。
2) redirect 重定向 和servlet中的重定向一样。
3) stream 流类型的结果,主要用于文件下载。
4) redirectAction 可以用于访问某个Action中某个业务方法
如:http://.....//FromAction!execute.action(访问FromAction 中的 execute方法)
重定向后浏览器的地址栏会变化:如
http://localhost:8080/ToAction!execute.action(用!将action名和业务方法名隔开)
<actionname="FromAction"class="com.maple.result.action.FromAction"method="execute">
<!-- 通过指定type=redirectAction重定向到另一个Action中执行指定的方法 -->
<resultname="goToAction"type="redirectAction">
<!-- 指定要重定向到的Action的名字 -->
<param name="actionName">ToAction</param>
<!-- 指定ToAction的名称空间,默认是/ -->
<param name="namespace">/</param>
<!-- 指定要执行哪个方法,默认是execute -->
<param name="method">execute</param>
<!-- 注意:三个参数的名字可以从其源码(org.apache.struts2.dispatcher.ServletActionRedirectResult)中查找到,也可以查文档。 -->
</result>
</action>
<action name="ToAction"class="com.maple.result.action.ToAction"method="execute">
<resultname="success"type="dispatcher">/WEB-INF/result_success.jsp</result>
</action>
4.2局部、全局结果类型
1)局部结果类型
在<action>标签中定义的<result>
2)全局结果类型
在<package>标签中的<global-results>标签中定义的<result>
<!-- 定义全局结果集 -->
<global-results>
<resultname="success"type="dispatcher">/WEB-INF/result_success.jsp</result>
</global-results>
3)当局部和全局共同作用时,局部优先(类似java中的局部变量);当局部没有配置时,则使用全局结果,项目中可以将每个<action>标签中的相同的<result>配置成全局结果。
5.异常处理
1)局部异常
在<action>标签中定义的<exception>
<actionname="ToAction"class="com.maple.result.action.ToAction"method="execute">
<!-- 局部结果类型 -->
<resultname="success"type="dispatcher">/WEB-INF/result_success.jsp</result>
<!-- 局部异常
result:为出现异常后要转到的结果的名称,和result标签的name属性值对应
exception:为要捕获的异常[全类名]
子异常(父子同在时,子起决定作用)-->
<exception-mappingresult="sonException"exception="java.lang.ArithmeticException"/>
<!-- 父异常 -->
<exception-mappingresult="fatherException"exception="java.lang.Exception"/>
</action>
2)全局异常
在<package>标签中的<global-exception-mappings>标签中定义的<exception>
<!-- 定义全局异常 -->
<global-exception-mappings>
<exception-mappingresult="fatherException"exception="java.lang.Exception"/>
</global-exception-mappings>
3)当局部和全局共同作用时,局部优先;当局部没有配置时,则使用全局结果,项目中可以将每个<action>标签中的相同的<exception>配置成全局异常。
当父子异常都存在时,子异常起决定作用。
6.类型转换器
6.1内置的类型转换器
com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter(默认类型转换器)
1)HTTP是没有类型的概念,只能将表单的参数,以String或String[]的方式接收
struts2提供了内置基本类型转换器,它能将String类型和8种基本类型自动转换。
8种基本数据类型:byte char int short long float double boolean
com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter
2)struts2的内置类型转换器,能够将String类型转换java.util.Date类型,
但Date类型的字符串必须是yyyy-MM-dd,而且能对输入日期的字符串格式进行
合理的判段(如月份不会有13月,日不会有32日等等)
3)内置转换器,不能将String类型转换任意JavaBean(模型)类型。
6.2自定义类型转换器
1)写一个类结成StrutsTypeConverte类,重写两个方法convertFromString和convertToString。
2)在src/xwork-conversion.properties文件,配置如下内容
JavaBean的全类名=自定义转换器的全类名
*在src下写一个配置文件,名字必须为xwork-conversion.properties
* 内容为key=value
* 如:要转换的JavaBean全类名=转换器全类名
方法的执行顺序:
AddressConverter()
convertFromString()
setAddress()
getAddress()
convertToString()
toString()
/**
* TODO自定义类型转换器,用于将字符串转换为Address类型
*要在src下写一个配置文件,名字必须为xwork-conversion.properties
*内容为key=value
*如:要转换的JavaBean全类名=转换器全类名
*/
public class AddressConverter extends StrutsTypeConverter {
public AddressConverter(){
/*
* 默认构造函数在服务器启动的时候就调用
* 但为什么会连续调用两次????(未解决)
* 自定义拦截器的也一样???(未解决)
*/
System.out.println("AddressConverter()");
}
//第二个参数中存放了JavaBean对象的内容,以字符串的形式,有几个JavaBean对象
//vaules中就有几个值, toClass指明要讲String转成什么类型。
public ObjectconvertFromString(Map context, String[] values, Class toClass) {
System.out.println("convertFromString()");
//将字符串转成JavaBean
if(Address.class.equals(toClass)) {//判断是否为要转换的类型
Stringstr = values[0];//取出第一个参数
String[]strs = str.split("-");//表单提交的时候是用-分隔的
returnnew Address(strs[0],strs[1], strs[2]); //将转换好的对象返回
}
returnnull; //如果不是要转换的类型则返回 null
}
public StringconvertToString(Map context, Object o) {
System.out.println("convertToString()");
//将JavaBean转成字符串,会调用JavaBean的toString方法
/*
* 如果o是要转换的类型,则进行强制类型转换
* 将o转换为Address,再调用toString方法,返回其值
*/
if(oinstanceof Address)return((Address)o).toString();
returnnull;
}
}