Struts2學習筆記_攔截器棧&標簽庫

Struts2_攔截器棧&標簽庫

一、攔截器棧

1. 攔截器

Java里的攔截器是動態攔截Action調用的對象。它提供了一種機制可以使開發者可以定義在一個action執行的前后執行的代碼,也可以在一個action執行前阻止其執行,同時也提供了一種可以提取action中可重用部分的方式。在AOP(Aspect-Oriented Programming)中攔截器用于在某個方法或字段被訪問之前,進行攔截然后在之前或之后加入某些操作。

幾個關鍵字:

  • 攔截Action:Struts2 攔截器在訪問某個 Action 方法之前或之后實施攔截,(在action之前調用的稱之為前置攔截器,之后也稱之為后置攔截)
  • 阻止執行:由于攔截器可以在Action執行之前執行,那么如果不想讓某個action執行,可以阻斷其執行。
  • 可重用部分:重復的代碼抽取出來形成攔截器,攔截器是可插拔的。
  • AOP(Aspect-Oriented Programming):一種編程思想,該思想的簡單理解就是:在不改變原來代碼的情況下,對原來的代碼功能進行增強(增加或減少)。它的通常是采用代理的機制實現(代理對目標代碼進行控制和增強 )

問題: 攔截器和過濾器的區別?

過濾器(filter)是javaweb階段的知識點,攔截服務器端所有資源的訪問 (靜態、 動態)。在web.xml配置。

攔截器(Interceptor),在struts2框架內部,只對Action訪問進行攔截 (默認攔截器 ,無法攔截靜態web資源, 
如果要攔截靜態資源,比如html、jsp,可以將靜態web資源放入WEB-INF\xxx, 通過Action間接訪問)

2. 攔截器棧

攔截器棧(Interceptor Stack):是將攔截器按照一定的順序連接成一條鏈后的一個稱呼。

在訪問被攔截的方法時,攔截器鏈的中攔截器就會按照其之前定義的順序被一次調用。
Struts2 將攔截器定義攔截器棧,作用于目標Action,攔截器棧的名字為defaultStack

defaultStackStruts2默認執行的攔截器棧。

img21.png
2.1 Struts2運行原理的底層分析(了解)
img03.png
1. 當web.xml被加載后,經過Struts2的前端控制器,會進入StrutsPrepareAndExecuteFilter,它會調用init
方法進行初始化,準備Struts2的相關環境,加載相應的配置文件,(6個,包括struts.xml)--它會將所有action的name
都加載到Struts2的環境中。

2. 當有請求訪問時,前端控制器攔截訪問doFilter方法,ActionMapping mapping = prepare.findActionMapping(request, response, true);
會在查找要訪問的action的name是否有配置,如果沒有配置,直接過濾攔截,忽略后面所有的攔截器與action的執行,
并告知action映射不存在,不往下運行。

如果存在,execute.executeAction(request, response, mapping);準備執行action

3. 準備執行action :
 ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
                namespace, name, method, extraContext, true, false);

生成action的代理對象---增強---使用過濾器進行增強

繼續向下走 : proxy.execute();

invocation.invoke();

ActionInvocation增強器里面的invoke方法,判斷if (interceptors.hasNext())-配置的那些攔截器有沒有
執行完,如果沒有執行完,就執行:
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this)
這個具體的攔截器,執行之后 return invocation.invoke();,返回到原來的調用對象,原來的
調用對象又會自動調用invoke方法。--(鏈式遞歸)遞歸調用。

4. 當攔截器都執行完成之后(增強完成之后),resultCode = invokeActionOnly();讓它去執行具體的Action:
invokeAction(getAction(), proxy.getConfig());返回結果集視圖。

3. 自定義攔截器

![Upload img22.png failed. Please try again.]

![Upload img23.png failed. Please try again.]

