08 09 Struts 2.x轉換器

在使用Struts 2.x接收參數的時候可以發現,如果傳遞的是數字,那麼可以自動的將參數的字符串內容變爲數字,那麼包括文件上傳的時候,能接收的數據類型爲File,那麼這些實際上都是由轉換器幫助我們用戶自動完成的轉換。

例如,如果想要實現字符串到Locale的轉換器,那麼默認沒有實現,必須自己手工完成。

例如,在進行數據刪除的時候往往都要傳遞的是id|id|id...,那麼也可以利用轉換器將其自動變爲Set集合。

1 數組轉換器

如果說現在要想進行參數的接收,默認情況下直接在Action裏面設置 一個與參數對應的名稱即可,但是在進行參數接收的時候一定要記住,Struts 2.x裏面可以接收數組。
範例:定義表單

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>Converter</title>
  </head>
  
  <body>
    <form action="InputAction!insert.action" method="post">
       Hobby: <input type="checkbox" name="inst" id="inst" value="swimming">swimming
       <input type="checkbox" name="inst" id="inst" value="basketball">basketball
       <input type="checkbox" name="inst" id="inst" value="soccer">soccer
       <input type="checkbox" name="inst" id="inst" value="climbing">climbing
       <input type="checkbox" name="inst" id="inst" value="skating">skating
       <input type="checkbox" name="inst" id="inst" value="skiing">skiing
       <input type="submit" value="submit">
    </form>
  </body>
</html>


在這個表單之中,使用的是一個複選框(在Struts 2.x裏面所有的參數都是按照request.getParameterValues()方法接收的。
範例:定義InputAction

package org.lks.action;

import java.util.Arrays;

import com.opensymphony.xwork2.ActionSupport;

@SuppressWarnings("serial")
public class InputAction extends ActionSupport {

	private String[] inst;
	
	public void setInst(String[] inst) {
		this.inst = inst;
	}
	
	public void insert(){
		System.out.println(Arrays.toString(inst));
	}
}

<?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>
	<package name="root" namespace="/" extends="struts-default">
		<action name="InputAction" class="org.lks.action.InputAction"></action>
	</package>
</struts>    

以上的代碼使用了對象數組,發現內容可以進行接收,但是Struts 2.x的轉換器功能遠遠不止這麼點。
範例:定義一個可以傳遞數字的表單

<div>
	<form action="InputAction!update.action" method="post">
		Hobby: <input type="checkbox" name="gid" id="gid" value="1">swimming
		<input type="checkbox" name="gid" id="gid" value="2">basketball
		<input type="checkbox" name="gid" id="gid" value="3">soccer
		<input type="checkbox" name="gid" id="gid" value="4">climbing
		<input type="checkbox" name="gid" id="gid" value="5">skating
		<input type="checkbox" name="gid" id="gid" value="6">skiing
		<input type="submit" value="submit">
	</form>
</div>
private int[] gid;
	
public void setGid(int[] gid) {
	this.gid = gid;
}
public void update(){
	System.out.println(Arrays.toString(this.gid));
}

在Struts 2.x裏面針對於數據的轉換操作也是自動完成的,但是在Struts 2.x裏面只依靠數組接收沒什麼意義,它還可以使用集合接收。
範例:利用Set集合接收

private Set<Integer> gid;
	
public void setGid(Set<Integer> gid) {
	this.gid = gid;
}
public void update(){
	System.out.println(this.gid);
}

此時數據可以接收到,但是屬於無序的數據,一定可以想到,爲Set接口實例化的一定是HashSet子類。此時的代碼即使使用List集合也可以正常接收,這一切的操作支持都來源於Struts 2.x的轉換器的支持。

2 接收對象數組

在Struts 2.x中默認的轉換器裏面可以使用對象數組進行內容的接收,這一點非常的強大。
範例:定義一個對象——Memeber.java

package org.lks.vo;

import java.io.Serializable;
import java.util.Date;

@SuppressWarnings("serial")
public class Memeber implements Serializable {

	private Integer mid;
	private String mname;
	private Double mscore;
	private Date mbirthday;
	
}

隨後如果要在Action裏面接收一個對象數組的信息內容,那麼必須設置一個List集合,而後泛型的類型是指定的Member類。
範例:定義MemberAction

package org.lks.action;

import java.util.List;

import org.lks.vo.Member;

import com.opensymphony.xwork2.ActionSupport;

@SuppressWarnings("serial")
public class MemberAction extends ActionSupport {

	private List<Member> allMembers;
	
	public void setAllMembers(List<Member> allMembers) {
		this.allMembers = allMembers;
	}
	
	public void insert(){
		System.out.println("======= receive =======");
		System.out.println(this.allMembers);
	}
}

但是接下來的關鍵部分在於表單的定義格式上。表單提交的參數名稱裏面一定要包含有allMembers這惡鬼名稱,否則無法正常接收到數據。
範例: 定義member.jsp頁面

    	<form action="MemberAction!insert.action" method="post">
    		Member A: ID:<input type="text" name="allMembers[0].mid" value="1" size="2" maxlength="2">
    		Name:<input type="text" name="allMembers[0].mname" value="lks1" size="5" maxlength="5">
    		Score:<input type="text" name="allMembers[0].mscore" value="90.45" size="5" maxlength="5">
    		Birthday:<input type="text" name="allMembers[0].mbirthday" value="1996-10-15" size="5" maxlength="5">
    		Member B: ID:<input type="text" name="allMembers[1].mid" value="2" size="2" maxlength="2">
    		Name:<input type="text" name="allMembers[1].mname" value="lks2" size="5" maxlength="5">
    		Score:<input type="text" name="allMembers[1].mscore" value="40.45" size="5" maxlength="5">
    		Birthday:<input type="text" name="allMembers[1].mbirthday" value="1997-10-15" size="5" maxlength="5">
    		Member C: ID:<input type="text" name="allMembers[2].mid" value="3" size="2" maxlength="2">
    		Name:<input type="text" name="allMembers[2].mname" value="lks3" size="5" maxlength="5">
    		Score:<input type="text" name="allMembers[2].mscore" value="60.45" size="5" maxlength="5">
    		Birthday:<input type="text" name="allMembers[2].mbirthday" value="1998-10-15" size="5" maxlength="5">
    		<input type="submit" value="submit">
	    </form>
    </div>

雖然使用List集合可以進行接收,但是Set卻絕對無法接收,原因很簡單,List實際上就是一個順序式的結構,在List集合裏面提供有get()、set()方法,都可以根據索引來操作。

3 自定義轉換器

雖然系統提供了許多的轉換器操作,但是在很多時候可能不夠用戶使用,例如,如果說現在用戶希望可以將String類型變爲Locale類型,那麼這樣的轉換器,Struts 2.x是不會提供的。

Locale主要是用於國際化程序的操作實現上,那麼如果在Locale類中定義了這樣的構造方法:
(1)Locale類的構造方法:public Locale(String language, String country)
同時在Struts 2.x的開發包裏面提供有這樣一個類:com.opensymphony.xwork2.util.LocalizedTextUtil
(1)讀取資源:public String findDefaultText(String aTextName, Locale locale)
如果要想使用國際化讀取資源,那麼應該根據語言和城市代碼定義資源文件。
範例:定義Messages_zh_CN.properties文件

welcome=歡迎光臨!

範例:定義Message_en_US.properties文件

welcome=WELCOME!

範例:配置struts.properties文件

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

struts.custom.i18n.resources=Messages

隨後在Action之中使用LocalizedTextUtil類,那麼結合Locale對象就可以讀取指定的資源文件中的內容。
範例:讀取資源文件

public void insert(){
	Locale loc = new Locale("zh","CN");
	String str = LocalizedTextUtil.findDefaultText("welcome", loc);
	System.out.println(str);
	
}

現在的代碼是固定寫完成的,那麼現在希望可以通過外部傳遞參數來決定使用什麼樣的文字顯示。
範例:定義一個表單

<div>
    	<form action="LocaleAction!insert.action" method="post">
	       Language: 
	       <select name="loc" >
	       	<option value="zh_CN">Chinese</option>
	       	<option value="en_US">English</option>
	       </select>
	       <input type="submit" value="submit">
	    </form>
    </div>

如果表單一提交,提交到LocaleAction中的loc是字符串,但是希望它可以變爲Locale對象出現。

範例:修改LocaleAction,讓其接收loc數據

package org.lks.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", loc);
		System.out.println(str);

	}
}

