1. 程式人生 > >Struts2學習筆記(十二) 型別轉換(Type Conversion)(下)

Struts2學習筆記(十二) 型別轉換(Type Conversion)(下)

null引用處理

我們知道,我們在Action中定義屬性時並沒有對他們進行初始化,那麼也就是Struts2在對請求引數進行型別轉換時,我們的Action屬性可能還是null。那麼框架會自動將這些null的屬性例項化一個預設的物件(在學習Parameters攔截器時已經看過原始碼了):

(1)   如果我們的屬性宣告為List(Collection)型別,那麼預設會給他例項化一個ArrayList物件

(2)   如果我們的屬性宣告為Map型別,那麼預設會給他例項化一個HashMap物件

(3)   如果我們的屬性是一個簡單的JavaBean,並且含有一個沒有引數的構造器,那麼Struts2會通過ObjectFactory類的buildBean方法來給他例項化一個對應的物件

自定義型別轉換器

要想實現自定義型別轉換器,只需我們自定義的Class實現ognl.TypeConterter介面即可。Strtus2中的DefaultTypeConverter類已經實現了這個介面,並且其子類StrutsTypeConverter對它做了更好的擴充,這是一個抽象類,需要我們繼承並實現其中的兩個方法。

(Map context,String[] values, Class toClass)
          Converts one or more String values to the specified class.

(Map context,Object

 o)
          Converts the specified object to a String.

這樣使得不用我們自己去判斷轉換的方向。 那麼要實現自定義的型別轉換器,選擇實現StrutsTypeConverter類顯得最方便。下面我們就動手自己寫一個自定義型別轉換器,來實現將字串轉換為User物件。

User.java

public class User {

	private String userName;
	private int age;

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

}

UserAction.java

public class UserAction extends ActionSupport {

	private User user;

	public User getUser() {
		return user;
	}

	public void setUser(User user) {
		this.user = user;
	}
	
	@Override
	public String execute() throws Exception {
		return SUCCESS;
	}
}

UserConverter.java

public class UserConverter extends StrutsTypeConverter {

	@Override
	public Object convertFromString(Map context, String[] values, Class toClass) {
		
		String[] input = values[0].split(":");
		if(input.length != 2){
			throw new TypeConversionException("請輸入正確的格式如,zhangsan:21");
		}
		
		try {
			
			User user = new User();
			String name = input[0];
			int age = Integer.parseInt(input[1]);
			
			user.setUserName(name);
			user.setAge(age);
			
			return user;
			
		} catch (Exception e) {
			throw new TypeConversionException("請輸入正確的格式如,zhangsan:21");
		}
		
	}

	@Override
	public String convertToString(Map context, Object o) {
		
		User user = (User) o;
		String str = "Name : "+user.getUserName()+";Age : "+user.getAge();
		
		return null;
	}

}

input.jsp

<body>

  <s:iftest="hasFieldErrors()">

    <s:iteratorvalue="fieldErrors"> 

        <fontcolor="#FF0000"><s:propertyvalue="value[0]"/></font><br> 

    </s:iterator> 

  </s:if>

  <formaction="user.action"method="post">

      user: <inputtype="text"name="user"/>

      <inputtype="submit"value="submit"/>

  </form>

success.jsp

<body>

    ${user.userName }<br/>

    ${user.age }

 </body>]

現在程式碼就寫完了,剩下的就是要來註冊我們的型別轉換器了。我們需要在Action類所在目錄下新建一個”ActionClass-conversion.properties”形式的檔案,並在其中為Action的屬性配置型別轉換器,格式為:屬性名=型別轉換器類全名(好包名)。例如本例中需要在UserAction類所在目錄下新建一個UserAction-conversion.properties檔案,內容如下:

user =action.UserConverter

測試:

提交:

定製錯誤訊息

這裡我們在表單中都是按照我們預定的格式輸入,那麼如果我們輸入的資料並不是按照我們規定的格式,那麼會是什麼結果呢?我們將輸入改為”zhangsan-21”,再次提交:

這樣的錯誤訊息可能並不是我們想要的,那麼我們怎麼去定製這些錯誤訊息呢。還記得我們在學習Action的時候學到的關於validate方法驗證失敗時的錯誤訊息定製吧。在這裡我們同樣可以使用這種方法。只需要在我們的ActionClass.properties檔案中按照指定的格式配置就可以了:

       invalid.fieldvalue.fieldName= 自定義訊息

這個檔案也需要和Action類放在同一目錄。例如本例中我們在UserAction.properties檔案中為user成員屬性新增一條型別轉換錯誤訊息:

invalid.fieldvalue.user=\u8BF7\u8F93\u5165\u6B63\u786E\u7684\u683C\u5F0F\u5982\uFF0Czhangsan\:21(內容為:請輸入正確的格式如,zhangsan:21)

還記得吧,訊息的內容都必須是unicode編碼的。

現在我們再來測試,輸入”zhangsan-21”,提交:

Action的屬性的自定義型別轉換

