(二十五)struts2.x中的转换器

时间:2023-02-04 22:02:28

1.转换器的主要使用以及作用;

2.实现自定义的转换器;

3.系统自带转换器;

具体内容

        在使用Struts2.x接收参数的时候,可以发现,如果传递的是数字,可以自动的将参数的字符串内容变为数字,那么包括文件上传的时候,能接收的数据类型为File,那么这些实际上都是由转换器帮助我们用户自动完成的转换.

        例如,如果要想实现字符串到"Locale"的转换,那么默认没有实现,必须自己手动实现

        例如.在进行数据删除的时候往往都要传递"id|id|id|..",那么也可以利用转换器将其自动变为Set集合.

数组转换器

        如果说现在要想进行参数的接收,默认情况下直接在Action里面设置一个与参数对应的名称即可,但是在进行参数接收的时候一定要记住,Struts 2.x里面可以接收数组.

	<form action="InputAction!inst.action" method="post">
   		兴趣:<input type="checkbox" name="inst" id="inst" value="吃饭">吃饭
   			<input type="checkbox" name="inst" id="inst" value="睡觉">睡觉
   			<input type="checkbox" name="inst" id="inst" value="麻将">麻将
   			<input type="checkbox" name="inst" id="inst" value="纸牌">纸牌
   			<input type="submit" value="提交">
   	</form>

        以上的带使用了对象数据,发现内容可以进行接收,但是Struts2.x的转换器功能远远不止这么点

范例:定义一个可以传递整数数组的表单

package cn.zwb.action;

import java.util.Arrays;

import com.opensymphony.xwork2.ActionSupport;

@SuppressWarnings("serial")
public class InputAction extends ActionSupport{
	private String inst[];
	private Integer gid[];
	public void setInst(String[] inst) {
		this.inst = inst;
	}
	public void setGid(Integer[] gid) {
		this.gid = gid;
	}
	public void inst(){
		System.out.println("[参数内容]"+Arrays.toString(this.inst));
	}
	public void gid(){
		System.out.println("[参数内容]"+Arrays.toString(this.gid));
	}
}
  	  <div>
   	<form action="InputAction!gid.action" method="post">
   		兴趣:<input type="checkbox" name="gid" id="gid" value="1">1
   			<input type="checkbox" name="gid" id="gid" value="2">2
   			<input type="checkbox" name="gid" id="gid" value="3">3
   			<input type="checkbox" name="gid" id="gid" value="4">4
   			<input type="submit" value="提交">
   	</form>
   	</div>

        在Struts2.x里面针对于数据的转换操作也是自动完成的,但是在Struts2.x里面只一开数组接收没什么意义,它还可以使用集合接收.

范例:利用Set集合接收参数

package cn.zwb.action;
import java.util.Arrays;
import java.util.Set;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class InputAction extends ActionSupport{
	private String inst[];
	private Set<Integer> gid;
	public void setInst(String[] inst) {
		this.inst = inst;
	}
	public void setGid(Set<Integer> gid) {
		this.gid = gid;
	}
	public void inst(){
		System.out.println("[参数内容]"+Arrays.toString(this.inst));
	}
	public void gid(){
		System.out.println("[参数内容]"+this.gid.toString());
	}
}

        此时数据可以接收到,但是属于无序的数据,一定可以想到,为Set接口实例化的一定是HashSet子类,此时即使是哦有那个了List集合也可以正常进行接收,这一切的操作支持都来源于Struts2.x的转换器

接收对象数组

        在Sturts2.x中默认的转换器里面可以使用对象数组进行内容的接收,这一点非常的强大.

范例:定义一个对象--Member.java类

package cn.zwb.vo;
import java.io.Serializable;
import java.util.Date;
@SuppressWarnings("serial")
public class Member implements Serializable {
	private Integer mid;
	private String name;
	private Double score;
	private Date birthday;
	public Integer getMid() {
		return mid;
	}
	public void setMid(Integer mid) {
		this.mid = mid;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Double getScore() {
		return score;
	}
	public void setScore(Double score) {
		this.score = score;
	}
	public Date getBirthday() {
		return birthday;
	}
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
	
}