img24.png
  • 程序中每個攔截器 都必須實現 Interceptor 接口
  • 也可以繼承 AbstractInterceptor 只需要覆蓋 intercept 方法
  • 也可以繼承 MethodFilterInterceptor ,只需要覆蓋 doIntercept 方法
    可以設置哪些方法 不進行過濾攔截(功能最強,推薦)

3.1 實現自定義攔截器

編寫測試的Action(被攔截的Action):

@Override
//通過攔截器增強這個Action
public String execute() throws Exception {
    System.out.println("TestAction執行了................");
    return NONE;
}

編寫一個自定義攔截器:

編寫一個類,繼承MethodFilterInterceptor,實現doFilter方法。
在doFilter方法內部編寫要對其增強的代碼。

public class MyInterceptor extends MethodFilterInterceptor {

    @Override
    //目標 :使用攔截器對Action進行增強
    protected String doIntercept(ActionInvocation invocation) throws Exception {
        
        System.out.println("攔截器執行了,增強Action方法.................");
        //增強后,放行將執行權交給下個攔截器或Action
        return invocation.invoke();
    }
}

注冊攔截器

定義全局攔截器:

寫在package內,action前

<interceptors>
    <!--注冊自定義攔截器 -->
    <interceptor name="myInterceptor" class="com.itdream.struts2.interceptor.MyInterceptor" />
    
    <!-- 注冊自定義攔截器棧 -->
    <interceptor-stack name="myStack">
        <!-- 先執行自己的攔截器(順序看需求) -->
        <interceptor-ref name="myInterceptor" />
        <!-- 執行默認的攔截器棧 -->
        <interceptor-ref name="defaultStack" />
    </interceptor-stack>
</interceptors>

<!-- 自定義攔截器棧覆蓋Struts2的默認攔截棧,使其生效 -->
<default-interceptor-ref name="myStack" />

-------------------------------------------------------------------------

繼承MethodFilterInterceptor有一個強大的功能,可以設置哪些Action不進行攔截。
如何使用 :

    <interceptor-ref name="myInterceptor">
        <!-- 排除哪些方法,不攔截它們,多個方法間用逗號隔開 -->
        <param name="excludeMethods">execute</param>
        <!-- 包含哪些方法,只有這些方法才被攔截,多個方法間用逗號隔開.與上面的排除配置互斥 -->
        <!-- <param name="includeMethods">execute</param> -->
    </interceptor-ref>  

定義局部攔截器 :

首先也要在package內,action注冊自攔截器:

<interceptors>
    <!--注冊自定義攔截器 -->
    <interceptor name="myInterceptor" class="com.itdream.struts2.interceptor.MyInterceptor" />
</interceptors>

然后在指定action內局部使用該攔截器:

<!-- 
    局部攔截器:寫在指定action內部,只對這個action有效
    局部攔截器會覆蓋全局配置
 -->
 <interceptor-ref name="myInterceptor">
 <interceptor-ref name="defaultStack"/>

結論:我們在自定義攔截器后,需要將其進行注冊,并且使用它.使用時都不會拋棄Struts2的攔截器棧。

實際上,我們自定義攔截器后基本上都是注冊全局攔截器讓其對所有的Action生效。

二、 自定義攔截器,攔截未登陸用戶訪問

攔截器只能攔截訪問Action請求,不能攔截靜態Web資源(jsp,html等),如果要想攔截它們,使用Action間接訪問這些資源即可。

目標:用戶在未登錄的情況下,不允許訪問系統的功能,讓其跳轉到用戶登錄頁面。

前提:所有的頁面請求都經過Action,自定義攔截器進行Action請求攔截,在執行Action之前進行判斷操作。

1. 修改新增客戶,讓其通過Action訪問add.jsp

menu.jsp :
    href="${pageContext.request.contextPath }/customer_showAdd.action"

CustomerAction動作類:
    //跳轉添加客戶頁面
    public String showAdd() {
        return "addjsp";
    }

struts.xml配置結果集視圖跳轉頁面:
    <!-- 跳轉添加客戶頁面 -->
    <result name="addjsp" type="redirect">/jsp/customer/add.jsp</result>

