天天看點

Struts2架構入門

Struts2架構

一、什麼是Struts2

  Struts2是一個基于MVC設計模式的Web應用架構,它本質上相當于一個servlet,在MVC設計模式中,Struts2作為控制器(Controller)來建立模型與視圖的資料互動。Struts 2是Struts的下一代産品,是在 struts 1和WebWork的技術基礎上進行了合并的全新的Struts 2架構。其全新的Struts 2的體系結構與Struts 1的體系結構差别巨大。Struts 2以WebWork為核心,采用攔截器的機制來處理使用者的請求,這樣的設計也使得業務邏輯控制器能夠與ServletAPI完全脫離開,是以Struts 2可以了解為WebWork的更新産品。雖然從Struts 1到Struts 2有着太大的變化,但是相對于WebWork,Struts 2的變化很小。

                                            

  -------------百度百科

二、快速搭建環境

  1、下載下傳官方的jar包

    官網:https://struts.apache.org/

  2、将其中核心j包中app檔案中的struts-blank字尾名改為zip,并進行解壓其中的lib檔案夾中的jar包為運作struts2的基本jar包

  3、建立新工程,将基本jar包拷入建立工程中的lib檔案夾

  

Struts2架構入門

  4、在web.xml檔案中配置核心過濾器

<filter>
      <filter-name>struts2</filter-name>
      <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>      

  其中<filter-class>中過濾器名是根據StrutsPrepareAndExecuteFilter類所在路徑來配置的

  

Struts2架構入門

  5、配置用于跳轉的頁面,此頁面是用于跳轉到Action中

<body>
       <h3>快速入門</h3>
       <a href="${pageContext.request.contextPath}/hello.action">跳轉</a>
  </body>      

  6、編寫Action

package com.clj.action;
/**
 * Struts架構都使用Action類處理使用者的請求
 * @author Administrator
 *
 */
public class HelloAction {    
    /**
     * Action類中的方法簽名有要求,必須這麼做
     * public 公共的
     * 必須有傳回值,必須String類型
     * 方法名稱可以是任意的,但是不能有參數清單
     * 頁面的跳轉:1.return 字元串 2.需要在struts.xml配置檔案中,配置跳轉的頁面
     */
    public String sayHello(){
        //編寫代碼接收請求的參數
        System.out.println("Hello Struts2!");
        return "success";
    }
    /**
     * 示範的method方法的預設值
     * 當配置檔案中沒有配置指定的method方法,他會預設執行Action中的execute方法
     * @return
     */
    public String execute(){
        System.out.println("method方法的預設值是execute");
        return null;
    }
}      

  7、設定Action配置檔案

      配置檔案是一個xml檔案,命名強制為struts2.xml,否則當伺服器開啟時無法識别不是該檔案名的配置檔案,該檔案建立在src下

      其中,其限制條件可以在struts2-blank\WEB-INF\src\java的struts2.xml檔案中可以找到

<?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>
    <!-- 包結構 ,包名不能重複 -->
    <package name="default" namespace="/" extends="struts-default">
        <!-- 配置Action -->
        <action name="hello" class="com.clj.action.HelloAction" method="sayHello">
            <!-- 配置跳轉的頁面,路經的寫法:在Struts2架構中,不管是轉發還是重定向,都不需要加項目名 
                name表示結果頁面邏輯視圖名稱
                 type 結果類型(轉發類型,預設值是轉發,也可以設定其他的值)
            -->    
            <result name="success">/jsp/success.jsp</result>
        </action>
     </package>
</struts>      

  8、此時通路跳轉頁面,即可執行execute方法

  總結:這裡當執行跳轉頁面時,會根據配置檔案中的action 中的 name屬性進行比對,找到有name為hello 比對到HelloAction類中的sayHello方法,通過方法反射等進行解析,根據方法的傳回值“success”進行頁面的跳轉,過渡到success.jsp中。值得注意的是,其package包名不能重複!

三、struts2執行流程為:

從用戶端發送請求過來 先經過前端控制器(核心過濾器StrutsPrepareAndExecuteFilter)過濾器中執行一組攔截器(一組攔截器 就會完成部分功能代碼)執行目标Action,在Action中傳回一個結果視圖,根據Result的配置進行頁面的跳轉.

Struts2架構入門
Struts2架構入門

四、Action書寫方式

  方式一:POJO類

/**
 * 三種action書寫方式之POJO類
 * 沒有任何繼承和實作
 * @author Administrator
 *
 */
public class Demo1Action {
    /**
     * execute是預設方法
     * return null:他不會跳轉
     * @return
     */
    public String execute(){
        System.out.println("Demo1Action就是一個POJO類");
        return null;
    }
}      

  方式二:實作Action的接口

