1. 程式人生 > >Struts2 快速筆記2

Struts2 快速筆記2

路線:

  1. Struts2的概述、Struts2的入門、Struts2常見的配置、Struts2的Action的編寫
  2. Struts2的資料的封裝、結果頁面配置
  3. Struts2的值棧和OGNL表示式
  4. Struts2的標籤庫

上一篇:Struts2 快速筆記 1

1. OGNL表示式

  1. OGNL:Object Graph Navigation Language,功能強大的表示式語言——比EL表示式,物件導航語言表示式。

    • EL:只能從域物件中獲取資料,11個域物件——request/session/pagecontext等:${name},${request}
    • OGNL:可以呼叫物件的方法,域物件資料,以及Struts2的值棧的資料。OGNL是獨立的第三方語言,Struts將其引用到了,單獨也可以使用。
  2. OGNL的功能

    • 支援運算子操作
    • struts2中支援物件方法的呼叫
    • Struts2中支援靜態方法和值的引用
    • 支援賦值和表示式串聯
    • 訪問OGNL上下文OGNLContext和ActionContext——與值棧有關
    • 操作集合物件,或者new新的物件
  3. 使用3要素:Java中使用,不僅僅在struts2

    • 表示式:從哪裡獲取什麼資料
    • 根物件:操作目標
    • Context物件:在哪裡操作,操作環境

    示例(Java中):

    • 包:
      1546600883034

    • Demo

      @Test
      /**
       * OGNL呼叫物件方法
       */
      public void demo1() throws OgnlException{
          //3要素  context root source
          OgnlContext ognlContext = new OgnlContext();
          Object root = ognlContext.getRoot();
          Object obj = Ognl.getValue("'helloworld'.length()", ognlContext, root);
          System.out.println(obj);
      }
      @Test
      /**
       * OGNL呼叫靜態方法和值:
       * @物件全路徑名@方法(引數)
       * @物件全路徑名@變數名
       */
      public void demo2() throws OgnlException{
          //3要素  context root source
          OgnlContext ognlContext = new OgnlContext();
          Object root = ognlContext.getRoot();
          Object obj = Ognl.getValue("@
      [email protected]
      ()", ognlContext, root); Object obj2 = Ognl.getValue("@[email protected]", ognlContext, root); System.out.println(obj); } @Test /** * Context中的資料需要加# * Root物件中的資料不需要 */ public void demo3() throws OgnlException{ User user = new User("Tom", "123"); OgnlContext ognlContext = new OgnlContext(); //1.Context中 /* ognlContext.put("user", user); Object obj = Ognl.getValue("#user", ognlContext, ognlContext.getRoot()); System.out.println(obj);*/ //2.Root中,訪問的是物件的屬性 ognlContext.setRoot(user); Object obj2 = Ognl.getValue("username", ognlContext, ognlContext.getRoot()); System.out.println(obj2); }
  4. OGNL在Struts2中使用:主要是從JSP頁面的資料互動

    • 頁面引入標籤:<%@ taglib uri="/struts-tags" prefix="s" %>

    • 獲取物件資料

      <h1>Struts2的OGNL表示式</h1>
      <h3>1.訪問物件方法</h3>
      <s:property value="'hello'.length()"/>
      <br/>
      <h3>2.訪問靜態方法-----struts2 預設將其關閉,如@[email protected]()-------可以在struts的核心包中default.properties中配置常量struts.ognl.allowStaticMethodAccess=true</h3>
      <s:property value="@java.lang.[email protected]()"/>
      <br/>
      <h3>3.訪問靜態成員-----struts2 可以如@[email protected]</h3>
      <s:property value="@[email protected]"/>
      
    • OGNL其最主要的作用——值棧

2. 值棧空間

  1. 概念

    ValueStack,是一個容器(棧),有struts框架建立。當前端頁面如JSP傳送一個請求時,Struts的預設攔截器就會將請求中的資料進行封裝,然後將其放入ValueStack的棧頂。

    1546604465917

    前端的請求將會被封裝成物件,放入值棧容器中。在使用Struts2框架的開發中,一般前端資料的中轉都由值棧來進行,儘量較少的使用域物件(request,pageContext等),但也要記住struts中也能使用這些物件。有一個Action的例項,就會有一個值棧的物件,Action是多例的。

    **作用:**存入ValueStack中的資料,在struts2中任何位置能獲取到,如頁面、配置檔案、Action中;而存入域物件如Request物件的資料,只能在JSP頁面中獲取。

  2. 內部結構

    1546605129641

    值棧中的兩個主要區域:root和context。root區繼承了一個ArrayList,context區來自OGNL的包,其實現了一個Map介面,包含了一些常見的Web物件的引用。

    • root區一般放置常見的物件,ArrayList——CompoundRoot物件;獲取root資料不需要加#

    • context放置物件的引用,Map——OgnlContext物件;獲取context資料需要加#

      • root引用
      • request
      • parameters
      • session
      • application
      • attr:小範圍的引用,使用它按序依次訪問request、session、application中的屬性,找到即停。
    • 一般而言,操作的值棧是指的操作Root區

      1546606294076

    • 也可以在JSP的debug中看到<h1>OGNL的值棧內部結構debug</h1><s:debug />

      1546607701397

  3. 值棧與ActionContext

    和ServletContext上下文物件一樣,ActionContext對應Action的生命週期。因Struts核心過濾器StrutsPrepareAndExecuteFilterinit方法中會執行載入配置檔案(xml中繼承自struts-default的,又會執行許多預設的攔截器,實現引數的校驗,型別轉化和封裝等功能)。

    ActionContext:請求產生------StrutsPrepareAndExecuteFilter------執行doFilter方法-----建立ActionContext-----再由其建立ValueStack,並封裝到ActionContext中;所有可以使用ActionContext獲取值棧物件。

    ActionContext有值棧的引用,於是能夠訪問Servlet中的request等域物件,就獲得其API的相關資料。ActionContext中的獲得的上下文物件,其實是通過當前執行緒來獲得的:

    public class ActionContext implements Serializable {
    
        static ThreadLocal<ActionContext> actionContext = new ThreadLocal<ActionContext>();
        /**
         * Constant for the name of the action being executed.
         */
        public static final String ACTION_NAME = "com.opensymphony.xwork2.ActionContext.name";
    
        /**
         * Constant for the {@link com.opensymphony.xwork2.util.ValueStack OGNL value stack}.
         */
        public static final String VALUE_STACK = ValueStack.VALUE_STACK;
    
        /**
         * Constant for the action's session.
         */
        public static final String SESSION = "com.opensymphony.xwork2.ActionContext.session";
        ......
         /**
         * Returns the ActionContext specific to the current thread.
         *
         * @return the ActionContext for the current thread, is never <tt>null</tt>.
         */
        public static ActionContext getContext() {
            return actionContext.get();
        }
    
  4. 獲得值棧

    1. 通過ActionContext獲取
    2. 在Struts2中也在request中存入了一個值棧引用
    //方式1.
    ValueStack valueStack1 = ActionContext.getContext().getValueStack();
    // 方式2.
    ValueStack valueStack2 = (ValueStack) ServletActionContext.getRequest()
            .getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
    // 二者屬於同一Action,ValueStack當然是同一個物件
    System.out.println(valueStack1 == valueStack2);
    return NONE;
    
  5. 操作值棧:操作資料

    1. 通過Action提供了Get方法的屬性,獲取其屬性值。

      • 原因:預設情況下,ValueStack會將Action物件本身也壓入棧(Root區)中,所有有get方法的屬性都可獲取
      public class DoValueStack extends ActionSupport {
      	private User user;
      	//方式1必須要有get屬性
      	public User getUser() {
      		return user;
      	}
      	@Override
      	public String execute() throws Exception {
      		//方式1.在action中提供get方法
      		user = new User("Helo", "1234");		
      		return SUCCESS;
      	}
      }
      -----------------------JSP----------------------------------
      <h2>操作值棧方式一,get</h1>
      <s:property value="user.username"/>
      <s:property value="user.password"/>
      
    2. 通過ValueStack自身的API:更常用,不需要太多的Get方法,程式碼更可讀

      @Override
      public String execute() throws Exception {	
          //方式2.使用ValueStack本身的API
          ValueStack valueStack = ActionContext.getContext().getValueStack();
          //新增資料,push(物件),set(key,value)
          User user = new User("ooooo", "4444");
          valueStack.push(user);  //push的物件儲存在棧頂位置!!!!!!!!!!!!!!!!
          valueStack.set("aaa", "123456");
          return SUCCESS;
      }
      ----------------------------JSP---------------------------------
      <s:property value="username"/>     ooooo
      <s:property value="aaa"/>      123456
      

      1546611744719

  6. 獲取值棧資料

    1. 獲取Root區中的資料——獲取一個物件或者集合

      public String execute() throws Exception {	
      		//
      		ValueStack valueStack = ActionContext.getContext().getValueStack();
      		//1個物件
      		User user = new User("張三", "123456");
      		valueStack.push(user);		
      		List<User> list = new ArrayList<>();
      		list.add(new User("李四", "123"));
      		list.add(new User("王五", "123"));
      		list.add(new User("趙六", "123"));
      		valueStack.set("list", list);
      		return SUCCESS;
      	}
      ----------------------------JSP--------------------------
      <h2>1.獲取Root區的資料 單個物件</h1>
      <s:property value="username"/>
      <s:property value="password"/>
      <h2>2.獲取Root區的資料 集合</h1>
      <s:property value="list[0].username"/>
      <s:property value="list[0].password"/><br/>
      <s:property value="list[1].username"/>
      <s:property value="list[1].password"/><br />
      <s:property value="list[2].username"/>
      <s:property value="list[2].password"/>
      
    2. 獲取context區中的資料(不常用)

      ServletActionContext.getRequest().setAttribute("myname", "孫七");
      ServletActionContext.getRequest().getSession().setAttribute("myname", "朱八");
      ServletActionContext.getServletContext().setAttribute("myname", "周九");
      ------------------------------JSP---------------------------
      <h2>3.獲取context區的資料 </h1>
      <s:property value="#request.myname"/>
      <s:property value="#session.myname"/>
      <s:property value="#application.myname"/>
      <s:property value="#attr.myname"/>
      <h2>4.獲取請求字串引數 </h1>
      <s:property value="#parameters.id"/>
      
  7. EL為何能訪問值棧資料

    原始碼:Struts中初始化時對request進行了增強包裝(request.getAttribute),框架將其進行了改造。使用EL表示式時,先會訪問原request等域物件中資料,沒有獲取到時則進入ActionContext中,進入值棧進行查詢。

    於是,EL表示式同樣能獲取到值棧資料。

    1546700629166

 

3. OGNL 中的特殊字元

  • <% request.setAttribute("name", "張三"); %>
    <h2>OGNL中#號的使用</h2>
    <h3>1.獲取context域中的資料</h3>
    <s:property value="#request.name"/>
    <h3>2.構造集合list</h3>
    <s:iterator var="i" value="{'aa','bb','bb'}">
    	<s:property value="i"/>------<s:property value="#i"/><br/>
    </s:iterator>
    
    <h3>2.構造集合map1</h3>
    <s:iterator value="#{'aa':'11','bb':'22','cc':'33'}">
    	<s:property value="key"/>------<s:property value="value"/><br/>
    </s:iterator>
    
    <h3>2.構造集合map2, 使用了var就必須加#號,從context區中獲取</h3>
    <s:iterator var="entry" value="#{'aa':'11','bb':'22','cc':'33'}">
    	<s:property value="#entry.key"/>------<s:property value="#entry.value"/><br/>
    </s:iterator>
    
    <h3>3.#號在一些常用表單中使用,構建集合</h3>
    性別:<input type="radio" value="1" name="sex1"><input type="radio" value="2" name="sex1"><br>
    <h4>3.1使用list</h4>
    <s:radio list="{'',''}" name="sex2" label="性別"></s:radio>
    <h4>3.2使用map</h4>
    <s:radio list="#{'1':'','2':''}" name="sex3" label="性別"></s:radio>
    
  • %

    常用於在HTML標籤中,顯示獲取資料(也可以直接巢狀,但在OGNL的標籤中無法巢狀);也就是強制解析OGNL語句,同樣也可以強制失效OGNL

    <s:textField name="name" value="%{#request.name}"/>
    <input type="text" name="name2" value="<s:propery value='#request.name'>"> //可巢狀
    <s:property value="%{‘#request.name’}"/>
    
  • $

    一般在配置檔案xml或properties中使用,來使用OGNL獲得資料。

    • 屬性檔案:使用$來限定OGNL表示式,否則會被視為字串,如${#session.user.username}
    • xml:同上使用

案例:查詢優化,將查詢的資料存入值棧中,在頁面使用OGNL獲取。


 

4. Struts攔截器

  1. interceptor,作用時攔截(過濾)Action;相比Filter過濾的是客戶端向伺服器傳送的請求,而攔截器攔截的是客戶端對Action的訪問,粒度更細,可以攔截到Action的具體方法。攔截器可以用來做一些許可權,控制之類的功能。

    Struts2框架核心的功能都是依賴攔截器實現

  2. Struts的執行流程

    1546751132551

    客戶端向伺服器傳送一個Action的請求,執行核心過濾器(doFilter)方法。在這個方法中,呼叫executeAction()方法,在這個方法內部呼叫dispatcher.serviceAction();在這個方法內部建立一個Action代理,最終執行的是Action代理中的execute(),在代理中執行的execute方法中呼叫ActionInvocation的invoke方法。在這個方法內部遞迴執行一組攔截器(完成部分功能),如果沒有下一個攔截器,就會執行目標Action,根據Action的返回的結果進行頁面跳轉。

    1546751078859

     

    1. 自定義攔截器:編寫一個類實現Interceptor介面或者繼承AbstractInterceptor類;同樣,在設定默寫許可權的時候,可以繼承MethodFilterInterceptor,來完成更小粒度(基於方法)的控制和過濾。

      public class InterceptorDemo3 extends AbstractInterceptor {
      	@Override
      	public String intercept(ActionInvocation invocation) throws Exception {
      		System.out.println("intercepto3 EXE。。。。");
      		//遞迴執行其他的攔截器
      		String obj = invocation.invoke();
      		System.out.println("intercepto3 Done。。。。");
      		return obj;
      	}
      }
      

      配置攔截器:

      <package name="demo1" extends="struts-default" namespace="/">
          <!-- 配置攔截器 -->
          <interceptors>
              <interceptor name="interceptorDemo1" class="com.leehao.strutslearning.interceptor.InterceptorDemo1" />
              <interceptor name="interceptorDemo2" class="com.leehao.strutslearning.interceptor.InterceptorDemo2" />
              <interceptor name="interceptorDemo3" class="com.leehao.strutslearning.interceptor.InterceptorDemo3" />
          </interceptors>	
          <action name="actionDemo1" class="com.leehao.strutslearning.action.ActionDemo1">
              <result>/demo1.jsp</result>
              <!-- 將攔截器引入棧,注:一旦添加了自定義的攔截器,則必須要手動選擇是否執行預設的那一堆攔截器 -->
              <interceptor-ref name="defaultStack"></interceptor-ref>
              <interceptor-ref name="interceptorDemo1"></interceptor-ref>
              <interceptor-ref name="interceptorDemo2"></interceptor-ref>
              <interceptor-ref name="interceptorDemo3"></interceptor-ref>
          </action>		
      </package>
      

      方法二:

      <package name="demo1" extends="struts-default" namespace="/">
          <!-- 配置攔截器 -->
          <interceptors>
              <interceptor name="interceptorDemo1" class="com.leehao.strutslearning.interceptor.InterceptorDemo1" />
              <interceptor name="interceptorDemo2" class="com.leehao.strutslearning.interceptor.InterceptorDemo2" />
              <interceptor name="interceptorDemo3" class="com.leehao.strutslearning.interceptor.InterceptorDemo3" />
              <interceptor-stack name="myStack">
                  <interceptor-ref name="defaultStack"></interceptor-ref>
                  <interceptor-ref name="interceptorDemo1"></interceptor-ref>
                  <interceptor-ref name="interceptorDemo2"></interceptor-ref>
                  <interceptor-ref name="interceptorDemo3"></interceptor-ref>
              </interceptor-stack>
          </interceptors>	
          <action name="actionDemo1" class="com.leehao.strutslearning.action.ActionDemo1">
              <result>/demo1.jsp</result>
              <interceptor-ref name="myStack"></interceptor-ref>			
          </action>		
      </package>
      
  3. 注意點

    • 使用攔截器是在各個action中配置的,建議使用method粒度的攔截器;
    • 攔截器主要針對不同的action,頁面如JSP的訪問使用過濾器來進行控制
    • 攔截器可以分別在action中配置,也可以設定為攔截器的棧,按名稱引用;
    • 一定記得同時配置預設的攔截器棧——defaultStack

 

5. Struts 的標籤

struts有自己的標籤,主要的好處是方便資料的記錄和回顯。分為通用標籤(語句)和UI標籤(表單等)。

  1. 通用標籤——表示式

    1546775993162

    最常用:

    • 判斷標籤if
    • 迭代標籤iterator
    • property
    • debug
    • date
  2. UI標籤——常用作資料回顯,且有自己的樣式,較為靈活。可以在struts.properties中設定屬性,也可以對單個的標籤進行theme=simple的設定,取消預設為xhtml的樣式。

    1546776468104