1. 程式人生 > >Java程式設計師從笨鳥到菜鳥之(四十八)細談struts2(十)ognl概念和原理詳解

Java程式設計師從笨鳥到菜鳥之(四十八)細談struts2(十)ognl概念和原理詳解

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

       引言眾所周知,在mvc中,資料是在各個層次之間進行流轉是一個不爭的事實。而這種流轉,也就會面臨一些困境,這些困境,是由於資料在不同世界中的表現形式不同而造成的:

  1. 資料在頁面上是一個扁平的,不帶資料型別的字串,無論你的資料結構有多複雜,資料型別有多豐富,到了展示的時候,全都一視同仁的成為字串在頁面上展現出來。

  2. 資料在Java世界中可以表現為豐富的資料結構和資料型別,你可以自行定義你喜歡的類,在類與類之間進行繼承、巢狀。我們通常會把這種模型稱之為複雜的物件樹。

  此時,如果資料在頁面和Java世界中互相流轉傳遞,就會顯得不匹配。所以也就引出了幾個需要解決的問題:

  1. 當資料從View層傳遞到Controller層時,我們應該保證一個扁平而分散在各處的資料集合能以一定的規則設定到

Java世界中的物件樹中去。同時,能夠聰明的進行由字串型別到Java中各個型別的轉化。

  2. 當資料從Controller層傳遞到View層時,我們應該保證在View層能夠以某些簡易的規則對物件樹進行訪問。同時,在一定程度上控制物件樹中的資料的顯示格式。

       如果我們稍微深入一些來思考這個問題,我們就會發現,解決資料由於表現形式的不同而發生流轉不匹配的問題對我們來說其實並不陌生。同樣的問題會發生在Java世界與資料庫世界中,面對這種物件與關係模型的不匹配,我們採用的解決方法是使用ORM框架,例如Hibernate

iBatis等等。那麼現在,在Web層同樣也發生了不匹配,所以我們也需要使用一些工具來幫助我們解決問題。為了解決資料從View層傳遞到Controller層時的不匹配性,Struts2採納了XWork一套完美方案。並且在此的基礎上,構建了一個完美的機制,從而比較完美的解決了資料流轉中的不匹配性。相信大家看到這一定猜出來了這裡的完美方案和完美機制了。對,這就是OGNL方案和OGNLValueStack機制

 

 基本概念

        OGNLObject Graph Navigation Language),是一種表示式語言。使用這種表示式語言,你可以通過某種表示式語法,存取Java物件樹中的任意屬性、呼叫Java物件樹的方法、同時能夠自動實現必要的型別轉化。如果我們把表示式看做是一個帶有語義的字串,那麼OGNL無疑成為了這個語義字串與Java物件之間溝通的橋樑。既然OGNL那麼強大,那麼讓我們一起來研究一下他的API,看看如何使用OGNL.

OGNLAPI看起來就是兩個簡單的靜態方法:

public static Object getValue( Object tree, Map context, Object root ) throws OgnlException;public static void setValue( Object tree, Map context, Object root, Object value ) throws OgnlException 


      我們可以看到,簡單的API,就已經能夠完成對各種物件樹的讀取和設值工作了。這也體現出OGNL學習成本非常低。需要特別強調進行區分的,是在針對不同內容進行取值或者設值時,OGNL表示式的不同。Struts2 Reference 寫道:

        The Framework uses a standard naming context to evaluate OGNL expressions. The top level object dealing with OGNL is a Map (usually referred as a context map or context). OGNL has a notion of there being a root (or default) object within the context. In expression, the properties of the root object can be referenced without any special "marker" notion. References to other objects are marked with a pound sign (#).

針對上面的話,我們可以簡單的理解為下面兩點:

A) 針對根物件(Root Object)的操作,表示式是自根物件到被訪問物件的某個鏈式操作的字串表示。

B) 針對上下文環境(Context)的操作,表示式是自上下文環境(Context)到被訪問物件的某個鏈式操作的字串表示,但是必須在這個字串的前面加上#符號,以表示與訪問根物件的區別。

OGNL三要素:

     很多人習慣上把傳入OGNLAPI的三個引數,稱之為OGNL的三要素。OGNL的操作實際上就是圍繞著這三個引數而進行的。

看下面一段測試程式碼:

 Ognl.setValue("department.name", user2, "dev");  System.out.println(user2.getDepartment().getName()); Ognl.setValue(Ognl.parseexpression_r("department.name"), context, user2, "otherDev"); System.out.println(user2.getDepartment().getName());