import com.opensymphony.xwork2.Action;

/**
 * 三種action書寫方式之實作Action的接口,Action是架構提供的接口
 * @author Administrator
 *
 */
public class Demo2Action implements Action{

    @Override
    public String execute() throws Exception {
        // TODO Auto-generated method stub
        System.out.println("Demo2Actions實作了Action的接口");
        //Action接口中定義了 public static final String SUCCESS = "success";
        return SUCCESS;
    }
    
}      

  其中Action有自定義的字元串常量,實作Action的接口傳回值就可調用它的常量

  方式三:繼承ActionSupport類

import com.opensymphony.xwork2.ActionSupport;

/**
 *  三種action書寫方式之編寫ActionSupport類
 *  ActionSupport類已經實作了Action和一些其他接口
 * @author Administrator
 *
 */
public class Demo3Action extends ActionSupport{

    
    public String execute() throws Exception {
        System.out.println("Demo3Action繼承了ActionSupport類。。");
        //不跳轉
        return NONE;
    }
     
}      

五、struts2中常量的配置

  1.這裡先看看當架構執行時配置檔案加載的順序

  檢視StrutsPrepareAndExecuteFilter:(核心過濾器)兩個功能 :預處理 和 執行 

  在預處理功能中 init 方法中會有加載配置檔案的代碼:

    dispatcher.init();

            init_DefaultProperties(); // [1] ---- 加載org.apache.struts.default.properties.配置的是struts2的所有常量.

            init_TraditionalXmlConfigurations(); // [2] ---- 加載struts-default.xml、struts-plugin.xml、struts.xml

            init_LegacyStrutsProperties(); // [3] ---- 加載使用者自定義struts.properties

            init_CustomConfigurationProviders(); // [5] ---- 加載Struts2定義Bean.

            init_FilterInitParameters() ; // [6] ---- 加載web.xml

            init_AliasStandardObjects() ; // [7] ---- 使用者自定義Bean

     結論:

       

    * 先加載default.properties檔案,在org/apache/struts2/default.properties檔案,都是常量。

       * 又加載struts-default.xml配置檔案,在核心的jar包最下方,struts2架構的核心功能都是在該配置檔案中配置的。

       * 再加載struts.xml的配置檔案,在src的目錄下,代表使用者自己配置的配置檔案

          * 最後加載web.xml的配置檔案

    * 後加載的配置檔案會覆寫掉之前加載的配置檔案(在這些配置檔案中可以配置常量)

            是以: 後配置的常量 會 覆寫先配置的常量.

       2、檢視常量

  

Struts2架構入門

  3、修改常量

  注意:修改常量不能在default.properties檔案中修改,可以在struts.xml或者web.xml檔案中

   1)在struts.xml中

  <!-- 編寫常量 -->
    <constant name="struts.action.extension" value="action,,"></constant>      

  struts2中通路action預設字尾為action或者不寫,這個配置可以進行更改

   2)在web.xml中

<filter>
      <filter-name>struts2</filter-name>
      <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
      <!-- 編寫常量 -->
      <init-param>
          <param-name>struts.action.extension</param-name>
          <param-value>action,,</param-value>
      </init-param>
  </filter>      

六、Action的通路方式

  1、傳統方式:配置method屬性

    1)Action類代碼

import com.opensymphony.xwork2.ActionSupport;

/**
 * 編寫的客戶的Action的類
 * @author Administrator
 *
 */
public class CustomerAction extends ActionSupport {
    //儲存客戶
    public String save(){
        System.out.println("儲存客戶");
        return NONE;
    }
    //删除客戶
    public String delete(){
        System.out.println("删除客戶");
        return NONE;
    }
}          

   2)配置檔案配置

<!-- 示範Action的通路,傳統方式 -->
     <package name="demo2" namespace="/" extends="struts-default">
         <action name="saveCust" class="com.clj.action2.CustomerAction" method="save"/>
         <action name="delCust" class="com.clj.action2.CustomerAction" method="delete"/>    
     </package>      

  2、通配符的配置

   1)Action類

import com.opensymphony.xwork2.ActionSupport;
/**
 * 通配符的方式
 * @author Administrator
 *
 */
public class LinkmanAction extends ActionSupport{
    //儲存客戶
    public String save(){
        System.out.println("儲存聯系人。。");
        return "saveOk";
    }
    //删除客戶
    public String delete(){
        System.out.println("删除聯系人。。");
        return "delOk";
    }
}      

   2)配置檔案

<!-- 示範Action的通路,通配符方式 -->
     <package name="demo3" namespace="/" extends="struts-default">      

   <!--這裡的{1}代表統配第一個*;若name=”linkman_*_*...methd=”{1}”,{1}表示比對第一個*,{2}表示通配第二個*,即數字表示*的位置,通過通配符調用指定方法-->