        随后如果要在Action里面接受一个对象数组的信息内容,那么必须设置一个List集合,而后泛型的类型是指定的Member类.

范例:定义MemberAction

package cn.zwb.action;
import java.util.List;

import com.opensymphony.xwork2.ActionSupport;

import cn.zwb.vo.Member;
@SuppressWarnings("serial")
public class MemberAction extends ActionSupport{
	private List<Member> all;
	public void setAll(List<Member> all) {
		this.all = all;
	}
	public void insert(){
		System.out.println("[参数内容]"+this.all.toString());
	}
}

        但是接下来的关键不服在于表单定义的格式上,表单提交的参数名称里面一定要包含有"allMembers"这个名称,否则无法正常接收到数据.

范例:定义member.jsp

<div>
   	<form action="MemberAction!insert.action" method="post">
   			成员A:编号:<input type="text" name="allMembers[0].mid" value="1" size="2">
   			姓名:<input type="text" name="allMembers[0].name" value="詹" size="5" maxlength="5">
   			成绩:<input type="text" name="allMembers[0].score" value="100.0" size="5" maxlength="5">
   			生日:成绩:<input type="text" name="allMembers[0].birthday" value="2018-06-11" size="5" maxlength="10"><br>
   			成员B:编号:<input type="text" name="allMembers[1].mid" value="1" size="2">
   			姓名:<input type="text" name="allMembers[1].name" value="闻博" size="5" maxlength="5">
   			成绩:<input type="text" name="allMembers[1].score" value="100.0" size="5" maxlength="5">
   			生日:成绩:<input type="text" name="allMembers[1].birthday" value="2018-06-17" size="5" maxlength="10"><br>
   			
   			<input type="submit" value="提交">
   	</form>
   	</div>

       虽然使用List集合可以进行结束,但是set却绝对无法接收,原因很简单,List实际上就是一个顺序式的结构,在List集合里面提供有get(),set()方法,都可以根据索引来操作.

自定义转换器

        虽然系统提供了许多的转换器操作,但是在很多时候依然可能不够用户使用,例如:如果说现在用户希望可以将Spring类型变为Locale类型,那么这样的转换器,Struts2.x是不会提供的.

        Locale主要用于国际化程序的操作实现上,那么在Locale类中定义了这样的构造方法:

            ●Locale类的构造方法

public Locale(String language,String country)

同时在Struts2.x的开发包里面提供有这样一个类:com.opensymphony.xwork2.util.LocalizedTextUtil

        ●读取资源:public static String findDefaultText(String aTextName,Locale locale);

如果想要使用国际化读取资源,那么应该根据语言和城市代码定义资源文件.

范例:定义Messages_zh_CN.properties文件

welcome=欢迎光临

范例:定义Messages_en_US.properties文件

welcome=WELCOME!!!!

范例:配置struts.properties

struts.i18n.enconding=UTF-8
struts.locale=zh_CN

struts.custom.i18n.resources=Messages_zh_CN,Messages_en_US

    随后在Action中使用LocalizedTextUtil类,那么结合Locale对象就可以读取指定的资源文件中的内容

范例:读取资源

package cn.zwb.action;
import java.util.Locale;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
@SuppressWarnings("serial")
public class LocaleAction extends ActionSupport{
	public void insert(){
		Locale loc = new Locale("zh","CN"); 
		String str = LocalizedTextUtil.findDefaultText("welcome", loc);
		System.out.println(str);
	}
}

               现在的代码是固定完成的,那么现在希望可以通过外部传递参数来决定使用什么样的文字显示.

范例:定义一个表单

	<form action="LocaleAction!inst.action" method="post">
   			选择信息语言:<select name="loc">
   						<option value="zh_CN">中文显示</option>
   						<option value="en_US">英文显示</option>
   					</select>
   			
   			<input type="submit" value="提交">
   	</form>

        如果表单一提交,提交到LocaleAction中的loc的是字符串,但是希望它可以变为Locale对象

范例:修改LocaleAction,让其接收loc数据,

package cn.zwb.action;
import java.util.Locale;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
@SuppressWarnings("serial")
public class LocaleAction extends ActionSupport{
	private Locale loc;
	public void setLoc(Locale loc) {
		this.loc = loc;
	}
	public void insert(){
		String str = LocalizedTextUtil.findDefaultText("welcome", this.loc);
		System.out.println(str);
	}
}

