struts2笔记
1、struts2 配置
文章目录
- struts2笔记
- 1、struts2 配置
- 2、常量
- 3、action方法的调用
- 4、接收请求参数(struts2不需要ActionForm)
- 5、自定义类型转换器
- 6、四大作用域
- 7、文件上传
- 8、自定义拦截器
- 9、效验器
- 10、国际化
- 11、OGNL表达式
- 11.1 OGNL表达式语言
- 11.2 为何使用EL表达式能够访问valueStack中对象的属性
- 11.3 采用OGNL表达式创建List/Map集合对象
- 11.4 采用OGNL表达式判断对象是否存在于集合中
- 11.5 OGNL表达式的投影功能
- 11.6 iterator标签
- 11.7 url标签
- 11.8 a标签(下面的id是action中的整型的属性,不加%{}会报错)
- 11.9 读取数据库数据
- 11.10 checkboxlist复选框(注意:name属性不能少,而且提交后的action中必须包括name属性)
- 11.12 radio单选框
- 11.13 select下拉选择框
- 11.14 标签防止重复提交
软件:Myeclipse 8.6
Struts2是在WebWork2基础发展而来的。和struts1一样, Struts2也属于MVC框架。
不过有一点大家需要注意的是:尽管Struts2和struts1在名字上的差别不是很大,但Struts2和struts1在代码编写风格上几乎是不一样的。
1.1 优点
- 在软件设计上Struts2没有像struts1那样跟Servlet API和struts API有着紧密的耦合;
Struts2的应用可以不依赖于Servlet API和struts API;
Struts2的这种设计属于无侵入式设计,而Struts1却属于侵入式设计。 - Struts2提供了拦截器,利用拦截器可以进行AOP编程,实现如权限拦截等功能。
- Strut2提供了类型转换器,我们可以把特殊的请求参数转换成需要的类型;
在Struts1中,如果我们要实现同样的功能,就必须向Struts1的底层实现BeanUtil注册类型转换器才行。 - Struts2提供支持多种表现层技术,如:JSP、freeMarker、Velocity等
- Struts2的输入校验可以对指定方法进行校验,解决了Struts1长久之痛。
- 提供了全局范围、包范围和Action范围的国际化资源文件管理实现。
1.2 搭建struts2开发环境
-
找到开发Struts2应用需要使用到的jar文件
现在struts2没与spring集成,所以不需要导入struts2-spring-plugin-2.1.8.jar
-
编写Struts2的配置文件struts.xml,该文件需要存放在src下或者WEB-INF/classes下
方式一:右击项目–>MyEclipse–>Add… (注意:在web.xml会自动配置struts2,访问时以.action结束)
使用这种方式就不需要上一步(不需要加入jar文件)方式二:拷贝下面文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<!-- name 包名,唯一
namespace 访问路径
extends 必须继承struts-default -->
<package name="user" namespace="/user" extends="struts-default">
<action name="userAction" class="action.UserAction"></action>
</package>
</struts>
创建一个Action,需要继承ActionSupport,实现execute方法
- 在web.xml中配置struts
<filter>
<filter-name>struts</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class></filter>
<filter-mapping>
<filter-name>struts</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
-
默认值
action的class默认访问ActionSupport
action的method默认为execute()
result的type默认为success- Action搜索的须序
/path1/path2/path3
http://localhost:8080/项目名/包的命名空间/Action名称.action
- result中的type
1、默认是dispatcher 转发
2、redirectAction 重定向
- Action搜索的须序
<result name="redi" type="redirectAction">testAction</result>
<!--重定向到第二个包(/path1)下的action(loginAction)中-->
<result name="redi" type="redirectAction">
<param name="actionName">loginAction</param>
<param name="namespace">/path1</param>
</result>
3. plainText 显示jsp源码
<result name="plain" type="plainText">
<param name="location">/index.jsp</param>
<param name="charSet">UTF-8</param>
</result>
2、常量
2.1、为Action的属性注入值
Struts2为Action中的属性提供了依赖注入功能,在struts2的配置文件中,我们可以很方便地为Action中的属性注入值。注意:属性必须提供setter方法。
<action name="helloworld" class="com.jyp.action.LoginAction">
<param name="savePath">/images</param>
<result name="success">/helloworld.jsp</result>
</action>
上面通过节点为action的savePath属性注入“/images”
2.2、指定需要Struts 2处理的请求后缀
默认使用.action后缀访问Action。其实默认后缀是可以通过常量”struts.action.extension“进行修改的。
<struts>
<constant name="struts.action.extension" value="do"></constant>
</struts>
如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。
<struts>
<constant name="struts.action.extension" value="do,action"></constant>
</struts>
2.3、细说常量定义
1、常量可以在struts.xml或struts.properties中配置,建议在struts.xml中配置;
两种配置方式如下:
在struts.xml文件中配置常量
<struts>
<constant name="struts.action.extension" value="do"/>
</struts>
<!--在struts.properties中配置常量
struts.action.extension=do-->
2、struts2加载常量的搜索顺序:struts-default.xml
struts-plugin.xml
struts.xml
struts.properties
web.xml
如果在多个文件中配置了同一个常量,则后一个文件中配置的常量值会覆盖前面文件中配置的常量值.
2.4、常用的常量介绍
<!--指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法 和freemarker 、velocity的输出 -->
<constant name="struts.i18n.encoding" value="UTF-8"/>
<!-- 该属性指定需要Struts 2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts2处理。如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。 -->
<constant name="struts.action.extension" value="do"/>
<!-- 设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭 -->
<constant name="struts.serve.static.browserCache" value="false"/>
<!-- 当struts的配置文件修改后,系统是否自动重新加载该文件,默认值为false(生产环境下使用),开发阶段最好打开 -->
<constant name="struts.configuration.xml.reload" value="true"/>
<!-- 开发模式下使用,这样可以打印出更详细的错误信息 -->
<constant name="struts.devMode" value="true" />
<!-- 默认的视图主题 -->
<constant name="struts.ui.theme" value="simple" />
<!-- 与spring集成时,指定由spring负责action对象的创建 -->
<constant name="struts.objectFactory" value="spring" />
<!--该属性设置Struts 2是否支持动态方法调用,该属性的默认值是true。如果需要关闭动态方法调用,则可设置该属性为false。-->
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
<!--上传文件的大小限制
单位:字节
21000000 (约21M)
-->
<constant name="struts.multipart.maxSize" value=“10701096"/>
2.5 Struts2的处理流程
2.6 为应用指定多个struts配置文件
为了避免struts.xml文件过于庞大、臃肿,提高struts.xml文件的可读性,我们可以将一个struts.xml配置文件分解成多个配置文件,然后在struts.xml文件中包含其他配置文件。
下面的struts.xml通过元素指定多个配置文件:
<struts>
<include file="struts-user.xml"></include>
<include file="struts-manager.xml"></include>
</struts>
3、action方法的调用
3.1 Action动态方法调用
如果Action中存在多个方法时,我们可以使用!+方法名调用指定方法
调用addUser对应action中的add方法
http://localhost:8080/Struts2_1/user/addUser!add.do
如果不想使用动态方法调用,我们可以通过常量struts.enable.DynamicMethodInvocation关闭动态方法调用。
<constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>
3.2 使用通配符定义action
<action name="addUser_*" class="com.jyp.action.UserAction" method="{1}">
<!--URL访问:http://localhost:8080/Struts2_1/user/addUser_add.do-->
3.3 直接在action中设置method方法(不设置默认调用execute方法)
<action name="loginAction" method="login" class="action.loginAction">
<result name="success">/list.jsp</result>
</action>
4、接收请求参数(struts2不需要ActionForm)
4.1 采用基本类型接收请求参数(get/post)
在Action类中定义与请求参数同名的属性,struts2便能自动接收请求参数并赋予给同名属性。
public void setUser_id(int userId) {//struts2通过反射技术调用与请求参数同名的属性的setter方法来获取请求参数值
user_id = userId;
4.2 采用复合类型接收请求参数
Struts2首先通过反射技术调用Users的默认构造器创建User对象,然后再通过反射技术调用User中与请求参数同名的属性的setter方法来获取请求参数值。
a、表单元素name="users.user_name"
b、显示页面${users.user_name}
5、自定义类型转换器
java.util.Date类型的属性可以接收格式为2009-07-20的请求参数值。但如果我们需要接收格式为20091221的请求参数,我们必须定义类型转换器。
5.1 自定义类型转换器
定义一个类继承DefaultTypeConverter,并实现convertValue方法
public class DateConverter extends DefaultTypeConverter {
@Override
public Object convertValue(Map<String, Object> context, Object value,
Class toType) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
try {
if (toType == Date.class) {
String[] params = (String[]) value;
return dateFormat.parse(params[0]);
}else if(toType==String.class){
Date date=(Date)value;
return dateFormat.format(date);
}
} catch (ParseException e) {}
return null;
}
}
5.2 将上面的类型转换器注册为局部类型转换器
在Action类所在的包下放置ActionClassName-conversion.properties
文件,ActionClassName是Action的类名,后面的-conversion.properties是固定写法。
在properties文件中的内容为:属性名称=类型转换器的全类名user_date=com.jyp.convertion.DateConverter
如果action中是复合类型,则写user.user_date=com.jyp.convertion.DateConverter
注意:属性名如user_date中不能存在大写字母,否则转换不成功,输出null
5.3 自定义全局类型转换器
将上面的类型转换器注册为全局类型转换器
1、在WEB-INF/classes或src根目录下放置xwork-conversion.properties文件 。
2、在properties文件中的内容为:
待转换的类型=类型转换器的全类名xwork-conversion.properties文件中的内容为:
java.util.Date=com.jyp.convertion.DateConverter
可以用表单或超链接测试(使用超链接测试时日期字符串不需要’引号):
<a href="official/official_add.action?mydate=20121123">测试类型转换器</a>
5.4 在Action中定义一个User类型的属性
public class MeetingAction extends ActionSupport {
private User user;
getter|setter方法...
}
jsp页面
定义了类型转换器,使用user和user.id都可以
用户ID:
用户名:
不用自定义类型转换器的话,上面必须写成name="user.id"和name=“user.name”
6、四大作用域
6.1 四大作用域
PageContext:
页面上下文[页面级容器,在本页面绑定的数据只能在本页面使用]request:
请求:只能在转发的目的地获取数据
例如: 从A发送请求到B,B没有选择转发,此请求销毁
请求是一次性请求,每一次发送一个请求都是一个新的请求的对象
session:
会话:绑定在session中的数据,任何地方默认30分钟内可以获取数据
绑定一次数据,30分钟,任何位置都可以得到数据
一般用来保存当前登陆者的信息[每一种浏览器都有自己独有的session]
application:
项目上下文
[项目级容器:绑定在此容器中的数据,项目不停止,任何地方都可以获取]
绑定一次,任何地方任何浏览器都可以取到数据
6.2 访问或添加request/session/application/context属性
public String scopeParam() throws Exception {
ActionContext ctx=ActionContext.getContext();
//Map<String, Object> session = ActionContext.getContext().getSession();
//HttpServletRequest request = ServletActionContext.getRequest();
//HttpServletResponse response = ServletActionContext.getResponse();
ctx.put("req","请求");
ctx.getSession().put("username","玛丽");
ctx.getApplication().put("password","123456");
ctx.put("age",18);
return "success";
}
7、文件上传
7.1 文件上传步骤
-
在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar
-
把form表的enctype设置为:“multipart/form-data”
<form enctype="multipart/form-data" action="${pageContext.request.contextPath}/upload/uploadimage.action" method="post"> <input type="file" name="uploadImage"/><br/> <input type="submit" value="上传"/> </form>
-
在Action类中添加以下属性,属性前部分对应于表单中文件字段的名称
private File uploadImage;//与表单元素名一致 private String uploadImageFileName;//文件名--前部分与表单元素名一致 private String uploadImageContentType;//文件类型--前部分与表单元素名一致 getter/setter方法 public String execute() throws Exception { String realPath=ServletActionContext.getServletContext().getRealPath("/images"); File file=new File(realPath); if(!file.exists()){ file.mkdir(); } FileUtils.copyFile(uploadImage,new File(file,uploadImageFileName)); return "success"; }
7.2 单文件上传
public class UploadImageAction extends ActionSupport {
private File uploadImage;//与表单元素名一致
private String uploadImageFileName;//文件名--前部分与表单元素名一致
private String uploadImageContentType;//文件类型--前部分与表单元素名一致
getter/setter方法
public String execute() throws Exception {
String realPath=ServletActionContext.getServletContext().getRealPath("/images");
File file=new File(realPath);
if(!file.exists()){
file.mkdir();
}
FileUtils.copyFile(uploadImage,new File(file,uploadImageFileName));
return "success";
}
}
7.3 多文件上传
在jsp页面多加几个File元素,把action中的属性改成数组,上传方法使用循环一个一个上传
异常信息: Unable to find ‘struts.multipart.saveDir’ property setting
解决方案:
struts.xml中添加常量
<constant name="struts.multipart.saveDir" value="/tmp"></constant>
jsp页面:
<form enctype="multipart/form-data" action="${pageContext.request.contextPath }/upload/uploadimage.action" method="post">
<input type="file" name="uploadImage"/><br/>
<input type="file" name="uploadImage"/><br/>
<input type="file" name="uploadImage"/><br/>
<input type="submit" value="上传"/>
</form>
Action文件:
public class UploadImageAction extends ActionSupport {
private File[] uploadImage;//与表单元素名一致
private String[] uploadImageFileName;//文件名--前部分与表单元素名一致
private String[] uploadImageContentType;//文件类型--前部分与表单元素名一致
getter/setter方法
public String execute() throws Exception {
String realPath=ServletActionContext.getServletContext().getRealPath("/images");
File file=new File(realPath);
if(!file.exists()){
file.mkdirs();//多文件上传,这里调用带s的方法
}
for(int i=0;i<uploadImage.length;i++){
FileUtils.copyFile(uploadImage[i],new File(file,uploadImageFileName[i]));
}
return "success";
}
}
8、自定义拦截器
8.1 自定义拦截器
-
实现com.opensymphony.xwork2.interceptor.Interceptor接口
public String intercept(ActionInvocation invocation) throws Exception { String result; Object obj=ActionContext.getContext().getSession().get("user"); if(obj!=null){//session里存在用户 result=invocation.invoke(); }else{ result="logon"; } return result; }
-
配置struts.xml
<package name="user" namespace="/user" extends="struts-default"> <interceptors> <interceptor name="permission" class="com.jyp.aop.PermissionInterceptor"/> <interceptor-stack name="permissionStack"> <interceptor-ref name="defaultStack"/> <interceptor-ref name="permission"/> </interceptor-stack> </interceptors> <action name="shopping" class="com.jyp.action.ShoppingAction" method="execute"> <result name="goShopping">/WEB-INF/page/goShopping.jsp</result> <interceptor-ref name="permissionStack"></interceptor-ref> </action> </package>
因为struts2中如文件上传,数据验证,封装请求参数到action等功能都是由系统默认的defaultStack中的拦截器实现的;
所以我们定义的拦截器需要引用系统默认的defaultStack,这样应用才可以使用struts2框架提供的众多功能。
8.2 定义默认拦截
<!-- 包中指定默认拦截器需放在interceptors以下 -->
<default-interceptor-ref name="permissionStack"></default-interceptor-ref>
包下的所有action都使用自定义的拦截器:
- 可以通过把拦截器定义为默认拦截器。
- 每个包只能指定一个默认拦截器。
- 一旦我们为该包中的某个action显式指定了某个拦截器,则默认拦截器不会起作用。
<interceptors>
<interceptor name="permission" class="com.jyp.aop.PermissionInterceptor"/>
<interceptor-stack name="permissionStack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="permission"/>
</interceptor-stack>
</interceptors>
<!-- 包中指定默认拦截器需放在interceptors以下,注意是interceptor-stack的名字 -->
<default-interceptor-ref name="permissionStack"></default-interceptor-ref>
9、效验器
在struts2中,我们可以实现对action的所有方法进行校验或者对action的指定方法进行校验。
对于输入校验struts2提供了两种实现方法:
- 采用手工编写代码实现。
- 基于XML配置方式实现。
9.1 手工编写代码实现对action中所有方法输入校验
-
新建CustomerAction,继承ActionSupport,添加jsp页面,修改配置文件(不能是*.action,要改成/*)。
<filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping></web-app>
-
调用addFieldError()方法往系统的fieldErrors添加校验失败信息,action需要继承ActionSupport。
@Override public void validate() { if(this.phone==null || "".equals(this.phone.trim())){ this.addFieldError("phone","手机号不能为空"); }else if(!Pattern.compile("^1[358]\\d{9}").matcher(this.phone).matches()){ this.addFieldError("phone","手机号格式不对"); } }
-
fieldErrors包含失败信息,struts2会将请求转发到【名为input的result】。
<%@ taglib uri="/struts-tags" prefix="s"%><!--导入struts标签--> <s:fielderror/>显示失败信息。 <s:fielderror fieldName="phonemsg">\</s:fielderror>显示指定的某个元素
9.2 手工编写代码实现对action指定方法输入校验
- 通过validateXxx()方法实现, validateXxx()只会校验action中方法名为Xxx的方法。其中Xxx的第一个字母要大写;其它与上面一样。
- 对指定方法进行校验后,对action中所有方法输入校验也有效果。
9.3 输入校验的流程
- 类型转换器对请求参数执行类型转换,并把转换后的值赋给action中的属性。
- 如果在执行类型转换的过程中出现异常,系统会将异常信息保存到ActionContext,conversionError拦截器将异常信息添加到fieldErrors里。不管类型转换是否出现异常,都会进入第3步。
- 系统通过反射技术先调用action中的validateXxx()方法,Xxx为方法名。
- 再调用action中的validate()方法。
- 经过上面4步,如果系统中的fieldErrors存在错误信息(即存放错误信息的集合的size大于0),系统自动将请求转发至名称为input的视图。如果系统中的fieldErrors没有任何错误信息,系统将执行action中的处理方法。
9.4 基于XML配置方式实现对action的所有方法进行输入校验
使用基于XML配置方式实现输入校验时,Action也需要继承ActionSupport,并且提供校验文件,校验文件和action类放在同一个包下;
文件的取名格式为:ActionClassName-validation.xml,其中ActionClassName为action的简单类名,-validation为固定写法。
- UsersAction继承ActionSupport
- UsersAction-validation.xml文件模块
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.3//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">
<validators>
<field name="username">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>用户名不能为空!</message>
</field-validator>
</field>
<!-- 方式一:使用字段校验器格式 -->
<field name="phone">
<field-validator type="regex">
注意2.3.15前的版本使用expression参数
之后的使用regexExpression
<param name="expression"><![CDATA[1[348]\d{9}]]></param>
<message>Tel格式不正确</message>
</field-validator>
</field>
-->
<!-- 方式二:使用非字段校验器格式:用来配置正则表达式校验器 -->
<validator type="regex">
<!-- 指定校验字段:phone -->
<param name="fieldName">phone</param>
<!-- 指定匹配的正则表达式 -->
<param name="expression"><![CDATA[1[348]\d{9}]]></param>
<!-- 指定校验失败的提示信息 -->
<message>手机号码格式不正确</message>
</validator>
</validators>
补充
<field name="repassword">
<field-validator type="fieldexpression">
<param name="expression"><![CDATA[repassword==password]]></param>
<message>二次密码不一致</message>
</field-validator>
</field>
9.5 编写ActionClassName-validation.xml校验文件时,不能出现帮助信息
windwos->preferences->myeclipse->files and editors->xml->xmlcatalog
点“add”,
在出现的窗口中的location中选“File system”,
然后在xwork-2.1.2解压目录的src\java目录中选择xwork-validator-1.0.3.dtd,
回到设置窗口的时候不要急着关闭窗口,应把窗口中的Key Type改为URI 。
Key改为http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd
9.6 基于XML配置方式对指定action方法实现输入校验
- 取名为ActionClassName-validation.xml时,会对 action中的所有处理方法实施输入验证;
- 取名为ActionClassName-ActionName-validation.xml,只对action中的某个action方法实施校验:
要对add2()方法实施验证,校验文件的取名为:UsersAction-user_add2-validation.xml
注意:ActionName是struts.xml中<action name="userAction_"的name的值。
9.7 基于XML校验的一些特点
当为某个action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml两种规则的校验文件时,系统按下面顺序寻找校验文件:
- AconClassName-validation.xml
- ActionClassName-ActionName-validation.xml
- 会把校验文件里的所有校验规则汇总,然后全部应用于action方法的校验;
- 如果两个校验文件中指定的校验规则冲突,则只使用后面文件中的校验规则。
- 当action继承了另一个action,父类action的校验文件会先被搜索到。
9.8 struts2提供的校验器列表
- required (必填校验器,要求field的值不能为null)
- requiredstring (必填字符串校验器,要求field的值不能为null,并且长度大于0,默认情况下会对字符串去前后空格)
- stringlength(字符串长度校验器,要求field的值必须在指定的范围内,否则校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field之前是否去除字符串前后的空格)
- regex(正则表达式校验器,检查被校验的field是否匹配一个正则表达式.expression参数指定正则表达式,caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true)
- int(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)
- double(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)
- fieldexpression(字段OGNL表达式校验器,要求field满足一个ognl表达式,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过)
- email(邮件地址校验器,要求如果field的值非空,则必须是合法的邮件地址)
- url(网址校验器,要求如果field的值非空,则必须是合法的url地址)
- date(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)
- conversion(转换校验器,指定在类型转换失败时,提示的错误信息)
- visitor(用于校验action中的复合属性,它指定一个校验文件用于校验复合属性中的属性)
- expression(OGNL表达式校验器,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过,该校验器不可用在字段校验器风格的配置中)
注意:
1、如果是复合类型接收请求参数:使用对象名.属性即可
2、一个属性可以跟多个来验证。
10、国际化
10.1 国际化
准备资源文件,资源文件的命名格式如下:baseName_language_country.properties
baseName_language.properties
baseName.properties其中baseName是资源文件的基本名,我们可以自定义,但language和country必须是java支持的语言和国家。
如:
*: baseName_zh_CN.properties
美国: baseName_en_US.properties
对于中文的属性文件,我们编写好后,应该使用jdk提供的native2ascii命令把文件转换为unicode编码的文件。
10.2 步骤
-
添加两个资源文件:
a、jyp_zh_CN.properties
welcome=欢迎来到中国
b、jyp_en_US.properties
welcome=welcome to china -
配置全局资源与输出国际化信息
1、在struts.xml中通过struts.custom.i18n.resources常量把资源文件定义为全局资源文件,如下:
2、在页面或在action中访问国际化信息:
(1)、在JSP页面中使用<s:text name=""/>标签输出国际化信息:
<s:text name=“welcome”/>,name为资源文件中的key
(2)、在Action类中,可以继承ActionSupport,使用getText()方法得到国际化信息,该方法的第一个参数用于指定资源文件中的key。
在表单标签中,通过key属性指定资源文件中的key,如:<s:textfield name="realname" key="user"/>
10.3 国际化—输出带占位符的国际化信息
-
修改资源文件
welcome 欢迎{0}来到中国–{1} -
修改页面或action中的方法
<s:text name="welcome"> <s:param value="realname"></s:param> <s:param>IT精英</s:param> </s:text>
或者
getText(String key, String[] args) //或 getText(String aTextName, List args)//方法 ActionContext.getContext().put("message",this.getText("welcome",new String[]{"小李子","中国IT"}));
注意:上面的realname是action的属性,请求时传参过来即可,如下。
输入下面网址测试jsp页面显示带占位符的国际化信息
http://localhost:8080/Struts2_7/person/personManager_executeJSPByParam.action?realname=MaLili
10.4 包范围资源文件
把国际化的内容都放置在全局资源属性文件中,会导致资源文件变的过于庞大、臃肿,不便于维护,这个时候我们可以针对不同模块,使用包范围来组织国际化文件。
-
在java的包下放置package_language_country.properties资源文件;
注意:例如package_zh_CN.properties,直接写package就行,不要写成真正的包啦 -
package为固定写法,处于该包及子包下的action都可以访问该资源;
-
当查找指定key的消息时,系统会先从package资源文件查找,当找不到对应的key时,才会从常量struts.custom.i18n.resources指定的资源文件中寻找。
10.5 Action范围资源文件
-
在Action类所在的路径,放置ActionClassName_language_country.properties资源文件,ActionClassName为action类的简单名称。
如:AuthorAction_zh_CN.properties -
当查找指定key的消息时,系统会先从ActionClassName_language_country.properties资源文件查找;
如果没有找到对应的key,然后沿着当前包往上查找基本名为package 的资源文件,一直找到最顶层包;
如果还没有找到对应的key,最后会从常量struts.custom.i18n.resources指定的资源文件中寻找。
10.5 JSP中直接访问某个资源文件
-
struts2为我们提供了<s:i18n>标签,使用<s:i18n>标签我们可以在类路径下直接从某个资源文件中获取国际化数据,而无需任何配置。
jyp为类路径下资源文件的基本名。
注意:类路径下只能有jyp_zh_CN.properties文件,不能有其它属性文件<s:i18n name="jyp"> <s:text name="welcome"> <s:param>凤姐</s:param> <s:param>(^_^)</s:param> </s:text> </s:i18n>
-
要访问的资源文件在类路径的某个包下,如属性名
package_zh_CN.properties
<s:i18n name="com/jyp/action1/package"> <s:text name="authorname"></s:text> </s:i18n>
上面访问com.jyp.action1包下基本名为package的资源文件。
-
要访问的资源文件在类路径的Action下,下面NewAction为action的类名
<s:i18n name="com/huarui/action1/NewAction"> <s:text name="username"></s:text> </s:i18n>
11、OGNL表达式
11.1 OGNL表达式语言
-
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts 2框架使用OGNL作为默认的表达式语言。
-
相对EL表达式,它提供了平时我们需要的一些功能:
a、支持对象方法调用,如xxx.sayHello();
b、支持类静态方法调用和值访问,表达式的格式为@[类全名(包括包路径)]@[方法名 | 值名],
例如:@java.lang.String@format(‘foo %s’, ‘bar’)或@cn.itcast.Constant@APP_NAME;
c、
操作集合对象。示例
action中的属性:private Student student=new Student(); public static double PI=3.1415926; public static String mm(){ return "mm方法"; }
jsp页面:
<s:property value="student.toString()"/> <s:property value="@com.huarui.domain.Student@mm()"/><br/> <s:property value="@com.huarui.domain.Student@PI"/>
总结:对于使用OGNL表达式语言来访问静态方法比较少用,如果获取不到值,是版本不稳定的原因,要换个版本。
不能访问静态方法:
解决方案:原来是在2.1.2中,如果要通过ognl访问静态方法,必须在struts.properties或者struts.xml中将选项struts.ognl.allowStaticMethodAccess设置为true
设置常量<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
- Ognl 有一个上下文(Context)概念,说白了上下文就是一个MAP结构,它实现了java.util.Map接口;
在Struts2中上下文(Context)的实现为ActionContext,下面是上下文(Context)的结构示意图:
|--- ValueStack(值栈,它是根对象) |--- parameters |--- request OGNL Context ---|--- session |--- application |--- attr
当Struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action 。
然后把action存放进ValueStack,所以action的实例变量可以被OGNL访问。-
OGNL表达式语言
a、访问上下文(Context)中的对象需要使用#符号标注命名空间,如#application、#sessionb、OGNL会设定一个根对象(root对象),在Struts2中根对象就是ValueStack(值栈)。可以省略#命名空间,直接访问该对象的属性即可。
c、在struts2中,根对象ValueStack的实现类为OgnlValueStack,该对象不是我们想像的只存放单个值,而是存放一组对象。
d、在root变量中处于第一位的对象叫栈顶对象。通常我们在OGNL表达式里直接写上属性的名称即可访问root变量里对象的属性;
搜索顺序是从栈顶对象开始寻找,如果栈顶对象不存在该属性,就会从第二个对象寻找,如果没有找到就从第三个对象寻找,依次往下访问,直到找到为止。
大家注意: Struts2中,OGNL表达式需要配合Struts标签才可以使用。如:<s:property value=“name”/>
-
JSP页面访问对象
a、用户访问ValueStack(值栈)中的对象,在JSP页面可以直接通过EL表达式访问值栈中对象的属性:
${foo} //获得值栈中某个对象的foo属性
b、如果访问其他Context中的对象,由于他们不是根对象,所以在访问时,需要添加#前缀。
-
application对象:用于访问ServletContext,例如#application.userName或者#application[‘userName’],相当于调用 ServletContext的getAttribute(“username”)。
-
session对象:用来访问HttpSession,例如#session.userName或者#session[‘userName’],相当于调用 session.getAttribute(“userName”)。
-
request对象:用来访问HttpServletRequest属性(attribute)的Map,例如#request.userName或者#request[‘userName’], 相当于调用request.getAttribute(“userName”)。
-
parameters对象:用于访问HTTP的请求参数,例如#parameters.userName或者#parameters[‘userName’],相当于调用 request.getParameter(“username”)。
a.jsp页面
<form method="post" action="b.jsp"> <input type="text" name="username"/><input type="submit" value="提交"/> </form>
b.jsp页面
<s:property value="#parameters.username"/>
- attr对象:用于按page->request->session->application顺序访问其属性。
<s:property value="#request.username"/>改成<s:property value="#attr.username"/>
- attr对象:用于按page->request->session->application顺序访问其属性。
- Ognl 有一个上下文(Context)概念,说白了上下文就是一个MAP结构,它实现了java.util.Map接口;
11.2 为何使用EL表达式能够访问valueStack中对象的属性
原因是Struts2对HttpServletRequest作了进一步的封装。
11.3 采用OGNL表达式创建List/Map集合对象
-
生成一个List对象
<s:set name="names" value="{'张三','李四','王五'}"></s:set> <s:iterator value="#names" id="n"> <s:property value="n"/><br/> </s:iterator>
-
生成一个Map对象
<!--生成一个Map对象,注意value前有一个#号 --> <s:set name="customers" value="#{'1':'李总','2':'王总','3':'蒋总'}"></s:set> <s:iterator value="#customers"> <s:property value="key"/>=<s:property value="value"/> </s:iterator>
-
Set标签用于将某个值放入指定范围。 scope:指定变量被放置的范围,该属性可以接受application、session、request、page或action。
如果没有设置该属性,则默认放置在OGNL Context中。
value:赋给变量的值.如果没有设置该属性,则将ValueStack栈顶的 值赋给变量。<s:set name="name" value="'kk'" /> 注意:需要单引号 引起来 <s:property value="#name"/>
-
取request、session、application中的数据
<% request.setAttribute("name","QQ姐"); request.getSession().setAttribute("username","小马同志"); application.setAttribute("online",22); %> <s:property value="#request.name"/> <s:property value="#session.username"/> <s:property value="#application.online"/>
11.4 采用OGNL表达式判断对象是否存在于集合中
对于集合类型,OGNL表达式可以使用in和not in两个元素符号。
in表达式:<s:if test="'foo' in {'foo','momo'}">
在
</s:if>
not in表达式:
<s:if test="'foo' not in {'foo','momo'}">
不在
</s:if>
11.5 OGNL表达式的投影功能
OGNL还允许使用某个规则(正则表达式)获得集合对象的子集,常用的有以下3个相关操作符。
? 获得所有符合逻辑的元素。
^ 获得符合逻辑的第一个元素。 这个没验证
$ 获得符合逻辑的最后一个元素。
action代码
private List<Student> students;
public String list() throws Exception{
students=new ArrayList<Student>();
students.add(new Student(1,"kitty"));
students.add(new Student(2,"timmy"));
students.add(new Student(3,"jack"));
}
getter/setter方法
jsp页面
<ul>
<!-- 找到所有年龄大于1的student的姓名 --- 结果:[timmy, jack] -->
<li>投影(过滤):<s:property value="students.{?#this.sid>1}.{sname}"/></li>
<!-- 找到所有年龄大于1的student,取第一个 --- 结果:[timmy] -->
<li>投影(过滤):<s:property value="students.{^#this.sid>1}.{sname}"/></li>
<!-- 找到所有年龄大于1的student,取第最后个 --- 结果:[jack] -->
<li>投影(过滤):<s:property value="students.{$#this.sid>1}.{sname}"/></li>
<!-- 找到所有年龄大于1的的集合,判断这个集合是否为空 --- 结果:false-->
<li>投影(过滤):<s:property value="students.{$#this.sid>1}.{sname}==null"/></li>
</ul>
11.6 iterator标签
iterator标签用于对集合进行迭代,这里的集合包含List、Set和数组。
<s:set name="list" value="{'zhangsan','lisi','wangwu','阿飞','小狗子'}"/>
<s:iterator value="#list" status="st">
<br/>
共迭代了几个元素:<s:property value="#st.count"/><br/>
当前元素的索引:<s:property value="#st.index"/><br/>
<font color='<s:if test="#st.first">red</s:if>
<s:elseif test="#st.odd">#ababab</s:elseif>
<s:else>green</s:else>'
>
<s:property/>
</font>
</s:iterator>
.first 第一个元素
.last 最后元素
11.7 url标签
<a href="<s:url namespace='/author' action='author_list'>
<s:param name='aid' value='11'/>
</s:url>"
>url标签测试
</a>
生成类似如下路径:
author/author_list.action?aid=11
当标签的属性值作为字符串类型处理时, “%”符号的用途是计算OGNL表达式的值。
11.8 a标签(下面的id是action中的整型的属性,不加%{}会报错)
<s:a action="student_delete?id=%{id}" οnclick="return confirm('确定要删除吗?')">删除</s:a>
action中取数据 (从action中转到上面的a标签所在的页面)
private Integer id=11;
String result = ServletActionContext.getRequest().getParameter("id");
jsp页面
<s:date name="workDate" format="yyyy-MM-dd HH:mm:ss"/>
11.9 读取数据库数据
-
Action中构造数据(List和Map集合)
public String execute() throws Exception { List<Fruit> fruits=new ArrayList<Fruit>(); fruits.add(new Fruit(1,"苹果")); fruits.add(new Fruit(2,"香蕉")); fruits.add(new Fruit(3,"梨")); ActionContext.getContext().getApplication().put("fruits",fruits); Map<Integer, Fruit> maps=new HashMap<Integer, Fruit>(); maps.put(1,new Fruit(1,"苹果")); maps.put(2,new Fruit(2,"香蕉")); maps.put(3,new Fruit(3,"梨")); ActionContext.getContext().getSession().put("maps",maps); return "success"; }
-
读取数据
方式一:JSP遍历application和session中的集合
ActionContext.getContext().getSession().put(“fruits”,lists);//在action中把集合放在session作用域中
//注意要添加getter和setter方法,否则不能显示数据<s:iterator value="#application.fruits" id="f"> <s:property value="#f.id"/>--- <s:property value="#f.name"/><br/> </s:iterator> <br/><br/><br/> <s:iterator value="#session.maps" id="fruit"> <s:property value="#fruit.key"/>### <s:property value="#fruit.value.name"/> </s:iterator>
方式二:遍历action中的属性集合
private List<Product> products;//这个集合属性在action中已经赋值 getter和setter方法
<s:iterator value="products" id="p"> <tr> <td><s:property value="#p.id"/></td> <td><s:property value="#p.name"/></td> <td><s:property value="#p.price"/></td> </tr> </s:iterator>
11.10 checkboxlist复选框(注意:name属性不能少,而且提交后的action中必须包括name属性)
-
如果集合为list
<s:checkboxlist name="list" list="{'Java','.Net','RoR','PHP'}" value="{'Java','PHP'}"></s:checkboxlist>
生成如下html代码
<input type="checkbox" name="list" value="Java" id="list-1" checked="checked"/> <label for="list-1" class="checkboxLabel">Java</label> ...
-
如果集合为MAP
<s:checkboxlist name="map" list="#{1:'Java',2:'.Net',3:'RoR',3:'PHP'}" value="{1,2}" listKey="key" listValue="value"></s:checkboxlist><br/>
生成如下html代码:
<input type="checkbox" name="map" value="1" id="map-1" checked="checked"/> <label for="map-1" class="checkboxLabel">Java</label> ...
-
如果集合里存放的是javabean
<% Author author1=new Author(1,"小林子"); Author author2=new Author(1,"凤姐"); List<Author> authors=new ArrayList<Author>(); authors.add(author1); authors.add(author2); request.setAttribute("authors",authors); %> <s:checkboxlist name="beans" list="#request.authors" listKey="aid" listValue="aname"></s:checkboxlist>
beans是List类型的集合
异常:org.apache.jasper.JasperException: tag ‘checkboxlist’, field ‘…’, name ‘beans’
解决方案:action中的属性List等需要实例化
11.12 radio单选框
该标签的使用和checkboxlist复选框相同,只是把<s:checkboxlist 改成<s:radio>
11.13 select下拉选择框
该标签跟复选框也相同,只是把*<s:checkboxlist>改成<s:select>*
11.14 <s:token />标签防止重复提交
用法如下:
-
JSP页面
<s:form action="/person_add"> <!-- 防止表单重复提交 第一步:生成id(客户端、服务器)--> <s:token></s:token> 这里放一些表单元素 <s:submit/> </s:form>
-
在struts.xml中配置
<action name="person_*" class="com.hr.action.PersonAction" method="{1}"> <!-- 防止表单重复提交,第二步:配置"防止表单重复提交拦截器" --> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="token"> <!-- 指定拦截哪些方法需要防止表单重复提交 注意:下面这个add是PersonAction中的一个方法名称; 指定多个方法时,使用,号 如add,edit(一般只有添加,因为修改的话,就算是第一次修改也认为是重复提交,会跳转到invalid.token指定的页面) --> <param name="includeMethods">add</param> </interceptor-ref> <!-- 防止表单重复提交,第三步:如果用户重复提交了跳转到的错误页面--> <result name="invalid.token">/example2/add.jsp</result>
以上配置加入了“token”拦截器和“invalid.token”结果,因为“token”拦截器在会话的token与请求的token不一致时,将会直接返回“invalid.token”结果。
在debug状态,控制台出现下面信息,是因为Action中并没有struts.token和struts.token.name属性,我们不用关心这个错误:
严重: ParametersInterceptor - [setParameters]: Unexpected Exception caught setting ‘struts.token’ on 'class xxx: Error setting expression ‘struts.token’ with value ‘[Ljava.lang.String;@39f16f’
严重: ParametersInterceptor - [setParameters]: Unexpected Exception caught setting ‘struts.token.name’