<action name="linkman_*" class="com.clj.action2.LinkmanAction" method="{1}">
             <result name="saveOk">/jsp/success.jsp</result>
             <result name="delOk">/jsp/success.jsp</result>
         </action>
     </package>      

   3、動态方法通路

   1)開啟動态通路常量(注意:常量的配置都是定義在包外)

<!-- 開啟動态方法通路 -->
    <constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>      

   2)Action類

import com.opensymphony.xwork2.ActionSupport;

/**
 * 動态方法通路
 * @author Administrator
 *
 */
public class UserAction extends ActionSupport{
    //儲存客戶
        public String save(){
            System.out.println("儲存使用者。。");
            return NONE;
        }
        //删除客戶
        public String delete(){
            System.out.println("删除使用者。。");
            return NONE;
        }
}      

   3)配置檔案

<!-- 示範Action的通路,動态方法通路方式 -->
     <package name="demo4" namespace="/" extends="struts-default">
         <action name="user" class="com.clj.action2.UserAction"/>
     </package>      

   4)域名書寫方式

<h3>動态方法通路</h3>
     <a href="${pageContext.request.contextPath}/user!save.action">儲存聯系人</a></br>
     <a href="${pageContext.request.contextPath}/user!delete.action">删除聯系人</a></br>      

 七、struts2中資料的之接收資料

  struts2怎麼傳值呢,這裡由兩種種方式

  1.定義一個jsp,利用表單進行資料傳輸

<form action="${pageContext.request.contextPath}/demo1Action.action" method="post">
              姓名:<input type="text" name="username"/></br>
              密碼:<input type="password" name="psssword"/></br>
              <input type="submit" value="注冊"/>
          </form></br>      

  2.編寫Action

   1)方式一:完全解耦合的方式

package com.clj.demo1;

import java.util.Arrays;
import java.util.Map;
import java.util.Set;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
/**
 * 完全解耦合的方式,使用Servlet的API
 * 通過操作ActionContexte類的方法來擷取Servlet API
 * @author Administrator
 *
 */
public class demo1Action extends ActionSupport{
    public String execute() throws Exception{
        //完全解耦合的方式
        ActionContext context=ActionContext.getContext();
        //擷取到請求的參數,擷取到所有請求的參數
        Map<String,Object> map=context.getParameters();
        //周遊擷取資料
        Set<String> keys=map.keySet();
        for(String key:keys){
            //通過key擷取到值
            String[] values=(String[]) map.get(key);
            //預設情況下,struts已經将字元編碼轉成了UTF-8,無需考慮中文亂碼
            System.out.println(key+":"+Arrays.toString(values));
        }
        //如果向request對象中存入值
        context.put("msg","小東東");
        //擷取其他map集合
        context.getSession().put("msg","小蒼");
        context.getApplication().put("msg","小澤");
        return SUCCESS;
        
    }
}      

  2)方式二:原生的Servlet API

package com.clj.demo1;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;

/**
 * 原生的Servlet API
 * @author Administrator
 *
 */
public class demo2Action extends ActionSupport{
    public String execute() throws Exception{
        //擷取到request對象
        HttpServletRequest request=ServletActionContext.getRequest();
        request.setAttribute("msg","小東東");
        request.getSession().setAttribute("msg","美美");
        ServletActionContext.getServletContext().setAttribute("msg", "小風");     
        return SUCCESS;
    }
}      

八、結果視圖處理

  1、關于全局頁面

       為什麼要設定全局頁面?因為在同一個包下,有時候多個action中所獲得的結果視圖是同一個,這要每個action配置會覺得備援,此時可以配置個全局頁面來共用

    列子:假如當action中方法傳回值是success,此時就可無需配置<result>标簽,它會自動跳轉到success.jsp中

<!-- 配置全局的結果頁面,必須定義在目前包下 -->
    <global-results>
        <!-- type:轉發類型 
                 dispatcher:轉發,預設值,Action->jsp
                 redirect:  重定向,Action->jsp
                 chain:     多個action之間跳轉,Action->Action(轉發)
                 redirectAction:多個action之間跳轉,Action->Action(重定向)
                 stream:檔案下載下傳
        -->
        <result name="success" type="dispatcher">/jsp/success.jsp</result>
    </global-results>      

  2.、結果頁面的類型

        * 結果頁面使用<result>标簽進行配置,包含兩個屬性

        > name  -- 邏輯視圖的名稱

        > type  -- 跳轉的類型,值一些,需要掌握一些常用的類型。常見的結果類型去struts-default.xml中查找。

            * dispatcher        -- 轉發.type的預設值.Action--->JSP

            * redirect          -- 重定向. Action--->JSP

            * chain             -- 多個action之間跳轉.從一個Action轉發到另一個Action.  Action---Action

            * redirectAction    -- 多個action之間跳轉.從一個Action重定向到另一個Action. Action---Action

            * stream            -- 檔案下載下傳時候使用的

     示範重定向

    action

