struts2笔记(2)

1 <context-param>
2     <param-name>pattern</param-name>
3     <param-value>yyyy-MM-dd hh:mm:ss</param-value>
4 </context-param>
5 //获取当前 WEB 应用的初始化参数 pattern
6 ServletContext servletContext = ServletActionContext.getServletContext();
7 System.out.println(servletContext); 
8 String pattern = servletContext.getInitParameter("pattern");
获取web.xml中配置的参数

Struts2 中, HTML 表单将被直接映射到一个 POJO,通过params拦截器,类中定义对应属性,及对应set方法即可。

params拦截器会把请求参数的值赋给栈顶对象对应的各个属性,如果栈顶对象没有对应属性,则往下找下一个对象对应的属性。

如果已经有了一个javabean,表单传过来了bean的属性,就不需要再action中再写一遍bean的每个属性来赋值了,可以直接写一个bean的变量,通过ModelDriven拦截器,如果 Action 类实现了 ModelDriven 接口,该拦截器将把 ModelDriven 接口的 getModel() 方法返回的对象置于栈顶,然后params拦截器就会将表单属性赋给栈顶的空的bean对象了。继承接口后这样写(可以没有setEmployee方法)

  
1 private Employee employee;
2 @Override
3 public Employee getModel() {
4     employee = new Employee();
5     return employee;
6 }
View Code

 代码里不能直接return new Employee();因为和成员变量employee没关系,所以其他用到employee对象的时候,它是空。

每次求情,只要有ModelDriven的getModel()方法,一般栈顶对象都会是该方法返回的对象!

用class.hashcode()方法,可以看对象是不是同一个。

关于回显:

  从值栈站顶开始查找匹配的属性,若找到,就添加到value属性中,就会自动给赋上。

下图29。18

 

通常情况下,用ModelDriven拦截器要和Preparable拦截器一起用。

Struts 2.0 中的 modelDriven 拦截器负责把 Action 类以外的一个对象压入到值栈栈顶

而 prepare 拦截器负责准备为 getModel() 方法准备 model

 1 5). 存在的问题: 
 2 
 3 getModel 方法
 4 
 5 public Employee getModel() {
 6     if(employeeId == null)
 7         employee = new Employee();
 8     else
 9         employee = dao.get(employeeId);
10     
11     return employee;
12 }
13 
14 I.   在执行删除的时候, employeeId 不为 null, 但 getModel 方法却从数据库加载了一个对象. 不该加载!
15 II.  指向查询全部信息时, 也 new Employee() 对象. 浪费!
16 
17 6). 解决方案: 使用 PrepareInterceptor 和 Preparable 接口. 
18 
19 7). 关于 PrepareInterceptor
20 
21 [分析后得到的结论]
22  
23 若 Action 实现了 Preparable 接口, 则 Struts 将尝试执行 prepare+ActionMethodName 方法,
24 若 prepare+ActionMethodName 不存在, 则将尝试执行 prepareDo+ActionMethodName 方法.
25 若都不存在, 就都不执行.
26 
27 若 PrepareInterceptor  的 alwaysInvokePrepare 属性为 false, 
28 则 Struts2 将不会调用实现了 Preparable 接口的  Action 的 prepare() 方法
29 
30 [能解决 5) 的问题的方案]
31 
32 可以为每一个 ActionMethod 准备 prepare[ActionMethdName] 方法, 而抛弃掉原来的 prepare() 方法
33 将 PrepareInterceptor  的 alwaysInvokePrepare 属性置为 false, 以避免 Struts2 框架再调用 prepare() 方法.
34 
35 如何在配置文件中为拦截器栈的属性赋值: 参看 /struts-2.3.15.3/docs/WW/docs/interceptors.html
36 一共有三种方法,有的可以为一个action修改值,有的可以为所有action修改至,下面是为所有action修改属性
37 <interceptors>
38     <interceptor-stack name="parentStack">
39         <interceptor-ref name="defaultStack">
40             <param name="params.excludeParams">token</param>
41         </interceptor-ref>
42     </interceptor-stack>
43 </interceptors>
44  
45 <default-interceptor-ref name="parentStack"/>
View Code

