Struts2
Struts2是在WebWork2基礎發展而來的,和struts1一樣,Struts2也是屬於MVC框架。
不顧有一點大家需要注意的是:盡管Struts2和struts1名字上差別不是很大,但是
Struts2和Struts1在代碼編寫風格上幾乎是不一樣的。那么既然有了struts1,為何
還需要推出Struts2呢?
主要是因為Struts2有以下優點:
1.在軟件設計上Struts2沒有像struts1那樣跟ServletAPI和StrutsAPI有着緊密的耦合。
Struts2的應用可以不依賴ServletAPI和SturtsAPI。Struts2的這種設計屬於無侵入式
設計,而strut1卻屬於侵入式設計
public class OrderListAction extends Action{
public ActionForward ectcute(ActionMapping mapping ,ActionForm form,
HeetServletRequest request, HttpServletResponse response)
throws Exception{
}
}
2.Struts2提供了攔截器,利用攔截器可以進行AOP編程,實現如權限攔截等功能
3.Struts2提供了類型轉換器,我們可以把特殊的請求參數轉換成需要的類型。
在struts1中,如果我們需要實現同樣的功能,就必須向Struts1的底層實現BeanUtil
注冊類型轉換器才行。
4.Struts2提供支持多種表現層技術。如JSP、freeMarker、Velocity。
5.Struts2的輸入校驗可以對指定方法進行校驗,解決了Strut1長久之痛。
6.提供了全局范圍,包范圍和Action范圍的國際化資源文件管理實現。
搭建Struts2開發環境
搭建Struts2環境是,我們一般需要做一下幾個步驟的工作:
1.找到開發Struts2應用需要使用到的jar文件。
2.編寫Struts2的配置文件 struts.xml
3.在web.xml中加入Struts2 MVC框架啟動配置
搭建Strut2開發環境 --開發Struts2應用依賴的jar文件
大家可以到。。。。。下載 struts-----all.zip。下載完后解壓文件,開發struts2
應用需要的jar文件在解壓目錄lib文件寫下。不同的應用需要的JAR包是不同的。
下面給出了開發Struts2程序最小需要的JAR包。
struts2-core---.jar Struts2框架的核心類庫
xwork-----.jar XWORK類庫,Struts2在其上構建
ognl------.jar 對象圖導航語言(Object Graph Navigation Language),struts2框架通過其讀寫對象的屬性
freemarker--.jar struts2的UI標簽的模板,使用FreeMaker編寫
commons-logging---.jar ASF出品的日至寶,Strut2框架使用這個日至寶來支持Log4j和JDK1.4+的日志記錄
commons-fileupload--.jar 文件上傳組件
搭建Struts2開發環境---Struts2應用的配置文件
Struts2默認的配置為struts.xml,該文件需要存放在WEB-INF/classes下,該文件
配置模板如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/strut-2.0.dtd" >
<struts>
</struts>
搭建Struts2開發環境---Struts2在web中的啟動配置
在struts1.X中,struts框架是通過Servlet啟動。
在Struts2中,strut框架是通過Filter啟動的,在web.xml中的配置如下:
<filter>
<filter-name>struts2</filter>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepaerAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
在SturtsPrepareAndExecuteFilter的init()方法中將會地區類路徑下默認的配置文件
struts.xml完成初始化的操作。
Strut2讀取到struts.xml的內容后,以javabean形式存放在內存中,以后struts2
對客戶端的每次請求處理將使用內存中的數據,而不是每次都讀取struts.xml文件的內容
Struts.xml 配置中的包介紹
<package name="soldier" namespace="/test" extends="struts-default">
<action name="hello" class="cn.soldier.acion.HelloAction" method="execute">
<result name="success">/WEB-INF/page/hello.jap</result>
</action>
</package>
在struts2框架中使用包來管理action,包的作用和java中的雷暴是非常相似的。
它主要用於管理同一組業務功能相關的action,在實際應用中,我們應該把一組
業務功能相關的action放在同一個包下。
配置包是必須指定name屬性,該那么屬性值可以任意取名,但必須唯一,他不對應
java的類包,如果其他包要繼承該包,必須通過屬性進行引用。
包的namespace屬性用於定義該包的命名空間,命名空間作為訪問包下action的路徑的一部分,
如訪問上面例子的,訪問路徑為/test/hello.action.
如果namespace不配置或配置為"" ,則該package為默認的命名空間。
通常每個包都應該繼承 struts-default包,因為struts2很多核心功能都是攔截器
實現的,如:從請求中把請求參數封裝到action,文件上傳和數據驗證等等,都是
通過攔截器實現的。struts-defaute定義了這些攔截器和Result類型。可以這么說
當包繼承了struts-default才能使用struts提供的核心功能。
struts-default包實在struts2-core-xxx.jar文件中的struts-default.xml中定義。
struts-default.xml也是struts2默認配置文件。
Struts2每次都自動加載struts-default.xml文件。
包還可以通過abstract=true定義為抽象包,抽象包總不能包含action
Acrion名稱的搜索順序
1.獲得請求路徑的url,例如url是:http://server/struts2/path1/path2/path3/test.action
2.首先尋找namespace為/path1/path2/path3的package,
如果不存在這個package則執行步驟3,如果存在這個package,則在這個package中尋找名字為test的action,
當在該package下尋找不到action時,就會直接跑到默認namespace的package里尋找action(默認的命名空間為空字符串""),
如果在默認namespace的package里面還尋找不到該action,則頁面提示找不到action。
3.尋找namespace為/path1/path2的package,如果不存在這package,則轉至步驟4,如果存在這個package
則在這個package中尋找名字為test的action,當在該package中尋找不到action是就會直接跑到
默認的namespace的package中去找名字為test的atcion,在默認的namespace的package里還尋找不到該action
則頁面提示找不到action。
4. 尋找namespace為/path1的package,如果不存在這個package則執行步驟5,如果存在這個package,
則在這個package中尋找名字為test的action。當裝在該package中尋找不到action是就會直接跑到默認的namespace
的package中去找名字為test的action,在默認的namespace的package里還尋找不到該action則頁面體制找不到
該action。
5.尋找namespace的package,如果存在這個package,則早這個package中尋找名字為test的action,當在package中
找不到action或不存在這個package是,都會去默認namespace的package里面尋找action,如果還是找不到則
頁面顯示找不到action。
action配置中的各項默認值
<package name="soldier" namespace="/test" exetends="struts-default">
<action name="helloworld" class="cn.soldier.HelloworldAction" method="extcute" >
<result name="success">/WIN-INF/page/hello.jsp</result>
</action>
<action name="addUI">
<result>/WIN-INF/page/hello.jsp</result>
</action>
</package>
1.如果沒有為action指定class,默認是ActionSupport
2.如果沒有為action指定method。默認執行action中的execute方法
3.如果沒有指定result的name屬性,默認值為success
action中result的各種轉發類型
<action name="hello" cless="cn.soldier.HelloAction" method="extcute">
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
result配置類似於struts1中的forward,struts2中提供了多種結果類型,常用的類型有有
dispatcher(默認值)、redirect、redirectAction、plainText
在result中還可以使用{$屬性名}表達式訪問action中的屬性,表達式的屬性名對應action中的屬性。
例子如下:
<result name="success" type="redirect">/WEB-INF/page/hello.jsp?id=${id}</result>
下面是redirectAction結果類型的例子,
如果重定向的action在同一個包下
<result name="success" type="redirect">hello</result>
如果重定向的action不在同一個包下
<result type="redirectAction">
<param name="actionName">action名</param>
<param name="namespace">namespace名</param>
</result>
plantext顯示原始文件內容,
例如:當我們需要原樣顯示jsp文件源碼的時候,我們可以用這個參數
<result name="soutce" type="plantext">
<param name="location">/xxx.jsp<param>
<param name="charSet">UTF-8</param><!--指定讀取文件的編碼-->
指定需要Struts2處理的請求后綴
前面我們都是默認使用.action后綴訪問action。其實默認后綴是可以通過
常量"struts.action.extesion"進行修改的。如下我們配置Struts2只處理
以.do為后綴的請求路徑:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.ort/dtds/struts-2.0.dtd">
<struts>
<constan name="stuts.action.extension" calue="do" />
</struts>
如果用戶需要指定多個請求后綴,則多個后綴之間以英文逗號隔開,如
<constant name="struts.action.extension" value="do,go">
細說常量定義
常量可以在struts.xml或struts.properties中配置,建議在struts.xml中配置
兩種配置方式如下
在stratus.xml文件中配置常量
<struts>
<constant name="stratus.action.extension" value="do" />
<struts/>
在stratus.properties中配置常量
stratus.action.extension=do
因為常量可以在多個配置文件中定義,所以我們需要里了解struts2加載常量的
搜索順序
struts.xml
struts-piugin.xml
struts.xml
struts.properties
web.xml
如果在多個文件中配置同一個常量,則后一個文件中配置的常量值會覆蓋
前面文件中配置的常量值。
常用的常量介紹
<!--指定默認編碼集。作用於HttpServletRequest的setCharracterEncoding方法
和freemaker、velocity的輸出-->
<constant name="struts.i18n.enconding" value="UTF-8" />
<!--指定需要Struts2處理的請求后綴,該屬性的默認值是action。即所有匹配
".acion"的請求都由struts2處理,如果用戶需要指定多個請求后綴,則多個后綴
之間以英文逗號隔開
<constant name="struts.action.extension="do" />
<!--設置瀏覽器是否緩存靜態內容默認值為true(生產化境下使用),開發階段最好關閉 -->
<constant name="strut.serve.static.browCache" value="false" />
<!--當struts配置文件修改后,系統是否自動重新加載該文件 默認值為false(生產環境
下使用),開發階段最好打開 -->
<constant name="struts.configuration.xml.reload" value="true" />
<!--開發模式下使用,這樣可以打印出更詳細的錯誤信息-->
<constant name="struts.devMode" value="true" />
<!--與spring集成時,指定由sping負責action對象的創建-->
<constant name="struts.objectFactory" value="spring" />
<!--默認視圖 -->
<constant name="struts.ui.theme" value="simple" />
<!--該屬性設置Struts2是否支持動態啊方法調用,該屬性的默認值是true。
如果需要關閉動態方法調用,則可以設置該屬性為false-->
<constant name="struts.enable.DynameicMethodInvocation" vanle="false">
<!--上傳文件的大小限制-->
<constant name="struts.multipart.maxSize" valie="107010196">
用戶請求-->StrutsPrepareAndExecuteFilter
-->Interceptor struts內置的一些攔截器或用戶自定義攔截器
-->Action 用戶編寫action類,類似struts中的action
-->Result 類似struts1中的forward
-->Jap/html
響應<--
StrutsPrepareAndExecuteFilter是struts2框架的核心控制器,它負責攔截
有<url-pattern>/<url-pattern>指定的所有用戶請求,當用戶請求到達時,
該Filter會過濾用戶的請求,默認情況下,如果用戶請求不帶后綴或者后綴
以.action結尾,這時請求將被轉入struts2框架處理,否則Struts2框架將忽
略過該請求的處理。當請求轉入struts2框架時會先經過一系列的攔截器,
然后再到Action。與Strut1不同,Struts2對用戶的每一請求都會創建一個
Action,所以Struts2中的Action是線程安全的。
為應用指定多個struts配置文件
在大部分應用里,隨着應用規模的增加,系統中Action的數量也會大量增加
導致struts.xml配置文件變得非常臃腫,為了避免struts.xml文件過於龐大、
臃腫,提高struts.xml問價你的可閱讀性,我們可以將struts.xml配置文件
分解為多個配置文件,然后再struts.xml文件中包含其他配置文件、
夏敏的struts.xml文件通過<include>元素指定多個配置文件:
<!--struts.xml-->
<?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="struts-user.xml" />
<include file="struts-order.xml" />
</struts>
/*********************************************************/
<!--struts-user.xml-->
<?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>
</struts>
/*********************************************************/
<!--struts-order.xml-->
<?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>
</struts>
通過這種方式,我們就可以將struts2的Action按模塊添加在多個配置文件中。
動態方法調用
如果Action中存在多個方法是,我們可以使用!+方法名調用指定的方法。
例如:
public class HelloWorldAction{
private String message;
public String ececute()throws Exception{
this.meaage= "呵呵呵";
return "success";
}
public String other()throews Exception{
this.message="第二個方法";
return "success";
}
}
假設訪問上面的action的URL路徑為: /struts/test/helloword.action
要想訪問action的other()方法,我們可以這樣調用:
/struts/test/hellowoild!other.action //關鍵字 !other !方法名
<!--如果不想只用動態方法調用,可以設置常量關閉動態方法 -->
<constant name="struts.enable.DynameicMethodInvocation" value="flase" />.
使用通配符定義action
<package name="soldier" namespace="/test" extends="struts-default">
<action name="hello_*" class="cn.soldier.action.HelloAction" method="{1}">
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
public class HelloAction{
private String message;
public String execute() throws Exception{
this.message="呵呵";
return "success";
}
public String other() throws Exception{
this.message="第二個方法";
return "success";
}
}
//要想訪問execute() 訪問url: /test/hello.atcion
//要想訪問other() 訪問url: /test/hello!other.action
接收請求參數
采用基本類型接收請求參數(get/post)
在action類中定義與請求參數同名的屬性,struts2便能自動接收請求參數並
賦予給同名屬性。
例如:
請求路徑為:http://locathost:8080/test/view.action?id=12
public class ProductAction{
private Integer id;
//struts2通過反射技術調用與請求參數同名的屬性的setter方法或獲取訪問請求參數值
public void setId(Integer id){ this.id = id;}
public Integer(){return id}
}
采用復合類型接收參數
請求路徑: http://locathost:8080/test/view.action?pruduct.id=78
public class ProductAction{
private Product prodect;
public void setProdect(Prodect product){
this.product = product;
}
public Produce getProduct(){return product;}
}
Struts2首先通過反射技術調用Product的默認構造器創建product對象,然后再通過
反射技術調用product中與請求參數同名的屬性setter方法獲取請求參數值。
自定義了類型換器的步驟
1.繼承 DefaultTypeConverter 類
2.重寫 converterValue 方法
局部類型轉換器
public DateConverter extends DefaultTypeConverter{
@Override
public Object converterValue(Map context,Object vlaue,Class toType){
try{
//當字符串向Data類型轉換時
if(toType == Date.class){
String[] params = (String[])value;
return dateFomat.parse(params[0]);
}else if(totype == String.class){
Data data = (object) value;
return deteFormat.format(date);
}
}catch(ParseException e){
return null;
}
}
}
注冊局部類型轉化器
轉換器寫好之后需要注冊才能使用,可以注冊全局的類型轉換器和局部類型轉換器
將類型轉換器注冊為局部類型轉化器:
1.在Action類所在的包下放置ActionClassName-conversion.properties文件
ActionClassName是Action的類名,后面的-conversion是固定的寫法,
2.在properties文件中的內容為:屬性名=類型轉化器的全類名
對於上例而言,文件的名稱應該為
HelloWorldAction-conversion.properties
HelloWorldAction-conversion.properties文件中的內容為:
createtime=cn.soldier.conversion.DateConverter
自定義全局類型轉換器
1.在WEB-INF/classes下放置xwork-conversion.properties文件
2.在properites文件中的內容為:待轉換的類型=類型轉換器的全類名
將上面類型注冊為全局類型轉換器:
xwork-conversion.properties文件中的內容為
java.util.Data = cn.soldier.conversion.DataConverter
訪問或添加request、session、application屬性
public String scope() throws Exceprion{
ActionContext ctx = ActionContext.getContext();
ctc.getAplictaion.put("app","應用范圍")//往ServletContext里嵌入app
ctx.getSession()put("ses","session范圍");//往session里放入ses
ctx.put("req","request范圍")//往request里放入req
return "scope";
}
JSP:
<body>
$(applicationScope.app);
$(sessionScope.ses);
$(requestScope.req)
</body>
獲取HttpServletRequest、HttpSession、ServletContext、HttpServletResponse對象
方法一:通過ServletActionContext類直接獲取:
public String rsal() throws Exception{
HttpServletRequest request =
ServletActionContext.getRequest();
ServletContext servletContext =
ServletActionContext.getServletContext();
HttpSession session = request.getSession();
HttpServletResponse response =
ServletActionContext.getResponse();
return "rsal";
}
方法二:通過實現特定的接口實現 XXXAware
public class HelloWorldAction imlpements ServletRequestAware,
ServletResponseAware,ServletContextAware{
private HttpServletRequest request;
private ServletContext servletContext;
private HttpServletResponse response;
public void setServletRequest(HttpServletRequest q){
this.request = q;
}
public void setServletResponse(HttpServletResponse res){
this.response = res;
}
public void setServletContext(ServletContex ser){
this.servletContext = ser;s
}
}
使用el表達式
1.添加jar文件 jstl.jar(version 1.1) standard.jar
2.添加taglib標識 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
3.使用
<c:forEach items="${names}" var ="name">
${name}<br />
</c:forEach>
文件上傳
第一步:添加jar文件commons-fileupload-1.2.jar、commons-io1.3.2.jar
第二步:把form表單的enctype設置為"multipart/form",如下:
<form enctype="multipart/form-data" action="
${pageContext.request.contextPath}/xxx.action" method="post">
<input type="file" name="uploadImage"></inpput>
</form>
第三步:在Action類中添加屬性,屬性應對應表單文件字段的名稱
public class HelloWorld(){
private File uploadImage;//得到上傳的文件
private String uploadImageContentType;//得到文件的類型
private String uploadImageFileName;//得到文件的名字
/*這里省略getter,ssetter方法*/
public String upload() throws Exception{
String realPath = ServletActionContext.getServletContext()
.getRealPath("/images");
File file = new File(realPath);
if(!file.exists()){
file.mkdirs();//創建目錄
}
FileUtils.copyFile(uploadImage,new File(file.uploadImageFileName));
return "success";
}
}
多文件上傳
第一步:添加jar文件commons-fileupload-1.2.jar、commons-io1.3.2.jar
第二步:把form表單的enctype設置為"multipart/form",如下:
<form enctype="multipart/form-data" action="
${pageContext.request.contextPath}/xxx.action" method="post">
<input type="file" name="uploadImages" />
<input type="file" name="uploadImages" />
<input type="file" name="uploadImages" />
</form>
第三步:在Action類中添加屬性,屬性應對應表單總文件字段的名稱
public class HelloWorld(){
private File[] uploadImages;//得到上傳的文件
private String[] uploadImagesContentType;//得到文件的類型
private String[] uploadImagesFileName;//得到文件的名字
/*這里省略getter,ssetter方法*/
public String upload() throws Exception{
String realPath = ServletActionContext.getServletContext()
.getRealPath("/images");
File file = new File(realPath);
if(!file.exists()){
file.mkdirs();//創建目錄
}
for(int i=0;i<uploadImages.lenth;i++){
File uploadImage = uploadImages[i];
FileUtils.copyFile(uploadImage,
new File(file.uploadImagesFileName[i]));
}
return "success";
}
}
自定義攔截器
1.要自定義攔截器需要實現
com.opensymphony.xwork2.interceptor.Intercepetor接口:
2.在intercept方法中定義攔截器
invcation.invoke();//允許通過,不攔截
3.在struts.xml中注冊攔截器
public class PremissionInterceptor implements Interceptor{
private static final long serialCersionUID =-5234513456632212342L;
public void destroy(){
}
public void init(){
}
public String intercept(ActionInvoction invocation) throws Exception{
System out Println("進入攔截器");
Object user = ActionContext.getContext().getSession.get("user");
if(user != null){
//如果不為null。則用戶已登陸,不攔截,余怒執行請求的action
return invovation.invoke();
}
ActionContext.getContext.put("message","你沒有權限操作");
return null;
}
}
struts.xml
<package name="employee" namespace="/control/employee" extends="struts-default">
<!--定義攔截器,這個方法不推薦-->
<interceptors>
<interceptor name="permission" claass="cn.soldier.interceptor.PremissionInterceptor"></interceper>
</interceptors>
<global-results>
<resutlt name="messagee">/WEB-INF/message.jsp</resulet>
</gloval-resuts>
<action name="list_*" class="XXXXX" method={1}>
<!--使用攔截器,為action配置攔截器的這種做法
,將會丟失Struts2的其它核心攔截功能。解決這個問題的辦法是
定義一個攔截器棧-->
<interceptor-ref name="permission" />
<resutlt name="success">/WEB-INF/page/employee/meaagge.jsp</tesult>
</action>
</package>
<!--
全局result(global-results)
有很多時候一個<result>可供很多<action>使用,
這時可以使用<global-results>標簽來定義全局的<result>l。
執行順序:當一個Action返回的String沒有相應的<result>與之對應,
Struts2就會查找全局的<result>。
-->
/******************************************************************/
struts.xml 的改進
<package name="employee" namespace="/control/employee" extends="struts-default">
<!--定義攔截器-->
<interceptors>
<interceptor name="permission" claass=" cn.soldier.interceptor.PremissionInterceptor" />
<interceptor-stack name="premissionStack">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="permission" />
</interceptor-stack>
</interceptors>
<action name="list_*" class="XXXXX" method={1}>
<resutlt name="success">/WEB-INF/page/employee/meaagge.jsp</tesult>
<interceptor-ref name="premissionStack" />
</action>
</package>
因為struts2中如文件上傳,數據驗證,封裝請求參數到action等功能都是由系統
默認的defaultStack中的攔截器實現的,所以我們定義的攔截器需要引用系統默認
的defaultStack,這樣應用才可以使用struts2框架提供的眾多功能。
如果希望包下的所有action都是用自定義的攔截器,可以通過
<default-interceptor-ref name="permissionStack" />,
把攔截器定義為默認攔截器。要注意的是每個包只能指定一個默認攔截器。
另外,一旦我們為包中的某個action顯式指定了某個攔截器,則默認攔截器不會器作用。
輸入檢驗
在struts2中,我們可以實現對action的所有反法進行校驗或者對action的指定方法進行校驗。
對於輸入校驗struts2提供了兩種實現方法:
1.采用手工編寫代碼實現
2.基於XML配置方式實現。
手工編寫代碼實現對action中所有方法輸入校驗
1.繼承 ActionSupport 類
2.通過重寫validate()方法檢驗
3.調用addFieldError()方法往新系統的fieldErrors添加校驗是失敗信息
(為了使用addfieldErrot()方法,action可以繼承ActionSupport)
4.在strut.xml中<resul >添件 input 視圖
<result name="input">/********/</resutlt>
5.在跳轉的jsp文件里添加struts2的標簽<s.fielderror />
(通過<s.fielderror /> 顯示失敗信息。)
通過重寫validate()方法實現,validate()方法會校驗action中所有與execute()
方法簽名相同的方法。當某個數據校驗失敗時,我們應該調用addFieldError()方
法往新系統的fieldErrors添加校驗是失敗信息(為了使用addfieldErrot()方法,
action可以繼承ActionSupport),如果系統的fieldErrors包含失敗信息,Struts2
會將請求轉發到名為input的result。在input視圖中可以通過<s.fielderror />
顯示失敗信息。
validata使用例子:
public void validata(){
if(this.moble==null || "".equals(this.mobile.trim())){
this.addFieldError("moble","手機號碼不為空");
}
}
驗證是失敗后,請求轉發至input視圖
<result name="input">/WEB-INF/page/addUser.jsp</resutlt>
在addUIUser.jsp頁面中使用<s:fielderror /> 顯示失敗信息
添加struts2的標簽庫
<% @ taglib uri="/struts-tags" prefix="s" %>
使用struts2標簽
<s:fielderror />
//校驗正則表達式
Pattern.compile("^1[]358]\\d{9}$").macher(this.moble).maches()
手工編寫代碼實現對action指定方法輸入檢驗
1.繼承ActionSupport類
2.添加validateXxx()方法,里面楷寫數據校驗
3.調用addFieldError()方法往系統fieldErrors添加校驗失敗信息
(為了使用addfieldErrot()方法,action可以繼承ActionSupport)
4.在strut.xml中<resul >添件 input 視圖
<result name="input">/********/</resutlt>
5.在跳轉的jsp文件里添加struts2的標簽<s.fielderror />
(通過<s.fielderror /> 顯示失敗信息。)
通過validateXxx()方法實現,validateXxx()只會校驗ation中名為Xxx的方法,
其中Xxx方法的第一個字母要大寫,當某個數據校驗失敗時,我們應該調用
addFieldError()方法往系統fieldErrors添加校驗失敗信息(為了使用addfieldErrot()方法,
action可以繼承ActionSupport),如果系統的fieldErrors包含失敗信息,Struts2
會將請求轉發到名為input的result。在input視圖中可以通過<s.fielderror />
顯示失敗信息。
validateXxx()方法使用例子:
public String add() throws Exception( return "success");
public void validateAdd(){
if(this.moble==null || "".equals(this.mobile.trim())){
this.addFieldError("moble","手機號碼不為空");
}
}
驗證是失敗后,請求轉發至input視圖
<result name="input">/WEB-INF/page/addUser.jsp</resutlt>
在addUIUser.jsp頁面中使用<s:fielderror /> 顯示失敗信息
輸入校驗的流程
1.類型轉換器對請求參數執行類型轉換,並把轉換后的值賦給action中的屬性。
2.如果在執行類型轉換的過程中出現異常,系統將異常信息保存到ActionContext。
conversionError攔截器將異常信息封裝到filedErrors里。不管類型轉換是否正常
都會進入第3步。
3.系統通過反射技術調用action中的validateXxx方法,Xxx為方法名
4.然后再調用action中的validate()方法。
5.經過上面4步后,如果系統中的fieldErrors存在錯誤信息(即存在錯誤信息學的集合size大於0),
系統自動將請求發至名稱為input的視圖。如果系統中的fieldErrors沒有任何錯誤信息,
系統將執行action中的處理方法。
基於XML配置方法實現對action的所有方法進行輸入檢驗
1.繼承ActionSupport類
2.同一個包下編寫ActionClassName-validation.xml檢驗文件
使用基於XML配置方式實現輸入檢驗時,Action也需要繼承ActionSupport。
並且提供校驗文件,校驗文件和action類存放在同一個包下,
文件的取名格式為:ActionClassName-validation.xml。
其中ActionClassName為action的簡單類名,-validation為固定寫法。
下面是校驗文件的模板:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE validators PUBLIC
"-//OpenSymphonyGroup//XWorkValidator1.0.3//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dta">
<validators>
<field name="username">
<field-validator type="requiredstring">
<param name="tirm">true</param>
<message>用戶名不能為空</message>
</field>
</validators>
<field>指定action中要校驗的屬性
<field-validator>指定校驗器,例子中的requiredstring有系統提供的。
系統提供了能滿足大部分驗證需求的校驗器。校驗器可以在xwork-xxxx.jar
中的com.opensymphony.xwork2.validator.validators下的default.xml中找到
<message>指定校驗失敗后的信息。如果需要國際化,可以為message指定key屬性,
key的值為資源文件中的key。
在這個校驗文件中,對action字符串類型的username屬性經行校驗,首先要求
調用trim()方法去掉空格,然后判斷用戶名是否為空。
其他校驗器的例子
email校驗器
<field-validator type="email">
<message>郵箱地址無效</message>
</field-validator>
regex:正則表達式校驗器
<field-validator type="regex">
<param name="expression"><![CDATA[^1[358]\d{9}$]]></param>
<message>手機號碼格式不正確</message>
</field-validator>
如果系統的fieldErrors包含失敗信息,Struts2會將請求轉發到名為input
的result。在input視圖中可以通過<s.fielderror />標簽顯示失敗信息。
struts2提供的的校驗器列表
系統提供的校驗器如下:
required(必填校驗器,要求field的值不能為null)
requiredstring(必填字符串檢驗器,要求field的值不能為null,並且長度大於0,
默認情況下會對字符串去前后空格)
stringlength(字符串 長度校驗器,要求field的值必須在指定的范圍內,否則
校驗失敗,minLength參數指定最小長度,maxLength參數指定最大長度,
trim參數指定校驗field之前是否去除字符串前后的空格)
regex(正則表達式校驗器,檢查白校驗的field是否匹配一個正則表達式,
expression參數指定正則表達式,caseSensitive參數指定進行正則表達
式時,是否區分大小寫,默認true)
int(整數校驗器)
double(雙精度浮點數校驗器)
fieldexpression(字段OGML表達式驗證器)
email(郵箱地址校驗器)
url(網址校驗器)
date(日期校驗器)
conversion(轉化校驗器)
visitor(用於校驗action中的復合屬性)
expression(OGNL表達式校驗器)
基於XML配置方式對指定action方法實現輸入校驗
當校驗文件取名為 ActionClassName-validation.xml時,會對action中的所有處理
方法實施輸入驗證,如果你只需要對action中的某個action方法實施校驗,那么。
校驗文件的取名應為,ActionClassName-ActionName-validation.xml,其中
ActionName為strurs.xml中的action的名稱。
例如,在實際應用中,常有以下配置:
<action name="user_*" class="cn.soldier.action.UserAction" method="{1}">
<result name="success">/WEB-INF/page/user/message.jsp</result>
<reult name="input">/WEB-INF/page/user/addUser.jsp</result>
</action>
UserAction中有以下兩個處理方法:
public String add() throws Exception{
}
public String update() throws Exception{
}
要對add()方法實施校驗,校驗文件的取名為:
UserAction-user_add-validation.xml
要對update()方法實施驗證,校驗文件取名為:
UserAction-user_update-validation.xml
國際化
准備資源文件,資源文件命名格式如下:
baseName_language_country.propertires
baseName_language.properties
baseName.properties
其中baseName是資源文件的基本名,我們可以自定義,但language和country必須是
java支持的語言和國家例如:
中國大陸, baseName_zh_CN.properties
美國,baseName_en_US.properties
請看下面的例子
現在為應用添加兩個資源文件:
第一個存放中文: soldier_zh_CN.properties
內容為 welcome=歡迎開到小兵工作室
第二個存放英文 soldier_en_US.properties
內容為 welcome=welcome to soldier
對於中文屬性文件,我們編寫好后,應該使用jdk提供的native2ascii命令將文件
轉換為unicode編碼的文件,命令的使用方式如下:
naticve2ascii 源文件.properties 目標文件.properties
配置全局資源與輸出國際化信息
當准備好資源文件之后,我們可以在struts.xml中通過
struts.custom.i18n.resources常量把資源文件定義為全局資源文件,如下:
<constant name="struts.custom.i18n.resources" value="soldier" />
soldier為資源文件的基本名。
后面我們就可以在頁面或在action中訪問國際化信息了:
在jsp頁面中使用 <s:text name="" />標簽輸出國際化信息
例如: <s:text name="welcome" /> name為資源文件中的key
在Action類中,可以通過繼承ActionSupport,使員工getText()方法得到國際化信息,
該方法的第一個參數用於指定資源文件中的key.
在表單文件中,通過key屬性指定資源文件中的key,如:
<s: textfield name="realname" key="welcome"/>
國際化--輸出帶占位符的國際化信息
資源文件的內容如下:
welcome={0},歡迎來到小兵工作室{1}
在jap頁面中輸出帶占位符的國際化信息
<s:text name="welcome">
<s:param><s:property value="realname" /></s:param>
<s:param>參觀</s:param>
</s:text>
在action類中獲取帶占位符的國際化信息,可以使用getText(String key,
Strng[] args)或getText(String aTextName,List arges)方法
國際化_配置包范圍資源文件(使用方法不變。先找包資源再找全局資源)
在一個大型應用中,整個應用有大量的內容需要實現國際化,如果我們把國際化
的內容全都放置在全局資源屬性文件中,顯然會導致資源文件變得過於龐大,
臃腫,並且不便於維護,這個時候我們可以針對不同模塊,使用包范圍來
組織國際化文件。
方法如下:
在java的包下放置package_language_country.properties資源文件,
package為固定寫法,處於該包及子包下的action都可以訪問該資源。
當查找指定key的消息時,系統會先從package資源文件中查找,當找不到
對應的key時,才會從常理struts.custom.i18n.resources指定的資源文件
查找。
國際化_配置action范圍的資源文件
我們也可以為某個action單獨指定資源文件。
方法如下:
在action類所在的路徑,放置ActionClassName_language_country.properties
資源文件,ActionClassName為類的簡單名稱。
查找順序:Action-->包-->全局資源
當查找指定key的信息是,系統會先從ActionClassName_language_country.properties
資源文件中查找,如果沒有找到對應的key,然后沿着當前包往上查找名為
package的資源文件。一直找到最好頂層包,如果還沒有找到對應的key,最后
會從常量struts.custom.i18n.resources指定的資源文件中尋找。
OGNL表達式語言
OGNL是 Object Graphic Navigation Language(對象圖導航語言)的縮寫。它是
一個開源項目,Struts2框架使用OGNL作為默認表示式語言。
相對EL表達式,他提供了平時我們需要的一些功能,例如:
1)支持對象方法調用,如xxx.sayHello();
2)支持類靜態的方法|值調用:格式為"@[類全名(包括包路徑)]@[方法名|值名]"。如:
@java.lagn.String@format('foo%s','bar')--調用類靜態方法
@tutorial.MyConstant@APP_NAME--訪問類的靜態值
3)操作集合對象
Ognl上有一個上下文概念(Context),說白了就是一個MAP結構,它實現了
java.utils.Map接口。
在Struts2中OGNL的上下文(Context)的實現者為ActionContext,
下面是上下文(Context)的結構示意圖:
--->ValueStack(值棧,它是根對象)
--->parameters
OGNL Context --->request
--->session
--->application
--->attr
當Struts2接收一個請求時,會迅速創建ActionContext,ValueStack,acion,然后
把action存放進ValueStack,所以action的實例變量可以被OGLN訪問。
訪問上下文中的對象需要使用#符號標注命名空間,如#application、#session
另外OGNL會設定一個根對象(root對象),在Struts2中跟對象就是ValueStack(值棧)
如果要訪問跟對象中對象的屬性,可以省略#號,直接訪問該對象的屬性即可。
在Struts2中,根對象ValueStack的實現類為OgnlValueStak,該對象不是我們想象
的只存放單個值,而是存放一組對象,在OgnlValueStack類里有一個List類型root
變量,就是用它存放一組對象
--->request
--->application
OGNL Context --->OgnlValueSteck root變量[action,OgnlUtil, ...]
--->session
--->attr
--->parameters
在root變量中處於第一位的對象叫棧頂對象。通過我們在OGNL表達式里直接寫上屬性
名稱即可訪問root變量對象的屬性,搜索順序是存棧頂對象開始尋找,如果棧頂
對象不存在該屬性,就會從第二個對象尋找,如果沒有找到就會從第三個對象
尋找,依次往下訪問,知道找到為止。
要注意的是:Struts2中OGNL表達式需要配合Sturts標簽才可以使用,
如:<s:property value="name" />
由於ValueStack是Struts2中OGNL的根對象,如果用戶需要訪問值棧中的對象,
在jsp頁面可以直接通過下面的el表達式訪問ValueStack中對象的屬性:
$(foo)//獲得值棧中某個對象的foo屬性
如果訪問其他Context中的對象。由於他們不是跟對象,所以在訪問時,
需要添加#前綴。
1)application對象:用於訪問ServletContext,例如#application.username
或者#applicaion['usrename'],相當於調用ServletContext的
getAttribute("username")..
2)session對象。用來訪問HttpSession對象。例如
#session.username或者#session['username']。
相當於調用session.getAttribute('usrename');
3)request對象。用來訪問HttpServletRequest屬性的MAP。例如
#request.username或者#request['username']。
相當於調用request.getAttribute('usrename');
4)parameter對象。用來訪問HTTP請求的參數,例如
#parameter.username或者#parameter['username']。
相當於調用request.getParameter('usrename');
5)arr對象,用於按照page->request->session->application順序訪問其屬性
采用OGNL表達式創建 List/Map集合對象
如果需要一個集合元素的時候(例如:List對象或者Map對象),可以使用OGNL中
同集合相關的表達式。
使用如下代碼直接生成一個List對象
<s:set name="list" value="{'zhangming','xiao','liming'}" />
<!--<s:iterator>標簽在迭代集合是有個特點,會把當前迭代的對象放置值棧頂-->
<s:iterator value="#list">
<s:property / > <br />
</s:iterator>
Set標簽用於將某個值放入指定范圍。
scope:指定變量被防止的范圍,該屬性可以接受 application、session、request、
page、action,如果沒有這只該屬性,則默放置在OGLN Context中。
value:賦給變量的值,如果沒有這只該屬性,則將ValueStack棧頂的值賦給變量。
生成一個map對象:
<s:set name="foobar" value="#{'foo1':'bar1','foo2','bar2'}" />
<s:iterator value="#foobar">
<s:property value="key" /> =
<s:property value="value /"> <br />
</s:iterator>
property標簽
property標簽用於輸出指定值:
<s:set name="name" value="kk" />
<s:property value="#name" />
default屬性:可選。如果要輸出的值為null,則顯示該屬性指定的值。
escape屬性:可選。指定是否格式化HTML代碼
value屬性:可選。 指定需要輸出的屬性值,如果沒有指定該屬性,則默認輸出
ValueStack棧頂的值。
id屬性:可選,指定改元素的標識
采用LGNL表達式判斷對象是否存在於集合中。
對於集合類型,OGNL表達式可以使用in和not in 兩個元素符號。其中,
in表達式用來判斷某個元素是否在指定的集合對象中。
not in 判斷某個元素是否不再指定的集合對象中。
例子如下:
in表達式
<s:if test="'foo' in {'foo','bar'}>
在
</s:if>
<s:else>
不在
</s:else>
not in 表達式
<s:if test="'foo' not in {'foo','bar'}">
不在
</s:if>
<s:else>
在
</s:else>
OGNL表達式的投影功能
除了in和not in 之外,OGNL表達式還允許使用某個規則獲得對象的子集,常用的有
以下3個相關操作符。
?:獲得所有符合邏輯的元素
^:獲得符合邏輯的第一個元素
$:獲得符合邏輯的最后一個元素
例子如下;
<s:iterator value="book{?#this.price>35}">
<s:property value="title" /> -$<s:property value="price" />
</s:iterator>
上面代碼中,直接在集合后綴跟{}運算符表明用於取出該集合的子集,{}內的表達式
用於獲得符合條件的元素,this指的是為了從大集合books篩選數據到集合
需要對大集合book進行迭代,this代表當前迭代的元素。本例的表達式用於
獲取集合中價格大於35的書集合。
public class BookAction exetends ActionSuppor{
private List<Book> books;
@Override
public String execute(){
books = new LinkedList<Book>();
books.add(new Book("1001","CSS","34"));
books.add(new Book("1002","HTML","54"));
}
}
property標簽
property標簽用於輸出指定值:
<s:set name="name" value="kk" />
<s:property value="#name" />
default屬性:可選。如果要輸出的值為null,則顯示該屬性指定的值。
escape屬性:可選。指定是否格式化HTML代碼
value屬性:可選。 指定需要輸出的屬性值,如果沒有指定該屬性,則默認輸出
ValueStack棧頂的值。
id屬性:可選,指定改元素的標識
iterator標簽
iterator標簽用於對記得進行迭代,這里的集合包括list、set和數組。
<s:set name="list" value="{'xiao','da'}">
<s:iterator value="#list" status="st">
<font1 color=<s:if test="#st odd">red</s:if><s:else>blue</s:else> >
<s:property></font></s:property>
</s:iterator>
value屬性:可選。指定被迭代的集合,如果沒有設置該屬性,則使用ValueStack
棧頂的集合
id屬性:可選。指定集合里元素的id(已過時)
status屬性:可選。該屬性紙迭代時的IteratorStatus實例。
該實例包含如下幾個方法:
int getCount()返回當前迭代了幾個元素
int getIndex()返回當前迭代元素的索引
boolean isEven()返回當前被迭代索引是否為偶數
boolean isOdd()返回當前被迭代的索引是否為基數
boolean isFirst()返回當前被迭代元素是否是第一個元素
boolean isLast()返回當前被迭代元素是否是最后一個元素
if else 標簽
<s:if></s:if>
<s:else></s:else>
url標簽
<s:url action="helloworld_add" namespace="/test">
<s:param name="presionid" value="23" /> </s:url>
生成類似如下路徑
/struts/test/helloworld_add.action?persionid=23
當標簽屬性值作為字符串類型處理時,"%"符號的用途是計算OGNL
表達式的值。
<s:set name="nyurl" value="http://www.baidu.com" />
<s:url value="#myurl" /> <br />
<s:url value="%{#myurl}" />
輸出結果:
#myurl
http://www.foshanshop.net
表單標簽<s:checkboxlist>復選框標簽
如果集合為list
<s:checkboxlist name="list" list="{'JAVA','PHP'}"
value={'java'} />
生成html代碼如下:
<input type="checkbox" name="list" value="java"
checked="checked" /> <lable>JAVA</lable>
<input type="checkbox" name="list" value="php"
/> <lable>PHP</lable>
如果集合為map
<s:checkboxlist name="map" list="#{1:'JAVA',2:'PHP',3:"CSS"}"
listkey="key" listValue="vlaue" value="1,3" />
生成html代碼如下:
<input type="checkbox" name="map" value="1"
checked="checked" /> <lable>JAVA</lable>
<input type="checkbox" name="map" value="2"
/> <lable>PHP</lable>
<input type="checkbox" name="map" value="3"
checked="checked" /> <lable>CSS</lable>
如果表集合存放的是javabean
<%
Person person1 = new Person(1,"aaa");
Person person2 = new Person(2,"bbb");
List<Person> list = new ArratList<Person>();
list.add(person1);
list.add(person2);
request.setAttribute("person",list);
%>
<!--Person里包含id和name這兩個屬性-->
<s:checkboxlist name="beans" list="#request.persons"
listKey="id" listValue="name" />
<input type="checkbox" name="beans" value="1" /> <lable>aaa</lable>
<input type="checkbox" name="beans" value="2" /> <lable>bbb</lable>
表單標簽—<s:radio> 單選框
該標簽和checkboxlist復選框相同
如果表集合存放的是javabean
<s:radio name="beans" list="#request.persons" listKey="id" listValue="name" />
<input type="radio" id="beans1" name="beans" value="1" /> <lable>aaa</lable>
<input type="radio" id="beans2" name="beans" value="2" /> <lable>bbb</lable>
如果集合為map
<s:checkboxlist name="map" list="#{1:'JAVA',2:'PHP'}"
listkey="key" listValue="vlaue" value="1" />
生成html代碼如下:
<input type="radio" id="map1" name="map" value="1"
checked="checked" /> <lable for="map1">JAVA</lable>
<input type="radio" id="map2" name="map" value="2"
/> <lable for="map2">JAVA</lable>
如果集合為list
<s:radio name="list" list="{'JAVA','PHP'}"
value={'java'} />
生成html代碼如下:
<input type="radio" name="list" value="java"
checked="checked" /> <lable>JAVA</lable>
<input type="radio" name="list" value="php"
/> <lable>PHP</lable>
表單標簽_select_下拉選擇框
如果集合為list
<s:select name="list" list="{Java,PHP}" value="Java" />
<select name="list" id="list">
<option value="Java" selected="selected">Java</option>
<option value="PHP" >PHP</option>
</select>
如果表集合為javabean
<s:select name="beans" list="#request.prosons" listKey="id" listValue="name" />
<select name="beans" id="beans">
<option value="1" >第一個</option>
<option value="2" >第二個</option>
</select>
如果集合為map
<s:checkboxlist name="map"
listkey="key" listValue="vlaue" value="1,3" />
<s:select name="map" list="#{1:'JAVA',2:'PHP',3:"CSS"}"
listKey="key" listBVlue="valie" value="1" />
<select name="map" id="map">
<option value="1" selected="selected">JAVA</option>
<option value="2" >PHP</option>
<option value="3" >CSS</option>
</select>
標簽<s:token /> 防止變淡重復提交
<s:token />標簽防止表單重復提交。用法如下:
第一步,在表單中加入<s:token />
<s:form action="hellowrold_other" method="post" namespace="/text">
<s:textfield name="person.name" />
<s:token />
<s:submit />
</s:form>
第二步:在struts.xml中配置
<action name="hellowrold_*" class="cn.soldier.action.HelloWorldAction"
method="{1}">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="token" />
<result name="invalid token">/WEB-INF/page/result.jsp</result>
</ation>
以上配置加入了 "token" 攔截器 和 "invalid token" 結果
因為"token"攔截器 在回話的token 和 請求的token 不一致時,將
直接返回 "invalid token" 結果 。