package com.clj.demo1;

import com.opensymphony.xwork2.ActionSupport;

/**
 * 示範重定向
 * @author Administrator
 *
 */
public class demo3Action extends ActionSupport{
    public String save(){
        System.out.println("儲存");
        return SUCCESS;
    }
    public String update(){
        System.out.println("更新成功");
        return NONE;
    }
}      

   配置檔案

<!-- 示範重定向到Action -->
        <action name="demo3Action_*" class="com.clj.demo1.demo3Action" method="{1}">
            <result name="success" type="redirectAction">demo3Action_update</result>
        </action>      

九、關于struts2架構資料的封裝

  封裝資料是為了便于開發,降低維護成本,這裡有兩種方式進行資料的封裝。

  > 提供對應屬性的set方法進行資料的封裝。

            * 表單的哪些屬性需要封裝資料,那麼在對應的Action類中提供該屬性的set方法即可。

            * 表單中的資料送出,最終找到Action類中的setXxx的方法,最後指派給全局變量。

  1、方式一:屬性驅動

       1)屬性驅動寫法方式一:在action類中直接寫從頁面中送出的屬性資料

         jsp:

<h3>屬性驅動的方式(直接在Action中編寫屬性)</h3>
         <!-- aciton接收表單值時屬性要提供set方法 -->
         <form action="${pageContext.request.contextPath}/regist1.action" method="post">
              姓名:<input type="text" name="username"/></br>
              密碼:<input type="password" name="password"/></br>
              年齡:<input type="text" name="age"/></br>
              <input type="submit" value="注冊"/>
          </form></br>      

  action:

package com.clj.demo2;

import com.opensymphony.xwork2.ActionSupport;
/**
 * 屬性驅動
 * 方式一:将Action當做JavaBean類
 * @author Administrator
 *
 */
public class regist1 extends ActionSupport{
    private String username;
    private String password;
    private Integer age;
    //傳值,用set,不需要要用get
    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String execute() throws Exception{
        System.out.println(username+" "+password+" "+age);
        return NONE;
    }
}      

    2)屬性驅動寫法方式二:将送出的資料封裝到一個類中

    jsp

<h3>屬性驅動的方式(把資料封裝到javaBean中)</h3>
          <!-- 注意:頁面的編寫風格為OGNL表達寫法 -->
             <form action="${pageContext.request.contextPath}/regist2.action" method="post">
                 <!-- 這裡的"user"是根據Aciton中封裝屬性的類的名稱屬性而定(private User user;) -->
              姓名:<input type="text" name="user.username"/></br>
              密碼:<input type="password" name="user.password"/></br>
              年齡:<input type="text" name="user.age"/></br>
              <input type="submit" value="注冊"/>
          </form></br>      

      javabean

package com.clj.demo2;

public class User {
    private String username;
    private String password;
    private Integer age;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User [username=" + username + ", password=" + password
                + ", age=" + age + "]";
    }
    
}      

   action

package com.clj.demo2;

import com.opensymphony.xwork2.ActionSupport;
/**
 * 屬性驅動的方式,将資料封裝到javaBean中
 * @author Administrator
 *
 */
public class regist2 extends ActionSupport{
    //注意:屬性驅動的方式,需要提供get和set方法
    private User user;
    
    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public String execute(){
        System.out.println(user);
        return NONE;
    }
}        

  * 注意:隻提供一個set方法還不夠,必須還需要提供user屬性的get和set方法!!!

        > 先調用get方法,判斷一下是否有user對象的執行個體對象,如果沒有,調用set方法把攔截器建立的對象注入進來,接下直接調用get方法擷取傳過來的參數

  拓展:

  1.将資料封裝到list中

    jsp

<h3>向List集合封裝資料(預設情況下,采用屬性驅動的方式)</h3>
          <!-- 采用OGNL表達式 -->
          <form action="${pageContext.request.contextPath}/regist4.action" method="post">
                 <!-- 這裡的"user"是根據Aciton中封裝屬性的類的名稱屬性而定(private User user;) -->
              姓名:<input type="text" name="list[0].username"/></br>
              密碼:<input type="password" name="list[0].password"/></br>
              年齡:<input type="text" name="list[0].age"/></br>
              
              姓名:<input type="text" name="list[1].username"/></br>
              密碼:<input type="password" name="list[1].password"/></br>
              年齡:<input type="text" name="list[1].age"/></br>
              <input type="submit" value="注冊"/>
          </form></br>      

    action

