基本介紹
OGNL是Object-Graph Navigation Language的縮寫,它是一種功能強大的表達式語言,通過它簡單一致的表達式語法,可以存取對象的任意屬性,調用對象的方法,遍歷整個對象的結構圖,實現欄位類型轉化等功能。它使用相同的表達式去存取對象的屬性。這樣可以更好的取得數據。
OGNL可以讓我們用非常簡單的表達式訪問對象層,例如,當前環境的根對象為user1,則表達式person.address[0].province可以訪問到user1的person屬性的第一個address的province屬性。
這種功能是模板語言的一個重要補充,像jsp2.0,velocity,jelly等等,都有類似的功能,但是ognl比它們完善得多,而且以一個獨立的lib出現,方便我們構建自己的框架。
webwork2和現在的
Struts2.x中使用OGNL取代原來的EL來做界面數據綁定,所謂界面數據綁定,也就是把界面元素(例如一個textfield,hidden)和對象層某個類的某個屬性綁定在一起,修改和顯示自動同步。
和
struts1.x的formbean相比,這樣做的好處非常明顯:在
webwork中不需要為每個頁面專門寫formbean,可以直接利用對象層的對象。例如在對象設計中,我們的User和Person是分開的,而一個註冊用戶界面需要填寫兩者的內容,在webwork中,就可以保持後台的對象結構,把屬於用戶屬性的界面元素用user.person.xxx綁定,把屬於賬號屬性的界面元素用user.xxx綁定。
OGNL技術
OGNL最初是一種使用屬性名稱映射前端組件和後端對象之間關聯的方法。隨著這些協會收集更多功能,Drew Davidson創建了鍵值編碼語言(KVCL)。然後Luke Blanshard使用
ANTLR重新實現了KVCL,並開始使用名稱OGNL。該技術再次使用Java Compiler Compiler(
JavaCC)重新實現。
OGNL使用Java
反射和
內省來解決運行時應用程式的
對象圖。這允許程式根據對象圖的狀態改變行為,而不是依賴於編譯時設定。它還允許更改對象圖。
項目支持
OGNL安全問題
由於OGNL能夠創建或更改可執行代碼,因此能夠為使用它的任何框架引入嚴重的安全漏洞。多個Apache Struts 2版本容易受到OGNL安全漏洞的攻擊。截至2017年10月,Struts 2的推薦版本為2.5.13。用戶被要求升級到最新版本,因為舊版本已經記錄了安全漏洞 - 例如,Struts 2版本2.3.5到2.3.31和2.5到2.5.10,允許遠程攻擊者執行任意代碼。
Struts 2
OGNL(Object-Graph Navigation Language),可以方便地操作對象屬性的開源
表達式語言,使頁面更簡潔;
支持
運算符(如+-*/),比普通的標誌具有更高的自由度和更強的功能;
Struts 2默認的表達式語言是OGNL,原因是它相對其它表達式語言具有下面幾大優勢:
支持對象方法調用,如xxx.doSomeSpecial();
支持類靜態的方法調用和值訪問,表達式的格式為@[類全名(包括包路徑)]@[方法名 | 值名],例如:@java.lang.String@format('foo %s', 'bar')或@tutorial.MyConstant@APP_NAME;
支持賦值操作和
表達式串聯,如price=100, discount=0.8, calculatePrice(price*discount),這個表達式會返回80;
訪問OGNL上下文(OGNL context)和ActionContext;
操作集合對象。
可以直接new一個對象
用法
OGNL是通常要結合Struts 2的標誌一起使用。主要是#、%和$這三個符號的使用。使用方法如下:
新建名為Struts2_OGNL的Web工程
#”主要有三種用途:
訪問OGNL上下文和Action上下文,#相當於ActionContext.getContext();下表有幾個ActionContext中有用的屬性: 名稱 作用 例子
parameters 包含當前
HTTP請求參數的Map #parameters.id[0]作用相當於request.getParameterValues("id").get(0);
request 包含當前HttpServletRequest的屬性(attribute)的Map #request.userName相當於request.getAttribute("userName")
session 包含當前HttpSession的屬性(attribute)的Map #session.userName相當於session.getAttribute("userName")
application 包含當前套用的ServletContext的屬性(attribute)的Map #application.userName相當於application.getAttribute("userName")
attr 用於按request > session > application順序訪問其屬性(attribute) #attr.userName相當於按順序在以上三個範圍(scope)內讀取userName屬性,直到找到為止
用於過濾和投影(projecting)集合,如books.{?#this.price<100};
構造Map,如#{'foo1':'bar1', 'foo2':'bar2'}。
下面讓我們它們的具體寫法,首先是Action類代碼:
package tutorial.action;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.
struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.util.ServletContextAware;
import tutorial.model.Book;
import com.opensymphony.xwork2.ActionSupport;
public class OgnlAction extends ActionSupport implements ServletRequestAware, SessionAware, ServletContextAware {
private static final long serialVersionUID = 1L;
private HttpServletRequest request;
private Map session;
private ServletContext application;
private List books;
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
@SuppressWarnings("unchecked")
public void setSession(Map session) {
this.session = session;
}
public void setServletContext(ServletContext application) {
this.application = application;
}
public List getBooks() {
return books;
}
@Override
public String execute() {
request.setAttribute("userName", "Max From request");
session.put("userName", "Max From session");
application.setAttribute("userName", "Max From application");
books = new LinkedList();
books.add(new Book("978 0735619678", "Code Complete, Second Edition", 32.99));
books.add(new Book("978 0596007867", "The Art of Project Management", 35.96));
books.add(new Book("978 0201633610", "Design Patterns: Elements of Reusable Object-Oriented Software", 43.19));
books.add(new Book("978 0596527341", "Information Architecture for the World Wide Web: Designing Large-Scale Web Sites", 25.19));
books.add(new Book("978 0735605350", "Software Estimation: Demystifying the Black Art", 25.19));
return SUCCESS;
}
}
以上代碼分別在request、session和application的範圍內添加“userName”屬性,然後再在JSP頁面使用OGNL將其取回。
下面是Ognl.jsp的代碼,內容如下:
訪問OGNL上下文和Action上下文
parameters.userName:
request.userName:
session.userName:
application.userName:
attr.userName:
用於過濾和投影(projecting)集合
Books more than $35
<s:iterator value="books.{?#this.price > 35}">
<s:property value="title" /> - $<s:property value="price" /><br>
</s:iterator>
The price of "Code Complete, Second Edition" is:
構造Map
The value of key "foo1" is
清單3 WebContent/Ognl.jsp 以上代碼值得注意的是“”,因為“books.{?#this.title=='Code Complete, Second Edition'}.{price}”返回的值是集合類型,所以要用“[索引]”來訪問其值。 最後是Struts 2的配置檔案
struts.xml,內容如下:
/Ognl.jsp
“%”符號的用途是在標誌的屬性為字元串類型時,計算OGNL
表達式的值。
“$”有兩個主要的用途,用於在國際化資源檔案中,引用OGNL表達式。在Struts 2和i18n中配置檔案
由來
OGNL stands for Object-Graph Navigation Language; it is an expression language for getting and setting properties of Java objects. You use the same expression for both getting and setting the value of a property.
The ognl.Ognl class contains convenience methods for evaluating OGNL expressions. You can do this in two stages, parsing an expression into an internal form and then using that internal form to either set or get the value of a property; or you can do it in a single stage, and get or set a property using the String form of the expression directly.
OGNL started out as a way to set up associations between UI components and controllers using property names. As the desire for more complicated associations grew, Drew Davidson created what he called KVCL, for Key-Value Coding Language, egged on by Luke Blanshard. Luke then reimplemented the language using ANTLR, came up with the new name, and, egged on by Drew, filled it out to its current state. Later on Luke again reimplemented the language using JavaCC. Further maintenance on all the code is done by Drew (with spiritual guidance from Luke).
發音
We pronounce OGNL as a word, like the last syllables of a drunken pronunciation of "orthogonal."
表達式
OGNL支持各種紛繁複雜的表達式。但是最最基本的表達式的原型,是將對象的引用值用點串聯起來,從左到右,每一次表達式計算返回的結果成為當前對象,後面部分接著在當前對象上進行計算,一直到全部表達式計算完成,返回最後得到的對象。OGNL則針對這條基本原則進行不斷的擴充,從而使之支持對象樹、
數組、容器的訪問,甚至是類似SQL中的投影選擇等操作。
1. 基本對象樹的訪問
對象樹的訪問就是通過使用點號將對象的引用串聯起來進行。
例如:xxxx,xxxx.xxxx,xxxx. xxxx. xxxx. xxxx. xxxx
2. 對容器變數的訪問
例如:#xxxx,#xxxx. xxxx,#xxxx.xxxxx. xxxx. xxxx. xxxx
3. 使用操作符號
OGNL表達式中能使用的操作符基本跟Java里的操作符一樣,除了能使用 +, -, *, /, ++, --, ==, !=, = 等操作符之外,還能使用 mod, in, not in等。
OGNL支持對數組和ArrayList等容器的順序訪問:例如:group.users[0]
同時,OGNL支持對Map的按鍵值查找:
例如:#session['mySessionPropKey']
例如:{"green", "red", "blue"}構造一個List,#{"key1" : "value1", "key2" : "value2", "key3" : "value3"}構造一個Map
例如:new Java.net.URL("xxxxxx/")
要引用類的靜態方法和欄位,他們的表達方式是一樣的@class@member或者@class@method(args):
例如:@com.javaeye.core.Resource@ENABLE,@com.javaeye.core.Resource@getAllResources
6. 方法調用
直接通過類似Java的方法調用方式進行,你甚至可以傳遞參數:
例如:user.getName(),group.users.size(),group.containsUser(#requestUser)
7. 投影和選擇
OGNL支持類似資料庫中的投影(projection) 和選擇(selection)。
投影就是選出集合中每個元素的相同屬性組成新的集合,類似於關係資料庫的欄位操作。投影操作語法為 collection.{XXX},其中XXX 是這個集合中每個元素的公共屬性。
例如:group.userList.{username}將獲得某個group中的所有user的name的列表。
選擇就是過濾滿足selection 條件的集合元素,類似於關係資料庫的紀錄操作。選擇操作的語法為:collection.{X YYY},其中X 是一個選擇操作符,後面則是選擇用的
邏輯表達式。而選擇操作符有三種:
? 選擇滿足條件的所有元素
^ 選擇滿足條件的第一個元素
$ 選擇滿足條件的最後一個元素
例如:group.userList.{? #txxx.xxx != null}將獲得某個group中user的name不為空的user的列表。