如图,私人订制,为需要的方法,添加上prepareMethod方法,来自自定义

修改拦截器中部分属性的配置如下:

paramsPrepareParmsStack拦截器栈,执行顺序:

params -> prepare -> modelDriven -> params

//***************************************类型转换*********

Parameters拦截器:把请求参数映射到action属性,能自动完成字符串和基本数据类型的转换。

如果类型转换失败:

若 Action 类没有实现 ValidationAware 接口: Struts 在遇到类型转换错误时仍会继续调用其 Action 方法, 就好像什么都没发生一样.

若 Action 类实现 ValidationAware 接口:Struts 在遇到类型转换错误时将不会继续调用其 Action 方法:  Struts 将检查相关 action 元素的声明是否包含着一个 name=input 的

result(一般情况下,继承ActionSupport类就好了,然后,都是将表单输入页面当做该result,好比,后台age需要int,你输入个string,输入的不合法的值,我还给你返回这个界面).  如果有, Struts 将把控制权转交给那个 result  元素; 若没有 input 结果, Struts 将抛出一个异常

此时返回到input的result页面后,输入框上面还有错误消息提示,输入不合法之类的(xhtml主题下)

覆盖默认的出错消息

在对应的 Action 类所在的包中新建  ActionClassName.properties 文件, ClassName 即为包含着输入字段的 Action 类的类名

在属性文件中添加如下键值对: invalid.fieldvalue.fieldName=xxx

这样就可以了,但是在simple主题下,没用。这个时候需要用标签来处理,值栈中有错误信息(${fieldErrors.arg[0]}、<s:fielderror fieldName="age"></s:fielderror>放在输入框后面即可)

 1 <body>
 2     
 3     <!--  
 4     问题1: 如何覆盖默认的错误消息?
 5     1). 在对应的 Action 类所在的包中新建  
 6         ActionClassName.properties 文件, ActionClassName 即为包含着输入字段的 Action 类的类名
 7     2). 在属性文件中添加如下键值对: invalid.fieldvalue.fieldName=xxx
 8     
 9     
10     问题2: 如果是 simple 主题, 还会自动显示错误消息吗? 如果不会显示, 怎么办 ?
11     1). 通过 debug 标签, 可知若转换出错, 则在值栈的 Action(实现了 ValidationAware 接口) 对象中有一个  fieldErrors 属性.
12     该属性的类型为 Map<String, List<String>> 键: 字段(属性名), 值: 错误消息组成的 List. 所以可以使用 LE 或 OGNL 的方式
13     来显示错误消息: ${fieldErrors.age[0]}
14     
15     2). 还可以使用 s:fielderror 标签来显示. 可以通过 fieldName 属性显示指定字段的错误.
16     
17     问题3. 若是 simple 主题, 且使用  <s:fielderror fieldName="age"></s:fielderror> 来显示错误消息, 则该消息在一个 
18     ul, li, span 中. 如何去除 ul, li, span 呢 ?
19     在 template.simple 下面的 fielderror.ftl 定义了 simple 主题下, s:fielderror 标签显示错误消息的样式. 所以修改该
20     配置文件即可. 在 src 下新建  template.simple 包, 新建 fielderror.ftl 文件, 把原生的 fielderror.ftl 中的内容
21     复制到新建的 fielderror.ftl 中, 然后剔除 ul, li, span 部分即可. 
22     
23     问题4. 如何自定义类型转换器 ?  
24     1). 为什么需要自定义的类型转换器 ? 因为 Struts 不能自动完成 字符串 到 引用类型 的 转换.
25     2). 如何定义类型转换器:
26     I.  开发类型转换器的类: 扩展 StrutsTypeConverter 类.
27     II. 配置类型转换器: 
28     有两种方式
29     ①. 基于字段的配置: 
30         > 在字段所在的 Model(可能是 Action, 可能是一个 JavaBean) 的包下, 新建一个 ModelClassName-conversion.properties 文件
31         > 在该文件中输入键值对: fieldName=类型转换器的全类名. 
32         > 第一次使用该转换器时创建实例. 
33         > 类型转换器是单实例的!    
34     
35     ②. 基于类型的配置:
36         > 在 src 下新建 xwork-conversion.properties
37         > 键入: 待转换的类型=类型转换器的全类名.
38         > 在当前 Struts2 应用被加载时创建实例. 
39         
40     -->
41     
42     <s:debug></s:debug>
43     
44     <s:form action="testConversion" theme="simple">
45         Age: <s:textfield name="age" label="Age"></s:textfield>
46         ${fieldErrors.age[0] }
47         ^<s:fielderror fieldName="age"></s:fielderror>
48         <br><br>
49         
50         Birth: <s:textfield name="birth"></s:textfield>
51         <s:fielderror fieldName="birth"></s:fielderror>
52         <br><br>
53         
54         <s:submit></s:submit>
55     </s:form>
56     
57 </body>
类型转换的笔记

 以上是字符串到基本数据类型的类型转换,下面是字符串到引用类型的类型转换(如日期,这个时候需要自定义类型转换器)