package com.clj.demo2;

import java.util.List;

import com.opensymphony.xwork2.ActionSupport;
/**
 * 屬性驅動的方式,把資料封裝List集合中
 * @author Administrator
 *
 */
public class regist4 extends ActionSupport{
    private List<User> list;

    public List<User> getList() {
        return list;
    }

    public void setList(List<User> list) {
        this.list = list;
    }
    public String execute() throws Exception{
        for(User user:list){
            System.out.println(user);
        }
        return NONE;
    }
    
}      

  2.将資料封裝到Map集合中

   jsp

<h3>向Map集合封裝資料(預設情況下,采用屬性驅動的方式)</h3>
          <!-- 采用OGNL表達式 -->
          <form action="${pageContext.request.contextPath}/regist5.action" method="post">
                 <!-- 這裡的"user"是根據Aciton中封裝屬性的類的名稱屬性而定(private User user;) -->
              姓名:<input type="text" name="map[\'one\'].username"/></br>
              密碼:<input type="password" name="map[\'one\'].password"/></br>
              年齡:<input type="text" name="map[\'one\'].age"/></br>
              
              姓名:<input type="text" name="map[\'two\'].username"/></br>
              密碼:<input type="password" name="map[\'two\'].password"/></br>
              年齡:<input type="text" name="map[\'two\'].age"/></br>
              <input type="submit" value="注冊"/>
          </form></br>      

   action

package com.clj.demo2;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.opensymphony.xwork2.ActionSupport;
/**
 * 屬性驅動的方式,把資料封裝Map集合中
 * @author Administrator
 *
 */
public class regist5 extends ActionSupport{
    private Map<String,User> map=new HashMap<String,User>(); 
    
    public Map<String, User> getMap() {
        return map;
    }

    public void setMap(Map<String, User> map) {
        this.map = map;
    }

    public String execute() throws Exception{
        System.out.println(map);
        return NONE;
    }
    
}      

  2、方式二:模型驅動

    1)手動執行個體化javabean

private User user=new User();      

    2)實作ModelDriven<T>接口,實作getModel()的方法,在getModel()方法中傳回javaBean

    jsp

<h3>模型驅動</h3>
             <form action="${pageContext.request.contextPath}/regist3.action" method="post">
                 <!-- 這裡的"user"是根據Aciton中封裝屬性的類的名稱屬性而定(private User user;) -->
              姓名:<input type="text" name="username"/></br>
              密碼:<input type="password" name="password"/></br>
              年齡:<input type="text" name="age"/></br>
              <input type="submit" value="注冊"/>
          </form></br>      

    action代碼

package com.clj.demo2;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
/**
 * 模型驅動的方式
 * 1.實作ModelDrivern接口
 * 2.必須手動實列化對象(需要自己new好)
 * @author Administrator
 */
public class regist3 extends ActionSupport implements ModelDriven<User>{
    //必須手動執行個體化
    private User user=new User();
    //擷取模型對象
    public User getModel() {
        // TODO Auto-generated method stub
        return user;
    }
    public String execute() throws Exception{
        System.out.println(user);
        return NONE;
    }
    
}      

十、struts2之攔截器

  1、什麼是攔截器?

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

                                                                        ---------360百科

     攔截器的利用是struts2架構的一個特點,即面向切面程式設計

  2、編寫一個攔截器

   1)定義一個攔截器類

package com.itheima.interceptior;

import com.google.common.collect.AbstractIterator;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;



/**
 * 編寫簡單的攔截器
 * @author Administrator
 *
 */
public class DemoInterceptor extends AbstractInterceptor{
    
    /**
     * intercept用來進行攔截的
     */
    public String intercept(ActionInvocation invocation) throws Exception {
        System.out.println("Action方法執行之前");
        //執行下一個攔截器
        String result=invocation.invoke();
        System.out.println("Action方法執行之後");
        return result;
    }
}      

  2) 定義一個action

package com.clj.demo3;

import com.opensymphony.xwork2.ActionSupport;

public class UserAction extends ActionSupport{
    public String execute() throws Exception{
        System.out.println("我是Action,我正常執行了");
        return NONE;
    }
}      

  3) 配置檔案配置

<package name="demo3" extends="struts-default" namespace="/">
         <interceptors>
             <!-- 定義了攔截器 -->
             <interceptor name="DemoInterceptor" class="com.itheima.interceptior.DemoInterceptor"/>
         </interceptors>
        <action name="userAction" class="com.clj.demo3.UserAction">
            <!-- 隻要是引用自己的攔截器,預設棧的攔截器就不執行了 ,必須手動引入預設棧-->
            <interceptor-ref name="DemoInterceptor"/>
            <interceptor-ref name="defaultStack"/>
        </action>
    </package>      

  3、使用攔截器驗證使用者登入

     1) 定義一個攔截器