1. 表示式(Expression


  表示式是整個OGNL的核心,所有的OGNL操作都是針對表示式的解析後進行的。表示式會規定此次OGNL操作到底要幹什麼。我們可以看到,在上面的測試中,namedepartment.name等都是表示式,表示取name或者department中的name的值。OGNL支援很多型別的表示式,之後我們會看到更多。

2. 根物件(Root Object

  根物件可以理解為OGNL的操作物件。在表示式規定了幹什麼以後,你還需要指定到底對誰幹

  在上面的測試程式碼中,user就是根物件。這就意味著,我們需要對user這個物件去取name這個屬性的值(對user這個物件去設定其中的department中的name屬性值)。

3. 上下文環境(Context

  有了表示式和根物件,我們實際上已經可以使用OGNL的基本功能。例如,根據表示式對根物件進行取值或者設值工作。不過實際上,在OGNL的內部,所有的操作都會在一個特定的環境中執行,這個環境就是OGNL的上下文環境(Context)。說得再明白一些,就是這個上下文環境(Context),將規定OGNL的操作在哪裡幹

  OGNL的上下文環境是一個Map結構,稱之為OgnlContext。上面我們提到的根物件(Root Object),事實上也會被加入到上下文環境中去,並且這將作為一個特殊的變數進行處理,具體就表現為針對根物件(Root Object)的存取操作的表示式是不需要增加#符號進行區分的。

       OgnlContext不僅提供了OGNL的執行環境。在這其中,我們還能設定一些自定義的parameterContext中,以便我們在進行OGNL操作的時候能夠方便的使用這些parameter。不過正如我們上面反覆強調的,我們在訪問這些parameter時,需要使用#作為字首才能進行。

 

OGNL表示式實現原理

    Struts 2中的OGNL Context實現者為ActionContext,它結構示意圖如下:

                                                                 

      Struts2接受一個請求時,會迅速建立ActionContextValueStackaction 。然後把action存放進ValueStack,所以action的例項變數可以被OGNL訪問。訪問上下文(Context)中的物件需要使用#符號標註名稱空間,如#application#session,另外OGNL會設定一個根物件(root物件),在Struts2中根物件就是ValueStack(值棧) 。如果要訪問根物件(即ValueStack)中物件的屬性,則可以省略#名稱空間,直接訪問該物件的屬性即可。

       在struts2中,根物件ValueStack的實現類為OgnlValueStack,該物件不是我們想像的只存放單個值,而是存放一組物件。在OgnlValueStack類裡有一個List型別的root變數,就是使用他存放一組物件 |--request |--application context ------|--OgnlValueStack root變數[action, OgnlUtil, ... ] |--session |--attr |--parameters,root變數中處於第一位的物件叫棧頂物件。通常我們在OGNL表示式裡直接寫上屬性的名稱即可訪問root變數裡物件的屬性,搜尋順序是從棧頂物件開始尋找,如果棧頂物件不存在該屬性,就會從第二個物件尋找,如果沒有找到就從第三個物件尋找,依次往下訪問,直到找到為止。 大家注意: Struts2中,OGNL表示式需要配合Struts標籤才可以使用。如:<s:property value="name"/>

       由於ValueStack(值棧)Struts 2OGNL的根物件,如果使用者需要訪問值棧中的物件,在JSP頁面可以直接通過下面的EL表示式訪問ValueStack(值棧)中物件的屬性: ${foo} //獲得值棧中某個物件的foo屬性。如果訪問其他Context中的物件,由於他們不是根物件,所以在訪問時,需要新增#字首。 

application物件:用於訪問ServletContext,例如#application.userName或者#application['userName'],相當於呼叫ServletContextgetAttribute("username")
session物件:用來訪問HttpSession,例如#session.userName或者#session['userName'],相當於呼叫session.getAttribute("userName")
request物件:用來訪問HttpServletRequest屬性(attribute)的Map,例如#request.userName或者#request['userName'],相當於呼叫request.getAttribute("userName")

parameters物件:用於訪問HTTP的請求引數,例如#parameters.userName或者#parameters['userName'],相當於呼叫request.getParameter("username")
attr物件:用於按page->request->session->application順序訪問其屬性。

            好了,基本概念和原理佔時先介紹到這,下篇部落格則著重說一下OGNL表示式的基本語法和用法,謝謝大家一直以來的支援。

 

           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述