struts2 笔记

2021/4/13 10:25:42

本文主要是介绍struts2 笔记,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

struts2笔记

1、struts2 配置

文章目录

  • struts2笔记
      • 1、struts2 配置
          • 1.1 优点
          • 1.2 搭建struts2开发环境
      • 2、常量
          • 2.1、为Action的属性注入值
          • 2.2、指定需要Struts 2处理的请求后缀
          • 2.3、细说常量定义
          • 2.4、常用的常量介绍
          • 2.5 Struts2的处理流程
          • 2.6 为应用指定多个struts配置文件
      • 3、action方法的调用
          • 3.1 Action动态方法调用
          • 3.2 使用通配符定义action
          • 3.3 直接在action中设置method方法(不设置默认调用execute方法)
      • 4、接收请求参数(struts2不需要ActionForm)
          • 4.1 采用基本类型接收请求参数(get/post)
          • 4.2 采用复合类型接收请求参数
      • 5、自定义类型转换器
          • 5.1 自定义类型转换器
          • 5.2 将上面的类型转换器注册为局部类型转换器
          • 5.3 自定义全局类型转换器
          • 5.4 在Action中定义一个User类型的属性
      • 6、四大作用域
          • 6.1 四大作用域
          • 6.2 访问或添加request/session/application/context属性
      • 7、文件上传
          • 7.1 文件上传步骤
          • 7.2 单文件上传
          • 7.3 多文件上传
      • 8、自定义拦截器
          • 8.1 自定义拦截器
          • 8.2 定义默认拦截
      • 9、效验器
          • 9.1 手工编写代码实现对action中所有方法输入校验
          • 9.2 手工编写代码实现对action指定方法输入校验
          • 9.3 输入校验的流程
          • 9.4 基于XML配置方式实现对action的所有方法进行输入校验
          • 9.5 编写ActionClassName-validation.xml校验文件时,不能出现帮助信息
          • 9.6 基于XML配置方式对指定action方法实现输入校验
          • 9.7 基于XML校验的一些特点
          • 9.8 struts2提供的校验器列表
      • 10、国际化
          • 10.1 国际化
          • 10.2 步骤
          • 10.3 国际化—输出带占位符的国际化信息
          • 10.4 包范围资源文件
          • 10.5 Action范围资源文件
          • 10.5 JSP中直接访问某个资源文件
      • 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 优点
  1. 在软件设计上Struts2没有像struts1那样跟Servlet API和struts API有着紧密的耦合;
    Struts2的应用可以不依赖于Servlet API和struts API;
    Struts2的这种设计属于无侵入式设计,而Struts1却属于侵入式设计。
  2. Struts2提供了拦截器,利用拦截器可以进行AOP编程,实现如权限拦截等功能。
  3. Strut2提供了类型转换器,我们可以把特殊的请求参数转换成需要的类型;
    在Struts1中,如果我们要实现同样的功能,就必须向Struts1的底层实现BeanUtil注册类型转换器才行。
  4. Struts2提供支持多种表现层技术,如:JSP、freeMarker、Velocity等
  5. Struts2的输入校验可以对指定方法进行校验,解决了Struts1长久之痛。
  6. 提供了全局范围、包范围和Action范围的国际化资源文件管理实现。
1.2 搭建struts2开发环境
  1. 找到开发Struts2应用需要使用到的jar文件

    现在struts2没与spring集成,所以不需要导入struts2-spring-plugin-2.1.8.jar
    在这里插入图片描述

  2. 编写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方法

  1. 在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>  
  1. 默认值

    action的class默认访问ActionSupport
    action的method默认为execute()
    result的type默认为success

    1. Action搜索的须序
      /path1/path2/path3

    http://localhost:8080/项目名/包的命名空间/Action名称.action

    1. result中的type
      1、默认是dispatcher 转发
      2、redirectAction 重定向
<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 文件上传步骤
  1. 在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar

  2. 把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>
    
    
  3. 在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 自定义拦截器
  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;
    }
    
    
  2. 配置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都使用自定义的拦截器:

  1. 可以通过把拦截器定义为默认拦截器。
  2. 每个包只能指定一个默认拦截器。
  3. 一旦我们为该包中的某个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提供了两种实现方法:

  1. 采用手工编写代码实现。
  2. 基于XML配置方式实现。
