四、Struts2的其它知識html
4.一、ModelDrivenjava
若是但願傳遞對象信息到action,但對象信息較多時,須要傳遞大量的參數而且要設置這些參數,工做量
很大,因此建議的方式是在action中直接建立相應的對象,此時在jsp頁面中能夠經過以下一種方式進行參
數的傳遞:數組
<form action="Message_add.action" method="post"> <!-- 在傳遞值的name中,加上action的對象msg的名稱 --> ID:<input type="text" name="msg.id"/><br/> Title:<input type="text" name="msg.title"/><br/> Content:<input type="text" name="msg.content"/><br/> <input type="submit"/> </form>
可是使用這樣的方式帶來的問題是在表單中的名稱不能按照咱們開發人員的習慣來定義,此時就須要經過
ModelDriven來實現以下傳值:服務器
一、讓action實現ModelDriven接口。app
二、覆蓋getModel()方法jsp
@Override public Message getModel() { if(msg==null) msg = new Message(); return msg; }
三、當一個類實現了ModelDriven以後,就會將這個model放入到root中,因此咱們的input中不用加任何的
對象信息就能夠直接放置到msg對象中。ide
特別注意:使用ModelDriven存在的問題:更新時獲取的是root中的新對象,全部的值都爲空。函數
能夠經過以下方式解決:post
public class MessageDao { public Message load() { Message msg = new Message(); msg.setId(12); msg.setTitle("辦證"); msg.setContent("專業辦證二十年"); return msg; }
public String updateInput() { try { //展現頁面須要經過value='<s:property value="msg.id"/>'來獲取值,若是但願直接經過id來獲取值, //能夠經過如下兩種方式來實現 MessageDao md = new MessageDao(); Message tmsg = md.load(); //第一種方式:將root中的對象經過取到的值從新賦值 /*msg.setId(tmsg.getId()); msg.setTitle(tmsg.getTitle()); msg.setContent(tmsg.getContent());*/ //第二種方式:經過BeanUtil實現賦值 BeanUtils.copyProperties(msg, tmsg); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return "success"; }
<s:debug/> <form action="Message_add.action" method="post"> <!-- action中從新賦值後就可直接經過id來獲取值(value='<s:property value="id"/>') --> ID:<input type="text" name="id" value='<s:property value="id"/>'/><br/> <!-- 若是沒有在action中賦值,則需經過action中的對象msg來獲取值(msg.title) --> Title:<input type="text" name="title" value='<s:property value="msg.title"/>'/><br/> Content:<input type="text" name="content" value='<s:property value="msg.content"/>'/><br/> <input type="submit"/> </form>
4.二、類型轉換ui
當使用ModelDriven來實現參數接收時,可能因爲類型的不一致(特別是一些複雜類型)致使接收參數報
錯,此時就須要使用類型轉換器。
類型轉換器有兩種模式:一種全局模式(全部的action均有做用),另一種是局部模式(針對某個特定
的action起做用)。
全局模式實現步驟:
一、寫一個類繼承StrutsTypeConverter
/** * 要爲某個對象增長相應的類型轉換器,首先得建立一個類繼承StrutsTypeConverter * @author PM * */ public class PointConverter extends StrutsTypeConverter { /** * 完成字符串到對象的轉換 */ @Override public Object convertFromString(Map context, String[] values, Class toClass) { Point p = null; if(values.length<=1) { String str = values[0]; //12,88 p = new Point(); String[] cods = str.split(","); p.setX(Integer.parseInt(cods[0])); p.setY(Integer.parseInt(cods[1])); } return p; } /** * 完成對象到字符串的轉換 */ @Override public String convertToString(Map context, Object o) { Point p = (Point)o; return p.getX()+","+p.getY(); }
二、在類路徑建立xwork-conversion.properties文件,在這個文件中說明要轉換的對象
org.struts.model.Point = org.struts.converter.PointConverter
org.struts.model.Point:表示要轉換的對象
org.struts.converter.PointConverter:表示使用哪個轉換器來轉換。
以上的全局轉換器是針對全部的須要轉換的類來指定,可是有時候可能會根據不一樣的action來進行轉換,
這個時候就會使用到局部轉換器。
一、建立一個類繼承StrutsTypeConverter
public class DateConverter01 extends StrutsTypeConverter { //定義了日期的格式 private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); @Override public Object convertFromString(Map context, String[] values, Class toClass) { try { if(values.length<=1) { String d = values[0]; return sdf.parse(d); } } catch (ParseException e) { e.printStackTrace(); } return null; } @Override public String convertToString(Map context, Object o) { return sdf.format((Date)o); }
public class DateConverter02 extends StrutsTypeConverter { //定義了日期的格式 private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); @Override public Object convertFromString(Map context, String[] values, Class toClass) { try { if(values.length<=1) { String d = values[0]; return sdf.parse(d); } } catch (ParseException e) { e.printStackTrace(); } return null; } @Override public String convertToString(Map context, Object o) { return sdf.format((Date)o); }
二、在action所在的包中建立ActionName-conversion.properties(如:MessageAction-
conversion.properties)
msg.createDate = org.struts.converter.DateConverter01 endDate = org.struts.converter.DateConverter02
msg.createDate、endDate:表示action中的要轉換的對象的名稱,此處等於要轉換該action中的msg的
createDate和action中的endDate兩個屬性。
在action所在的包中建立ActionName-conversion.properties來設置轉換器。
4.三、文件上傳
Struts2對文件上傳提供了天生的支持,只要設置一個表單域爲file而且將form的類型提供爲
multipart/form-data,Struts2會自動完成普通表單和文件表單的識別。
一、定義一個文件提供相應的上傳form
<s:debug/> <form action="Message_file.action" method="post" enctype="multipart/form-data"> <!-- 有一組file只要在Action中設定一個數組來接收, 若是隻有一個file,就設定一個對象來接收 --> Title:<input type="text" name="title"/><br/> File:<input type="file" name="photo"/><br/> File:<input type="file" name="photo"/><br/> File:<input type="file" name="photo"/><br/> File:<input type="file" name="photo"/><br/> File:<input type="file" name="photo"/><br/> <input type="submit"/> </form>
二、在Action中定義相應的屬性來接收file對象,若是是一組屬性定義一個數組,若是是一個屬性定義一個
對象。屬性的名稱必須是表單中的name名稱。
//photo名稱必須和表單中的名稱同樣,可是這個file中沒有相應的文件名 private File[] photo; //文件名須要經過定義其它的參數來接收,關鍵是須要兩個set方法, //一個用來設置文件名稱 private String[] photoFileName; //一個用來設置文件類型 private String[] photoContentType; /** * 會把上傳的文件名經過該方法獲取 * @return */ public String[] getPhotoFileName() { return photoFileName; } /** * 上傳的文件名會經過該方法設置 * 用來接收文件名的方法,若是表單的名稱是photo,這個名稱必須是setPhotoFileName() * @param photoFileName */ public void setPhotoFileName(String[] photoFileName) { this.photoFileName = photoFileName; } public String[] getPhotoContentType() { return photoContentType; } /** * 設置文件類型,不管是文件名仍是文件類型,所使用的格式是固定的 * xxxFileName,xxxContentType * 用來接收上傳文件的類型的,若是名稱是photo,這個函數的名稱必須是setPhotoContentType() * @param photoContentType */ public void setPhotoContentType(String[] photoContentType) { this.photoContentType = photoContentType; } public File[] getPhoto() { return photo; } public void setPhoto(File[] photo) { this.photo = photo; }
特別注意:Struts2默認上傳文件的大小是有限制的(default.properties),能夠經過struts.xml來配置修改
<!-- 設置上傳文件的大小(字節數) --> <constant name="struts.multipart.maxSize" value="10240000"/>
4.四、攔截器
攔截器是Struts2中很是重要的一種控制手段,在開發中可使用攔截器來攔截用戶的請求,而且進行相應
的權限控制。
一、建立攔截器:寫一個類繼承AbstractInterceptor
public class HelloInterceptor extends AbstractInterceptor { @Override public String intercept(ActionInvocation invocation) throws Exception { System.out.println("heelo interceptor"); //攔截請求完成以後,須要繼續向下走,調用invocation.invoke() return invocation.invoke(); }
二、配置攔截器
2.一、先建立攔截器
<package name="default" namespace="/" extends="struts-default"> <interceptors> <!-- 建立了一個攔截器的配置,可是此時配置並無生效,須要在action中配置以後才能生效 --> <interceptor name="helloInterceptor" class="org.struts.interceptor.HelloInterceptor"/> </interceptors>
2.二、在action中使用攔截器
<action name="*_*" class="org.struts.action.{1}Action" method="{2}"> <!-- 在action中引入相應的攔截器 --> <interceptor-ref name="helloInterceptor"/>
問題:當時用了這個攔截器以後,出現了一個問題,原來應該傳的值都變爲null了?
是由於當咱們設定了攔截器以後,默認攔截器就不起做用了(默認攔截器是在struts-default.xml中設定
的),因此在建立新的攔截器時必須能夠擁有原有的默認攔截器,可使用攔截器棧來處理。
<package name="default" namespace="/" extends="struts-default"> <interceptors> <!-- 建立了一個攔截器的配置,可是此時配置並無生效,須要在action中配置以後才能生效 --> <interceptor name="helloInterceptor" class="org.struts.interceptor.HelloInterceptor"/> <!-- 定義了一個攔截器棧,包含有默認的攔截器和本身定義的攔截器 --> <interceptor-stack name="helloStack"> <interceptor-ref name="defaultStack"/> <interceptor-ref name="helloInterceptor"/> </interceptor-stack> </interceptors>
<action name="*_*" class="org.struts.action.{1}Action" method="{2}"> <!-- 引用時就引用本身的攔截器棧 --> <interceptor-ref name="helloStack"/>
4.五、國際化(I18N)
一、國際化的概念:
ResourceBundle能夠支持國際化,經過配置文件(.properties)完成國際化的支持的。
二、建立配置文件的要求:
Message_zh_CN.properties(Message表示基礎名稱,zh表示中文,_CN表示的是國家的編碼,能夠
省略)。
Message_en_UK.properties(針對美國英語的配置文件,一樣也能夠省略_UK)。
三、使用java.util中ResourceBundle能夠實現
public class TestI18n { public static void main(String[] args) { //建立ResourceBundle對象,而且說明區域名稱(CHINESE或者ENGLISH等) ResourceBundle rb = ResourceBundle.getBundle("Message", Locale.CHINESE); //根據key來獲取值,此時就會根據ResourceBundle建立時的區域名稱來相應的properties文件中取數據 System.out.println(rb.getObject("hello")+","+rb.getObject("world")); }
四、Struts2的實現:局部的、全局的
局部的通常不使用。
全局的有兩種方式:
4.一、針對package的全局設定
在Action的所在包的任何路徑下建立package_zh.properties和package_en.properties。
在這兩個文件中加入相應的key和value。
message.id=標識 message.title=標題 message.content=內容
message.id=ID message.title=Title message.content=Content
讓須要進行國際化的Action實現ActionSupport類以後在頁面中使用<s:text name=""/>完成調用。
<s:text name="message.id"/>:<input type="text" name="id"/><br/> <s:text name="message.title"/>:<input type="text" name="title"/><br/> <s:text name="message.content"/>:<input type="text" name="content"/><br/> <input type="submit"/>
4.二、全局國際化
在類路徑下建立相應的資源文件。
在struts.xml配置中加入全局資源文件的名稱.
<!-- 設置國際化,value="Message"指定國際化文件的baseName(基本名稱) --> <constant name="struts.custom.i18n.resources" value="Message"/>
4.六、Struts2的經常使用標籤
Struts2提供了一組很是好用的Form來幫助咱們完成開發。
一、Struts2的theme:Struts2提供一組主題幫助開發者來實現界面操做。默認主題是xhtml。
修改主題能夠在struts.xml配置文件中設置。
<!-- 設置顯示主題樣式 --> <!-- <constant name="struts.ui.theme" value="simple"/> -->
二、經常使用標籤
public class GroupAction implements ModelDriven<Group> { private Group group; public Group getGroup() { return group; } public void setGroup(Group group) { this.group = group; } public String addInput() { group.setId(1); group.setName("財務部"); List<String> interest = new ArrayList<String>(); // interest.add("football"); interest.add("pingpong"); List<Group> groups = new ArrayList<Group>(); groups.add(new Group(1,"財務部")); groups.add(new Group(2,"文章審覈人員")); groups.add(new Group(3,"超級管理員")); groups.add(new Group(4,"文章發佈人員")); ActionContext.getContext().put("username", "張三"); ActionContext.getContext().put("groups", groups); ActionContext.getContext().put("interest", interest); return "success"; } @Override public Group getModel() { if(group==null) group = new Group(); return group; }
<s:debug/> <s:form action="Group_add" method="post"> <!-- s:textfield會顯示文本輸入框,而且會自動將root中的值根據name設置進來 --> <s:textfield label="組標識" name="id"/> <s:textfield label="組名稱" name="name"/> <!-- 在Struts2中要在Struts2的標籤中引入相應的ActionContext中的值,要使用%{xxx} --> <s:textfield label="用戶名" name="username1" value="%{username}"/> <!-- 對於列表而言,在新版本中已經可使用#xxx來訪問,可是依然建議使用%{}來訪問 --> <s:checkboxlist label="興趣" name="interest" list="#{'football':'足球','basketball':'籃球','pingpong':'乒乓球' }" listKey="key" listValue="value" value="%{interest}"/> <!-- 建立了一組單選框 --> <s:radio list="#{'0':'男','1':'女' }" value="0" name="gender" label="性別"/> <!-- 建立了一個下拉列表框 --> <s:select list="#groups" listKey="id" listValue="name" label="選擇組" headerKey="-1" headerValue="請選擇相應的組" value="2"/> <s:submit value="添加"/> </s:form>
4.七、Struts2提供了大量的服務器端驗證方法(XML、Annotation等)。
/** * 在執行某個方法以前若是有validateXX,都會先執行這個方法 */ public void validateAdd() { if(msg.getId()<0) { this.addFieldError("id", "標識必須大於0"); } if(msg.getTitle()==null||"".equals(msg.getTitle())) { this.addFieldError("title", "標題不能爲空"); } }
<!-- 錯誤信息在一處顯示 --> <s:fielderror/> <!-- 顯示錯誤信息必須使用Struts的標籤 --> <s:form action="Message_add" method="post"> <s:textfield key="message.id" name="id"/> <s:textfield key="message.title" name="title"/> <s:textfield key="message.content" name="content"/> <s:submit/> </s:form> <!-- 只在一處顯示id的錯誤信息 --> <s:fielderror fieldName="id"/>
4.八、Struts2異常處理
<global-results> <result name="error">/WEB-INF/inc/error.jsp</result> <!-- 異常信息頁面 --> <result name="exception">/WEB-INF/inc/exception.jsp</result> <result name="loginInput">/WEB-INF/Login/input.jsp</result> </global-results> <!-- 定義異常處理 --> <global-exception-mappings> <exception-mapping result="exception" exception="org.struts.exception.MyException"/> </global-exception-mappings>
<h1 style="color: #f00">發生錯誤</h1> <!-- 獲取異常信息 --> ${exception.getMessage() }