        这个时候发现根本就不能实现转换处理的操作,因为在Struts2.x里面没有这种转换器,那么现在我们需要这种转换器,于是可以采用自定义转换器的方式完成.单独定义一个转换器的类,但是这个类在继承结构上是由明确要求的,要求其必须继承:"com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter;"

此类定义如下:

public abstract class DefaultTypeConverter
extends Object
implements TypeConverter

        可以发现此类是一个抽象类,如果是抽象类,那么使用原则就是必须有子类.但是这个类里面没有明确的定义抽象方法,它的存在意义就是:需要你去继承,不能够直接使用,做了一个父类,但是在子类中目的进行转换,那么要想转换子类则重写方法:

public Object convertValue(Map<String,Object> context,
                  Object value,
                  Class toType)

Map<String,Object> context   表示当前操作的上下文环境
Object value   描述的是接收到的请求参数
Class toType  要接收的数据类型,本次目标类型是Locale

范例:定义转换器 

package cn.zeb.converter;

import java.util.Locale;
import java.util.Map;

import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter;

public class StringToLocaleConverter extends DefaultTypeConverter{
		@Override
		public Object convertValue(Map<String, Object> context, Object value, Class toType) {
			//主要实现数据转换的
			//在Struts2.x里面所有的参数都是以String[]形式出现的
			String result=((String[]) value)[0];
			if(Locale.class.equals(toType)){  //要转换的目标类型为Locale
				String str []=result.split("_");
				Locale loc=new Locale(str[0],str[1]);
				return loc;
				
			}
			return null;
		}
}

        虽然现在转换器开发完成了,但是依然不可以使用,因为如果要想使用转换器,则还需要配置一个专门的转换器操作属性,文件为--xwork-conversion.properties

范例:在src目录下建立xwork-conversion.properties文件

java.util.Locale=cm.zwb.converter.StringToLocaleConverter

        以上的这种转换器实际上定义的是全局转换器,如果有需要也可以单独为某一个Action定义转换器

范例:在cn.mldn.action包中定义一个LocaleAction-convertion.properties

loc=cn.zwb.converter.StringToLocaleConverter

此处表示针对于LocaleAction中的loc属性使用的转换器.

转换器实际应用

        如果说到转换器,可能大部分人会认为以上的操作意义不大,实际上以上只是给大家展示了一下转换器的基本结构,而如果结合到实际的开发中来讲,例如:有时候为了进行数据的批量删除,可能会传递一组的id谁,并且每一个ID之间使用了"丨"进行分割(2丨3丨3),所以在这种情况下就可以利用转换器,将这些字符串的数据转换为Set集合.

范例:定义一个转换器IntegerToSetConverter

package cn.zwb.converter;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter;

public class IntegerToSetConverter extends DefaultTypeConverter {
	@Override
	public Object convertValue(Map<String, Object> context, Object value, Class toType) {
		if(Set.class.equals(toType)){
			Set<Integer> set=new HashSet<Integer>();
			String val=((String[]) value)[0];
			String result[]=val.split("\\丨");
			for (int i = 0; i < result.length; i++) {
				set.add(Integer.parseInt(result[i]));
			}
			return set;
		}
		return null;
	}
}

        那么随后编写一个Action,里面要求接收一组ID数据.

范例:定义NewsAction

package cn.zwb.action;
import java.util.Set;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class NewsAction extends ActionSupport{
	private Set<Integer> ids;
	public void setIds(Set<Integer> ids) {
		this.ids = ids;
	}
	public void insert(){
		
		System.out.println("要删除的数据::"+this.ids);
	}
}
<action name="NewsAction" class="cn.zwb.action.NewsAction"/>

        随后浏览器的输入:"http://localhost:8080/ConverterProject/NewsAction!insert.action?ids=11|2"

        但是有一个问题,可能很对时候的ID类型可能不是数字,而是字符串,所以如果要想接受字符串的话,那么就需要再次定义一个转换器.

范例:定义字符串转化为Set集合的转换器

        

package cn.zwb.converter;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter;
private Set<String> nids;
	public void setNids(Set<String> nids) {
		this.nids = nids;
	}

public class StringToSetConverter extends DefaultTypeConverter {@Overridepublic Object convertValue(Map<String, Object> context, Object value, Class toType) {if(Set.class.equals(toType)){Set<String> set=new HashSet<String>();System.out.println(value);String val=((String[]) value)[0];String result[]=val.split("-");for (int i = 0; i < result.length; i++) {set.add(result[i]);}return set;}return null;}}

