一、Struts2的国际化(需要知道)
配置国际化资源消息包
1.准备资源文件,资源文件的命名格式如下:
baseName_language_country.properties
baseName_language.properties
baseName.properties
其中baseName是资源文件的基本名,我们可以自定义,但language和country必须是java支持的语言和国家。如:
中国大陆: baseName_zh_CN.properties
美国: baseName_en_US.properties
现在为应用添加两个资源文件:
第一个存放中文:baseName_zh_CN.properties
内容为:welcome=欢迎学习国际化第二个存放英语(美国): baseName_en_US.properties
内容为: welcome=Welcome study to internationalization
2.在struts.xml配置全局国际化消息资源包 value为资源文件的基本名
<constant name="struts.custom.i18n.resources" value="beseName"></constant>
3.在页面或者action中访问国际化信息
在动作中
自由配置
国际化—输出带占位符的国际化信息 需要配置全局
配置局部消息资源包
一定要经过Action才行:
书写规范:在动作类所在包中,建立名字”动作类名-zh-CN.properties”的配置文件。动作类中访问,发现局部的比全局的优先级高。
总结:
二、Struts2中的拦截器
在执行动作方法前或执行结果后,做拦截处理。AOP编程思想。
1、自定义拦截器
a、定义一个类继承AbstractInterceptor或者实现Interceptor
b、struts.xml中定义
c、动作中使用
2:练习
因为struts2中如文件上传,数据验证,封装请求参数到action等功能都是由系统默认的defaultStack中的拦截器实现的,所以我们定义的拦截器需要引用系统默认的defaultStack,这样应用才可以使用struts2框架提供的众多功能。
如果希望包下的所有action都使用自定义的拦截器,可以通过<default-interceptor-ref name=“permissionStack”/>把拦截器定义为默认拦截器。注意:每个包只能指定一个默认拦截器。另外,一旦我们为该包中的某个action显式指定了某个拦截器,则默认拦截器不会起作用。
把自己定义的拦截器放到拦截器栈 在package里配置后 其他类继承这个package就可以不再配置拦截器了
<!-- 定义拦截器栈 -->
<interceptor-stack name="<span style="color:#ff0000;">mydefalutstack</span>">
<!-- 把自己的定义的拦截器也加入进来 -->
<span style="color:#ff0000;"><interceptor-ref name="sessionCheckInterceptor"/>
</span> <interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="debugging"/>
<interceptor-ref name="demo1Interceptor"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 配置默认拦截器栈 -->
<default-interceptor-ref name="<span style="color:#ff0000;">mydefalutstack</span>"></default-interceptor-ref>
3、methodFilterInterceptor
拦截器是拦截所有动作方法的
Struts2还提供了一个MethodFilterIntercepter类,该类是AbstractInterceptor类的子类,如果你需要实现的拦截器支持方法过滤性 可以指定不拦截那些方法和拦截那些
例子:判断是否有登录 没有登录就返回到登录视图 如果是登录就不拦截
三、文件的上传和下载(重要 记住拦截器和结果类型stream)
文件的上传是由一个FileUpload拦截器实现的
上传的前提:表单的method是post enctype是multipart/form-data 提供 type=file 的上传输入域
在 Action 中新添加 3 个和文件上传相关的属性. 这 3 个属性的名字必须是以下格式
uploadImage 是 jsp 页面上的 file 标签的名字.
上传文件:<input type="file" name="uploadImage">
1.单文件的上传
<span style="font-size:12px;">//单文件上传
public class Upload1Action extends ActionSupport {
private String name;
private File photo;// 和上传的输入域名称一致,必须File类型
private String photoFileName;// 文件名:上传的输入域名FileName
private String photoContentType;// 上传的文件的MIME类型
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public File getPhoto() {
return photo;
}
public void setPhoto(File photo) {
this.photo = photo;
}
public String getPhotoFileName() {
return photoFileName;
}
public void setPhotoFileName(String photoFileName) {
this.photoFileName = photoFileName;
}
public String getPhotoContentType() {
return photoContentType;
}
public void setPhotoContentType(String photoContentType) {
this.photoContentType = photoContentType;
}
public String upload() throws IOException{
System.out.println(name+":"+photoFileName+":"+photoContentType);
//存储文件的路径
String storeDirectory=ServletActionContext.getServletContext().getRealPath("/files");
//存储上传的文件
FileUtils.copyFile(photo, new File(storeDirectory,photoFileName));
return SUCCESS;
}
}</span>
jsp页面
2、多文件上传
//多文件上传
public class Upload2Action extends ActionSupport {
// 用数组或者list
private String name;
private File[] photo;// 和上传输入域名称一致 必须是File类型
private String[] photoFileName;// 文件名: 上传的输入域名FileName
private String[] photoContentType;// 上传文件的MIME类型
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public File[] getPhoto() {
return photo;
}
public void setPhoto(File[] photo) {
this.photo = photo;
}
public String[] getPhotoFileName() {
return photoFileName;
}
public void setPhotoFileName(String[] photoFileName) {
this.photoFileName = photoFileName;
}
public String[] getPhotoContentType() {
return photoContentType;
}
public void setPhotoContentType(String[] photoContentType) {
this.photoContentType = photoContentType;
}
public String upload() throws IOException{
//存储目录
String storeDirectory =ServletActionContext.getServletContext().getRealPath("files");
//存储
if(photo!=null&&photo.length>0){
for(int i=0;i<photo.length;i++){
FileUtils.copyFile(photo[i], new File(storeDirectory,photoFileName[i]));
}
}
return SUCCESS;
}
jsp
3、FileUpload拦截器
FileUpload 拦截器负责处理文件的上传操作, 它是默认的 defaultStack 拦截器栈的一员.
FileUpload 拦截器有 3 个属性可以设置.
maximumSize: 上传文件的最大长度(以字节为单位), 默认值为 2 MB
allowedTypes: 允许上传文件的类型, 各类型之间以逗号分隔
allowedExtensions: 允许上传文件扩展名, 各扩展名之间以逗号分隔
可以在 struts.xml 文件中覆盖这 3 个属性
4、参数配置及不符合要求时的错误提示
a、如果出现上传失败的情况,框架自动转向一个input的逻辑视图。
b、更改默认的消息提示:
struts2-core.jar org.apache.struts2.struts-message.properties 找到对应的参数
改变:配置一个全局消息资源包
5、文件的下载
动作类
public class DownLoad1Action extends ActionSupport {
private InputStream photoIn;// 要下载的输入流
private String filename;// 下载的文件名
public InputStream getPhotoIn() {
return photoIn;
}
public void setPhotoIn(InputStream photoIn) {
this.photoIn = photoIn;
}
public String getFileName() {
return filename;
}
public void setFileName(String fileName) {
this.filename = fileName;
}
public String download() throws Exception {
// 给输入流赋值
String realPath = ServletActionContext.getServletContext().getRealPath(
"/中文.jpg");
filename = FilenameUtils.getName(realPath);
filename = URLEncoder.encode(filename, "UTF-8");// 中文文件名下载乱码 先编码 客户端再解码
photoIn = new FileInputStream(realPath);
return SUCCESS;
}
配置stream结果类型
<action name="download" class="it.test.action.DownLoad1Action" method="download">
<!-- 下载时由Stream结果类型负责 -->
<result type="stream">
<!-- 靠配置结果类型参数 -->
<param name="inputName">photoIn</param>
<!-- 告知客户端用下载的方式打开 -->
<!-- 动态获取下载的文件名:在该配置文件中使用OGNL表达式。就相当于调用当前动作的getFilename方法 -->
<param name="contentDisposition">attachment;filename=${filename}</param>
<!-- 告知客户端文件的MIME类型 -->
<param name="contentType">application/octet-stream</param>
</result>
</action>
四、OGNL表达式
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,
它是一个开源项目。 Struts2框架使用OGNL作为默认的表达式语言。
OGNL相对其它表达式语言具有下面几大优势:
1、支持对象方法调用,如xxx.doSomeSpecial();
2、支持类静态的方法调用和值访问,表达式的格式:
@[类全名(包括包路径)]@[方法名 | 值名],例如:
@java.lang.String@format('foo %s', 'bar')
或@tutorial.MyConstant@APP_NAME;
在struts.xml中 设置常量 struts.ognl.allowStaticMethodAccess=true
3、支持赋值操作和表达式串联,如price=100, discount=0.8,
calculatePrice(),这个表达式会返回80;
4、访问OGNL上下文(OGNL context)和ActionContext;
5、操作集合对象。
五、ValueStack(重要理解)
在jsp中使用struts标签的debug可以看到ValueStack
ValueStack实际是一个接口,在Struts2中利用OGNL时,实际上使用的是实现了该接口的OgnlValueStack类,这个类是Struts2利用OGNL的基础
OgnlValueStack 类包含两个重要的属性 一个root和一个context。
* 其中root本质上是一个ArrayList.
* 而context 是一个Map(更确切的说是一个OgnlContext对象)
在这个OgnlContext对象(context)中,有一个默认的顶层对象 _root,OGNL访问context中这个默认顶层对象中的元素时,是不需要#号的,直接通过元素的名称来进问,
而访问其他对象时,如 request、session、attr等,则需要#号引用。
注:Struts2将OgnlValueStack的root对象赋值给了OgnlContext 中的_root对象,在OgnlValueStack的root对象中,保存着调用Action的实例,因此,在页面上通过Struts2标签访问Action 的属性时,就不需要通过#号来引用
总结:ognl Context包含 ObjectStack属性和ContextMap属性
1、ValueStack的生命周期
每次动作访问都会创建一个ValueStack。动作类的实例声明周期也是每次访问时都创建。
2、ValueStack和ActionContext的关系
相互引用
通过ValueStack操作Map和栈(根)。
OGNL的contextMap。ValueStack是作为他的一个叫做根(实际上是一个List)的形式存在的。
3、ValueStack常用的方法
void set(String key,Object value):先获取根栈栈顶的Map,如果不存在,压入一个新的Map,把key和value放到这个Map中。如果存在,直接放key和value。
Object findValue(String expr):参数是一个OGNL表达式。如果以#开头,从contextMap中找key值所对应的对象。如果不是以#开头,搜索根栈中对象的属性(getter方 法)
特别注意:如果编写的表达式不是以#开头,先搜索根栈对象的所有属性,如果没有找到,会把它当做key值到contextMap中找。
String findString(String expr):和findValue功能一样,但把OGNL表达式获取的对象转换成String
练习
//练习
public class Demo2Action extends ActionSupport {
private String name="gg";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String execute(){
ValueStack vs1=ActionContext.getContext().getValueStack();
vs1.setValue("name","呐呐");//给栈中对象的name属性赋值
vs1.setValue("#name", "nana2");//相当于向contextMap中存放数据
//总结:操作跟中对象的属性,直接写属性名称。 操作contextMap中的数据,要加上#。
//向跟栈中压入2个Date对象
//跟栈中对象“属性"的设置:默认情况下,从栈顶底依次搜索属性名称,找到就赋值,不再往下找。找不到报错
//也可以指定从第几个开始找,索引从0开始
vs1.push(new Date());
vs1.push(new Date());
vs1.setValue("[1].month", 9);//修改第二个date的月属性
vs1.setValue("name", "ee");//修改属性
return SUCCESS;
}
4、ContextMap中存放的常用内容
OGNL Context是struts2的数据中心
当Struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action 。然后把action存放进ValueStack,所以action的实例变量可以被OGNL访问。
注意: Struts2中,OGNL表达式需要配合Struts标签才可以使用。如:<s:property value="name"/>
request:存放的ServletRequest中的属性(attributes),他是一个Map<String,Object>
session:存放的HttpSession中的属性(attributes),他是一个Map<String,Object>
application:存放的ServletContext中的属性(attributes),他是一个Map<String,Object>
action:当前的动作类
parameters:请求参数的那个Map
attr:依次从page\request\session\appliction范围依次搜索
Demo:
#号的用法
用法1:访问OGNL上下文和Action上下文,#相当ActionContext.getContext()
1、 如果访问其他Context中的对象,由于他们不是根对象,所以在访问时,
需要添加#前缀。
2 、OGNL会设定一个根对象(root对象),在Struts2中根对象就是ValueStack
(值栈) 。如果要访问根对象(即ValueStack)中对象的属性,则可以省略
#命名对象,直接访问该对象的属性即可。
为何使用EL表达式能够访问valueStack中对象的属性
从request范围中找不到那个属性,EL表达式就变成了OGNL表达式。其他EL功能都不变。
结论:
原来EL顺序:${p} page----->request----->session---->application
Struts2EL顺序:${p} page------>request----->根栈中的属性getP()--->从contextMap中找---->session---->application
从request范围中找不到那个属性,EL表达式就变成了OGNL表达式。其他EL功能都不变。