這個時候發現根本就不可能實現轉換的處理操作,因爲在Struts 2.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),此方法參數如下:
(1)Map<String,Object> context:表示當前操作的上下文環境;
(2)Object value:描述的是接收到的請求參數;
(3)Class toType:要接收的數據類型,本次目標類型是Locale。
範例:定義轉換器

package org.lks.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) {//主要實現數據轉換的
		//在Struts 2.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=org.lks.converter.StringToLocaleConverter

以上的這種轉換器實際上定義的是全局轉換器,如果有需要也可以單獨爲某一個Action定義轉換器。
範例:在org.lks.action包中定義一個LocaleAction-conversion.properties

loc=org.lks.converter.StringToLocaleConverter

此處表示針對於LocaleAction中的loc屬性使用轉換器。

4 轉換器實際應用

如果說到轉換器,可能大部分人會認爲以上的操作意義不大,實際上以上只是給大家展示了以下轉換器的基本結構,而如果結合到實際的開發來講,例如,有些時候爲了進行數據的批量刪除,可能會傳遞一組的ID數據,並且每個ID之間使用了|進行分割(1|2|3|4),所以在這種情況下就可以利用轉換器,將這些字符串的數據轉換爲Set集合。

範例:定義一個轉換器IntegerToSetConverter

package org.lks.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集合
			String[] results = ((String[])value)[0].split("\\|");
			Set<Integer> set = new HashSet<Integer>();
			for(int i = 0; i < results.length; i++){
				set.add(Integer.decode(results[i]));
			}
			return set;
		}
		return super.convertValue(context, value, toType);
	}
}