除了Action的成員屬性以外,我們還可以給其他類的成員變數定義自定義型別轉換器。比如User類中有一個Address型別的屬性,Address是我們自定義的類。那麼我們可以為User類中的Address屬性定義一個型別轉換器。和Action一樣,我們需要在這個類所在的目錄下新建一個ClassName-conversion.properties的檔案,其中內容定義的格式和上面講的一樣。

全域性型別轉換器

有時候我們定義的類(bean/model)並不只是在一個Action中會被用到,那麼如果需要定義自定義型別轉換器的話,如果為每個Action都建立一個ActionClass-conversion.properties來完成對該型別的轉換,顯得很不靈活。Strtus2還提供了全域性的型別轉換配置,它的配置不再是針對類的屬性,而是正對類本身。我們需要在src(classpath)下新建一個名為xwork-conversion.properties的檔案。檔案的內容和為action屬性定義型別轉換器的格式相似,只不過key需要是類的全路徑。例如:

       bean.User = action.UserConverter

這樣我們在多個Action中使用該型別的屬性時就可以不用在一一的配置型別轉換器了。

關於ListMap型別的型別轉換

在上一篇中我們討論過如何讓Struts2來完成List/Map型別的屬性的自動型別轉換,但是條件是List/Map中的物件型別必須是Struts2內建型別轉換器能夠完成轉換的型別,那麼如果List/Map中的物件是我們自定義的資料型別,那麼該怎麼讓Struts2來完成轉換呢。

首先我們要告訴Struts2,List/Map型別屬性中元素的型別,通過在ActionClass-conversion.properties檔案中使用如下形式:

List: Element_ActionList型別屬性名 = List中元素型別(類的全路徑)

Map:key:  Key_ActionMap型別屬性名 = MapKey的型別(類的全路徑)

        Value  Element_ActionMap型別屬性名 = MapValue的型別(類的全路徑)

struts2的文件中只介紹了這些。我疑惑的地方就是根據文件中的介紹,我們只是告訴了Struts2集合中元素的型別,但是並沒告訴Struts2如何將請求引數的String型別轉換為這些指定的型別。但是我按照文件中的說法來做了,發現轉換並不能成功。由於集合中元素的型別並沒有包含在Action的屬性中,那麼肯定不能在ActionClass-conversion.properties中配置型別轉換器了。那麼就只有為這個類配置一個全域性轉換器了,配置了型別轉換器之後,Map型別可成功轉換。

例子程式碼如下:

User.java略

UserAction.java

public class UserAction extends ActionSupport {

    private List<User>users;

    public List<User> getUsers() {

       returnusers;

    }

    public void setUsers(List<User> users) {

       this.users = users;

    }

    @Override

    public String execute()throws Exception {

       System.out.println(users);

       returnSUCCESS;

    }

}

UserAction-conversion.properties

    Element_users = bean.User

CreateIfNull_users=true

xwork-conversion.properties

   bean.User =action.UserConverter

UserConverter.java 

public class UserConverter extends StrutsTypeConverter {

	@Override
	public Object convertFromString(Map context, String[] values, Class toClass) {
		
		String[] input = values[0].split(":");
		if(input.length != 2){
			throw new TypeConversionException("請輸入正確的格式如,zhangsan:21");
		}
		
		try {
			
			User user = new User();
			String name = input[0];
			int age = Integer.parseInt(input[1]);
			
			user.setUserName(name);
			user.setAge(age);
			
			return user;
			
		} catch (Exception e) {
			throw new TypeConversionException("請輸入正確的格式如,zhangsan:21");
		}
		
	}

	@Override
	public String convertToString(Map context, Object o) {
		
		User user = (User) o;
		String str = "Name : "+user.getUserName()+";Age : "+user.getAge();
		
		return null;
	}

}

input.jsp

<body>

  <s:iftest="hasFieldErrors()">

    <s:iteratorvalue="fieldErrors"> 

        <fontcolor="#FF0000"><s:propertyvalue="value[0]"/></font><br> 

    </s:iterator> 

  </s:if>

  <formaction="user.action"method="post">

      user: <inputtype="text"name="users[0]"/>

      <inputtype="submit"value="submit"/>

  </form>

  </body>

success.jsp

<body>

    ${users[0].userName}<br/>

 </body>

測試:



結果:


備註:我們不能將自定義的interface作為Action的屬性,這是因為Struts2並不知道該給它例項化一個什麼樣的物件。還有就是在使用泛型的時候要注意型別擦出問題。

總結:在List/Map這個問題上不知道我理解的是否正確。如果理解的有問題還請路過的前輩指正一下啊!學了型別轉換之後,覺得自定義型別轉換器還真是麻煩,感覺沒啥必要,我想應該沒幾個人將User的name和age放在一個input中吧,更何況還是需要按照指定的格式來。使用系統內建的型別轉換器就好了,最多不過在表單上多做一點。不過學習一下型別轉換還是對理解Struts2框架有幫助的。這裡還忘了一個攔截器了,Conversion Error Interceptor,他會將從ActionContext中conversionErrors屬性中的所有錯誤資訊新增為Actin的field errors,這也正是我可以在Input頁面使用hasFieldErrors等獲取field error的方式來獲取型別轉換錯誤資訊的原因。