2. 完成用戶登錄功能

  • 創建用戶的數據庫表user
    • 默認給定用戶名和密碼
  • 創建用戶持久化類User
  • 完成用戶持久化類與數據庫表user的映射文件mapping
  • 用戶在頁面輸入用戶名密碼,數據庫校驗是否存在
  • 登陸成功跳轉首頁,登陸失敗,跳轉回登陸頁面,告知提示信息
2.1 ORM關系創建(省略)
2.2 修改登錄頁面login.jsp
<FORM id=form1 name=form1 method="post" action="${pageContext.request.contextPath }/user_login.action">

表單中name屬性修改與模型類的屬性一致,用于Struts2框架的攔截器封裝數據。(省略)

struts.xml配置訪問的Action:
<action name="user_*" class="com.itdream.crm.web.action.UserAction" method="{1}"></action>
2.3 Action處理請求,查詢數據庫
Action動作類處理請求:

public class UserAction extends ActionSupport implements ModelDriven<User> {

    // 創建一個模型對象用于封裝參數
    private User user = new User();

    @Override
    // 提供getter方法Struts2框架獲取model對象
    public User getModel() {
        return user;
    }

    // 用戶登錄
    public String login() {
        // 獲取參數(模型驅動)
        // 調用業務層查詢是否有符合賬號密碼的User存在
        UserService service = new UserServiceImpl();
        User loginUser = service.findUserByUsernameAndPassword(user.getUsername(), user.getPassword());

        if (loginUser != null) { // 登陸成功
            // 將User對象存入Session域中
            ServletActionContext.getRequest().getSession().setAttribute("user", loginUser);
            // 跳轉首頁
            return "loginSuccess";
        }
        // 登陸失敗
        addActionError("用戶名或密碼不正確,請重新輸入");
        return LOGIN;
    }
}

---------------------------------------------------------------------------------

Service層:

public class UserServiceImpl implements UserService {

    @Override
    //根據賬號密碼查詢用戶
    public User findUserByUsernameAndPassword(String username, String password) {
        //獲取Session對象
        Session session = HibernateUtils.getCurrentSession();
        //開啟事務
        Transaction transaction = session.beginTransaction();
        User user = null;
        try {
            //業務邏輯
            //調用dao層
            UserDAO dao = new UserDAOImpl();
            user = dao.findUserByUsernameAndPassword(username,password);
            
        } catch (Exception e) {
            //回滾事務
            transaction.rollback();
            e.printStackTrace();
        }finally {
            //提交事務
            transaction.commit();
        }
        return user;
    }
}

--------------------------------------------------------------------------

dao層:

public class UserDAOImpl implements UserDAO {

    @Override
    //根據用戶名和密碼查詢User
    public User findUserByUsernameAndPassword(String username, String password) {
        //獲取Session對象
        Session session = HibernateUtils.getCurrentSession();
        
        //使用Criteria查詢(面向對象的無語句查詢)
        Criteria criteria = session.createCriteria(User.class);
        System.out.println("username:"+username);
        System.out.println("password:"+password);
        //添加限制條件
        criteria.add(Restrictions.eq("username", username));
        criteria.add(Restrictions.eq("password", password));
        //執行查詢
        User user = (User) criteria.uniqueResult();
        return user;
    }
}
2.4 自定義登陸攔截器
用戶未登錄的情況下訪問服務器的資源時,跳轉到登陸頁面,并提示用戶登錄。
1. 繼承MethodFilterInterceptor完成自定義攔截器。
2. 將需要寫回的信息放入ActionError集合中在jsp頁面回顯。
3. 最后需要放行,invocation.invoke()。

自定義攔截器,攔截Action請求:

public class LoginInterceptor extends MethodFilterInterceptor {

