Struts2介紹
Struts2是在WebWork2基礎(chǔ)發(fā)展而來的沸停。和Struts1一樣兔甘,Struts2也屬于mvc框架摩窃。不過有一點(diǎn)大家要注意的是:盡管Struts2和Struts1名字上相差不大,但是在代碼編寫風(fēng)格上幾乎是不一樣的萄喳,那么既然有了Struts1,為何還要推出Struts2.主要是因?yàn)镾truts2有以下優(yōu)點(diǎn):
- 在軟件設(shè)計(jì)上Struts2沒有像Struts1那樣跟Servletapi和Strutsapi有緊密的耦合蹋半,Struts2的應(yīng)用可以不依賴Servlet api和struts api他巨,Struts2的這種設(shè)計(jì)屬于無侵入式設(shè)計(jì),而Struts1卻屬于侵入式設(shè)計(jì)减江。
- Struts2提供了攔截器染突,利用攔截器可以進(jìn)行aop編程,實(shí)現(xiàn)權(quán)限攔截等功能辈灼。
- Struts2提供了類型轉(zhuǎn)換器份企,我們可以把特殊的請求參數(shù)轉(zhuǎn)成需要的類型。在Struts1中巡莹,如果我們要實(shí)現(xiàn)同樣的功能司志,就必須向Struts1的底層實(shí)現(xiàn)beanutil注冊類型轉(zhuǎn)換器才行甜紫。
- Struts2提供支持多種表現(xiàn)層技術(shù),如:jsp骂远,freemarker囚霸,velocity等。
- Struts2的輸入校驗(yàn)可以對指定方法進(jìn)行校驗(yàn)激才,解決了Struts1的痛點(diǎn)拓型。
- 提供了全局范圍、包范圍和action范圍的國際化資源文件管理實(shí)現(xiàn)瘸恼。
搭建Struts2開發(fā)環(huán)境
搭建Struts2環(huán)境時(shí)劣挫,我們一般需要做以下幾個(gè)步驟的工作:
1.找到開發(fā)Struts2應(yīng)用需要使用到的jar包。
2.編寫Struts2的配置文件东帅。
3.在web.xml中加入Struts2 mvc框架啟動配置压固。
搭建Struts2開發(fā)環(huán)境-----開發(fā)Struts2應(yīng)用依賴的jar文件
可以到Struts官網(wǎng)查看那些包是必須引入的,下面的這寫有點(diǎn)版本老了冰啃。
開發(fā)struts2應(yīng)用需要依賴的jar文件在解壓目錄的lib文件夾下邓夕,不同的應(yīng)用需要的jar包是不同的,下面給出了開發(fā)Struts2程序最少需要的jar阎毅。
Struts2-core-2.x.x.jar: Struts2 框架的核心類庫
xwork-2.x.x.jar : XWork類庫焚刚,Struts2在其上構(gòu)建
ognl-2.6.x.jar:對象圖導(dǎo)航語言(object graph navigation language),Struts2框架通過其讀寫對象的屬性扇调。
freemarker-2.3.x.jar:Struts2的UI標(biāo)簽的模板使用FreeMarker編寫矿咕。
commons-logging-1.1.x.jar:ASF出品的日志包,Struts2框架使用這個(gè)日志包來支持Log4J和JDK1.4+的日志記錄狼钮。
commons-fileupload-1.2.1.jar 文件上傳組件碳柱,2.1.6版本后必須要加入此文件。
搭建Struts2開發(fā)環(huán)境—Struts2應(yīng)用的配置文件
Struts2默認(rèn)的配置文件為struts.xml熬芜,該文件需要存放在WEB-INF/classes下莲镣,該文件的配置模板如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.devMode" value="false" />
<package name="myjson" namespace="/" extends="json-default">
<action name="transfer" class="cn.itcast.action.TransferAction">
<result type="json"></result>
</action>
<action name="queryOrders" class="cn.itcast.action.QueryOrdersAction">
<result type="json"></result>
</action>
</package>
</struts>
搭建Struts2開發(fā)環(huán)境—Struts2在web中的啟動配置
在Struts1.x中,Struts框架是通過Servlet啟動的涎拉。在Struts2中瑞侮,Struts框架是通過Filter啟動的。他在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>
在StrutsPrepareAndExecuteFilter的init()方法中將會讀取類路徑下默認(rèn)的配置文件struts.xml完成初始化操作鼓拧。
注意:struts2讀取到struts.xml的內(nèi)容后半火,以javabean形式存放在內(nèi)存中,以后struts2對用戶的每次請求處理將使用內(nèi)存中的數(shù)據(jù)季俩,而不是每次都讀取struts.xml文件钮糖。
Struts.xml配置中等包介紹
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<package name="basicstruts2" namespace="/test" extends="struts-default">
<action name="index">
<result>/index.jsp</result>
</action>
</package>
</struts>
在struts2框架中使用包來管理Action,包的作用和java中的類包是非常類似的酌住,它主要用于管理一組業(yè)務(wù)功能相關(guān)的action店归,在實(shí)際應(yīng)用中阎抒,我們應(yīng)該把一組業(yè)務(wù)功能相關(guān)的action放在同一個(gè)包下。
配置包時(shí)必須指定name屬性,該屬性值可以任意取名娱节,但必須唯一挠蛉,他對應(yīng)java的類包,如果其他包要繼承該報(bào)肄满,必須通過該屬性進(jìn)行引用谴古,包的namespace屬性用于定義包的命名空間,命名空間的作為訪問該報(bào)下的action的路徑的一部分稠歉。如訪問上面的action掰担,訪問路徑為:/test/index。namespace屬性可以不設(shè)置怒炸,如果不指定該屬性带饱,默認(rèn)的命名空間為“”(空字符串)。
通常每個(gè)包都應(yīng)用繼承struts-default包阅羹,因?yàn)閟truts2很多核心的功能都是攔截器來實(shí)現(xiàn)勺疼,如:從請求中把請求參數(shù)封裝到action、文件上傳和數(shù)據(jù)驗(yàn)證等等都是通過攔截器實(shí)現(xiàn)的捏鱼。struts-default定義了這些攔截器和Result類型执庐。可以這么說:當(dāng)包繼承了struts-default才能使用struts2提供的核心功能导梆。struts-default包是在struts2-core-2.x.jar文件中的struts-default.xml中定義轨淌。struts-default.xml也是struts2默認(rèn)配置文件。struts2每次都會自動加載struts-default.xml文件看尼。
包還可以通過abstract=“true”定義為抽象包递鹉,抽象包中不能包含action。
第一個(gè)struts程序:
struts.xml配置
<package name="wgp" extends="struts-default">
<action name="helloworld" class="com.wgp.action.HelloWorldAction" method="execute" >
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
action類:
package com.wgp.action;
public class HelloWorldAction {
private String message;
public String execute(){
message = "我的第一個(gè)struts2應(yīng)用藏斩!";
return "success";
}
public String getMessage() {
return message;
}
}
jsp頁面:hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>第一個(gè)struts2應(yīng)用</title>
</head>
<body>
${message}
</body>
</html>
瀏覽器的訪問路徑:http://localhost:8080/struts2demo_war_exploded/test/helloworld
Action名稱的搜索順序
1.獲得請求路徑的URI躏结,例如url是:htttp://server/struts2/path1/path2/path3/test.action
2.首先尋找namespace為/path1/path2/path3的package,如果沒找到則執(zhí)行步驟3狰域;如果存在這個(gè)package窜觉,則在這個(gè)包中尋找對應(yīng)的action,當(dāng)在這個(gè)package下找不到action時(shí)就會直接跑到默認(rèn)namespace的package中尋找北专,默認(rèn)命名空間為空空字符串啦粹,如果默認(rèn)中還是找不到恰响,頁面提示找不到action。
3.尋找namespace為/path1/path2的package关筒,如果不存在描孟,則轉(zhuǎn)至步驟4驶睦;如果存在砰左,則在這個(gè)package中訓(xùn)中名字為test的action,當(dāng)該package尋找不到场航,則會去默認(rèn)命名空間的package中尋找action缠导,默認(rèn)空間找不到,頁面提示action找不到溉痢。
4.尋找namespace為/path1的package僻造,如果不存在就執(zhí)行步驟5;存在則在該package下尋找action孩饼,如果找不到action髓削,就去默認(rèn)空間找action,默認(rèn)空間找不到镀娶,頁面就提示該action找不到立膛。
5.尋找namespace為/的package,如果存在這個(gè)package梯码,則在該package下尋找action宝泵,如果找不到或不存在該package時(shí),去默認(rèn)空間的package里面尋找action轩娶,如果還是找不到儿奶,頁面提示找不到該action。
總結(jié):一個(gè)方位url路徑罢坝,會先去找全路徑的package廓握,如果找不到就找上一個(gè)路徑的package,依次類推嘁酿,如果都不到就會去默認(rèn)空間的package中尋找action隙券,默認(rèn)空間還是找不到,那么頁面就會提示找不到該action闹司。
Action配置中的各項(xiàng)默認(rèn)值
- 如果沒有為action指定class娱仔,默認(rèn)是ActionSupport。
- 如果沒有為action指定method游桩,默認(rèn)執(zhí)行action中的execute()方法牲迫。
- 如果沒有指定result的name屬性,默認(rèn)值為success借卧。
action中的子節(jié)點(diǎn)result配置的各種試圖轉(zhuǎn)發(fā)類型
struts2中提供了多種結(jié)果類型盹憎,常用的類型有:dispatcher(默認(rèn)配置)、redirect(重定向到某個(gè)路徑)铐刘、redirectAction(重定向到某個(gè)action)陪每、plainText。
在result中還可以使用${屬性名}表達(dá)式訪問action中的屬性,表達(dá)式里的屬性名對應(yīng)action中的屬性檩禾。如下:
<result type="redirect">/view.jsp?id=${id}</result>
下面是redirectAction結(jié)果類型的例子挂签,如果重定向的Action中同一個(gè)包下:
<result type="redirectAction" > helloworld</result>
如果重定向的action在別的命名空間下:
<result type="redirectAction">
? <param name="actionName">hello world</param>
? <param name="namespace">/test</param>
</result>
plaintext:顯示原始文件內(nèi)容,例如:當(dāng)我們需要原樣顯示jsp文件源代碼的時(shí)候盼产,我們可以使用此類型饵婆。
<result name="source" type="plainText">
? <param name="location">/xx.jsp</param>
? <param name="charSet">UTF-8</param>
</result>
定義全局消息頁面的方式:其他package繼承這個(gè)base就行了。
<package name="base" extends="struts-default">
<global-results>
<result name="message">/WEB-INF/page/message.jsp</result>
</global-results>
</package>
為action屬性注入值:
public class HelloWorldAction {
private String message;
public String execute(){
return "success";
}
public String getMessage() {
return message;
}
//為要注入的屬性設(shè)置set方法
public void setMessage(String message) {
this.message = message;
}
}
<package name="wgp" namespace="/test" extends="struts-default">
<action name="helloworld" class="com.wgp.action.HelloWorldAction" method="execute" >
<!--為action類中的屬性 設(shè)置值-->
<param name="message">這是我第一個(gè)struts2程序</param>
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
指定struts2處理的請求后綴
我們都是默認(rèn)使用的.action后綴訪問action戏售,其實(shí)默認(rèn)后綴是可以通過常量“struts.action.extension”進(jìn)行修改的侨核,例如我們可以配置struts2只處理以.do為后綴的請求路徑:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<!--如果用戶需要指定多個(gè)請求后綴,則多個(gè)后綴直接以英文逗號分隔-->
<constant name="struts.action.extension" value="do,go"/>
<struts>
細(xì)說常量定義:
常量可以定義在struts.xml或者struts.properties中配置蜈项,建議在struts.xml中配置芹关,兩種配置方式如下:
在struts.xml文件配置:
<struts>
<!--如果用戶需要指定多個(gè)請求后綴,則多個(gè)后綴直接以英文逗號分隔-->
<constant name="struts.action.extension" value="do,go"/>
<struts>
在struts.properties中配置常量
struts.action.extension=do
因?yàn)槌A靠梢栽谙旅娑鄠€(gè)配置文件中進(jìn)行定義紧卒,所以需要了解struts2加載常量的搜索順序:
struts-default.xml
struts-plugin.xml
struts.xml
struts.properties
web.xml
如果在多個(gè)文件中配置了同一個(gè)常量侥衬,則后一個(gè)文件中配置會覆蓋前面文件中配置的常量值。
常用的常量介紹
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<!--常用的常量-->
<!--指定默認(rèn)編碼集,作用于HttpServletRequest的setCharacterEncoding方法和freemarker跑芳、velocity的輸出轴总,post提交過來的數(shù)據(jù)-->
<constant name="struts.custom.i18n.resources" value="UTF-8"/>
<!--該屬性指定需要struts2處理的請求后綴,該屬性值默認(rèn)是action,及匹配*.action的請求由struts2處理博个。
如果用戶需要指定多個(gè)請求后綴怀樟,則多個(gè)后綴之間以英文逗號(,)隔開。
-->
<constant name="struts.action.extension" value="do"/>
<!--設(shè)置瀏覽器是否緩存靜態(tài)內(nèi)容盆佣,默認(rèn)值為true(生產(chǎn)環(huán)境下使用)往堡,開發(fā)階段最好關(guān)閉-->
<constant name="struts.serve.static.browserCache" value="false"/>
<!--當(dāng)struts的配置文件修改后,系統(tǒng)是否自動重新加載該文件共耍,默認(rèn)值為false(生產(chǎn)環(huán)境下使用)虑灰,開發(fā)階段最好打開-->
<constant name="struts.configuration.xml.reload" value="true"/>
<!--開發(fā)模式下使用,這樣可以打印出更詳細(xì)的錯(cuò)誤信息-->
<constant name="struts.devMode" value="true" />
<!--默認(rèn)的視圖主題-->
<constant name="struts.ui.theme" value="simple"/>
<!--與spring集成時(shí)痹兜,指定由spring負(fù)責(zé)action對象的創(chuàng)建-->
<constant name="struts.objectFactory" value="spring"/>
<!--該屬性設(shè)置struts2是否支持動態(tài)方法調(diào)用穆咐,該屬性的默認(rèn)值是true。
如果需要關(guān)閉動態(tài)方法調(diào)用字旭,則可設(shè)置屬性為false对湃。-->
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
<!--上傳文件的總大小限制-->
<constant name="struts.multipart.maxSize" value="10701096"/>
<struts>
struts2的處理流程
[圖片上傳失敗...(image-d688f5-1608134184287)]
為應(yīng)用指定多個(gè)struts配置文件
在大部分應(yīng)用里,隨著應(yīng)用規(guī)模的增加遗淳,系統(tǒng)中action的數(shù)量也會大量增加拍柒,導(dǎo)致struts.xml配置文件變得非常臃腫。為了避免struts.xml文件過于龐大屈暗、臃腫拆讯,提高struts.xml文件的可讀性剧包,我們可以將一個(gè)struts.xml配置文件分解成多個(gè)配置文件,然后中struts.xml文件中包含其他配置文件往果。下面的struts.xml通過<include>元素指定多個(gè)配置文件;
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<include file="struts-user.xml"/>
<include file="struts-order.xml"/>
<struts>
通過這種方式一铅,我們就可以將struts2的action按模塊添加在多個(gè)配置文件中陕贮。
動態(tài)方法調(diào)用和使用通配符定義action
動態(tài)方法調(diào)用有兩種方式:
1.如果action中存在多個(gè)方法時(shí),我們可以使用!+方法名調(diào)用指定方法潘飘。官方不推薦0怪!
2.使用通配符定義action
<package name="wgp" namespace="/test" extends="struts-default">
<action name="helloworld_*" class="com.wgp.action.HelloWorldAction" method="{1}" >
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
public class HelloWorldAction{
private String message;
...
public String execute()throws Exception{
this.message = "我的第一個(gè)struts2程序";
}
public String other()throws Exception{
this.message = "第二個(gè)方法";
return "success";
}
}
要訪問other()方法卜录,可以通過這樣的url方法:/test/helloworld_other.action或/test/helloworld_other戈擒;
接收請求參數(shù)
采用基本數(shù)據(jù)類型接收參數(shù)(get/post)
在action類中定義與請求參數(shù)同名的屬性,struts2便能自動接收請求參數(shù)并賦予給同名屬性艰毒。
請求路徑:http://localhost:8080/test/view.action?id=78
public class ProductAction{
private Integer id;
//struts2通過反射技術(shù)調(diào)用與請求參數(shù)同名的屬性的setter方法來設(shè)置獲取到的請求參數(shù)筐高。
public void setId(Integer id){
this.id = id;
}
public Integer getId(){
return id;
}
}
采用復(fù)合類型接收請求參數(shù)
請求路徑:http://localhost:8080/test/view.action?product.id=78
public class ProductAction{
private Product product;
public void setProduct(Product product){
this.product=product;
}
public Product getProduct(){
return product;
}
}
//struts2首先通過反射接收調(diào)用Product的默認(rèn)構(gòu)造器創(chuàng)建product對象,然后再通過發(fā)射技術(shù)調(diào)用product中與請求參數(shù)同名的屬性的setter方法來設(shè)置獲取到的請求參數(shù)丑瞧。
關(guān)于struts2.1.6接收中文請求參數(shù)亂碼問題
Struts2.1.6版本中存在一個(gè)bug柑土,接收到的中文請求參數(shù)為亂碼(post方式提交),原因是該版本在獲取并使用了請求參數(shù)后才調(diào)用HttpServletRequest的setCharacterEncoding()方法進(jìn)行編碼設(shè)置绊汹,導(dǎo)致應(yīng)用使用的就是亂碼請求參數(shù)稽屏。要在該版本中解決這個(gè)問題,我們可以這樣做:定義一個(gè)Filter過濾器西乖,把我們自定義的過濾器放在struts2的過濾器之前狐榔。
自定義類型轉(zhuǎn)換器
當(dāng)我們當(dāng)action中有個(gè)Date類型的屬性 ,需要接受一個(gè)那么當(dāng)我們客戶端傳過來的是“20181221”這樣一個(gè)字符串就會報(bào)錯(cuò)获雕,這時(shí)我們就需要自定義類型轉(zhuǎn)換器薄腻,把該字符串轉(zhuǎn)換成Date類型。
類型轉(zhuǎn)換器作用范圍分為局部類型轉(zhuǎn)換器(只作用于action)和全局類型轉(zhuǎn)換典鸡。
自定義類型轉(zhuǎn)換器:
//自定義日期轉(zhuǎn)換器
public class DateConverter extends DefaultTypeConverter {
@Override
public Object convertValue(Map<String, Object> context, Object value, Class toType) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
try {
if (toType== Date.class){//當(dāng)要轉(zhuǎn)換的類型是Date類型
//value是Request.getParameterValues();
String[] params = (String[]) value;
return dateFormat.parse(params[0]);
}else if (toType==String.class){//當(dāng)要轉(zhuǎn)換成的類型是字符串時(shí)
Date date = (Date) value;
return dateFormat.format(date);
}
}catch (ParseException e){
e.printStackTrace();
}
return null;
}
}
局部類型轉(zhuǎn)換器的配置
將上面的類型轉(zhuǎn)換器注冊為局部類型轉(zhuǎn)換器:
在action類所在的包下創(chuàng)建一個(gè)ActionClassName(action的類名)-conversion.properties文件被廓,ActionClassName是action的類名,后面的-conversion.properties是固定寫法萝玷。
在properties文件中的內(nèi)容為:
屬性名字=類型轉(zhuǎn)換器的全類名
自定義全集類型轉(zhuǎn)換器的配置
在WEB-INF/classes下放置xwork-conversion.properties文件嫁乘,在properties文件中內(nèi)容為:待轉(zhuǎn)換的類型=類型轉(zhuǎn)換器的全類名
例如:java.util.Date=com.wgp.conversion.DateConverter
訪問或添加Request、Session球碉、application屬性
ActionContext ctx = ActionContext.getContext();
ctx.getApplication().put("app","應(yīng)用范圍");//ServletContext域中
ctx.getSession().put("ses","sesssion范圍");//session域中
ctx.put("req","request范圍");//Request域中
<!--頁面中獲取域中屬性,用之前學(xué)習(xí)javaweb時(shí)的正常方式也是可以拿到的-->
${applicationScope.app}
${sesssionScope.ses}
${requestScope.req}
獲取HttpServletRequest蜓斧、HttpSession、ServletContext睁冬、HttpServletRequest對象
兩種方式:
方式一挎春,通過ServletActionContext類直接獲取
//獲取Request對象
HttpServletRequest request = ServletActionContext.getRequest();
//獲取ServletContext對象
ServletContext servletContext = ServletActionContext.getServletContext();
//獲取Session對象
request.getSession()
//獲取Response對象
HttpServletResponse response = ServletActionContext.getResponse();
方式二看疙,實(shí)現(xiàn)指定接口,有struts框架運(yùn)行時(shí)自動注入
public class HelloAction implements ServletRequestAware,ServletResponseAware,ServletContextAware{
private HttpServletRequest request;
private ServletContext servletContext;
private HttpServletResponse response;
public void setServletRequest(HttpServletRequest req){
this.request=req;
}
public void setServletResponse(HttpServletResponse resp){
this.response=resp;
}
public void setServletContext(ServletContext ser){
this.servletContext=ser;
}
}
文件上傳
第一步:在WEB-INF/lib下加入commons-fileupload.jar直奋、commons-io.jar能庆。這兩個(gè)包可以去Apache官網(wǎng)下載。
第二步:把form表單的enctype設(shè)置為:“multipart/form-data”
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件上傳</title>
</head>
<body>
<form enctype="multipart/form-data" action="${pageContext.request.contextPath}/test/fileUpload" method="post">
文件:<input type="file" name="uploadImage"><br>
<input type="submit" value="上傳">
</form>
</body>
</html>
第三步:在action類中添加以下屬性脚线,屬性部分對應(yīng)表單中文件字段的名稱
public class FileUploadAction {
//得到上傳的文件
/**
* 該字段名稱必須和頁面上傳的文件的參數(shù)名稱一致搁胆,這樣struts框架就會自動接收該文件
* 必須設(shè)置該字段的setter方法,
*/
private File uploadImage;
//得到文件的類型
/**
* 上傳文件的類型邮绿,struts框架會自動幫我們獲取文件的MIME類型渠旁,
* 該字段的命名規(guī)則是 "提交的文件字段名+ContentType" 固定寫法,必須設(shè)置該字段的setter方法
*/
private String uploadImageContentType;
//得到文件的名稱
/**
* 上傳文件的文件名船逮,帶后綴的文件名顾腊。
* 該字段struts框架會幫我們字段獲取并設(shè)置上信息,該字段也必須設(shè)置setter方法
* 該字段的命名規(guī)則是 "提交的文件字段名+FileName" 固定寫法
*/
private String uploadImageFileName;
public File getUploadImage() {
return uploadImage;
}
public void setUploadImage(File uploadImage) {
this.uploadImage = uploadImage;
}
public String getUploadImageContentType() {
return uploadImageContentType;
}
public void setUploadImageContentType(String uploadImageContentType) {
System.out.println(uploadImageContentType);
this.uploadImageContentType = uploadImageContentType;
}
public String getUploadImageFileName() {
return uploadImageFileName;
}
public void setUploadImageFileName(String uploadImageFileName) {
System.out.println(uploadImageFileName);
this.uploadImageFileName = uploadImageFileName;
}
public String upload()throws Exception{
ServletContext servletContext = ServletActionContext.getServletContext();
String realPath = servletContext.getRealPath("/images");
File file = new File(realPath);
if (!file.exists()){//如果該目錄不存在就創(chuàng)建
file.mkdirs();
}
FileUtils.copyFile(uploadImage,new File(file,uploadImageFileName));
return "success";
}
}
多文件上傳:對應(yīng)的字段改成數(shù)組類型或集合類型就可以自動接收多文件上傳
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>多文件上傳</title>
</head>
<body>
<form enctype="multipart/form-data" action="${pageContext.request.contextPath}/test/fileUpload" method="post">
文件1:<input type="file" name="uploadImage"><br>
文件2:<input type="file" name="uploadImage"><br>
文件3:<input type="file" name="uploadImage"><br>
<input type="submit" value="上傳">
</form>
</body>
</html>
public class FileUploadAction {
//得到上傳的文件
/**
* 該字段名稱必須和頁面上傳的文件的參數(shù)名稱一致挖胃,這樣struts框架就會自動接收該文件
* 必須設(shè)置該字段的setter方法杂靶,
*/
private File[] uploadImage;
//得到文件的類型
/**
* 上傳文件的類型,struts框架會自動幫我們獲取文件的MIME類型冠骄,
* 該字段的命名規(guī)則是 "提交的文件字段名+ContentType" 固定寫法伪煤,必須設(shè)置該字段的setter方法
*/
private String[] uploadImageContentType;
//得到文件的名稱
/**
* 上傳文件的文件名,帶后綴的文件名凛辣。
* 該字段struts框架會幫我們字段獲取并設(shè)置上信息抱既,該字段也必須設(shè)置setter方法
* 該字段的命名規(guī)則是 "提交的文件字段名+FileName" 固定寫法
*/
private String[] uploadImageFileName;
public File[] getUploadImage() {
return uploadImage;
}
public void setUploadImage(File[] uploadImage) {
this.uploadImage = uploadImage;
}
public String[] getUploadImageContentType() {
return uploadImageContentType;
}
public void setUploadImageContentType(String[] uploadImageContentType) {
this.uploadImageContentType = uploadImageContentType;
}
public String[] getUploadImageFileName() {
return uploadImageFileName;
}
public void setUploadImageFileName(String[] uploadImageFileName) {
this.uploadImageFileName = uploadImageFileName;
}
public String upload()throws Exception{
ServletContext servletContext = ServletActionContext.getServletContext();
String realPath = servletContext.getRealPath("/images");
File file = new File(realPath);
if (!file.exists()){//如果該目錄不存在就創(chuàng)建
file.mkdirs();
}
for (int i = 0; i < uploadImage.length; i++) {
FileUtils.copyFile(uploadImage[i],new File(file,uploadImageFileName[i]));
}
return "success";
}
}
自定義攔截器
我們可以用攔截器去進(jìn)行權(quán)限攔截等功能。
要自定義攔截器需要實(shí)現(xiàn)com.opensymphony.xwork2.interceptor.Interceptor這個(gè)接口扁誓。
示例:這個(gè)種方式把struts自帶的攔截器都給屏蔽了防泵,這樣不好,我們還需要使用到struts自帶的核心功能
public class PermissionInterceptor implements Interceptor {
@Override
public void destroy() {
}
@Override
public void init() {
}
@Override
public String intercept(ActionInvocation actionInvocation) throws Exception {
//在該方法中做攔截處理
Map<String, Object> session = ActionContext.getContext().getSession();
Object user = session.get("user");
if (user!=null){
//actionInvocation.invoke() 這個(gè)方法就是調(diào)用的action中的用戶調(diào)用的方法蝗敢,我猜是用動態(tài)代理搞的
String invoke = actionInvocation.invoke();//放行 用戶調(diào)用的方法
return invoke;//吧方法返回的字符串返回
}
return "failed";//用戶沒有權(quán)限
}
}
<package name="wgp2" namespace="/test" extends="struts-default">
<!--攔截器配置-->
<interceptors>
<interceptor name="permission" class="com.wgp.action.PermissionInterceptor"/>
</interceptors>
<action name="fileUpload" class="com.wgp.action.FileUploadAction" method="upload">
<!--為這個(gè)action配置攔截器-->
<interceptor-ref name="permission"></interceptor-ref>
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
struts本身的默認(rèn)的攔截和我們自定義的攔截器都使用上:
示例:
只需要更改配置struts配置文件
<package name="wgp2" namespace="/test" extends="struts-default">
<!--攔截器配置-->
<interceptors>
<!--配置自定義的攔截器-->
<interceptor name="permission" class="com.wgp.action.PermissionInterceptor"/>
<!--第定義一個(gè)攔截器棧-->
<interceptor-stack name="permissionStack">
<!--添加struts的默認(rèn)攔截器捷泞,這行代碼要在自己的自定義的攔截器之前-->
<interceptor-ref name="defaultStack"/>
<!--添加自己定義的攔截器-->
<interceptor-ref name="permission"/>
</interceptor-stack>
</interceptors>
<action name="fileUpload" class="com.wgp.action.FileUploadAction" method="upload">
<!--為這個(gè)action配置攔截器,引用攔截器棧-->
<interceptor-ref name="permissionStack"/>
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
當(dāng)我自定義攔截器的時(shí)候寿谴,在struts的配置文件中配置了我們的自定義的攔截器锁右,那么struts框架的默認(rèn)攔截器就不會再起作用了,我們需要顯示的在配置文件添加上struts框架的默認(rèn)攔截器讶泰,因?yàn)閟truts2中如文件上傳咏瑟、數(shù)據(jù)驗(yàn)證、獲取客戶端提交的參數(shù)痪署、國際化等码泞,都是struts2的默認(rèn)攔截器幫我們做的,如果我們不引用就不能用這些功能了狼犯。系統(tǒng)默認(rèn)的攔截的名字是defaultStack余寥。
如果希望包下的所有action都使用自定義攔截器领铐,可以通過<default-interceptor-ref name="permissionStack"/>把攔截器定義為默認(rèn)攔截器。注意:每個(gè)包智能指定一個(gè)默認(rèn)攔截器宋舷,另外绪撵,一旦我們?yōu)樵摪械哪硞€(gè)action顯式指定了某個(gè)攔截器,則默認(rèn)攔截器不會起作用了祝蝠。如果即想讓默認(rèn)攔截器permissionStack起作用 還要給action配置另一個(gè)攔截器莲兢,那么可以在action標(biāo)簽內(nèi)配置多個(gè)攔截器標(biāo)簽指定。
輸入校驗(yàn)
在struts2中续膳,我們可以實(shí)現(xiàn)對aciton的所有方法進(jìn)行校驗(yàn)或者action的指定方法進(jìn)行校驗(yàn)。
對于輸入校驗(yàn)收班,struts2提供了兩種實(shí)現(xiàn)方法:
1.采用手工編寫代碼實(shí)現(xiàn)坟岔。
2.基于xml配置方式實(shí)現(xiàn)。
手工代碼實(shí)現(xiàn)方式
通過重新validate()方法實(shí)現(xiàn)摔桦,validate()方法會校驗(yàn)action中所有與execute方法簽名相同的方法社付。當(dāng)狗哥數(shù)據(jù)校驗(yàn)失敗時(shí),我們應(yīng)該調(diào)用addFieldError()方法往系統(tǒng)的fieldErrors添加校驗(yàn)失敗信息(為了使用addFieldError()方法邻耕,action可以繼承ActionSupport)鸥咖,如果系統(tǒng)的fieldErrors包含失敗信息,struts2會將請求轉(zhuǎn)發(fā)到名為input的result兄世。在input試圖中可以通過< s:fielderror />顯示失敗信息啼辣。
示例:該示例中對action中的所有方法都會執(zhí)行校驗(yàn)
//繼承ActionSupport類
public class PersonAction extends ActionSupport {
private String username;
private String mobile;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String update(){
ActionContext.getContext().put("message","更新成功");
return "success";
}
public String save(){
ActionContext.getContext().put("message","保存成功");
return "success";
}
/**
*這個(gè)方法進(jìn)行自動校驗(yàn)的,我們在復(fù)寫這個(gè)方法來進(jìn)行判斷
*/
@Override
public void validate() {
super.validate();
if (this.username==null || "".equals(username.trim())){
addFieldError("username","用戶名不能空御滩!");
}
if (this.mobile==null || "".equals(mobile.trim())){
addFieldError("mobile","手機(jī)號不能為空鸥拧!");
}else {
if (!Pattern.compile("^1[358]\\d{9}$").matcher(mobile).matches()){
addFieldError("mobile","手機(jī)號格式不對!");
}
}
}
}
<package name="wgp" namespace="/test" extends="struts-default">
<action name="person_*" class="com.wgp.action.PersonAction" method="{1}" >
<!--驗(yàn)證失敗后削解,請求轉(zhuǎn)發(fā)到input試圖-->
<result name="input">/user.jsp</result>
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>輸入校驗(yàn)</title>
</head>
<body>
<%-- 在該頁面中使用<s:fielderror /> 標(biāo)簽來顯示失敗信息--%>
<s:fielderror />
<form action="${pageContext.request.contextPath}/test/person_save" method="post" >
用戶名:<input type="text" name="username"><br>
手機(jī)號:<input type="text" name="mobile"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
對action指定方法校驗(yàn)
通過validateXxx()方法實(shí)現(xiàn)富弦,validateXxx()方法只會校驗(yàn)action中的方法名為xxx的方法。其中Xxx第一個(gè)字母要大寫氛驮,當(dāng)某個(gè)數(shù)據(jù)校驗(yàn)失敗時(shí)腕柜,我們應(yīng)該調(diào)用addFieldError()方法往系統(tǒng)的fieldErrors添加校驗(yàn)失敗信息,如果系統(tǒng)的fieldError包含了失敗信息矫废,struts2會將請求轉(zhuǎn)發(fā)到名為input的result標(biāo)簽盏缤。在input視圖中可以通過< s:fielderror />顯示失敗信息。
示例:如上面的validate方法改成validateUpdate()磷脯,那么就會只對訪問update()方法時(shí)才會進(jìn)行校驗(yàn)蛾找。
輸入校驗(yàn)的流程
- 類型轉(zhuǎn)換器對請求參數(shù)執(zhí)行類型轉(zhuǎn)換,并把轉(zhuǎn)換后的值賦給action中的屬性赵誓。
- 如果在執(zhí)行類型轉(zhuǎn)換的過程中出現(xiàn)了異常打毛,系統(tǒng)會將異常信息保存到ActionContext柿赊,conversionError攔截器將異常信息添加到fieldErrors里,不管類型轉(zhuǎn)換是否出現(xiàn)異常幻枉,都會進(jìn)入第3步
- 系統(tǒng)通過發(fā)射技術(shù)先調(diào)用action中的validateXxx()方法碰声,Xxx為方法名。
- 再調(diào)用action中的validate()方法熬甫。
- 經(jīng)過上面4步胰挑,如果系統(tǒng)中的fieldErrors存在錯(cuò)誤信息,系統(tǒng)會自定將請求轉(zhuǎn)發(fā)至名稱為input視圖椿肩。如果系統(tǒng)中的fieldErros沒有任何錯(cuò)誤信息瞻颂,系統(tǒng)將執(zhí)行action中的處理方法。
XML配置方式實(shí)現(xiàn)action的所有方法進(jìn)行輸入校驗(yàn)
使用xml配置方式實(shí)現(xiàn)輸入校驗(yàn)時(shí)郑象,Action也需要繼承ActionSupport贡这,并且提供校驗(yàn)文件,校驗(yàn)文件和action類放在同一個(gè)包下厂榛,文件的取名格式為:ActionClassName-validation.xml盖矫,其中ActionClassName為action的簡單類名,-validation為固定寫法击奶。如果action類為com.wgp.UserAction辈双,那么該文件的取名應(yīng)為:UserAction-validation.xml。
下面是校驗(yàn)文件的模板:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<validators>
<field name="username">
<field-validator type="requiredstring">
<!--高版本不用配置該屬性 默認(rèn)就是去除空格的-->
<param name="trim">true</param>
<message>用戶名不能為空柜砾!</message>
</field-validator>
<field-validator type="regex">
<!--這個(gè)regexExpression 其實(shí)就是regex這個(gè)自帶標(biāo)簽對應(yīng)的類的屬性-->
<param name="regexExpression"><![CDATA[^1[358]\d{9}$]]></param>
<message>手機(jī)格式不正確湃望!</message>
</field-validator>
</field>
</validators>
< field>指定action中要校驗(yàn)的屬性,<field-validator>指定校驗(yàn)器痰驱,上面指定的校驗(yàn)器requiredstring是由系統(tǒng)提供的喜爷,系統(tǒng)提供了能滿足大部分校驗(yàn)需求的校驗(yàn)器,這些校驗(yàn)器的定義可以在xwork-2.x.jar中com.opensymphony.xwork2.validator.validators下的default.xml中找到萄唇。高版本的比如struts2.5版本的中吧xwork2包集成到了struts2-core-2.5.18.jar包中檩帐。
<message>為校驗(yàn)失敗后的提示信息,如果需要國際化另萤,可以為message指定key屬性湃密,key的值為資源文件中的key。
在這個(gè)校驗(yàn)文件中四敞,對action中字符串類型的username屬性進(jìn)行驗(yàn)證泛源,首先要求調(diào)用trim()方法去掉空格,然后判斷用戶名是否為空忿危。
[圖片上傳失敗...(image-96e0c6-1608134184287)]
基于XML配置方式對action類中指定的方法實(shí)現(xiàn)輸入校驗(yàn)
如果想要對action中的某個(gè)action方法實(shí)施校驗(yàn)达箍,那么,校驗(yàn)文件的取名應(yīng)為:ActionClassName-ActionName-validation.xml,其中ActionName為struts.xml中action的名稱铺厨。
示例:
<action name="person_*" class="com.wgp.action.PersonAction" method="{1}" >
<result name="input">/user.jsp</result>
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
PersonAction中有以下兩個(gè)方法:
public String add(){}
Public String update(){}
要對add()方法實(shí)施驗(yàn)證缎玫,校驗(yàn)文件的取名為:UserAction-person_add-validation.xml
要對update()方法實(shí)施驗(yàn)證硬纤,校驗(yàn)文件取名為:UserAction-person_update-validation.xml
基于XML校驗(yàn)的一些特點(diǎn)
當(dāng)為某個(gè)action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml兩種規(guī)則的校驗(yàn)文件時(shí),系統(tǒng)按下面順序?qū)ふ倚r?yàn)文件:
先找ActionClassName-validation.xml 赃磨,然后再找ActionClassName-ActionName-validation.xml筝家。
系統(tǒng)找到第一個(gè)校驗(yàn)文件時(shí)會繼續(xù)找后面的校驗(yàn)文件,當(dāng)搜索到所有校驗(yàn)文件時(shí)邻辉,會吧校驗(yàn)文件里的所有校驗(yàn)規(guī)則匯總溪王,然后全部應(yīng)用于action方法的校驗(yàn)。如果兩個(gè)校驗(yàn)文件中指定的校驗(yàn)規(guī)則沖突值骇,則只使用后面文件的校驗(yàn)規(guī)則莹菱。
當(dāng)action繼承了另一個(gè)action,父類action的校驗(yàn)文件會先被搜索到吱瘩。也遵循以上規(guī)則芒珠,也就是說把父類的校驗(yàn)文件和子類的校驗(yàn)文件匯總,然后應(yīng)用action的方法的校驗(yàn)搅裙。有沖突的后面的覆蓋前面的。
國際化
Struts2 提供了全局范圍的裹芝、包范圍的部逮、action范圍的國際化文件。
準(zhǔn)備資源文件嫂易,資源文件的命名格式如下:
baseName_language_country.properties
baseName_language.properties
baseName.properties
其中baseName是資源文件的基本名兄朋,我們可以自定義,但language和country必須是java支持的語言和國家怜械。
如: 中國大陸:baseName_zh_CN.properties 美國:baseName_en_US.properties
現(xiàn)在為應(yīng)用添加兩個(gè)資源文件:
第一個(gè)存放中文:wgp_zh_CN.properties 內(nèi)容為:welcome=歡迎
第二個(gè)放英語(美國):wgp_en_US.properties 內(nèi)容為:welcome=welcome
對于中文的屬性文件颅和,我們編寫好后,應(yīng)該使用jdk提供的native2ascii命名把文件轉(zhuǎn)換為unicode編碼的文件缕允。命令的使用方式如下:native2ascii 源文件.properties 目標(biāo)文件.properties
這些屬性文件放在和struts.xml同目錄下峡扩。
配置全局資源與輸出國際化信息
當(dāng)準(zhǔn)備好資源文件后,我們可以在struts.xml中通過<constant name="struts.custom.i18n.resources" value="wgp"/> wgp為資源文件的基本名障本。
后面我們就可以在頁面或action中訪問國際化信息:
- 在jsp頁面中使用<s:text name="" />標(biāo)簽輸出國際化信息:name為資源文件中的key
- 在action類中教届,可以繼承ActionSupport,使用getText()方法得到國際化信息驾霜,該方法的第一個(gè)參數(shù)用于指定資源文件中的key案训。
- 在表單標(biāo)簽中,通過key屬性指定資源文件中的key粪糙,如:<s:textfield name="realname" key="user"/>
國際化:輸出帶有占位符的國際化信息
資源文件中的內(nèi)容如下:welcome={0}强霎,歡迎來到中國{1}
在jsp頁面中輸出帶有占位符的國際化信息
<s:text name="welcome">
? < s:param> < s:property value="realname" /> </ s:param>
? < s:param> 學(xué)習(xí)</ s:param>
</s:text >
在action類中獲取帶占位符的國際化信息,可以使用getText(key,String[] args)或getText(String aTextName,List args)蓉冈。
國際化:包范圍資源文件
在一個(gè)大型項(xiàng)目中城舞,整個(gè)應(yīng)用有大量的內(nèi)容需要實(shí)現(xiàn)國際化轩触,如果我們把國際化的內(nèi)容都放在全局的資源屬性文件中,顯然會導(dǎo)致資源文件過于龐大臃腫椿争,不便于維護(hù)怕膛,這個(gè)時(shí)候我們可以只對不同模塊,使用包范圍來組織國際化文件秦踪。
方法如下:
在java的包下放置packege_language_country.properties資源文件褐捻,package為固定寫法,處于該包及子包下的action都可以訪問該資源文件椅邓。當(dāng)炒作指定key的消息時(shí)柠逞,系統(tǒng)會先從package資源文件中查找,當(dāng)找不到對應(yīng)的key時(shí)景馁,才會從常量<constant name="struts.custom.i18n.resources" value="wgp"/>指定的資源文件中查找板壮。
國際化:Action范圍的資源文件
我們可以為某個(gè)action單獨(dú)指定資源文件,方法如下:在action類所在的路徑合住,放置ActionClassName_language_country.properties資源文件绰精,ActionClassName為action類的簡單名稱。
當(dāng)查找指定key消息時(shí)透葛,系統(tǒng)會先從ActionClassName_language_country.properties資源文件中查找笨使,如果沒有找到對應(yīng)的key,然后沿著當(dāng)前包往上炒作基本名為package的資源文件僚害,一直找到最頂層包硫椰。如果還沒有找到對應(yīng)的key,最后從
<constant name="struts.custom.i18n.resources" value="wgp"/>指定的資源文件中尋找萨蚕。
OGNL表達(dá)式語言
ognl是Object graphic Navigation Language(對象圖導(dǎo)航語言)的縮寫靶草,它是一個(gè)開源項(xiàng)目。Struts2框架使用ognl作為默認(rèn)的表達(dá)式語言岳遥。
對于el表達(dá)式奕翔,ognl提供了平時(shí)我們需要的一些功能更,如:
- 支持對象方法調(diào)用浩蓉,如 xxx.sayHello();
- 支持類靜態(tài)方法調(diào)用和值訪問糠悯,表達(dá)式的格式為:@[類全名(包括包路徑)] @[方法名 | 值名],例如:@java.lang.String@format('foo %s','bar')或@com.wgp.Constant@APP_NAME;
- 操作集合對象妻往。
OGNL有一個(gè)上下文(Context)概念互艾,說白了上下文就是一個(gè)MAP結(jié)構(gòu),它實(shí)現(xiàn)了java.utils.Map接口讯泣,在Struts2中上下文(Context)的實(shí)現(xiàn)為ActionContext纫普,下面是上下文的結(jié)構(gòu)示意圖
[圖片上傳失敗...(image-3c6ee2-1608134184288)]
訪問上下文中的對象需要使用#符號標(biāo)注命名空間,如#application、#session昨稼。
另外ognl會設(shè)定一個(gè)根對象(root對象)节视,在Struts2中根對象就是ValueStack(值棧),如果要訪問跟對象也就是值棧中的對象的屬性假栓,則可以省略#命名空間寻行,直接訪問該對象的屬性即可。
在Struts2中匾荆,根對象ValueStack的實(shí)現(xiàn)類為OgnlValueStack拌蜘,該對象不是我們想象的只存放單個(gè)值,而是存放一組對象牙丽。在OgnlValueStack類里面有一個(gè)List類型的root變量简卧,就是使用他存放一組對象。
在root變量中處于第一位的對象叫棧頂對象烤芦。通常我們在ognl表達(dá)式里直接寫上屬性的名稱即可訪問root變量里對象的屬性举娩,搜索順序是從棧頂對象開始尋找,如果棧頂對象不存在該屬性构罗,就會從第二個(gè)對象尋找铜涉,如果沒有找到就從第三個(gè)對象尋找,依次往下訪問遂唧,直到找到為止芙代。
注意:在Struts2中,ognl表達(dá)式需要配合Struts標(biāo)簽才可以使用蠢箩。如:<s:property value="name" />、<s:property value="#request.user" />,獲取request域?qū)ο笾衭ser屬性
由于ValueStack是Struts2中ognl的根對象事甜,如果用戶需要范圍值棧中的對象谬泌,在jsp頁面中可以直接通過下面的el表達(dá)式訪問ValueStack中的對象的屬性:${foo} //獲得值棧中某個(gè)對象的foo屬性
如果訪問其他的Context中的對象,由于他們不是根對象逻谦,所以在訪問時(shí)掌实,需要添加#前綴。
- application對象:用于訪問ServletContext邦马,例如#application.username或者#application['username'],相當(dāng)于調(diào)用了ServletContext的getAttribute("username")贱鼻。
- session對象:用來訪問HttpSession,例如#session.username 或者#session['username'],相當(dāng)于調(diào)用了session.getAttribute("username")滋将。
- request對象:用來訪問HttpServletRequest屬性(attribute)的map邻悬,例如#request.username或者#request['username'],相當(dāng)于調(diào)用了request.getAttribute("username")随闽。
- parameters對象:用于訪問Http的請求參數(shù)父丰,例如#parameters.username或者#parameters['username'],相當(dāng)于調(diào)用了request.getParameter("username")。
- attr對象:用于按page->request->session->application順序訪問期屬性掘宪。
之前我們寫的action類里面的屬性生成setter和getter方法后蛾扇,jsp頁面可以用el表達(dá)獲取到屬性值呢攘烛?,我們知道el表達(dá)式是獲取域?qū)ο笾械膶傩缘?{name}就相當(dāng)于域?qū)ο笳{(diào)用getAttribute("name")镀首,那么為什么Struts框架中的action中屬性也可以被el表達(dá)式獲取呢坟漱,是因?yàn)閍ction是被放到valueStack中的,而Struts框架對HttpServletRequest進(jìn)行了進(jìn)一步的封裝更哄,
為什么使用EL表達(dá)式能夠范圍valueStack中的對象屬性呢芋齿?
[圖片上傳失敗...(image-e67666-1608134184288)]
采用ONGL表達(dá)式創(chuàng)建List/Map集合對象
如果需要一個(gè)集合元素的時(shí)候,我們可以使用ognl中同集合相關(guān)的表達(dá)式竖瘾。使用如下代碼直接生產(chǎn)一個(gè)List對象:
<s:set name="list" value="{'zhang','wang','li'}" />
<s:iterator value="#list">
? <s:property />
</s:iterator >
Set標(biāo)簽用于將某個(gè)值放入指定范圍沟突。
scope:指定變量被放置的范圍,該屬性可以接受application捕传、session惠拭、request、page或action庸论。如果沒有指定該屬性职辅,則默認(rèn)放置在OGNLContext中。
value:賦給變量的聂示,如果沒有設(shè)置該屬性域携,則將ValueStack棧頂?shù)闹蒂x值給變量。
生成一個(gè)Map對象:
<s:set name="foobar" value="#{'foo1':'bar1','foo2':'bar2'}" />
<s:iterator value="#foobar">
? <s:property value="key" /> = <s:property value="value"/>
</s:iterator >
property標(biāo)簽
property標(biāo)簽用于輸出指定值:
<s:set name="name" value="kk" />
<s:property value="#name" />
default:可選屬性鱼喉,如果需要輸出的屬性值為null秀鞭,則顯示屬性指定的默認(rèn)值。
escape:可選屬性扛禽,指定是否格式化HTML代碼锋边。
value:可選屬性,指定需要輸出的屬性值编曼,如果沒有指定該屬性豆巨,則默認(rèn)輸出ValueStack棧頂?shù)闹怠?/p>
id: 可選屬性,指定該元素的標(biāo)識掐场。
采用OGNL表達(dá)式判斷對象是否存在集合中
對于集合類型往扔,ognl表達(dá)式可以使用in和not in 連個(gè)元素符號。其中熊户,in表達(dá)式用來判斷某個(gè)顏色是否在指定的集合對象中萍膛;not in判斷某個(gè)元素是否不在指定的集合對象中,如下所示:
in表達(dá)式:
<s:if test=" 'foo' in {'foo','bar'} ">
? 在
</s:if >
<s:else >
? 不在
</ s:else>
not in 表達(dá)式:
<s:if test=" 'foo' not in {'foo','bar'} ">
? 不在
</s:if >
<s:else >
? 在
</ s:else>
OGNL表達(dá)式的投影功
除了in和not in之外嚷堡,ognl還允許使用某個(gè)規(guī)則獲得集合對象的子集卦羡,常用的有一下3個(gè)相關(guān)操作符。
?: 獲得所有符號邏輯的元素
^: 獲得符合路徑的第一個(gè)元素
$: 獲得符合邏輯的最后一個(gè)元素
例如代碼:
<s:iterator value="books.{?#this.price >35}">
? <s:property value="title" /> -$<s:property value="price" />
</ s:iterator>
在上面代碼中,直接在集合后面緊跟.{}運(yùn)算符表明取出該集合的子集绿饵,{}內(nèi)的表達(dá)式用于獲取符合添加的元素欠肾,this指的是為了從大集合books篩選數(shù)據(jù)到小集合,需要對大集合books進(jìn)行迭代拟赊,this代表當(dāng)前迭代的元素刺桃。本例的表達(dá)式用于獲取集合中價(jià)格大于35的書集合。
public class BookAction extends ActionSupport{
private List<Book> books
//省略 books的setter和getter方法
public String excute(){
books = new LinkedList<Book>();
books.add(new Book("A123","spring"),67)
books.add(new Book("A456","ejb3.0"),15)
}
}
OGNL表達(dá)式使用比較少吸祟,我們完全可以使用jstl和el表達(dá)式想以前學(xué)習(xí)javaweb一樣瑟慈,
Struts2常用標(biāo)簽
iterator標(biāo)簽用于對集合進(jìn)行迭代,包含list、set和數(shù)組。
value:可選屬性尊浓,指定被迭代的集合,如果沒有設(shè)置該屬性进泼,則使用ValueStack棧頂?shù)募稀?/p>
id:可選屬性,指定集合里元素的id(已過時(shí))
status:可選屬性纤虽,該屬性指定迭代是的IteratorStatus實(shí)例乳绕,該實(shí)例包含如下幾個(gè)方法:
? int getCount() :返回當(dāng)前迭代了幾個(gè)元素
? int getIndex(): 返回當(dāng)前迭代元素的索引
? boolean isEven(): 返回當(dāng)前被迭代元素的索引是否是偶數(shù)
? boolean isOdd(): 返回當(dāng)前被迭代元素的索引是否是奇數(shù)
? boolean isFirst(): 返回當(dāng)前被迭代元素是否是第一個(gè)元素
? boolean isLast(): 返回當(dāng)前被迭代元素是否是最后一個(gè)元素
url標(biāo)簽
<s:url action="hello_add" namspace="/test" >
? <s:param name="personid" value="23"/>
</ s:url>
生成的路徑是/struts/test/hello_add.action?personid=23
當(dāng)標(biāo)簽的屬性值作為字符串類型處理時(shí),”%“符號的用途是計(jì)算ognl表達(dá)式的值逼纸。
<s:set name="myurl" value=" 'http://www.baidu.com'" />
<s:url value="#myurl" /> 輸出結(jié)果是:#myurl
<s:url value="%{#myurl}" /> 輸出結(jié)果是:http://www.baidu.com
表單標(biāo)簽—chackboxlist復(fù)選框
如果集合為list
<s:checkboxlist name="list" list="{'java','.net','php'}" value="{'java','.net'}" />
上面這句代碼會生成如下HTML代碼:
<input type="checkbox" name="list" value="java" checked="checked" /><label>java</label>
<input type="checkbox" name="list" value=".net" checked="checked" /><label>.net</label>
<input type="checkbox" name="list" value="php" /><label>php</label>
如果集合是Map
那么<s:checkboxlist name="map" list="#{1:'瑜伽用品', 2:'戶外用品', 3:'球類',4:'自行車'}" listKey="key" listValue="value" value="{1,2,3}" />
生成的html代碼如下:
<input type="checkbox" name="map" value="1" checked="checked" /><label>瑜伽用品</label>
<input type="checkbox" name="map" value="2" checked="checked" /><label>戶外用品</label>
<input type="checkbox" name="map" value="3" checked="checked" /><label>球類</label>
<input type="checkbox" name="map" value="4" /><label>自行車</label>
如果集合里存放的是javabean
<%
? Person p1 = new Person(1,"第一個(gè)")洋措;
? Person p2 = new Person(2,"第二個(gè)");
? list.add(p1);
? list.add(p2);
? request.setAttribute("persons",list)杰刽;
%>
那么<s:checkboxlist name="beans" list="#request.persons" listKey="personid" listValue="name" />
personid和name為Person的屬性
會生成如下html代碼:
<input type="checkbox" name="beans" value="1" /><label>第一個(gè)</label>
<input type="checkbox" name="beans" value="2" /><label>第二個(gè)</label>
表單標(biāo)簽—radio單選框
該標(biāo)簽的使用和CheckBoxlist復(fù)選框相同菠发。
表單標(biāo)簽-select下拉框
<s:select name="list" list="{'java','.net'}" value="java" />
生成的html頁面代碼:
<select name="list" id ="list">
<option value="java" selected="selected">java</option>
<option value=".net" >.net</option>
</select>
如果集合里是javabean數(shù)據(jù)或map數(shù)據(jù) 都是類似的。
防止表單重復(fù)提交<s:token />
<s:token />標(biāo)簽防止重復(fù)提交贺嫂,用法如下:
第一步:在表單中加入<s:token />
<s:form action="hello_add" method="post" namespace="/test">
? <s:textfield name="person.name"/>
? <s:token />
? <s:submit />
</s:form >
第二步:
<action name="hello_*" class="com.wgp.HelloAction" method="{1}">
? <interceptor-ref name="defaultStack" />
? <interceptort-ref name="token" />
? <result name="invalid.token">/WEB-INF/page/message.jsp</result>
? <result>/WEB-INF/page/result.jsp</result>
</ation>
以上配置加入了token攔截器和 invalid.token結(jié)果滓鸠,因?yàn)閠oken攔截器在會話的token與請求的token不一致時(shí),將會直接返回invalid.token結(jié)果涝婉。
在debug狀態(tài)哥力,控制臺出現(xiàn)錯(cuò)誤信息蔗怠,因?yàn)锳ction中并沒有Struts.token和Struts.token.name屬性墩弯,我們不用關(guān)心這個(gè)錯(cuò)誤。
Struts2+Spring+Hibernate整合
先集成spring寞射,再集成hibernate渔工,最后Struts。