9.1 手工编写代码实现对action中所有方法输入校验
  1. 新建CustomerAction,继承ActionSupport,添加jsp页面,修改配置文件(不能是*.action,要改成/*)。

    <filter-mapping>
      	<filter-name>struts2</filter-name>
      	<url-pattern>/*</url-pattern>
    </filter-mapping></web-app>
    
    
  2. 调用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","手机号格式不对");
    	}
    }
    
    
  3. fieldErrors包含失败信息,struts2会将请求转发到【名为input的result】。

    <%@ taglib uri="/struts-tags" prefix="s"%><!--导入struts标签-->
    
    <s:fielderror/>显示失败信息。
    <s:fielderror fieldName="phonemsg">\</s:fielderror>显示指定的某个元素
    
    
9.2 手工编写代码实现对action指定方法输入校验
  1. 通过validateXxx()方法实现, validateXxx()只会校验action中方法名为Xxx的方法。其中Xxx的第一个字母要大写;其它与上面一样。
  2. 对指定方法进行校验后,对action中所有方法输入校验也有效果。
9.3 输入校验的流程
  1. 类型转换器对请求参数执行类型转换,并把转换后的值赋给action中的属性。
  2. 如果在执行类型转换的过程中出现异常,系统会将异常信息保存到ActionContext,conversionError拦截器将异常信息添加到fieldErrors里。不管类型转换是否出现异常,都会进入第3步。
  3. 系统通过反射技术先调用action中的validateXxx()方法,Xxx为方法名。
  4. 再调用action中的validate()方法。
  5. 经过上面4步,如果系统中的fieldErrors存在错误信息(即存放错误信息的集合的size大于0),系统自动将请求转发至名称为input的视图。如果系统中的fieldErrors没有任何错误信息,系统将执行action中的处理方法。
9.4 基于XML配置方式实现对action的所有方法进行输入校验

使用基于XML配置方式实现输入校验时,Action也需要继承ActionSupport,并且提供校验文件,校验文件和action类放在同一个包下;

文件的取名格式为:ActionClassName-validation.xml,其中ActionClassName为action的简单类名,-validation为固定写法。

  1. UsersAction继承ActionSupport
  2. 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方法实现输入校验
  1. 取名为ActionClassName-validation.xml时,会对 action中的所有处理方法实施输入验证;
  2. 取名为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两种规则的校验文件时,系统按下面顺序寻找校验文件:

  1. AconClassName-validation.xml
  2. ActionClassName-ActionName-validation.xml
  3. 会把校验文件里的所有校验规则汇总,然后全部应用于action方法的校验;
  4. 如果两个校验文件中指定的校验规则冲突,则只使用后面文件中的校验规则。
  5. 当action继承了另一个action,父类action的校验文件会先被搜索到。
9.8 struts2提供的校验器列表
  1. required (必填校验器,要求field的值不能为null)
  2. requiredstring (必填字符串校验器,要求field的值不能为null,并且长度大于0,默认情况下会对字符串去前后空格)
  3. stringlength(字符串长度校验器,要求field的值必须在指定的范围内,否则校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field之前是否去除字符串前后的空格)
  4. regex(正则表达式校验器,检查被校验的field是否匹配一个正则表达式.expression参数指定正则表达式,caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true)
  5. int(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)
  6. double(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)
  7. fieldexpression(字段OGNL表达式校验器,要求field满足一个ognl表达式,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过)
  8. email(邮件地址校验器,要求如果field的值非空,则必须是合法的邮件地址)
  9. url(网址校验器,要求如果field的值非空,则必须是合法的url地址)
  10. date(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)
  11. conversion(转换校验器,指定在类型转换失败时,提示的错误信息)
  12. visitor(用于校验action中的复合属性,它指定一个校验文件用于校验复合属性中的属性)
  13. 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 步骤
  1. 添加两个资源文件:
    a、jyp_zh_CN.properties
    welcome=欢迎来到中国
    b、jyp_en_US.properties
    welcome=welcome to china

  2. 配置全局资源与输出国际化信息

    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 国际化—输出带占位符的国际化信息
  1. 修改资源文件
    welcome 欢迎{0}来到中国–{1}

  2. 修改页面或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 包范围资源文件

把国际化的内容都放置在全局资源属性文件中,会导致资源文件变的过于庞大、臃肿,不便于维护,这个时候我们可以针对不同模块,使用包范围来组织国际化文件。

  1. 在java的包下放置package_language_country.properties资源文件;
    注意:例如package_zh_CN.properties,直接写package就行,不要写成真正的包啦在这里插入图片描述

  2. package为固定写法,处于该包及子包下的action都可以访问该资源;

  3. 当查找指定key的消息时,系统会先从package资源文件查找,当找不到对应的key时,才会从常量struts.custom.i18n.resources指定的资源文件中寻找。

10.5 Action范围资源文件
  1. 在Action类所在的路径,放置ActionClassName_language_country.properties资源文件,ActionClassName为action类的简单名称。
    如:AuthorAction_zh_CN.properties

  2. 当查找指定key的消息时,系统会先从ActionClassName_language_country.properties资源文件查找;
    如果没有找到对应的key,然后沿着当前包往上查找基本名为package 的资源文件,一直找到最顶层包;
    如果还没有找到对应的key,最后会从常量struts.custom.i18n.resources指定的资源文件中寻找。

10.5 JSP中直接访问某个资源文件
  1. 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>
    
    
  2. 要访问的资源文件在类路径的某个包下,如属性名

    package_zh_CN.properties

    <s:i18n name="com/jyp/action1/package">
          <s:text name="authorname"></s:text>
    </s:i18n>
    
    

    上面访问com.jyp.action1包下基本名为package的资源文件。

  3. 要访问的资源文件在类路径的Action下,下面NewAction为action的类名

    <s:i18n name="com/huarui/action1/NewAction">
          <s:text name="username"></s:text>
    </s:i18n>
    
    

11、OGNL表达式

11.1 OGNL表达式语言
  1. OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts 2框架使用OGNL作为默认的表达式语言。

  2. 相对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>
    
    
    1. 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访问。

    1. OGNL表达式语言
      a、访问上下文(Context)中的对象需要使用#符号标注命名空间,如#application、#session

      b、OGNL会设定一个根对象(root对象),在Struts2中根对象就是ValueStack(值栈)。可以省略#命名空间,直接访问该对象的属性即可。

      c、在struts2中,根对象ValueStack的实现类为OgnlValueStack,该对象不是我们想像的只存放单个值,而是存放一组对象。

      d、在root变量中处于第一位的对象叫栈顶对象。通常我们在OGNL表达式里直接写上属性的名称即可访问root变量里对象的属性;
      搜索顺序是从栈顶对象开始寻找,如果栈顶对象不存在该属性,就会从第二个对象寻找,如果没有找到就从第三个对象寻找,依次往下访问,直到找到为止。

    大家注意: Struts2中,OGNL表达式需要配合Struts标签才可以使用。如:<s:property value=“name”/>

    1. 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"/>
11.2 为何使用EL表达式能够访问valueStack中对象的属性

原因是Struts2对HttpServletRequest作了进一步的封装。

11.3 采用OGNL表达式创建List/Map集合对象
  1. 生成一个List对象

    <s:set name="names" value="{'张三','李四','王五'}"></s:set>
    <s:iterator value="#names" id="n">
        <s:property value="n"/><br/>
    </s:iterator>
    
    
  2. 生成一个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>
    
    
  3. Set标签用于将某个值放入指定范围。 scope:指定变量被放置的范围,该属性可以接受application、session、request、page或action。

    如果没有设置该属性,则默认放置在OGNL Context中。
    value:赋给变量的值.如果没有设置该属性,则将ValueStack栈顶的 值赋给变量。

    <s:set name="name" value="'kk'" />   注意:需要单引号 引起来
    <s:property value="#name"/>
    
    
  4. 取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 读取数据库数据
  1. 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";
    
    }
    
    
  2. 读取数据

    方式一: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属性)
  1. 如果集合为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>
    ...
    
    
  2. 如果集合为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>
    ...
    
    
  3. 如果集合里存放的是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 />标签防止重复提交

用法如下:

  1. JSP页面

    <s:form action="/person_add">
    <!-- 防止表单重复提交  第一步:生成id(客户端、服务器)-->
      <s:token></s:token>
      这里放一些表单元素
      <s:submit/>
    
    </s:form>
    
    
  2. 在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’



这篇关于struts2 笔记的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程