package com.clj.interceptor;

import org.apache.struts2.ServletActionContext;

import com.heima.domain.User;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;

/**
 * 自動以攔截器,判斷目前系統是否已經登入,如果登入,繼續執行
 * 如果沒有登入,跳轉到登入頁面
 * AbstractInterceptor會攔截所有方法
 * MethodFilterInterceptor可以對指定的方法進行攔截
 * @author Administrator
 *
 */
public class UserInterceptor extends MethodFilterInterceptor{

    /**
     * 進行攔截的方法
     */
    protected String doIntercept(ActionInvocation invocation) throws Exception {
        //獲得session對象
        User user=(User) ServletActionContext.getRequest().getSession().getAttribute("existUser");
        if(user==null){
            //沒有登入,傳回字元串,後面就不會執行了
            return "login";
        }
        return invocation.invoke();
    }
    
}      

   2)action

package com.itheima.action;

import java.lang.reflect.InvocationTargetException;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.struts2.ServletActionContext;

import com.heima.domain.User;
import com.itheima.service.UserService;
import com.opensymphony.xwork2.ActionSupport;

/**
 * 使用者登入子產品控制器
 * @author Administrator
 *
 */
public class UserAction extends ActionSupport{
    /**
     * 處理登入功能
     * @return
     */
    public String login(){
        //這邊沒有學習功能,封裝資料,現在還需要request對象
        //怎麼擷取reqeust方式
        HttpServletRequest request=ServletActionContext.getRequest();
        //擷取請求參數
        Map<String,String[]> map=request.getParameterMap();
        User user=new User();
        try {
            BeanUtils.populate(user,map);
            //調用業務層
            User existUser=new UserService().login(user);
            //判斷
            if(existUser==null){
                //說明,使用者名或者密碼錯誤了
                return LOGIN;
            }else{
                //存入session中
                request.getSession().setAttribute("existUser", existUser);
                return SUCCESS;
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        return NONE;
    } 
}      

   配置檔案

<!-- 配置使用者的子產品 -->
        <action name="user_*" class="com.itheima.action.UserAction" method="{1}">
            <!--<result name="login">/login.jsp</result>-->
            <result name="success">/index.jsp</result>
            <interceptor-ref name="UserInterceptor">
                <param name="excludeMethods">login</param>
            </interceptor-ref>
            <interceptor-ref name="defaultStack"/>
        </action>      

十一、OGNL

  1、什麼是OGNL

   OGNL是Object-Graph Navigation Language的縮寫,它是一種功能強大的表達式語言,通過它簡單一緻的表達式文法,可以存取對象的任意屬性,調用對象的方法,周遊整個對象的結構圖,實作字段類型轉化等功能。它使用相同的表達式去存取對象的屬性。

                                                                     ------------360百科

Struts2架構使用OGNL作為預設的表達式語言

  2、示範OGNL表達式

package com.clj.demo1;

import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;

import org.junit.Test;

/**
 * 示範OGNL表達式
 * @author Administrator
 *
 */
public class OgnlDemo {
    /**
     * OGNL通路對象的方法
     * @throws OgnlException 
     */
    @Test
    public void run1() throws OgnlException{
        //上下文對象
        OgnlContext context=new OgnlContext();
        //擷取到根對象(ognl都是從根對象進行取值)
        Object root=context.getRoot();
        //存儲資料
        context.put("name","美美");
        //擷取值,表達式寫法
        Object obj=Ognl.getValue("#name",context,root);
        System.out.println(obj);
    }
    /**
     * OGNL表達式調用方法
     * @throws OgnlException
     */
    @Test
    public void run2() throws OgnlException{
        //上下文對象
        OgnlContext context=new OgnlContext();
        //擷取到根對象(ognl都是從根對象進行取值)
        Object root=context.getRoot();
        //擷取值,表達式寫法(這裡是讓字元串調用他的方法)
        Object obj=Ognl.getValue("\'haha\'.length()",context,root);
        System.out.println(obj);
    }
}
      

  3、jsp中示範OGNL

    1) 先引入Strust2标簽庫

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

   2) 調用

<body>
       <h3>條件:假如值棧中已經存入值了,在JSP頁面上從值棧中擷取值</h3>
       <!-- 
           1.先引入Struts架構中提供的标簽庫,s标簽
           2.可以使用提供的标簽(很多,掌握重點的标簽)
        -->
        <!-- 從值棧中擷取值的value,中間編寫就是OGNL表達式 -->
        <s:property value="username"/>
        <!-- 顯示常量:加\'\'号 -->
         <s:property value="\'username\'"/>
         <!-- 調用方法:加.加方法 -->
         <s:property value="\'username\'.length()"/>
  </body>      

