struts2簡介
html
struts2是在webwork2基礎上發展而來的。和struts1同樣,struts2也屬於MVC框架。不過有一點須要注意的是:struts2和struts2雖然名字很類似,可是在二者在代碼編寫風格上幾乎是不同的。那麼既然有了struts1,爲何還要推出struts2。主要的緣由是struts2有如下優勢:java
1.在軟件設計上struts2沒有像struts1那樣跟servlet API和struts API有着緊密的耦合,struts2的應用能夠不依賴於servlet API和struts API。struts2的這種設計屬於無侵入式設計,而struts1卻屬於侵入式設計。web
2.struts2提供了攔截器,利用攔截器能夠進行AOP編程,實現如權限攔截等功能。spring
3.struts2提供了類型轉換器,能夠把特殊的請求參數轉化成須要的類型。在struts1中,若是咱們要實現一樣的功能,就必須向struts1的底層實現BeanUtil註冊類型轉換器才行。express
4.struts2提供支持多種表現層技術,如:jsp、freemarker、velocity等。apache
5.struts2的輸入校驗能夠對指定的方法進行校驗,解決了struts1長久之痛。編程
6.提供了全局範圍、包範圍和Action範圍的國際化資源文件實現。
數組
struts2開發環境搭建瀏覽器
搭建struts2(這裏使用的是2.1.8版的)的開發環境的時,通常都會按以下的步驟:緩存
1.引入struts2須要的jar文件(通常須要commons-fileupload-1.2.1.jar、commons-logging-1.0.4.jar、freemarker-2.3.15.jar、ognl-2.7.3.jar、struts2-core-2.1.8.1.jar和xwork-core-2.1.6.jar這6個jar文件,能夠從struts2自帶的示例項目中拷貝,粘貼到WebRoot/WEB-INF/lib下面)
2.編寫struts2的配置文件(能夠從struts2自帶的示例項目中拷貝struts.xml,粘貼到src目錄下,而後在這個基礎上按照本身的須要來更改)
3.在web.xml中加入struts2框架啓動配置,具體的方法是在web.xml中加入以下的代碼:
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
從上面能夠看出,struts2框架是經過filter啓動的。在StrutsPrepareAndExecuteFilter的init()方法中讀取類路徑下默認的配置文件struts.xml完成初始化操做。
注意:struts2讀取到struts.xml中的內容後,以javabean的形式保存在內存中,之後struts2對用戶的每次請求處理將使用內存中的數據,而不是每次都讀取struts.xml文件。
struts2配置中的包介紹
<packagename="upload"namespace="/upload"extends="struts-default">
<actionname="upload"class="zhchljr.action.HelloWorldAction"method="upload">
<resultname="success">/success.jsp</result>
</action>
</package>
<package name="upload" namespace="/upload" extends="struts-default">
<action name="upload" class="zhchljr.action.HelloWorldAction" method="upload">
<result name="success">/success.jsp</result>
</action>
</package>
struts2中使用包來管理action。包的做用和java中類包是很是相似的,它主要用於管理一組業務功能相關的action。在實際應用中,應該把一組業務功能相關的action放在同一個包下面。
配置包是必須指定name屬性,該name能夠隨意取名,可是必須惟一,它不對應java的類包,若是其餘類要繼承該包,必須使用該屬性進行使用。包的namespace屬性用於定義包的命名空間,命名空間做爲該action路徑的一部分,如訪問上面的例子中的action的路徑爲:/upload/upload.action。namespace屬性能夠不用配置,若是不指定該屬性,默認的命名空間爲「」(空字符串)。
一般每一個包都必須繼承struts-default包,由於struts不少核心的功能都是在這個包中定義的攔截器實現的,如:從請求中把請求參數封裝到action、文件上傳和數據驗證等功能搜是經過攔截器實現的。struts-default包中定義了這些攔截器和result類型。換句話說,當包繼承了strtus-default包才能使用struts提供的核心功能。struts-default包是在struts2-core-2.x.x.x.jar文件中的struts-default.xml中定義的。struts-default.xml是struts2的默認配置文件,struts2每次都會自動加載struts-default.xml文件。
包還能夠經過abstract=「true」定義爲抽象包,抽象包中不能包含action。
注意,在配置文件struts.xml中沒有提示的解決辦法:window->preference->xml catalog中添加struts-2.0.dtd文件,key type爲URI,key爲http://struts.apache.org/dtds/struts-2.0.dtd。
action名稱的搜索順序
1.得到請求的URI,例如uri是:http://server/struts2/path1/path2/path3/test.action
2.首先尋找namesp爲/path1/path2/path3的package,若是不存在這個package,就轉第三步,若是存在這個package,則在這個package中尋找名字爲test的action,當在該package中找不到action時就到默認namespace的package中尋找(默認package的命名空間爲空字符串「」),若是在默認的package中還找不到該action,頁面提示找不到action。
3.尋找namespace爲/path1/path2的package,若是不存在這個package,則轉第四步,若是存在這個package,則在這個package中尋找名字爲test的action,當在該package中找不到action時就到默認namespace的package中尋找(默認package的命名空間爲空字符串「」),若是在默認的package中還找不到該action,頁面提示找不到action。
4.尋找namespace爲/path1的package,若是不存在這個package,則轉第五步,若是存在這個package,則在這個package中尋找名字爲test的action,當在該package中找不到action時就到默認namespace的package中尋找(默認package的命名空間爲空字符串「」),若是在默認的package中還找不到該action,頁面提示找不到action。
5.尋找namespace爲/的package,若是存在這個package,則在這個package中尋找名字爲test的action,當在該package中找不到action或不存在這個package時就到默認namespace的package中尋找(默認package的命名空間爲空字符串「」),若是在默認的package中還找不到該action,頁面提示找不到action。
action配置中的默認值
<packagename="department"namespace="/department"extends="struts-default">
<actionname="helloworld"class="zhchljr.action.HelloWorldAction"method="execute">
<paramname="savepath">department</param>
<resultname="success">/employeeAdd.jsp?username=${username}</result>
</action>
</package>
<package name="department" namespace="/department" extends="struts-default">
<action name="helloworld" class="zhchljr.action.HelloWorldAction" method="execute">
<param name="savepath">department</param>
<result name="success">/employeeAdd.jsp?username=${username}</result>
</action>
</package>
1.若是沒有爲action指定class,默認的class是ActionSupport。
2.若是沒有爲action指定method,默認執行action中的execute方法。
3.若是沒有爲result指定name屬性,默認值爲success。
action中result的各類轉發類型
result配置相似於struts1中的forward,但struts2提供了多種結果類型,經常使用的類型有dispatcher(默認值)、redirect、redirectAction、plainText。
result中還可使用${屬性名}表達式來訪問action中的屬性,表達式中的屬性名爲action中的屬性名,以下:
<resulttype="redirect">/employeeAdd.jsp?id=4{id}</result>
<result type="redirect">/employeeAdd.jsp?id=4{id}</result>
下面是結果類型爲redirectAction的例子:
重定向到同一個包中的action:
<resulttype="redirectAction">add</result>
<result type="redirectAction">add</result>
重定向到別的namespace中的action:
<resulttype="redirectAction">
<paramname="actionName">xxx</param>
<paramname="namespace">/redirectAction</param>
</result>
<result type="redirectAction">
<param name="actionName">xxx</param>
<param name="namespace">/redirectAction</param>
</result>
plainText:顯示原始文件內容,例如:當須要原樣顯示jsp文件源代碼的時候,就可使用此類型。
<actionname="source">
<resulttype="plainText">
<paramname="location">/index.jsp</param>
<paramname="charSet">UTF-8</param>
</result>
</action>
<action name="source">
<result type="plainText">
<param name="location">/index.jsp</param>
<param name="charSet">UTF-8</param> <!--指定讀取文件的編碼方式-->
</result>
</action>
爲action的屬性注入值
struts2爲action的屬性提供了依賴注入功能,在struts2的配置文件中,能夠很方便的爲action中的屬性值注入值。注意,屬性必須有setter方法。
<actionname="helloworld_*"class="zhchljr.action.HelloWorldAction"method="{1}">
<paramname="savepath">/upload</param>
<resultname="success">/employeeAdd.jsp?username=${username}</result>
</action>
<action name="helloworld_*" class="zhchljr.action.HelloWorldAction" method="{1}">
<param name="savepath">/upload</param>
<result name="success">/employeeAdd.jsp?username=${username}</result>
</action>
上面就是經過<param>節點爲action中的savePath注入"/upload"。
經常使用的常量介紹
default.properties文件中定義了不少常量,下面就說說一般使用到的幾個。
struts.i18n.encoding 指定默認編碼集
struts.action.extension struts2處理的默認後綴,若是須要定義多個後綴,則用逗號「,"隔開
struts.serve.static.browserCache 設置瀏覽器是否緩存靜態內容,默認爲true(生產環境下使用),開發階段最好關閉
struts.configuration.xml.reload 當struts的配置文件修改後,系統是否自動從新加載該文件,默認值爲false(生產環境下),開發階段最好打開。
struts.devMode 是否爲開發模式,默認爲false,(生產環境下),開發階段最好打開,以便打印出更詳細的信息
struts.ui.theme 視圖主題,通常用simple
struts.objectFactory 於spring集成時,指定由spring負責action對象的建立
struts.enable.DynamicMethodInvocation 是否支持動態方法調用,action!methodname,默認爲true
struts.multipart.maxSize 上傳文件的大小
struts處理流程
StrutsPrepareAndExecuteFilter是struts2框架的核心控制器,它負責攔截由<url-pattern>/*</url-pattern>指定的全部用戶請求,當用戶請求到達時,該Filter會過濾用戶請求。默認狀況下,若是用戶請求的路徑不帶後綴或是後綴是action,這時請求將被struts2框架處理,不然struts2將略過該請求。當請求轉入struts2處理時會先通過一些列的攔截器,而後到action。與struts1不一樣,struts2對用戶的每個請求都會建立一個action,因此struts2是線程安全的。
爲應用指定多個struts配置文件
在大部分應用裏,隨着應用規模的增長,系統中action的數量也會大量增長,致使struts.xml配置文件變的很是臃腫。爲避免struts.xml文件過於龐大、臃腫,提升struts.xml文件的可讀性,能夠將一個struts.xml文件分解成過個配置文件,而後在struts.xml文件中包含其餘的配置文件。下面的struts.xml文件經過include元素指定多個配置文件:
<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<includefile="department.xml"></include>
<includefile="employee.xml"></include>
<includefile="upload.xml"></include>
<includefile="interceptor.xml"></include>
</struts>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<include file="department.xml"></include>
<include file="employee.xml"></include>
<include file="upload.xml"></include>
<include file="interceptor.xml"></include>
</struts>
經過這種方式,能夠將struts中的action按模塊添加在多個
配置文件中。
動態方法調用
在配置文件的action中不指定method,而是在訪問的時候經過actionname!methodname這種方式來訪問,前提是struts.enable.DynamicMethodInvocation要設置爲true。
例如:
<packagename="employee"namespace="/employee"extends="struts-default">
<actionname="helloworld"class="zhchljr.action.HelloWorldAction">
<resultname="success">/employeeAdd.jsp?username=${username}</result>
</action>
</package>
<package name="employee" namespace="/employee" extends="struts-default">
<action name="helloworld" class="zhchljr.action.HelloWorldAction">
<result name="success">/employeeAdd.jsp?username=${username}</result>
</action>
</package>
假設zhchljr.action.HelloWorldAction類中有add()和update()兩個方法,可使用"/employee/helloworld!add"來訪問add()方法。
使用通配符的定義action
使用通配符*定義action,能夠有效的減少配置文件的規模。下面就是一個簡單的例子,和上面動態方法調用中的配置文件相似:
<packagename="employee"namespace="/employee"extends="struts-default">
<actionname="helloworld_*"class="zhchljr.action.HelloWorldAction"method="{1}">
<paramname="savepath">employee</param>
<resultname="success">/employeeAdd.jsp?username=${username}</result>
</action>
</package>
<package name="employee" namespace="/employee" extends="struts-default">
<action name="helloworld_*" class="zhchljr.action.HelloWorldAction" method="{1}">
<param name="savepath">employee</param>
<result name="success">/employeeAdd.jsp?username=${username}</result>
</action>
</package>
假設zhchljr.action.HelloWorldAction類中有add()和update()兩個方法,這樣定義後,就可使用」/employee/helloworld_add「來訪問add()方法。
接收請求參數
1.採用基本類型接收參數(get/post)
在action類中定義與請求參數同名的屬性,而且該屬性有set和get方法,struts2就能自動接收請求參數並賦予同名屬性。
請求路徑:http://localhost:8080/struts2/product/view.action?id=1
publicclass ProductAction {
privateint id;
publicint getId() {
return id;
}
publicvoid setId(int id) {
this.id=id;
}
}
public class ProductAction {
private int id;
public int getId() {
return id;
}
public void setId(int id) {//經過反射技術調用與與請求參數同名的屬性的setter方法來爲該屬性設置值
this.id=id;
}
}
2.採用複合類型接收參數
請求路徑:http://localhost:8080/struts2/product/view.action?product.id=1
publicclass ProductAction {
private Product product;
public Product getProduct() {
return product;
}
publicvoid setProduct(Product product) {
this.product=product;
}
}
public class ProductAction {
private Product product;
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product=product;
}
}
struts2首先經過反射技術調用Product的默認構造器建立product對象,而後再經過反射技術調用product中與請求參數同名的屬性的setter方法來設置請求參數值。
struts2.1.6接收中文請求參數亂碼的問題是個bug,緣由是在獲取並使用了請求參數後才調用HttpServletRequest的setCharacterEncoding()方法進行編碼設置,致使應用使用的就是亂碼參數。這個bug在struts2.1.8中已經被解決,若是使用的是struts2.1.6,要解決這個問題,能夠採用下面的方法:
新建一個filter,把這個filter放在struts2的filter前面,而後在doFilter()方法裏添加以下的代碼:
publicvoid doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws java.io.IOException,ServletException {
HttpServletRequest req = (HttpServletRequest)request;
req.setCharacterEncoding("UTF-8");
chain.doFilter(request,response);
}
public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws java.io.IOException,ServletException {
HttpServletRequest req = (HttpServletRequest)request;
req.setCharacterEncoding("UTF-8");//根據實際使用的編碼替換
chain.doFilter(request,response);
}
自定義類型轉換器
1.定義類
publicclass DateTypeConverter extends DefaultTypeConverter {
@Override
public Object convertValue(Map<String, Object> context, Object value,Class toType) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
try {
if(toType == Date.class) {
String[] params = (String[])value;
return sdf.parse(params[0]);
} elseif(toType == String.class) {
Date date = (Date)value;
return sdf.format(date);
}
} catch (ParseException e) {
e.printStackTrace();
}
returnnull;
}
}
public class DateTypeConverter extends DefaultTypeConverter {
@Override
public Object convertValue(Map<String, Object> context, Object value,Class toType) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
try {
if(toType == Date.class) {//字符串向date類型轉換
String[] params = (String[])value;
return sdf.parse(params[0]);
} else if(toType == String.class) {//date轉換成string類型
Date date = (Date)value;
return sdf.format(date);
}
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
2.將上面的類註冊爲局部類型轉換器
在action所在的包下放置ActionClassName-conversion.properties文件,ActionClassName是action的類名,後面的-conversion.properties是固定寫法,在本例中,文件名爲HelloWorldAction-conversion.properties。該文件的內容爲:
屬性名稱=類型轉換器的全類名
就本例而言,HelloWorldAction-conversion.properties文件中的內容爲:
birthday=zhchljr.type.converter.DateTypeConverter
birthday=zhchljr.type.converter.DateTypeConverter
3.將上面的類註冊爲全局類型轉換器
在WEB-INF/classes下放置xwork-conversion.properties文件。該文件中的內容爲:
待轉換的類型=類型轉換器的類全名
對於本例而言,xwork-conversion.properties文件中的內容爲:
java.util.Date=zhchljr.type.converter.DateTypeConverter
java.util.Date=zhchljr.type.converter.DateTypeConverter
訪問或添加request/session/application屬性
1.若是隻是添加或訪問request/session/application中的屬性,就能夠用ActionContext類來實現。以下代碼所示:
ActionContext ctx = ActionContext.getContext();
ctx.getApplication().put("app", "應用範圍");
ctx.getSession().put("ses", "session應用範圍");
ctx.put("req", "request應用範圍");
ctx.put("names", Arrays.asList("劉明","茫茫大海"));
ActionContext ctx = ActionContext.getContext();
ctx.getApplication().put("app", "應用範圍");//至關於往ServletContext中放入app
ctx.getSession().put("ses", "session應用範圍");//至關於往session中加入ses
ctx.put("req", "request應用範圍");//至關於往request中加入req
ctx.put("names", Arrays.asList("劉明","茫茫大海"));
2.獲取HttpServletRequest/HttpSession/ServletContext/HttpServletResponse對象,能夠有兩種方法實現:
(1)經過ServletActionContext直接獲取:
HttpServletRequest request = ServletActionContext.getRequest();
ServletContext context = ServletActionContext.getServletContext();
request.setAttribute("req", "請求範圍屬性");
request.getSession().setAttribute("ses", "會話範圍屬性");
context.setAttribute("app", "應用範圍屬性");
HttpServletRequest request = ServletActionContext.getRequest();
ServletContext context = ServletActionContext.getServletContext();
request.setAttribute("req", "請求範圍屬性");
request.getSession().setAttribute("ses", "會話範圍屬性");
context.setAttribute("app", "應用範圍屬性");
(2)實現指定接口,由struts2框架運行時注入:
publicclass TestAction implements ServletRequestAware, ServletResponseAware, ServletContextAware {
private HttpServletRequest request;
private HttpServletResponse response;
private ServletContext servletContext;
publicvoid setServletRequest(HttpServletRequest request) {
this.request = request;
}
publicvoid setServletResponse(HttpServletResponse response) {
this.response = response;
}
publicvoid setServletContext(ServletContext context) {
this.servletContext = context;
}
}
public class TestAction implements ServletRequestAware, ServletResponseAware, ServletContextAware {
private HttpServletRequest request;
private HttpServletResponse response;
private ServletContext servletContext;
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
public void setServletResponse(HttpServletResponse response) {
this.response = response;
}
public void setServletContext(ServletContext context) {
this.servletContext = context;
}
}
文件上傳
文件上傳在項目中常常會用到,下面就來講說struts2中怎麼上傳文件的:
1.引入相應的jar包(commons-fileupload-1.2.1.jar和commons-io-1.3.2.jar)
2.把form的enctype設置爲"multipart/form-data",以下所示:
<formaction="<%=basePath%>upload/upload.action"method="post"name="form"enctype="multipart/form-data">
文件1:<inputtype="file"name="upload"/><br/>
<inputtype="submit"value="上傳"/>
</form>
<form action="<%=basePath%>upload/upload.action" method="post" name="form" enctype="multipart/form-data">
文件1:<input type="file" name="upload"/><br/>
<input type="submit" value="上傳" />
</form>
3.在action類中添加
以下代碼中註釋的幾個屬性。
publicclass HelloWorldAction {
private File upload;
private String uploadContentType;
private String uploadFileName;
public File getUpload() {
return upload;
}
publicvoid setUpload(File upload) {
this.upload = upload;
}
public String getUploadContentType() {
return uploadContentType;
}
publicvoid setUploadContentType(String uploadContentType) {
this.uploadContentType = uploadContentType;
}
public String getUploadFileName() {
return uploadFileName;
}
publicvoid setUploadFileName(String uploadFileName) {
this.uploadFileName = uploadFileName;
}
public String upload() throws IOException {
String realpath = ServletActionContext.getServletContext().getRealPath("/upload");
if(upload != null) {
File savefile = new File(realpath,uploadFileName);
if(!savefile.getParentFile().exists()) {
savefile.getParentFile().mkdirs();
}
FileUtils.copyFile(upload, savefile);
ActionContext.getContext().put("msg", "文件上傳成功!");
}
return"success";
}
}
public class HelloWorldAction {
private File upload;//獲得上傳的文件
private String uploadContentType;//獲得上傳文件的擴展名
private String uploadFileName;//獲得上傳文件的名稱
public File getUpload() {
return upload;
}
public void setUpload(File upload) {
this.upload = upload;
}
public String getUploadContentType() {
return uploadContentType;
}
public void setUploadContentType(String uploadContentType) {
this.uploadContentType = uploadContentType;
}
public String getUploadFileName() {
return uploadFileName;
}
public void setUploadFileName(String uploadFileName) {
this.uploadFileName = uploadFileName;
}
public String upload() throws IOException {
String realpath = ServletActionContext.getServletContext().getRealPath("/upload");
if(upload != null) {
File savefile = new File(realpath,uploadFileName);
if(!savefile.getParentFile().exists()) {
savefile.getParentFile().mkdirs();
}
FileUtils.copyFile(upload, savefile);
ActionContext.getContext().put("msg", "文件上傳成功!");
}
return "success";
}
}
注意,若是在上傳的過程當中文件的大小超過了struts2默認的文件大小的話,就會上傳失敗,這時候,能夠根據具體的狀況設置struts.multipart.maxSize的值來知足上傳的需求。
多文件上傳
在實際的項目中,有時候可能會要求上傳多個文件的狀況,下面就來講說上傳多個文件的狀況。
1.同上。
2.form以下所示:
<formaction="<%=basePath%>upload/upload"method="post"name="form"enctype="multipart/form-data">
文件1:<inputtype="file"name="upload"/><br/>
文件2:<inputtype="file"name="upload"/><br/>
文件3:<inputtype="file"name="upload"/><br/>
<inputtype="submit"value="上傳"/>
</form>
<form action="<%=basePath%>upload/upload" method="post" name="form" enctype="multipart/form-data">
文件1:<input type="file" name="upload"/><br/>
文件2:<input type="file" name="upload"/><br/>
文件3:<input type="file" name="upload"/><br/>
<input type="submit" value="上傳" />
</form>
3.action中添加的幾個屬性都是數組形式的。
publicclass HelloWorldAction {
private File[] upload;
private String[] uploadContentType;
private String[] uploadFileName;
public File[] getUpload() {
return upload;
}
publicvoid setUpload(File[] upload) {
this.upload = upload;
}
public String[] getUploadContentType() {
return uploadContentType;
}
publicvoid setUploadContentType(String[] uploadContentType) {
this.uploadContentType = uploadContentType;
}
public String[] getUploadFileName() {
return uploadFileName;
}
publicvoid setUploadFileName(String[] uploadFileName) {
this.uploadFileName = uploadFileName;
}
public String upload() throws IOException {
String realpath = ServletActionContext.getServletContext().getRealPath("/upload");
if(upload != null) {
for(int i=0; i<upload.length; i++) {
File savefile = new File(realpath,uploadFileName[i]);
if(!savefile.getParentFile().exists()) {
savefile.getParentFile().mkdirs();
}
FileUtils.copyFile(upload[i], savefile);
}
ActionContext.getContext().put("msg", "文件上傳成功!");
}
return"success";
}
}
public class HelloWorldAction {
private File[] upload;//獲得上傳的文件
private String[] uploadContentType;//獲得上傳文件的擴展名
private String[] uploadFileName;//獲得上傳文件的名稱
public File[] getUpload() {
return upload;
}
public void setUpload(File[] upload) {
this.upload = upload;
}
public String[] getUploadContentType() {
return uploadContentType;
}
public void setUploadContentType(String[] uploadContentType) {
this.uploadContentType = uploadContentType;
}
public String[] getUploadFileName() {
return uploadFileName;
}
public void setUploadFileName(String[] uploadFileName) {
this.uploadFileName = uploadFileName;
}
public String upload() throws IOException {
String realpath = ServletActionContext.getServletContext().getRealPath("/upload");
if(upload != null) {
for(int i=0; i<upload.length; i++) {
File savefile = new File(realpath,uploadFileName[i]);
if(!savefile.getParentFile().exists()) {
savefile.getParentFile().mkdirs();
}
FileUtils.copyFile(upload[i], savefile);
}
ActionContext.getContext().put("msg", "文件上傳成功!");
}
return "success";
}
}
自定義攔截器
自定義攔截器要實現com.opensymphony.xwork2.interceptor.Interceptor接口。下面是一個自定義攔截器的例子:
publicclass PermissionInterceptor implements Interceptor {
publicvoid destroy() {
}
publicvoid init() {
}
public String intercept(ActionInvocation invocation) throws Exception {
Object user = ActionContext.getContext().getSession().get("user");
if(user != null) {
return invocation.invoke();
} else {
ActionContext.getContext().put("message", "你沒有執行權限!");
}
return"success";
}
}
public class PermissionInterceptor implements Interceptor {
public void destroy() {
}
public void init() {
}
public String intercept(ActionInvocation invocation) throws Exception {
Object user = ActionContext.getContext().getSession().get("user");
if(user != null) {
return invocation.invoke();
} else {
ActionContext.getContext().put("message", "你沒有執行權限!");
}
return "success";
}
}
接下來,就要在配置文件中註冊攔截器,具體的作法是:
<interceptors>
<interceptorname="permission"class="zhchljr.interceptor.PermissionInterceptor"></interceptor>
</interceptors>
<interceptors>
<interceptor name="permission" class="zhchljr.interceptor.PermissionInterceptor"></interceptor>
</interceptors>
爲action指定攔截器,具體的作法是:
<actionname="interceptor"class="zhchljr.action.HelloWorldAction"method="interceptor">
<interceptor-refname="permission"></interceptor-ref>
</action>
<action name="interceptor" class="zhchljr.action.HelloWorldAction" method="interceptor">
<interceptor-ref name="permission"></interceptor-ref>
</action>
可是這樣作了之後,就會出現一個問題,struts2中爲一個action指定攔截器後,默認的defaultStack中的攔截器就不起做用了,也就是說struts2的衆多核心功能都使用不了了(struts2的許多核心功能都是經過攔截器實現的),爲了解決這個問題,引入攔截器棧,先使用系統默認的攔截器,而後再來使用自定義的攔截器,具體的作法是:
<interceptors>
<interceptorname="permission"class="zhchljr.interceptor.PermissionInterceptor"></interceptor>
<interceptor-stackname="permissionStack">
<interceptor-refname="defaultStack"></interceptor-ref>
<interceptor-refname="permission"></interceptor-ref>
</interceptor-stack>
</interceptors>
<interceptors>
<interceptor name="permission" class="zhchljr.interceptor.PermissionInterceptor"></interceptor>
<interceptor-stack name="permissionStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="permission"></interceptor-ref>
</interceptor-stack>
</interceptors>
若是但願包下的全部action都使用自定義的攔截器,能夠把攔截器設置爲默認攔截器,具體的實現方式是:
<default-interceptor-refname="permissionStack"></default-interceptor-ref>
<default-interceptor-ref name="permissionStack"></default-interceptor-ref>
注意:每一個包中只能有一個默認的攔截器;一旦爲包中的某個action指定了攔截器,則默認的攔截器就不起做用了。
輸入校驗
struts2中能夠實現對action中的全部方法進行校驗,也能夠實現對指定的方法進行校驗。能夠用以下兩種方式來實現輸入校驗。
1.採用手工編寫代碼實現:
<1>手工編寫代碼實現對action中的全部方法進行校驗:經過編寫validate()方法實現,validate()會校驗action中全部與execute方法簽名相同的方法。當某個數據校驗失敗時,應該採用addFieldError()方法往系統的filedErrors添加校驗失敗信息(爲了使用該方法,action能夠繼承ActionSupport),若是系統的filedErrors包含失敗信息,struts2會將請求發到名爲input的result,在input視圖中能夠經過<s:fielderror/>顯示失敗信息。具體的參見下面的例子:
<s:fielderror></s:fielderror>
<formmethod="post"action="<%=basePath%>person/manage_save.action">
用戶名:<inputtype="text"name="username"/>不能爲空<br/>
手機號:<inputtype="text"name="mobile"/>不能爲空,而且要符合手機號的格式,1,3/5/8,後面是9個數字<br/>
<inputtype="submit"value="提交"/>
</form>
<s:fielderror></s:fielderror>
<form method="post" action="<%=basePath%>person/manage_save.action">
用戶名:<input type="text" name="username"/>不能爲空<br/>
手機號:<input type="text" name="mobile"/>不能爲空,而且要符合手機號的格式,1,3/5/8,後面是9個數字<br/>
<input type="submit" value="提交"/>
</form>
頁面中使用<s:fielderror></s:fielderror>來顯示失敗信息。
publicvoid validate() {
if(this.username == null || this.username.trim().equals("")) {
this.addFieldError("username", "用戶名不能爲空!");
}
if(this.mobile == null || this.mobile.trim().equals("")) {
this.addFieldError("mobile", "手機號不能爲空!");
} else {
if(!Pattern.compile("^1[358]\\d{9}{1}quot;).matcher(this.mobile).matches()) {
this.addFieldError("mobile", "手機號格式不正確!");
}
}
}
public void validate() {//會對action中的全部方法進行校驗
if(this.username == null || this.username.trim().equals("")) {
this.addFieldError("username", "用戶名不能爲空!");
}
if(this.mobile == null || this.mobile.trim().equals("")) {
this.addFieldError("mobile", "手機號不能爲空!");
} else {
if(!Pattern.compile("^1[358]\\d{9}{1}quot;).matcher(this.mobile).matches()) {
this.addFieldError("mobile", "手機號格式不正確!");
}
}
}
< 2>手工編寫代碼實現對action中的指定方法進行校驗:經過編寫validateXxx()方法實現,validateXxx()會校驗action中方法名爲xxx的方法。當某個數據校驗失敗時,應該採用addFieldError()方法往系統的filedErrors添加校驗失敗信息(爲了使用該方法,action能夠繼承ActionSupport),若是系統的filedErrors包含失敗信息,struts2會將請求發到名爲input的result,在input視圖中能夠經過<s:fielderror/>顯示失敗信息。該校驗方法示例以下:
publicvoid validateUpdate() {
if(this.username == null || this.username.trim().equals("")) {
this.addFieldError("username", "用戶名不能爲空!");
}
if(this.mobile == null || this.mobile.trim().equals("")) {
this.addFieldError("mobile", "手機號不能爲空!");
} else {
if(!Pattern.compile("^1[358]\\d{9}{1}quot;).matcher(this.mobile).matches()) {
this.addFieldError("mobile", "手機號格式不正確!");
}
}
}
public void validateUpdate() {//會對action中的update方法進行校驗
if(this.username == null || this.username.trim().equals("")) {
this.addFieldError("username", "用戶名不能爲空!");
}
if(this.mobile == null || this.mobile.trim().equals("")) {
this.addFieldError("mobile", "手機號不能爲空!");
} else {
if(!Pattern.compile("^1[358]\\d{9}{1}quot;).matcher(this.mobile).matches()) {
this.addFieldError("mobile", "手機號格式不正確!");
}
}
}
*.輸入校驗的流程:
(1)類型轉換器對請求參數進行執行類型轉換,並把轉換後的值賦給action中的屬性。
(2)若是在執行轉換的過程當中出現異常,系統會將異常信息保存到ActionContext中,conversionError攔截器將異常信息封裝到fieldErrors中。無論類型轉換是否異常,都會轉入第3步。
(3)系統經過放射技術先調用action中的validateXxx()方法,xxx爲方法名。
(4)再調用action的validate()方法。
(5)通過上面四步,若是系統中的fieldErrors存在錯誤信息(即存放錯誤信息的集合的size大於0),系統會將請求轉發至名爲input的視圖。若是系統中的filedErrors中沒有錯誤信息,系統將執行action中的處理方法。
2.基於xml配置方式來實現:
<1>基於xml配置方式實現對action中的全部方法進行校驗:Action要繼承ActionSupport,而且提供校驗文件,校驗文件和action類在同一個包下,文件的命名規則是:ActionClassName-validation.xml,其中的ActionClassName爲action的簡單類名,-validation爲固定寫法。下面是一個校驗文件(PersonAction-validation.xml)的例子:
<?xmlversion="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>
<fieldname="username">
<field-validatortype="requiredstring">
<paramname="trim">true</param>
<message>用戶名不能爲空!</message>
</field-validator>
</field>
<fieldname="mobile">
<field-validatortype="requiredstring">
<paramname="trim">true</param>
<message>手機號不能爲空!</message>
</field-validator>
<field-validatortype="regex">
<paramname="expression"><![CDATA[^1[358]\d{9}$]]></param>
<message>手機格式不正確!</message>
</field-validator>
</field>
</validators>
<?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><!-- 默認爲true -->
<message>用戶名不能爲空!</message>
</field-validator>
</field>
<field name="mobile">
<field-validator type="requiredstring">
<param name="trim">true</param><!-- 默認爲true -->
<message>手機號不能爲空!</message>
</field-validator>
<field-validator type="regex">
<param name="expression"><![CDATA[^1[358]\d{9}$]]></param>
<message>手機格式不正確!</message>
</field-validator>
</field>
</validators>
說明:<field>指定action中要校驗的屬性,<field-validator>指定校驗器,上面指定的requiredstring校驗器是由系統提供的,系統提供了能知足大部分驗證需求的校驗器,這些校驗器能夠在文件xwork-core-2.X.X.jar中的com.opensymphony.xwork2.validator.validators下的default.xml中找到。<message>爲校驗失敗後的提示信息,若是須要國際化,能夠爲message指定key屬性,key的值爲資源文件中的key。在這個配置文件中,對action中字符串類型的username屬性進行驗證,首先要求調用trim()去掉空格,而後判斷用戶名是否爲空。對string類型的mobile字段的校驗使用了regex這個校驗器。
<2>基於xml配置方式實現對action中指定的方法進行校驗:當校驗文件名爲ActionClassName-validation.xml時,會對action中的全部方法進行校驗。若是要對action中的某一個方法進行校驗,那麼校驗文件的名稱爲:ActionClassName-ActionName-validation.xml。其中ActionName爲struts.xml中的action的名稱。配置文件以下:
<packagename="person"namespace="/person"extends="struts-default">
<actionname="manage_*"class="zhchljr.action.PersonAction"method="{1}">
<resultname="message">/WEB-INF/page/message.jsp</result>
<resultname="input">/index.jsp</result>
</action>
</package>
<package name="person" namespace="/person" extends="struts-default">
<action name="manage_*" class="zhchljr.action.PersonAction" method="{1}">
<result name="message">/WEB-INF/page/message.jsp</result>
<result name="input">/index.jsp</result>
</action>
</package>
假設PersonAction中有save()和update()兩個方法。對save方法進行校驗的文件名爲PersonAction-manage_save-validation.xml,對update方法進行校驗的文件名位PersonAction-manage_update-validation.xml。
*.基於xml校驗的一些特色:
當爲某個action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml兩種規則的校驗文件時,系統按下面的順序尋找校驗文件:
(1)ActionClassName-validation.xml
(2)ActionclassName-ActionName-validation.xml
系統搜索到第一個校驗文件時,還會繼續搜索後面的校驗文件,當搜索到全部校驗文件時,會把校驗文件裏的全部校驗規則彙總,而後所有應用於action方法的校驗。若是兩個文件指定的校驗規則衝突,則只使用後面文件中的校驗規則。
當action繼承了另外一個action時,父類action的校驗文件會被先搜索到。仍是按照上面的順序,校驗規則爲四個文件的總和。
國際化
1.準備資源文件,資源文件的命名格式是:
baseName_language_country.properties
其中baseName爲資源文件的基本名,能夠自定義。但language和country必須是java支持的語言和國家。如:
中國大陸:baseName_zh_CN.properties
美國:baseName_en_US.properties
如今爲應用添加兩個資源文件:
第一個存放中文:welcome_zh_CN.properties
內容爲:welcome=歡迎來到中國
第二個存放英文:welcome_en_US.properties
內容爲:welcome=welcome to china
對於中文的屬性文件,編寫好後,應該使用jdk自帶的native2ascii命令把文件轉成unicode編碼的文件,命令的使用方式以下:
native2ascii src.properties dest.properties
2.配置全局資源文件
準備好資源文件後, 能夠在struts.xml中經過struts.custom.i18n.resources把資源文件定義爲全局資源文件,以下所示:
<constantname="struts.custom.i18n.resources"value="welcome"></constant>
<constant name="struts.custom.i18n.resources" value="welcome"></constant>
3.輸出國際化信息
(1)在jsp頁面中使用<s:text name=""/>標籤輸出國際化信息,如:<s:text name="welcome"/>,name爲資源文件中的key
(2)在action類中,能夠繼承ActionSupport,使用getText()方法獲得國際化信息,該方法的第一個參數用於指定資源文件中的key
(3)在表單標籤中,經過key屬性指定資源文件中的key,如:<s:textfield name="realname" key="welcome"/>
*.輸出帶佔位符的國際化信息
資源文件中的內容爲:welcome=package:{0},歡迎來到傳智播客{1}
在jsp頁面輸出帶佔位符的國際化信息
<s:textname="welcome">
<s:param>liuming</s:param>
<s:param>study</s:param>
</s:text>
<s:text name="welcome">
<s:param>liuming</s:param>
<s:param>study</s:param>
</s:text>
在action類中獲取帶佔位符的國際化信息,可使用getText(String key,String[] args)或getText(String key,List args)。
4.包範圍的資源文件
在一個大型應用中,整個應用有大量的內容須要實現國際化,若是把國際化的內容都放置在全局資源文件屬性中,會致使資源文件過於龐大、臃腫,不便於維護,這個時候能夠針對不一樣模塊,使用包範圍來組織國際化文件。具體的作法以下:
在java的包下放置package_language_country.properties資源文件,package爲固定寫法,處於該包及子包下的action均可以訪問該資源。當查找指定key的消息時,系統會先從package資源文件中查找,當找不到對應的key時,纔會從全局資源文件中找。
5.action範圍的資源文件
能夠爲某個action單獨指定資源文件,方法以下:
在action類所在的路徑,放置ActionClassName_language_country.properties資源文件,ActionClassName爲action類的簡單名稱。
當查找指定key的消息時,系統會先從ActionClassName_language_country.properties資源文件中查找,若是沒有找到對應的key,而後沿着當前包向上查找基本名爲package的資源文件,一直找到最頂層包。若是尚未找到指定的key,最後會從全局資源文件中查找。
6.jsp中直接訪問某個資源文件
struts2提供了<s:i18n>標籤,使用該標籤能夠在類路徑下直接從某個資源文件中獲取國際化數據,而無需任何配置。
<s:i18nname="welcome">
<s:textname="welcome"/>
</s:i18n>
<s:i18n name="welcome">
<s:text name="welcome"/>
</s:i18n>
若是要訪問的資源在類路徑的某個包下,能夠這樣訪問:
<s:i18nname="zhchljr/action/package">
<s:textname="welcome"/>
</s:i18n>
<s:i18n name="zhchljr/action/package">
<s:text name="welcome"/>
</s:i18n>
訪問包zhchljr.action包下基本名爲package的資源文件。
還能夠訪問action範圍的資源文件:
<s:i18nname="zhchljr/action/PersonManageAction">
<s:textname="welcome">
<s:param>liuming</s:param>
<s:param>study</s:param>
</s:text>
</s:i18n>
<s:i18n name="zhchljr/action/PersonManageAction">
<s:text name="welcome">
<s:param>liuming</s:param>
<s:param>study</s:param>
</s:text>
</s:i18n>
OGNL表達式語言
OGNL是Object Graphic Navigation Language(對象導航圖語言)的縮寫,它是一個開源項目。Struts2框架使用OGNL做爲默認的表達式語言。
相對於EL表達式,它提供了一些平時須要的功能。
(1)支持對象方法調用,如xxx.save()
(2)支持類靜態方法調用和值訪問,表達式的格式爲@全類名(包括包路徑)@方法名(參數),如:@java.lang.String@format('foo%s','bar')或@java.util.Math@PI
(3)操做集合對象
Ognl有一個上下文(Context)概念,說白了上下文就是一個Map結構,它實現了java.util.Map接口,在struts2中context的實現爲ActionContext,下面是它的結構示意圖:
當struts2接受一個請求時,會迅速建立出ActionContext,ValueStack,action,而後把action存放進ValueStack,因此action的實例變量能夠被ognl訪問。
訪問上下文中的對象須要使用#標註命名空間,如#application,#session等。
ognl有一個根對象(root對象),在struts2中根對象就是ValueStack,若是要訪問根對象中對象的屬性,則能夠省略#命名空間,直接訪問該對象的屬性便可。
在struts2中,根對象ValueStack的實現爲OgnlValueStack,該對象不是隻存放單個值,而是存放一組對象。在OgnlValueStack中有一個List類型的root變量,就是使用它存放一組對象。
在root變量中處於第一位的對象叫作棧頂對象。一般在OGNL表達式裏面直接寫上屬性的名稱便可訪問root變量裏對象的屬性,搜索順序是從棧頂對象開始,依次往下搜索,直到找到爲止。
須要注意的一點是,struts2中ognl表達式要配合struts標籤纔可使用。如:<s:property value="name"/>
因爲ValueStack是Struts2中的ognl的根對象,若是用戶須要訪問ValueStack中的對象,在jsp頁面能夠經過下面的EL表達式訪問ValueStack中對象的屬性。
${foo}//得到ValueStack中某個對象的foo屬性
若是訪問Context中的別的對象,因爲它們不是根對象,因此在訪問是須要添加#前綴。
(1)application對象:用於訪問ServletContext,例如#application.userName或#application['userName'],至關於調用session的getAttribute("userName")
(2)session對象:用於訪問HttpSession,例如#session.userName或#session['userName'],至關於調用ServletContext的getAttribute("userName")
(3)request對象:用於訪問HttpServletRequest對象,例如#request.userName或#request['userName'],至關於調用request的getAttribute("userName")
(4)parameters對象:用於訪問Http的請求參數,例如#parameters.userName或#parameters['userName'],至關於調用request的getParameter("userName")
(5)attr對象:用於按page->request->session->application的順序訪問其屬性。
採用ognl表達式建立List/Map集合對象:若是須要一個集合元素的時候(例如list或map),可使用ognl中通集合相關的表達式,使用以下代碼能夠直接生成一個list對象:
<s:setvar="list"value="{'第一個','第二個','第三個'}"></s:set>
<s:iteratorvalue="#list">
<s:property/><br/>
</s:iterator>
<s:set var="list" value="{'第一個','第二個','第三個'}"></s:set><!-- 默認放在OGNL context中 -->
<!--s:iterator中有一個特色,會把當前迭代的對象放在棧頂中 -->
<s:iterator value="#list">
<s:property/><br/>
</s:iterator>
set標籤用於將某個值放入某個範圍。
scope指定變量被放置的範圍,該屬性能夠接受application、session、request、page和action。若是沒有該屬性,則默認放在OGNL context中。
value賦給變量的值,若是沒有該屬性,則將ValueStack棧頂的值放入賦給變量。
生成一個map對象:
<s:setvar="maps"value="#{'key1':90,'key2':35,'key3':12}"></s:set>
<s:iteratorvalue="#maps">
<s:propertyvalue="key"/>=<s:propertyvalue="value"/><br/>
</s:iterator>
<s:set var="maps" value="#{'key1':90,'key2':35,'key3':12}"></s:set>
<s:iterator value="#maps">
<s:property value="key"/>=<s:property value="value"/><br/>
</s:iterator>
採用ognl表達式判斷對象是否在集合中:對於集合類型,ognl表達式可使用in和not in兩個符號,其中in表示指定的元素是否在集合中,而not in表示指定的元素是否再也不集合中。以下所示:
<s:iftest="'foo' not in {'xxx','foo1','foo2'}">
不在
</s:if>
<s:else>
在
</s:else>
<s:if test="'foo' not in {'xxx','foo1','foo2'}">
不在
</s:if>
<s:else>
在
</s:else>
OGNL表達式的投影功能:
除了in和not in外,ognl還容許使用某個規則得到集合對象的子集,經常使用的有如下三個操做符:
?:得到全部符合邏輯的元素
^:得到符合邏輯的第一個元素
$:得到符合邏輯的最後一個元素
例如:
<s:iteratorvalue="books.{?#this.price>60}">
<s:propertyvalue="name"/>,價格:<s:propertyvalue="price"/><br/>
</s:iterator>
<s:iterator value="books.{?#this.price>60}">
<s:property value="name"/>,價格:<s:property value="price"/><br/>
</s:iterator>
在上面的代碼中,直接在集合後跟.{}運算符代表用於取出該集合的子集,{}內的表達式用於獲取符合條件的元素,this表示爲了從大集合books中篩選數據到小集合,須要對大集合books進行迭代,this表明當前迭代的元素。本例中用與獲取集合中價格大於60的書的集合。
struts2中的標籤
struts2中標籤分爲通用標籤和UI標籤,通用標籤包含控制標籤和數據標籤,以下圖所示:
UI標籤包含form標籤,非form標籤和Ajax標籤,以下圖所示:
這裏有不少的標籤,掌握一些經常使用的,而後在用到的時候查struts2幫助文檔。
使用<s:token/>標籤防止重複提交
使用<s:token/>標籤能夠防止重複提交,具體的用戶以下:
1.在表單中加入<s:token/>,例如:
<s:formaction="token"namespace="/test"method="post">
姓名:<s:textfieldname="name"label="姓名"></s:textfield>
<s:token></s:token>
<s:submitlabel="提交"></s:submit>
</s:form>
<s:form action="token" namespace="/test" method="post">
姓名:<s:textfield name="name" label="姓名"></s:textfield>
<s:token></s:token>
<s:submit label="提交"></s:submit>
</s:form>
2.在action中配置token攔截器,以下所示:
<actionname="token"class="zhchljr.action.PersonAction">
<interceptor-refname="defaultStack"></interceptor-ref>
<interceptor-refname="token"></interceptor-ref>
<resultname="success">/WEB-INF/page/message.jsp</result>
<resultname="invalid.token">/index.jsp</result>
</action>
上面加入了token攔截器和invalid.token result,由於token攔截器在會話狀態的token與請求狀態的token不一致時,直接返回invalid.token result。
http://blog.csdn.net/hudie1234567/article/details/6730481