        随后在需要的NewsAction里面定义一个新的操作属性,例如,名称为nids.

范例:修改NewsAction.java类

private Set<String> nids;
	public void setNids(Set<String> nids) {
		this.nids = nids;
	}

        如果要转换器使用,还需要在转换器的配置文件上增加新的数据.

范例:修改NewsAction-converter.properties文件

nids=cn.zwb.converter.StringToSetConverter

此时的输入地址为:

http://localhost:8080/ConverterProject/NewsAction!insert.action?ids=11-2-3&nids=4564fewfwef-dwqdqw

如果真要强调转换器的作用,可能认为只有这样的形式才可以说它有用.

StrutsTypeConverter转换器

        首先要明确一点,如果要定义转换器,那么肯定最大的父类使用的是"DefaultypeConverte"r,但是struts核心包里面又提供有一个转换器"org.apache.struts2.util.StrutsTypeConterver",这个转换器可以实现输入和输出的转换操作.此类定义结构如下:

public abstract class StrutsTypeConverter
extends DefaultTypeConverter

        但是这个类中定义有两个抽象方法

[接收数据]将接收的String参数变为指定对象:

public abstract Object convertFromString(Map context,
                       String[] values,
                       Class toClass)

[输出数据]将对象变为String:

public abstract String convertToString(Map context,
                     Object o)

        假设现在有一个表示坐标点的Point类, 里面可以接收的x坐标和y坐标的类型都是Double,如果说前端用户传递参数的时候可能给出的类型是"point=(1.1,2.5)",接收的时候需要将其利用转换器变为指定的Point类对象,但是如果输入的时候也可以将Point对象的内容转化为大家可以读懂的坐标点.

范例:定义一个Point类

package cn.zwb.vo;

public class Point {
	private Double x;
	private Double y;
	public Double getX() {
		return x;
	}
	public void setX(Double x) {
		this.x = x;
	}
	public Double getY() {
		return y;
	}
	public void setY(Double y) {
		this.y = y;
	}
	
}
范例:定义转换器
package cn.zwb.converter;

import java.util.Map;

import org.apache.struts2.util.StrutsTypeConverter;

import cn.zwb.vo.Point;

public class PointConverter extends StrutsTypeConverter{

	@Override
	public Object convertFromString(Map context, String[] value, Class toClass) {
		if(Point.class.equals(toClass)){
			String result[]=value[0].split(",");
			String xValue=result[0].substring(1);
			String yValue=result[1].replace(")", "");
			Point point=new Point();
			point.setX(Double.parseDouble(xValue));
			point.setY(Double.parseDouble(yValue));
			return point;
		}
		return null;
	}

	@Override
	public String convertToString(Map content, Object o) {
		if(o instanceof Point){
			Point point=(Point)o;
			return "[坐标数据]("+point.getX()+","+point.getY()+")";
		}
		return null;
	}

}

范例:定义PointAaction

package cn.zwb.action;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;

import cn.zwb.vo.Point;

public class PointAction extends ActionSupport {
	private Point point;
	public void setPoint(Point point) {
		this.point = point;
	}
	public Point getPoint() {
		return point;
	}
	public String insert(){
		System.out.println("[参数内容]X坐标:"+this.point.getX()+",Y坐标:"+this.point.getY());
		return Action.SUCCESS;
	}
}
范例:定义show.jsp页面,这个页面使用标签输出

<%@taglib prefix="s" uri="/struts-tags" %>

<s:property value="point"/>

        此处利用标签输出,而且输出的是point属性,完成之后通过浏览器输入如下路径信息

http://localhost:8080/ConverterProject/PointAction!insert.action?point=(12,24)

        所以Struts中提供的转换器更加适合于自己的标签输出;大部分情况下,如果没有特殊要求的数据,转换器使用意义不大,因为所有的框架都会提供有一些核心数据类型的转换操作.