    @Override
    //攔截未登錄用戶
    protected String doIntercept(ActionInvocation invocation) throws Exception {
        
        //獲取Session域中的user是否存在來判斷是否登陸
        User user = (User) ServletActionContext.getRequest().getSession().getAttribute("user");
        
        if (user == null) { //如果未登陸,執行攔截,跳轉到登錄頁面
            //使用Struts2的大管家獲取攔截的Action,寫回友好信息
            ActionSupport action = (ActionSupport) invocation.getAction();
            action.addActionError("對不起,您還沒有登陸");
            
            //跳轉到登陸頁面
            return action.LOGIN;
        }
        
        //否則就放行,將執行權交到下一個攔截器或者Action
        return invocation.invoke();
    }
}


頁面回顯提示:
    先引入Struts2的標簽庫。
    <%@ taglib prefix="s" uri="/struts-tags" %>
    友好提示:
    <s:actionerror/>
2.5 struts2.xml最終版本
1. Action的結果集視圖跳轉對應頁面。
    1. 這里配置了一個全局結果集"login",未登陸用戶訪問服務器資源/登陸失敗,讓其全跳回登陸頁

2. 注冊自定義攔截器,并使用自定義攔截器棧覆蓋默認攔截器棧


<?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="true" />
    <!-- 簡單樣式 -->
    <constant name="struts.ui.theme" value="simple" />

    <package name="default" namespace="/" extends="struts-default">
    
        <!-- 自定義攔截器 -->
        <interceptors>
            <!-- 注冊自定義攔截器 -->
            <interceptor name="loginInterceptor" class="com.itdream.crm.web.interceptor.LoginInterceptor"/>
            
            <!-- 注冊自定義攔截器棧 -->
            <interceptor-stack name="myStack">
                <!-- 先執行登陸攔截,驗證是否登陸需要攔截 -->
                <interceptor-ref name="loginInterceptor">
                    <!-- 如果是執行登錄操作的方法,就不攔截.標簽體內填寫要排除的方法名 -->
                    <param name="excludeMethods">login</param>
                </interceptor-ref>
                <!-- 再執行Struts2默認攔截棧 -->
                <interceptor-ref name="defaultStack"/>
            </interceptor-stack>
        </interceptors>
        
        <!-- 使用自定義攔截器棧(自定義攔截器棧覆蓋默認棧) -->
        <default-interceptor-ref name="myStack"/>
    
    
        <!-- 全局結果集,未登錄用戶訪問任何頁面都跳轉login.jsp -->
        <global-results>
            <result name="login">/login.jsp</result>
        </global-results>
        
        <action name="customer_*" class="com.itdream.crm.web.action.CustomerAction"
            method="{1}">
            <!-- 跳轉添加客戶頁面 -->
            <result name="addjsp" type="redirect">/jsp/customer/add.jsp</result>
            <result name="flushListCustomer" type="redirectAction">customer_list.action
            </result>
            <result name="listCustomer">/jsp/customer/list.jsp</result>
            <!-- 默認轉發,傳遞customer數據 -->
            <result name="editjsp">/jsp/customer/edit.jsp</result>
        </action>

        <!-- 與User有關的action請求 -->
        <action name="user_*" class="com.itdream.crm.web.action.UserAction"
            method="{1}">
            <!-- 登陸成功 -->
            <result name="loginSuccess">/index.jsp</result>
        </action>
    </package>
</struts>

二、標簽庫

測試Struts2的標簽庫:
Struts.xml配置:
    <!-- 標簽庫測試 -->
    <action name="tag_*" class="com.itdream.struts2.taglib.TagAction" method="{1}">
        <result name="result">/result.jsp</result>
    </action>   

1. 通用(Generic)標簽

1.1. <s:property/>標簽

作用:將OGNL表達式的內容輸出到頁面

value:屬性。接收OGNL表達式

default:屬性, 如果OGNL表達式,取不到值,default設置顯示默認值。默認為""

escapeHtml:屬性, 是否對HTML標簽轉義輸出 (默認是轉義,可以關閉)

Action類:    