十二、Struts2之值棧

  1、什麼是值棧

  * 值棧就相當于Struts2架構的資料的中轉站,向值棧存入一些資料。從值棧中擷取到資料。

    * ValueStack 是 struts2 提供一個接口,實作類 OgnlValueStack ---- 值棧對象 (OGNL是從值棧中擷取資料的 )

    * Action是多例的,有一起請求,建立Action執行個體,建立一個ActionContext對象,代表的是Action的上下文對象,還會建立一個ValueStack對象。

    * 每個Action執行個體都有一個ValueStack對象 (一個請求 對應 一個ValueStack對象 )

    * 在其中儲存目前Action 對象和其他相關對象

    * Struts 架構把 ValueStack 對象儲存在名為 “struts.valueStack” 的請求屬性中,request中 (值棧對象 是 request一個屬性)

        * ValueStack vs = (ValueStack)request.getAttribute("struts.valueStack");

  2、關于值棧的内部結構

  * 值棧由兩部分組成

        > root      -- Struts把動作和相關對象壓入 ObjectStack 中--List(底層ArrayList)

        > context   -- Struts把各種各樣的映射關系(一些 Map 類型的對象) 壓入 ContextMap 中(底層Map,當講Servlet的API時,完成接耦合方式ActionContext.getRequest()所傳回的      Map集合就是此集合)

        * Struts會預設把下面這些映射壓入ContextMap(context)中

        * 注意:request代表的是Map集合的key值(字元串名),value的值其實也是一個Map集合。

        > parameters: 該 Map 中包含目前請求的請求參數  ?name=xxx&password=123

        > request: 該 Map 中包含目前 request 對象中的所有屬性

        > session: 該 Map 中包含目前 session 對象中的所有屬性

        > application:該 Map 中包含目前 application  對象中的所有屬性

        > attr: 該 Map 按如下順序來檢索某個屬性: request, session, application

        * ValueStack中 存在root屬性 (CompoundRoot) 、 context 屬性 (OgnlContext )

        > CompoundRoot 就是ArrayList

        > OgnlContext 就是 Map

        * context 對應Map 引入 root對象

        > context中還存在 request、 session、application、 attr、 parameters 對象引用

        > OGNL表達式通路值棧中的資料

            * 通路root中資料時 不需要 #

            * 通路 request、 session、application、 attr、 parameters 對象資料 必須寫 #

        > 操作值棧 預設指 操作 root 元素(至于context是架構自動将請求參數或者其他資料封裝到conttext中)

Struts2架構入門
Struts2架構入門

   3、在jsp中檢視值棧

<!-- 在JSP頁面上,檢視值棧的内部結構 -->
     <s:debug/>      

   4、關于值棧中壓值

      1)壓字元串之push方法

/**
     * 示範值棧對象的目錄結構
     * 想棧中壓元素,先進先出
     * push:壓入的對象
     * set:壓入集合(先判斷壓入的對象是否是集合,不是則強轉為集合,再建立鍵值對;是則在原有集合中壓入元素)
     * @author Administrator
     *
     */
    public String execute() throws Exception{
        /*//使用擷取值棧對象
        HttpServletRequest request=ServletActionContext.getRequest();
        ValueStack vs=(ValueStack) request.getAttribute("struts.valueStack");
        System.out.println(vs);*/
        //擷取值棧對象,先擷取到ActionContext對象
        ValueStack vs=ActionContext.getContext().getValueStack();
        System.out.println(vs);
        //棧頂壓的是字元串
        vs.push("      
美美      
");return NONE; }      

   取值

<h3>從值棧中擷取值</h3>
     <!--值棧中擷取的對象,當存入字元串時,棧頂第一個位置為對象,第二位置為Action,第三位置為封轉棧的資訊
         棧的預設索引是從0開始:[0]表示擷取棧頂的封裝對象的一些列資訊,[0].top擷取對象值
     //vs.push("美美");-->
     <s:property value="[0]"/>
     <s:property value="[0].top"/>      

   2)壓字元串之set方法

ValueStack vs=ActionContext.getContext().getValueStack();
//棧頂壓的是集合
vs.set("msg","金在中");      

   取值

<!-- 從棧頂擷取字元串 ,方法為set,值棧中存的是Map集合>
     //vs.set("msg","金在中");-->
     <s:property value="[0].top.msg"/>      

   3)棧頂放的是User對象,用push方法

