2020-12-16

Struts2介紹

Struts2是在WebWork2基礎(chǔ)發(fā)展而來的沸停。和Struts1一樣兔甘,Struts2也屬于mvc框架摩窃。不過有一點(diǎn)大家要注意的是:盡管Struts2和Struts1名字上相差不大,但是在代碼編寫風(fēng)格上幾乎是不一樣的萄喳,那么既然有了Struts1,為何還要推出Struts2.主要是因?yàn)镾truts2有以下優(yōu)點(diǎn):

  1. 在軟件設(shè)計(jì)上Struts2沒有像Struts1那樣跟Servletapi和Strutsapi有緊密的耦合蹋半,Struts2的應(yīng)用可以不依賴Servlet api和struts api他巨,Struts2的這種設(shè)計(jì)屬于無侵入式設(shè)計(jì),而Struts1卻屬于侵入式設(shè)計(jì)减江。
  2. Struts2提供了攔截器染突,利用攔截器可以進(jìn)行aop編程,實(shí)現(xiàn)權(quán)限攔截等功能辈灼。
  3. Struts2提供了類型轉(zhuǎn)換器份企,我們可以把特殊的請求參數(shù)轉(zhuǎn)成需要的類型。在Struts1中巡莹,如果我們要實(shí)現(xiàn)同樣的功能司志,就必須向Struts1的底層實(shí)現(xiàn)beanutil注冊類型轉(zhuǎn)換器才行甜紫。
  4. Struts2提供支持多種表現(xiàn)層技術(shù),如:jsp骂远,freemarker囚霸,velocity等。
  5. Struts2的輸入校驗(yàn)可以對指定方法進(jìn)行校驗(yàn)激才,解決了Struts1的痛點(diǎn)拓型。
  6. 提供了全局范圍、包范圍和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)值

  1. 如果沒有為action指定class娱仔,默認(rèn)是ActionSupport。
  2. 如果沒有為action指定method游桩,默認(rèn)執(zhí)行action中的execute()方法牲迫。
  3. 如果沒有指定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)的流程

  1. 類型轉(zhuǎn)換器對請求參數(shù)執(zhí)行類型轉(zhuǎn)換,并把轉(zhuǎn)換后的值賦給action中的屬性赵誓。
  2. 如果在執(zhí)行類型轉(zhuǎn)換的過程中出現(xiàn)了異常打毛,系統(tǒng)會將異常信息保存到ActionContext柿赊,conversionError攔截器將異常信息添加到fieldErrors里,不管類型轉(zhuǎn)換是否出現(xiàn)異常幻枉,都會進(jìn)入第3步
  3. 系統(tǒng)通過發(fā)射技術(shù)先調(diào)用action中的validateXxx()方法碰声,Xxx為方法名。
  4. 再調(diào)用action中的validate()方法熬甫。
  5. 經(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。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末桥温,一起剝皮案震驚了整個(gè)濱河市引矩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖旺韭,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件氛谜,死亡現(xiàn)場離奇詭異,居然都是意外死亡区端,警方通過查閱死者的電腦和手機(jī)值漫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來织盼,“玉大人杨何,你說我怎么就攤上這事×ち冢” “怎么了危虱?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長唐全。 經(jīng)常有香客問我埃跷,道長,這世上最難降的妖魔是什么芦瘾? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任捌蚊,我火速辦了婚禮,結(jié)果婚禮上近弟,老公的妹妹穿的比我還像新娘缅糟。我一直安慰自己,他們只是感情好祷愉,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布窗宦。 她就那樣靜靜地躺著,像睡著了一般二鳄。 火紅的嫁衣襯著肌膚如雪赴涵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天订讼,我揣著相機(jī)與錄音髓窜,去河邊找鬼。 笑死欺殿,一個(gè)胖子當(dāng)著我的面吹牛寄纵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播脖苏,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼程拭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了棍潘?” 一聲冷哼從身側(cè)響起恃鞋,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤崖媚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后恤浪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體畅哑,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年水由,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了敢课。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绷杜,死狀恐怖直秆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鞭盟,我是刑警寧澤圾结,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站齿诉,受9級特大地震影響筝野,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜粤剧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一歇竟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧抵恋,春花似錦焕议、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至世囊,卻和暖如春别瞭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背株憾。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工蝙寨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嗤瞎。 一個(gè)月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓墙歪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親猫胁。 傳聞我的和親對象是個(gè)殘疾皇子箱亿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容

  • 詳談 Struts2 的核心概念 本文將深入探討Struts2 的核心概念跛锌,首先介紹的是Struts2 的體系結(jié)構(gòu)...
    可愛傻妞是我的愛閱讀 1,117評論 0 2
  • 標(biāo)簽 如果要配置的標(biāo)簽弃秆,那么必須要先配置標(biāo)簽届惋,代表的包的概念。 包含的屬性 name包的名稱菠赚,要求是唯一的脑豹,管理a...
    偷偷得路過閱讀 1,325評論 0 0
  • ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■↓↓↓↓↓↓↓↓↓ Struts2...
    _Levi__閱讀 623評論 1 0
  • 一、struts2的執(zhí)行流程 先來了解一下struts1的執(zhí)行流程: Struts1運(yùn)行原理:(了解) Strut...
    聶叼叼閱讀 577評論 1 3
  • 非本人總結(jié)的筆記衡查,抄點(diǎn)筆記復(fù)習(xí)復(fù)習(xí)瘩欺。感謝傳智博客和黑馬程序猿記筆記啊記筆記 結(jié)果頁面的設(shè)置 在action標(biāo)簽里面...
    鍵盤瞎閱讀 542評論 2 4