那麼隨後編寫一個Action,裏面要求接收一組ID數據。
範例:定義NewsAction

package org.lks.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="org.lks.action.NewsAction"></action>

範例:在NewsAction-conversion.properties中定義轉換器

ids=org.lks.converter.IntegerToSetConverter

隨後的瀏覽器的輸入:http://localhost:8080/ConverterProject/NewsAction!insert.action?ids=1%7C2%7C3%7C4

但是有一個問題,可能很多時候的ID類型不是數字,而是字符串,所以如果要想繼續接收字符串,那麼就需要再次定義一個轉換器。
範例:定義字符串轉化爲Set集合的轉換器

package org.lks.converter;

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

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

public class StringToSetConverter extends DefaultTypeConverter {

	@Override
	public Object convertValue(Map<String, Object> context, Object value, Class toType) {
		if(Set.class.equals(toType)){ //目標類型是否是Set集合
			String[] results = ((String[])value)[0].split("\\|");
			Set<String> set = new HashSet<String>();
			for(int i = 0; i < results.length; i++){
				set.add(results[i]);
			}
			return set;
		}
		return super.convertValue(context, value, toType);
	}
}

隨後在需要的NewsAction裏面定義一個新的操作屬性,例如,名稱爲nids。
範例:修改NewsAction.java類

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

如果要轉換器使用,還需要在轉換器的配置文件上增加新的數據。
範例:修改NewsAction-conversion.properties文件

nids=org.lks.converter.StringToSetConverter

此時的輸入地址爲:http://localhost:8080/ConverterProject/NewsAction!insert.action?ids=1%7C2&nids=hello%7Cworld

如果真要強調轉換器的作用,可能認爲只有這樣的形式纔可以說它有用。

5 StrutsTypeConverter轉換器

首先要明確一點,如果要定義轉換器,那麼肯定最大的父類使用的就是DefaultTypeConverter,但是在Struts 2.x的核心包裏面又提供有一個轉換器:org.apache.struts2.util.StrutsTypeConverter,這個轉換器可以實現輸入和輸出的轉換操作。此類定義結構如下:

public abstract class StrutsTypeConverter
extends DefaultTypeConverter

但是這個類中定義有兩個抽象方法:
(1)【接收數據】將接收的String參數變爲指定對象:public abstract Object convertFromString(Map context, String[] values, Class toClass)
(2)【輸出數據】將對象變爲String:public abstract String convertToString(Map context, Object o)

假設現在有一個表示座標點的Point類,裏面可以接收的x座標類型和y座標類型都是Double,如果說前端用戶傳遞參數的時候可能給出的類型是point=(1.1, 2.5),接收的時候需要將其利用轉換器變爲指定的Point類對象,但是輸出的時候也可以將Point對象的內容轉化爲大家可以讀懂的座標點。
範例:定義一個Point類

package org.lks.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 org.lks.converter;

import java.util.Map;

import org.apache.struts2.util.StrutsTypeConverter;
import org.lks.vo.Point;

public class PointConverter extends StrutsTypeConverter {

	@Override
	public Object convertFromString(Map context, String[] values, Class toClass) {
		if(Point.class.equals(toClass)){ //要轉換的類型爲Point類型
			String[] result = values[0].split(",");
			Point point = new Point();
			point.setX( Double.valueOf( result[0].substring(1) ) );
			point.setY( Double.valueOf( result[1].replaceAll("\\)", "") ) );
			return point;
		}
		return null;
	}

	@Override
	public String convertToString(Map context, Object o) {
		if(o instanceof Point){
			Point point = (Point)o;
			return "("+ point.getX() +","+ point.getY() +")";
		}
		return null;
	}

}

下面隨後定義一個Action,並且使用Point進行接收處理。
範例:定義PointAction

package org.lks.action;

import org.lks.vo.Point;

import com.opensymphony.xwork2.ActionSupport;

@SuppressWarnings("serial")
public class PointAction extends ActionSupport {

	private Point point;
	
	public Point getPoint() {
		return point;
	}
	
	public void setPoint(Point point) {
		this.point = point;
	}
	
	public void insert(){
		System.out.println(this.point.getX() +","+ this.point.getY());
	}
}

<action name="PointAction" class="org.lks.action.PointAction"></action>

範例:定義PointAction-conversion.properties文件

point=org.lks.converter.PointConverter

範例:定義show.jsp頁面,這個頁面使用標籤輸出

<s:property value="point"/>

此處利用標籤輸出,而且輸出的是point屬性,完成之後通過瀏覽器輸入如下路徑信息:http://localhost:8080/ConverterProject/PointAction!insert.action?point=(1.23,4.56)

所以Struts中提供的轉換器更加適合於它自己的標籤輸出。大部分情況下,如果沒有特殊要求的數據,轉換器使用意義不大,因爲所有的框架都會提供有一些核心數據類型的轉換操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章