struts2笔记(2)

时间:2021-04-29 17:53:50
 <context-param>
<param-name>pattern</param-name>
<param-value>yyyy-MM-dd hh:mm:ss</param-value>
</context-param>
//获取当前 WEB 应用的初始化参数 pattern
ServletContext servletContext = ServletActionContext.getServletContext();
System.out.println(servletContext);
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方法)

  
 private Employee employee;
@Override
public Employee getModel() {
employee = new Employee();
return employee;
}

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

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

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

关于回显:

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

下图29。18

struts2笔记(2)

struts2笔记(2)

struts2笔记(2)

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

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

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

 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
一共有三种方法,有的可以为一个action修改值,有的可以为所有action修改至,下面是为所有action修改属性
<interceptors>
<interceptor-stack name="parentStack">
<interceptor-ref name="defaultStack">
<param name="params.excludeParams">token</param>
</interceptor-ref>
</interceptor-stack>
</interceptors> <default-interceptor-ref name="parentStack"/>

struts2笔记(2)

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

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

struts2笔记(2)

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>放在输入框后面即可)

struts2笔记(2)

 <body>

     <!--
问题1: 如何覆盖默认的错误消息?
1). 在对应的 Action 类所在的包中新建
ActionClassName.properties 文件, ActionClassName 即为包含着输入字段的 Action 类的类名
2). 在属性文件中添加如下键值对: invalid.fieldvalue.fieldName=xxx 问题2: 如果是 simple 主题, 还会自动显示错误消息吗? 如果不会显示, 怎么办 ?
1). 通过 debug 标签, 可知若转换出错, 则在值栈的 Action(实现了 ValidationAware 接口) 对象中有一个 fieldErrors 属性.
该属性的类型为 Map<String, List<String>> 键: 字段(属性名), 值: 错误消息组成的 List. 所以可以使用 LE 或 OGNL 的方式
来显示错误消息: ${fieldErrors.age[0]} 2). 还可以使用 s:fielderror 标签来显示. 可以通过 fieldName 属性显示指定字段的错误. 问题3. 若是 simple 主题, 且使用 <s:fielderror fieldName="age"></s:fielderror> 来显示错误消息, 则该消息在一个
ul, li, span 中. 如何去除 ul, li, span 呢 ?
在 template.simple 下面的 fielderror.ftl 定义了 simple 主题下, s:fielderror 标签显示错误消息的样式. 所以修改该
配置文件即可. 在 src 下新建 template.simple 包, 新建 fielderror.ftl 文件, 把原生的 fielderror.ftl 中的内容
复制到新建的 fielderror.ftl 中, 然后剔除 ul, li, span 部分即可. 问题4. 如何自定义类型转换器 ?
1). 为什么需要自定义的类型转换器 ? 因为 Struts 不能自动完成 字符串 到 引用类型 的 转换.
2). 如何定义类型转换器:
I. 开发类型转换器的类: 扩展 StrutsTypeConverter 类.
II. 配置类型转换器:
有两种方式
①. 基于字段的配置:
> 在字段所在的 Model(可能是 Action, 可能是一个 JavaBean) 的包下, 新建一个 ModelClassName-conversion.properties 文件
> 在该文件中输入键值对: fieldName=类型转换器的全类名.
> 第一次使用该转换器时创建实例.
> 类型转换器是单实例的! ②. 基于类型的配置:
> 在 src 下新建 xwork-conversion.properties
> 键入: 待转换的类型=类型转换器的全类名.
> 在当前 Struts2 应用被加载时创建实例. --> <s:debug></s:debug> <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> </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 可以完成自动的类型转换吗 ?

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

 public class DateConverter extends StrutsTypeConverter {

     private DateFormat dateFormat;

     public DateConverter() {
System.out.println("DateConverter's constructor...");
} public DateFormat getDateFormat(){
if(dateFormat == null){
//获取当前 WEB 应用的初始化参数 pattern
ServletContext servletContext = ServletActionContext.getServletContext();
System.out.println(servletContext);
String pattern = servletContext.getInitParameter("pattern");
dateFormat = new SimpleDateFormat(pattern);
} return dateFormat;
} @Override
public Object convertFromString(Map context, String[] values, Class toClass) { System.out.println("convertFromString..."); if(toClass == Date.class){
if(values != null && values.length > 0){
String value = values[0];
try {
return getDateFormat().parseObject(value);
} catch (ParseException e) {
e.printStackTrace();
}
}
} //若没有转换成功, 则返回 values
return values;
} @Override
public String convertToString(Map context, Object o) { System.out.println("convertToString..."); if(o instanceof Date){
Date date = (Date) o;
return getDateFormat().format(date);
} //若转换失败返回 null
return null;
} }

Date类型自定义转换器示例

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

 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 <constant name="struts.custom.i18n.resources" value="baseName"/> 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 的方式。
1.在action中访问国际化资源文件的value值
String username = getText("username");
2.带占位符的
String time = getText("time",Arrays.asList(new Date()));
syso,就可以打印出来 II. 页面上可以使用 s:text 标签; 对于表单标签可以使用表单标签的 key 属性值
> 若有占位符, 则可以使用 s:text 标签的 s:param 子标签来填充占位符
> 可以利用标签和 OGNL 表达式直接访问值栈中的属性值(对象栈 和 Map 栈) time=Time:{0}(这个是在i18n.properties中写的) <s:text name="time">
<s:param value="date"></s:param>
</s:text> ------------------------------------ time2=Time:${date}(类似一个ongl表达式的情况,在i18n.properties中直接获取值) <s:text name="time2"></s:text> 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 的请求参数, 值是 语言国家 代码.
<a href="testI18n.action?request_locale=en_US">English</a>
<a href="testI18n.action?request_locale=zh_CN">中文</a> > 注意: 超链接必须是一个 Struts2 的请求, 即使 i18n 拦截器工作!

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

struts2笔记(2)

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

struts2笔记(2)

国际化资源文件:

struts2笔记(2)

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

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

i18n拦截器

struts2笔记(2)

具体实现代码:

struts2笔记(2)

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