//測試property標簽
public String property() {
    //模擬業務層返回的數據壓入值棧,轉發jsp頁面接收
    // 獲取值棧
    ValueStack valueStack = ActionContext.getContext().getValueStack();
    
    //壓入root棧,匿名
    valueStack.push("push壓入root棧");
    //壓入root棧,有名字
    valueStack.set("msg", "set壓入root棧");
    //壓入map棧,匿名
    ActionContext.getContext().put("msg", "put壓入map棧");

    //跳轉頁面(值棧和request的生命周期一樣,采用轉發方式)
    return "result";
}


result.jsp :

<%@ taglib prefix="s" uri="/struts-tags" %>

<h3>------------測試Property標簽-------------------</h3>
<!-- 從值棧中取值 -->
<!-- 取壓入root棧的匿名對象 -->
<s:property value="[1].top"/><br/>

<!-- 取壓入root棧的有名字對象 
    1. 值棧的默認查找機制
    2. 索引獲取值棧的對象,再通過key取值
    3. 神奇的request(el表達式,Struts2增強了request.getAttribute方法)。
        先到request域中查找,再使用值棧的默認查找機制到值棧中查找
-->
<s:property value="msg"/>|<s:property value="[0].top.msg"/>|${msg}<br/>

<!-- 取存入map棧的值
    1.可以使用值棧的默認查找機制取map棧的值,但如果root棧中有重名的key就取不到map棧的值
        因為值棧的默認查找機制默認先找root棧,root棧沒有才查找map棧.
        這里因為root棧已經有msg了,就不能使用這種方法了
    2. 神奇的request,先找request,再走值棧默認查找
    3.使用#+key指定查找map棧的key,取出map棧的值
 -->
 <s:property value="#msg"/><br/>
 <hr/>

-----------------------------------------------------------------------------
1.2. <s:iterator/>標簽

作用:遍歷集合對象(可以是List、set和數組等),顯示集合對象的數據。

遍歷的過程:

每次遍歷時,將遍歷的值壓入棧頂(匿名),并且在map棧中放入一個副本,key是var的變量名,map的value就是要遍歷的值。
每次遍歷完一個值,就將它從棧頂彈出,并刪除map棧key為var變量名的鍵值對。

由于這種原理,因此取遍歷的值有幾種方式:

1. 直接使用棧頂取值.[0].top  
2. 使用值棧默認查找機制取map棧的值,key為var的變量名
3. 指定取map棧的值,#key
4. el表達式使用神奇的request(先找request,request沒有就走值棧默認查找機制)

value:迭代的集合。支持OGNL表達式,如果沒有設置該屬性,則默認使用值棧棧頂的集合來迭代。(類似于jstl中c:foreach標簽的items屬性)

var:引用變量的名稱,該變量是集合迭代時的子元素。

begin: 開始的數字

end: 結束的數字

status:引用迭代時的狀態對象IteraterStatus實例(類似于varstatus),其有如下幾個方法:

1. int getCount(),返回當前迭代了幾個元素;
2. int getIndex(),返回當前迭代元素的索引;
3. Boolean isEvent(),偶數
4. boolean isOdd(),奇數
5. boolean isFirst(),第一個
6. boolean isLast(),最后一個


Action 類:

// 測試iterator標簽遍歷集合
List<User> users = new ArrayList<>();
// username和password
User user1 = new User("tom", "123");
User user2 = new User("jerry", "123");
User user3 = new User("tony", "123");
User user4 = new User("lucy", "123");
users.add(user1);
users.add(user2);
users.add(user3);
users.add(user4);
// 將集合壓入值棧
valueStack.push(users);
valueStack.set("users", users);
ActionContext.getContext().put("users", users);

// 跳轉頁面(值棧和request的生命周期一樣,采用轉發方式)
return "result";


jsp頁面遍歷集合:

<!-- 遍歷action轉發的集合,根據壓入棧選擇,這里我壓入了三種,匿名的被有名字的壓下去了,所以可以使用:
    索引獲取集合:[1].top,取root棧:users,取map棧:#users
 -->