ValueStack vs=ActionContext.getContext().getValueStack();
//建立User對象
User user=new User();
user.setUsername("美美");
user.setPassword("123");
//壓棧
vs.push(user);      

   取值

<!--棧頂放的是User對象,用push方法
         //[0].top關鍵字可以省略,值棧中提供getValue方法,從結果中找指定屬性
        /* User user=new User();
        user.setUsername("美美");
        user.setPassword("123");
        //壓棧
        vs.push(user);*/-->
     <s:property value="[0].top.username"/>
     <s:property value="[0].top.password"/>
     <s:property value="password"/>      

   4)棧頂放的是User對象,用set方法

ValueStack vs=ActionContext.getContext().getValueStack();
//建立User對象
User user=new User();
user.setUsername("美美");
user.setPassword("123");
//壓棧
vs.set("user",user);      

   取值

  <!--棧頂放的是User對象,用set方法
     // vs.set("user",user);-->
     <s:property value="[0].top.user.username"/>
     <s:property value="[0].top.user.password"/>
     <s:property value="user.password"/>
            

   5)在action中配置成員屬性,并提供set/get方法,jsp同樣可以來接收

private User user=new User("小澤","456");
//提供set/get方法      

   取值

    <!-- 在ValueStack1Action提供了成員屬性
       //private User user=new User("小澤","456");
       //提供set/get方法
      //vs.push(user);
               從棧頂開始查找,找user的屬性,顯示名稱,傳回的小澤
      [1].top擷取到ValueStack1Action,而該Action封裝了user屬性
      [0].top.user傳回的是user對象-->
      <s:property value="user.username"/>      

   6)擷取值棧中(root)集合中的值push

List<User> list=new ArrayList<User>();
        list.add(new User("熊大","123"));
        list.add(new User("熊二","456"));
        list.add(new User("熊三","789"));
        //把集合壓棧
        vs.push(list);      

   取值

     <!-- 擷取list集合
             //vs.push(list);-->
      <s:property value="[0].top[0].username"/>
      <s:property value="[0].top[1].username"/>      

   7)擷取值棧中(root)集合中的值set

List<User> list=new ArrayList<User>();
        list.add(new User("熊大","123"));
        list.add(new User("熊二","456"));
        list.add(new User("熊三","789"));
        //把集合壓棧
         vs.set("list",list);      

   取值

    <!-- 擷取list集合
              //vs.set("list",list); -->
       <s:property value="list[0].username"/>      

   8)利用疊代标簽取值

ValueStack vs=ActionContext.getContext().getValueStack();
        List<User> list=new ArrayList<User>();
        list.add(new User("熊大","123"));
        list.add(new User("熊二","456"));
        list.add(new User("熊三","789"));
        //把集合壓棧
         vs.set("list",list);      

    取值

<!-- 疊代标簽 
             屬性: * value 要疊代的集合,需要從值棧中擷取
                *  var   疊代過程中,周遊的對象
                      * var編寫上,會把疊代産生的對象預設壓入到context棧中
                      * var不編寫上,預設吧疊代長生的對象壓入到root棧中
               
      -->
      <!-- 編寫var:value="[0].top"等同于value="list" -->
      <s:iterator value="list" var="u">
             <!--此時是壓入context,取值要加#号-->
             <s:property value="#u.username"/>
             <s:property value="#u.password"/>
      </s:iterator>      
<!-- 沒有編寫var關鍵字-->
      <s:iterator value="list">
           <s:property value="[0].top.username"/>
           <s:property value="password"/>
      </s:iterator>       

   9) 從context棧中擷取值

ValueStack vs=ActionContext.getContext().getValueStack();
//從context棧中擷取值(注意:以上push與set預設都是操縱的root棧)
        //context底層已經封裝request session對象,操作的都是map集合
    HttpServletRequest request=ServletActionContext.getRequest();
        request.setAttribute("msg","美美");
        
        request.getSession().setAttribute("msg", "小風");      

   取值

<!--  從context棧中擷取值,加#号-->
      <s:property value="#request.msg"/>
      <s:property value="#session.msg"/>
                  

   10)針對域名傳過來的值,怎麼接收

    加入域名為http://localhost:8080/xx/xx.action?id=5

<!--  針對于域名傳過來的參數(參數名為id),架構會自動封裝 -->
      <s:property value="#parameters.id"/>      

   11) 利用EL表達式接收資料

     引入jstl标簽庫

<%@taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>      

    取值

<!-- 在JSP頁面上可以使用EL和JSTL标簽庫來取值
          使用裝飾者模式,連接配接池全棧編碼
          getAttribute()增強了 
      -->
      <c:forEach items="${list }" var="user">
            ${user.username}--${user.passwords}
      </c:forEach>