自定义类型转换器必须实现 ongl.TypeConverter 接口或对这个接口的某种具体实现做扩展

通常情况下,自定义类型转换器的类对StrutsTypeConverter进行扩展就好了

1. Department 是模型, 实际录入的 Department. deptName 可以直接写到

s:textfield 的 name 属性中. 那 mgr 属性如何处理呢 ?

struts2 表单标签的 name 值可以被赋为 属性的属性: name=mgr.name, name=mgr.birth

2. mgr 中有一个 Date 类型的 birth 属性, Struts2 可以完成自动的类型转换吗 ?

全局的类型转换器可以正常工作!

 1 public class DateConverter extends StrutsTypeConverter {
 2 
 3     private DateFormat dateFormat;
 4     
 5     public DateConverter() {
 6         System.out.println("DateConverter's constructor...");
 7     }
 8     
 9     public DateFormat getDateFormat(){
10         if(dateFormat == null){
11             //获取当前 WEB 应用的初始化参数 pattern
12             ServletContext servletContext = ServletActionContext.getServletContext();
13             System.out.println(servletContext); 
14             String pattern = servletContext.getInitParameter("pattern");
15             dateFormat = new SimpleDateFormat(pattern);
16         }
17         
18         return dateFormat;
19     }
20     
21     @Override
22     public Object convertFromString(Map context, String[] values, Class toClass) {
23         
24         System.out.println("convertFromString...");
25         
26         if(toClass == Date.class){
27             if(values != null && values.length > 0){
28                 String value = values[0];
29                 try {
30                     return getDateFormat().parseObject(value);
31                 } catch (ParseException e) {
32                     e.printStackTrace();
33                 }
34             }
35         }
36         
37         //若没有转换成功, 则返回 values
38         return values;
39     }
40 
41     @Override
42     public String convertToString(Map context, Object o) {
43         
44         System.out.println("convertToString...");
45         
46         if(o instanceof Date){
47             Date date = (Date) o;
48             return getDateFormat().format(date);
49         }
50         
51         //若转换失败返回 null
52         return null;
53     }
54 
55 }
Date类型自定义转换器示例

//**************************************国际化**************************************

 1 1. 国际化的目标
 2 
 3 1). 如何配置国际化资源文件
 4 
 5 I.   Action 范围资源文件: 在Action类文件所在的路径建立名为 ActionName_language_country.properties 的文件
 6 II.  包范围资源文件: 在包的根路径下建立文件名为 package_language_country.properties 的属性文件,
 7 一旦建立,处于该包下的所有 Action 都可以访问该资源文件。注意:包范围资源文件的 baseName 就是package,不是Action所在的包名。
 8 III. 全局资源文件
 9     > 命名方式: basename_language_country.properties