<s:iterator value="#users" var="user" status="status">
    <!-- 1.因為每次遍歷的對象都在棧頂,可以直接獲取棧頂對象的屬性.(推薦)
        2. 神奇的request.通過el表達式默認直接獲取到站定對象的屬性值
        3. var的變量名是存入map棧的副本的key. 通過默認查找機制找,通過#直接取。通過request的el表達式取
    -->
    1<s:property value="[0].top.username"/>|2<s:property value="username"/>|3${username }|4<s:property value="#user.username"/>|5${user.username }<br/>
</s:iterator>

遇到的問題:

  • 可以直接獲取棧頂對象的屬性
  • <s:property/>標簽會將value屬性內的字符串當成一個整體去執行值棧的默認查找機制。即不能使用<s:property value="user.username" />企圖通過默認查找機制獲取map棧中的user對象,再通過getUsername獲取屬性值。(例外:[index].top.屬性名不會當成一個整體字符串,即:<s:property value="[0].top.username" />它會先找到到Root棧的對象,再獲取他的value值)

擴展了解:

1. 遍歷集合時配合begin與end可以控制遍歷一定數量的元素

2.<s:property/>如果沒有value值,默認獲取棧頂的對象


#####1.3. ```<s:if> <s:elseif> <s:else>```標簽    

**支持OGNL表達式**

    //模擬代表用戶狀態的標識存入值棧
    valueStack.set("userRole", 1);
    // 跳轉頁面(值棧和request的生命周期一樣,采用轉發方式)
    return "result";


    <s:if test="userRole==0">管理員</s:if><br/>
    <s:elseif test="userRole==1">普通用戶</s:elseif><br/>
    <s:else>游客</s:else>

#####1.4. ```<s:a>```標簽 
作用:生成a標簽鏈接

    <!-- Html超鏈接 -->
    <a href="${pageContext.request.contextPath }/product_find.action?name=水果">我是超鏈接</a>
    <br/>

    <!-- action是action的名字 -->
    <s:a action="product_find" namespace="/">
        <!-- name:是參數名,value:參數值
        原因:value:是個ognl表達式
         -->
        <s:param name="name" value="'蘋果'"/>
        我是新的超鏈接
    </s:a>