10     > struts.xml <constant name="struts.custom.i18n.resources" value="baseName"/>
11 
12 IV.  国际化资源文件加载的顺序如何呢 ? 离当前 Action 较近的将被优先加载. 
13 
14 假设我们在某个 ChildAction 中调用了getText("username"):
15 
16 (1) 加载和 ChildAction 的类文件在同一个包下的系列资源文件 ChildAction.properties
17 (2) 加载  ChildAction 实现的接口 IChild,且和 IChildn 在同一个包下 IChild.properties 系列资源文件。
18 (3) 加载 ChildAction 父类 Parent,且和 Parent 在同一个包下的 baseName 为 Parent.properties 系列资源文件。
19 (4) 若 ChildAction 实现 ModelDriven 接口,则对于getModel()方法返回的model 对象,重新执行第(1)步操作。
20 (5) 查找当前包下 package.properties 系列资源文件。
21 (6) 沿着当前包上溯,直到最顶层包来查找 package.properties 的系列资源文件。
22 (7) 查找 struts.custom.i18n.resources 常量指定 baseName 的系列资源文件。
23 (8) 直接输出该key的字符串值。
24 
25 
26 2). 如何在页面上 和 Action 类中访问国际化资源文件的  value 值
27 
28 I. 在 Action 类中. 若 Action 实现了 TextProvider 接口, 则可以调用其 getText() 方法获取 value 值
29     > 通过继承 ActionSupport 的方式。 
30     1.在action中访问国际化资源文件的value值
31         String username = getText("username");
32     2.带占位符的
33         String time = getText("time",Arrays.asList(new Date()));
34         syso,就可以打印出来
35     
36 II. 页面上可以使用 s:text 标签; 对于表单标签可以使用表单标签的 key 属性值
37     > 若有占位符, 则可以使用 s:text 标签的 s:param 子标签来填充占位符
38     > 可以利用标签和 OGNL 表达式直接访问值栈中的属性值(对象栈 和 Map 栈)
39     
40     time=Time:{0}(这个是在i18n.properties中写的)
41     
42     <s:text name="time">
43         <s:param value="date"></s:param>
44     </s:text>
45 
46     ------------------------------------
47     
48     time2=Time:${date}(类似一个ongl表达式的情况,在i18n.properties中直接获取值)
49     
50     <s:text name="time2"></s:text>
51     
52 
53 3). 实现通过超链接切换语言. 
54 
55 I.  关键之处在于知道 Struts2 框架是如何确定 Local 对象的 !
56 II. 可以通过阅读 I18N 拦截器知道. 
57 III. 具体确定 Locale 对象的过程:
58 
59     > Struts2 使用 i18n 拦截器 处理国际化,并且将其注册在默认的拦截器栈中
60     > i18n拦截器在执行Action方法前,自动查找请求中一个名为 request_locale 的参数。
61           如果该参数存在,拦截器就将其作为参数,转换成Locale对象,并将其设为用户默认的Locale(代表国家/语言环境)。
62           并把其设置为 session 的 WW_TRANS_I18N_LOCALE 属性
63     > 若 request 没有名为request_locale 的参数,则 i18n 拦截器会从 Session 中获取 WW_TRANS_I18N_LOCALE 的属性值,
64          若该值不为空,则将该属性值设置为浏览者的默认Locale 
65     > 若 session 中的 WW_TRANS_I18N_LOCALE 的属性值为空,则从 ActionContext 中获取 Locale 对象。
66     
67 IV.  具体实现: 只需要在超连接的后面附着  request_locale 的请求参数, 值是 语言国家 代码.
68     <a href="testI18n.action?request_locale=en_US">English</a>
69     <a href="testI18n.action?request_locale=zh_CN">中文</a>
70     
71     > 注意: 超链接必须是一个 Struts2 的请求, 即使 i18n 拦截器工作!
72     
73 
74  
View Code

在页面上使用国际化(xhtml主题):

在页面上使用国际化(simple主题):

国际化资源文件:

**************************************国际化**************************************//

//**************************************动态加载国际化**************************************

i18n拦截器

具体实现代码:

**************************************动态加载国际化**************************************//

 

posted @ 2017-01-04 09:28  Orc_Warrior  阅读(166)  评论(0编辑  收藏  举报