![img25.png](http://upload-images.jianshu.io/upload_images/5303154-a67f7ff3462bd645.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


#####1.5 其他一些用到的標簽

    <s:fielderror/> 
    <s:actionerror/>
    <s:actionmessage/>
    <s:i18n>
    <s:param>

####2. 用戶界面(UI)標簽
用戶界面標簽主要是包括表單類標簽和其他類標簽
#####2.1 ```<s:form>```標簽
作用:生成form標簽。

屬性:

* action屬性,對應 struts.xml <action>元素name屬性;

* namespace屬性,對象 struts.xml <package>元素 namespace屬性

Html表單:
<form action="${pageContext.request.contextPath }/form.action" method="post"></form>

Struts2表單:

<s:form action="form" namespace="/" method="post"></form>

#####2.2 ```<s:textfield>, <s:password>, <s:hidden>, <s:textarea>```標簽
    <s:textfield> 文本域 ,相當于 <input type=”text” >
    <s:password> 密碼域 ,相當于<input type=”password” >
        showpassword:表單回顯時是否顯示密碼
    <s:hidden> 隱藏域 , 相當于 <input type=”hidden” >
    <s:textarea> 文本框 , 相當于 <textarea></textarea>

    例:

    Html:
    <input type="hidden" name="name" value="jack"/>
    用戶名:<input type="text" name="username"/>
    密碼:<input type="password" name="password">
    備注:<textarea rows="3" cols="20" name="memo"></textarea>

    Struts2:
    <s:hidden name="name" value="jack"/><br/>
    用戶名:<s:textfield name="username"/>
    密碼:<s:password name="password"/>
    備注:<s:textarea name="memo" rows="3" cols="20"/>

#####2.3 ```<s:radio>、<s:checkboxlist>、<s:select>```標簽
* ```<s:radio>``` 接收list或者map 生成一組單選按鈕 
* ```<s:select>``` 接收list或者map ,生成一組下拉列表 
* ```<s:checkboxlist>``` 接收list或者map ,生成一組復選框

單選項 radio:
Html:
    性別:<input type="radio" name="sex" value="male"/>男
    <input type="radio" name="sex" value="female"/>女<br/>

Struts2:
    性別:<s:radio name="sex" list="{'男','女'}" value="{'male','female'}"/>
    //顯示的內容與value的值相同時可以省略value(不推薦)

-----------------------------------------------------------------------------

下拉框 select:
Html:
    城市:<select name="city">
            <option value="">--請選擇城市--</option>
            <option value="bj">北京</option>
            <option value="sz">深圳</option>
    </select><br/>

Struts2:
    <!-- 構建map集合 -->
    使用#{key:value構建map集合,key為html中value的值,這里的value就是顯示的內容。以逗號隔開

    城市:<s:select name="city" list="#{'bj':'北京','sz':'深圳'}" headerKey="" headerValue="--請選擇城市--"/><br/>

----------------------------------------------------------------------------

復選框 checkbox:
Html:
    愛好:<input type="checkbox" name="hobby" value="football"/>足球
    <input type="checkbox" name="hobby" value="basketball"/>籃球
    <input type="checkbox" name="hobby" value="pinpong"/>乒乓球<br/>

Struts2:
    <!-- 構建map集合 -->
    使用#{key:value構建map集合,key為html中value的值,這里的value就是顯示的內容。以逗號隔開

    愛好:<s:checkboxlist name="hobby" list="#{'football':'足球','basketball':'籃球','pingpong':'乒乓球' }"/>

#####2.4 ```<s:file>、<s:submit>、<s:reset>```標簽
* ```<s:file>對應html中input標簽的file```
* ```<s:submit>、<s:reset>```分別對應html中的提交和重置

文件上傳 file:
Html:
    頭像:<input type="file" name="icon"/>

Struts2:
    頭像:<s:file name="icon"/>

-------------------------------------------------------------------------

提交表單 submit:
Html:
    <input type="submit" value="提交"/>

Struts2:
    <s:submit value="提交"/>
-------------------------------------------------------------------------
重置表單 reset:
Html:
    <input type="reset" value="重置"/>
Struts2:
    <s:reset value="重置"/>
-------------------------------------------------------------------------

#####2.5 主題樣式
經過上面表單標簽的編寫,我們會發現用```struts2標簽庫```編寫完的表單頁面不好看。這是因為Struts2提供了不同的主題。

Struts2 模板文件,支持兩種Freemarker生成 (.ftl模板文件) , Velocity生成 (.vm 模板文件)

    struts2默認采用 Freemarker 。

    提供四種主題 :
        Simple 沒有任何修飾效果,最簡單主題 
        Xhtml 通過 布局表格 自動排版 (默認主題 )
        css_xhtml 通過CSS進行排版布局 
        ajax 以Xhtml模板為基礎,增加ajax功能  

    問題: 如何修改主題 


![img26.png](http://upload-images.jianshu.io/upload_images/5303154-99cd7e5e990fd212.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


    開發中,在struts.xml 配置常量,修改默認主題樣式,對所有form生效

    <!-- 簡單主題 -->
    <constant name="struts.ui.theme" value="simple"/> 

將主題修改為簡單樣式后,效果如下圖:


![img27.png](http://upload-images.jianshu.io/upload_images/5303154-1492f21411dcc079.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)



#####2.6 小結
Struts2的標簽有兩種:通用標簽、表單界面標簽。

注意:這兩種標簽屬性支持ognl表達式的屬性名字是不一樣。

**表單標簽:name是支持ognl表達式,value不支持,直接顯示值。**

**但,除此之外,其他的所有通用標簽,value是支持ognl表達式的。**
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容