要談OGNL在Struts2中的應用,首先得明白OGNL到底是什麼
OGNL 的歷史
OGNL 最初是爲了能夠使用對象的屬性名來建立 UI 組件 (component) 和 控制器 (controllers) 之間的聯繫,簡單來說就是:視圖 與 控制器 之間數據的聯繫。後來爲了應付更加複雜的數據關係,Drew Davidson 發明了一個被他稱爲 KVCL(Key-Value Coding Language) 的語言。 Luke 參與進來後,用 ANTLR 來實現了該語言,並給它取了這個新名字,他後來又使用 JavaCC 重新實現了該語言。目前 OGNL 由 Drew 來負責維護。目前很多項目中都用到了 OGNL,其中不乏爲大家所熟知的,例如幾個流行的 web 應用框架:WebWork【當然struts2也可以說是WebWork升級版】,Tapestry 等。
什麼是 OGNL?
OGNL 是 Object-Graph Navigation Language 的縮寫,從語言角度來說:它是一個功能強大的表達式語言,用來獲取和設置 java 對象的屬性 , 它旨在提供一個更高抽象度語法來對 java 對象圖進行導航,OGNL 在許多的地方都有應用,例如:
1)作爲 GUI 元素(textfield,combobox, 等)到模型對象的綁定語言。
2)數據庫表到 Swing 的 TableModel 的數據源語言。
3)web 組件和後臺 Model 對象的綁定語言 (WebOGNL,Tapestry,WebWork,WebObjects) 。
4)作爲 Jakarata Commons BeanUtils 或者 JSTL 的表達式語言的一個更具表達力的替代語言。
另外,java 中很多可以做的事情,也可以使用 OGNL 來完成,例如:列表映射和選擇。對於開發者來說,使用 OGNL,可以用簡潔的語法來完成對 java 對象的導航。通常來說:通過一個“路徑”來完成對象信息的導航,這個“路徑”可以是到 java bean 的某個屬性,或者集合中的某個索引的對象,等等,而不是直接使用 get 或者 set 方法來完成
OGNL 的基本語法
OGNL 表達式一般都很簡單。雖然 OGNL 語言本身已經變得更加豐富了也更強大了,但是一般來說那些比較複雜的語言特性並未影響到 OGNL 的簡潔:簡單的部分還是依然那麼簡單。比如要獲取一個對象的 name 屬性,OGNL 表達式就是 name, 要獲取一個對象的 headline 屬性的 text 屬性,OGNL 表達式就是 headline.text 。 OGNL 表達式的基本單位是“導航鏈”,往往簡稱爲“鏈”。最簡單的鏈包含如下部分:
表達式組成部分 示例
屬性名稱 如上述示例中的 name 和 headline.text
方法調用 hashCode() 返回當前對象的哈希碼。
數組元素 listeners[0] 返回當前對象的監聽器列表中的第一個元素。
所有的 OGNL 表達式都基於當前對象的上下文來完成求值運算,鏈的前面部分的結果將作爲後面求值的上下文。你的鏈可以寫得很長,例如:
name.toCharArray()[0].numericValue.toString()
上面的表達式的求值步驟:
提取根 (root) 對象的 name 屬性。
調用上一步返回的結果字符串的 toCharArray() 方法。
提取返回的結果數組的第一個字符。
獲取字符的 numericValue 屬性,該字符是一個 Character 對象,Character 類有一個 getNumericValue() 方法。
調用結果 Integer 對象的 toString() 方法。
上面的例子只是用來得到一個對象的值,OGNL 也可以用來去設置對象的值。當把上面的表達式傳入 Ognl.setValue() 方法將導致 InappropriateExpressionException,因爲鏈的最後的部分(toString())既不是一個屬性的名字也不是數組的某個元素。瞭解了上面的語法基本上可以完成絕大部分工作了。
OGNL 表達式
1)常量:字符串:“ hello ” 字符:‘ h ’ 數字:除了像 java 的內置類型 int,long,float 和 double,Ognl 還有如例:
0.01B,相當於 java.math.BigDecimal,使用’ b ’或者’ B ’後綴。 100000H,相當於 java.math.BigInteger,使用’ h ’ 或 ’ H ’ 後綴。
2)屬性的引用例如:user.name
3)變量的引用例如:#name
4)靜態變量的訪問使用 @class@field
5)靜態方法的調用使用 @class@method(args), 如果沒有指定 class 那麼默認就使用 java.lang.Math.
6)構造函數的調用例如:new java.util.ArrayList();
其它的 Ognl 的表達式可以參考 Ognl 的語言手冊。
OGNL的性能
OGNL,或者說表達式語言的性能主要又兩方面來決定,一個就是對表達式的解析 (Parser),另一個是表達式的執行,OGNL 採用 javaCC 來完成 parser 的實現,在 OGNL 2.7 中又對 OGNL 的執行部分進行了加強,使用 javasisit 來 JIT(Just-In-Time) 的生成 byte code 來完成表達式的執行。 Ognl 給這個功能的名字是:OGNL Expression Compilation 。基本的使用方法是:
SimpleObject root = new SimpleObject();
OgnlContext context = (OgnlContext) Ognl.createDefaultContext(null);
Node node = (Node) Ognl.compileExpression(context, root, "user.name");
String userName = (String)node.getAccessor().get(context, root);
表達式語言主要有以下幾大好處:
1)避免(MyType) request.getAttribute()和myBean.getMyProperty()之類的語句,使頁面更簡潔;
2)支持運算符(如+-*/),比普通的標誌具有更高的自由度和更強的功能;
3)簡單明瞭地表達代碼邏輯,使用代碼更可讀與便於維護。
Struts 2中的表達式語言
Struts 2支持以下幾種表達式語言:
1)OGNL(Object-Graph Navigation Language),可以方便地操作對象屬性的開源表達式語言;
2)JSTL(JSP Standard Tag Library),JSP 2.0集成的標準的表達式語言;
3)Groovy,基於Java平臺的動態語言,它具有時下比較流行的動態語言(如Python、Ruby和Smarttalk等)的一些起特性;
4)Velocity,嚴格來說不是表達式語言,它是一種基於Java的模板匹配引擎,具說其性能要比JSP好。
Struts 2默認的表達式語言是OGNL,原因是它相對其它表達式語言具有下面幾大優勢:
支持對象方法調用,如xxx.doSomeSpecial();
支持類靜態的方法調用和值訪問,表達式的格式爲@[類全名(包括包路徑)]@[方法名 | 值名],例如:@java.lang.String@format('foo %s', 'bar')或@tutorial.MyConstant@APP_NAME;
支持賦值操作和表達式串聯,如price=100, discount=0.8, calculatePrice(),這個表達式會返回80;
訪問OGNL上下文(OGNL context)和ActionContext;
操作集合對象。
Struts2中ONGL的使用示例
index.html
- <span style="font-size: large;"><span style="font-size: large;"><html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
- <meta http-equiv="refresh" content="0; url=ognl.action" />
- </head>
- </html></span></span>
User.java
- <span style="font-size: large;"><span style="font-size: large;">package com.javacrazyer.web.action;
- import java.util.Date;
- public class User {
- private Integer id;
- private String loginname;
- private Double score;
- private Boolean gender;
- private Character cha;
- private Date birthday;
- public User(){}
- public User(Integer id, String loginname, Double score, Boolean gender,
- Character cha, Date birthday) {
- this.id = id;
- this.loginname = loginname;
- this.score = score;
- this.gender = gender;
- this.cha = cha;
- this.birthday = birthday;
- }
- public Integer getId() {
- return id;
- }
- public void setId(Integer id) {
- this.id = id;
- }
- public String getLoginname() {
- return loginname;
- }
- public void setLoginname(String loginname) {
- this.loginname = loginname;
- }
- public Double getScore() {
- return score;
- }
- public void setScore(Double score) {
- this.score = score;
- }
- public Boolean getGender() {
- return gender;
- }
- public void setGender(Boolean gender) {
- this.gender = gender;
- }
- public Character getCha() {
- return cha;
- }
- public void setCha(Character cha) {
- this.cha = cha;
- }
- public Date getBirthday() {
- return birthday;
- }
- public void setBirthday(Date birthday) {
- this.birthday = birthday;
- }
- public String info() {
- return "User [birthday=" + birthday + ", cha=" + cha + ", gender="
- + gender + ", id=" + id + ", loginname=" + loginname
- + ", score=" + score + "]";
- }
- }</span></span>
OGNLAction.java
- <span style="font-size: large;"><span style="font-size: large;">package com.javacrazyer.web.action;
- import java.util.ArrayList;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.LinkedHashSet;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- import org.apache.struts2.ServletActionContext;
- import com.opensymphony.xwork2.ActionContext;
- import com.opensymphony.xwork2.ActionSupport;
- public class OGNLAction extends ActionSupport {
- private static final long serialVersionUID = -2554018432709689579L;
- private String loginname;
- private String pwd;
- private User user;
- private Set<String> courseSet;
- private List<String> list;
- private Map<String,String> map;
- private List<User> userList;
- public String execute() throws Exception{
- this.loginname = "xkkkkkkkkkkkkkkkkkkkkkkkk";
- this.user = new User(123, "wrr", 88.9, true, 'B', new Date());
- this.courseSet = new LinkedHashSet<String>();
- this.courseSet.add("corejava");
- this.courseSet.add("JSP/Servlet");
- this.courseSet.add("S2SH");
- this.list = new ArrayList<String>(this.courseSet);
- this.map = new HashMap<String, String>();
- this.map.put("x", "xxx");
- this.map.put("y", "yyy");
- this.map.put("z", "zzz");
- ActionContext context = ActionContext.getContext();
- context.put("uname", "cheney");
- context.put("inte", Integer.valueOf(888888));
- context.put("user2", new User(123, "xxk", 88.9, true, 'B', new Date()));
- this.userList = new ArrayList<User>();
- this.userList.add(new User(1, "zs", 48.9, true, 'D', new Date()));
- this.userList.add(new User(2, "ls", 68.1, true, 'C', new Date()));
- this.userList.add(new User(3, "ww", 78.2, false, 'B', new Date()));
- this.userList.add(new User(4, "zl", 88.3, true, 'A', new Date()));
- //-----------------------------------------------------------------
- //推薦方式:不會跟Servlet API耦合
- context.put("reqAtt", "往ActionContext中put的屬性");
- context.getSession().put("sesAtt", "往ActionContext.getSession()中put的屬性");
- context.getApplication().put("appAtt", "往ActionContext.getApplication()中put的屬性");
- ServletActionContext.getRequest().setAttribute("reqAtt2", "Request作用域中的屬性");
- ServletActionContext.getRequest().getSession().setAttribute("sesAtt2", "Session作用域中的屬性");
- ServletActionContext.getServletContext().setAttribute("appAtt2", "Application作用域中的屬性");
- return SUCCESS;
- }
- public String getAppName(){
- return "這是OGNL的使用示例代碼";
- }
- public String getLoginname() {
- return loginname;
- }
- public void setLoginname(String loginname) {
- this.loginname = loginname;
- }
- public String getPwd() {
- return pwd;
- }
- public void setPwd(String pwd) {
- this.pwd = pwd;
- }
- public User getUser() {
- return user;
- }
- public void setUser(User user) {
- this.user = user;
- }
- public Set<String> getCourseSet() {
- return courseSet;
- }
- public void setCourseSet(Set<String> courseSet) {
- this.courseSet = courseSet;
- }
- public List<String> getList() {
- return list;
- }
- public void setList(List<String> list) {
- this.list = list;
- }
- public Map<String, String> getMap() {
- return map;
- }
- public void setMap(Map<String, String> map) {
- this.map = map;
- }
- public List<User> getUserList() {
- return userList;
- }
- public void setUserList(List<User> userList) {
- this.userList = userList;
- }
- }</span></span>
src/struts.xml
- <span style="font-size: large;"><span style="font-size: large;"><?xml version="1.0" encoding="UTF-8" ?>
- <!DOCTYPE struts PUBLIC
- "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
- "http://struts.apache.org/dtds/struts-2.1.7.dtd">
- <struts>
- <!-- 請求參數的編碼方式 -->
- <constant name="struts.i18n.encoding" value="UTF-8"/>
- <!-- 指定被struts2處理的請求後綴類型。多個用逗號隔開 -->
- <constant name="struts.action.extension" value="action,do,go,xkk"/>
- <!-- 當struts.xml改動後,是否重新加載。默認值爲false(生產環境下使用),開發階段最好打開 -->
- <constant name="struts.configuration.xml.reload" value="true"/>
- <!-- 是否使用struts的開發模式。開發模式會有更多的調試信息。默認值爲false(生產環境下使用),開發階段最好打開 -->
- <constant name="struts.devMode" value="false"/>
- <!-- 設置瀏覽器是否緩存靜態內容。默認值爲true(生產環境下使用),開發階段最好關閉 -->
- <constant name="struts.serve.static.browserCache" value="false" />
- <!-- 是否允許在OGNL表達式中調用靜態方法,默認值爲false -->
- <constant name="struts.ognl.allowStaticMethodAccess" value="true"/>
- <!-- 指定由spring負責action對象的創建
- <constant name="struts.objectFactory" value="spring" />
- -->
- <!-- 是否開啓動態方法調用 -->
- <constant name="struts.enable.DynamicMethodInvocation" value="false"/>
- <package name="my" extends="struts-default" namespace="/">
- <action name="ognl" class="com.javacrazyer.web.action.OGNLAction">
- <result>/ognl_info.jsp</result>
- </action>
- </package>
- </struts></span></span>
ognl_info.jsp
- <span style="font-size: large;"><span style="font-size: large;"><%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
- <%@ taglib uri="/struts-tags" prefix="s" %>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <title>OGNL的使用</title>
- </head>
- <body>
- <h3>OGNL的使用</h3><hr/>
- 訪問Action中的普通屬性: <s:property value="loginname"/><br/>
- 訪問Action中的對象屬性: <s:property value="user.birthday"/><br/>
- 訪問Action中的Set屬性: <s:property value="courseSet.toArray()[0]"/><br/>
- 訪問Action中的List屬性: <s:property value="list[0]"/><br/>
- 訪問Action中的Map屬性的鍵: <s:property value="map.keys.toArray()[1]"/><br/>
- 訪問Action中的Map屬性的值: <s:property value="map.values.toArray()[1]"/><br/>
- 訪問Action中的Map屬性的指定鍵對應的值: <s:property value="map['z']"/><br/>
- 訪問Action中的Map屬性的大小: <s:property value="map.size"/><br/>
- <hr/>
- 訪問ActionContext中的普通屬性:<s:property value="#inte"/><br/>
- 訪問ActionContext中的對象屬性:<s:property value="#user2.loginname"/><br/>
- <hr/>
- 訪問Action中的普通方法:<s:property value="getAppName()"/><br/>
- 訪問ActionContext中的某個對象上的普通方法:<s:property value="#user2.info()"/><br/>
- <hr/>
- 訪問靜態屬性:<s:property value="@java.lang.Math@PI"/><br/>
- 訪問靜態方法:<s:property value="@java.lang.Math@floor(44.56)"/><br/>
- 訪問Math類中的靜態方法:<s:property value="@@floor(44.56)"/><br/>
- <hr/>
- 調用java.util.Date的構造方法:<s:date name="new java.util.Date()" format="yyyy-MM-dd HH:mm:ss"/><br/>
- 調用java.util.Date的構造方法創建對象,再調用它的方法:<s:property value="new java.util.Date().getTime()"/><br/>
- <hr/>
- 投影查詢:獲取userList中所有loginname的列表:<s:property value="userList.{loginname}"/><br/>
- 選擇查詢:獲取userList中所有score大於60的loginname列表:<s:property value="userList.{?#this.score>60.0}.{loginname}"/><br/>
- 選擇查詢:獲取userList中所有score大於60並且gender爲true的loginname列表:<s:property value="userList.{?(#this.score>60.0 && #this.gender)}.{loginname}"/><br/>
- 選擇查詢:獲取userList中所有score大於60並且gender爲true的第一個元素的loginname:<s:property value="userList.{^(#this.score>60.0 && #this.gender)}.{loginname}"/><br/>
- 選擇查詢:獲取userList中所有score大於60並且gender爲true的最後一個元素的loginname:<s:property value="userList.{$(#this.score>60.0 && #this.gender)}.{loginname}"/><br/>
- <hr/>
- 訪問名爲xxx的請求參數對應的第一個值:<s:property value="#parameters.xxx[0]"/><br/>
- 訪問通過ActionContext中放入Request中的屬性:<s:property value="#request.reqAtt"/><br/>
- 訪問通過ServletActionContext中放入Request中的屬性:<s:property value="#request.reqAtt2"/><br/>
- 訪問通過ActionContext中放入Session中的屬性:<s:property value="#session.sesAtt"/><br/>
- 訪問通過ServletActionContext中放入Session中的屬性:<s:property value="#session.sesAtt2"/><br/>
- 訪問通過ActionContext中放入ServletContext中的屬性:<s:property value="#application.appAtt"/><br/>
- 訪問通過ServletActionContext中放入ServletContext中的屬性:<s:property value="#application.appAtt2"/><br/>
- 直接訪問屬性域中指定名稱的屬性對應的值:<s:property value="#attr.sesAtt2"/><br/>
- <br/><br/><hr/>
- <s:iterator value="userList" status="vs">
- <s:if test="%{#vs.odd}">
- <span style="color: red">
- <s:property value="#vs.count"/>: <s:property value="loginname"/>,<s:date name="birthday" format="yyyy-MM-dd HH:mm:ss"/><br/>
- </span>
- </s:if>
- <s:else>
- <span style="color: blue">
- <s:property value="#vs.count"/>: <s:property value="loginname"/>,<s:date name="birthday" format="yyyy-MM-dd HH:mm:ss"/><br/>
- </span>
- </s:else>
- </s:iterator>
- <hr/><s:debug/>
- </body>
- </html></span></span>
總結:
在上邊大家都好奇爲什麼都用struts的S標籤,因爲OGNL是通常要結合Struts 2的標誌一起使用,如<s:property value="xx" />等
Action類與JSP頁面之間的數據傳遞
1) 通過HttpServletRequest,HttpSession,ServletContext來傳遞數據。
a) Action中傳數據:在Action類的請求處理方法中先獲取各個作用域對象
ServletActionContext.getRequest();
ServletActionContext.getRequest().getSession();
ServletActionContext.getServletContext();
然後調用相應的setAttribute(String "鍵", Object 值);
b) 在JSP頁面中取數據:可以使用EL表達式或代碼片段來取出對應作用域中屬性值。
c) 頁面中的請求參數傳遞到Action中時,Action中直接定義對應名稱的屬性,並提供setter方法即可封裝此數據。
2) 通過ActionContext實例來傳遞數據。 ActionContext針對每個正在執行Action的線程中綁定一份。
a) Action中通過ActionContext傳遞數據。
ActionContext提供了put(String "鍵", Object 值); //數據不會映射到HttpServletRequest中。
ActionContext提供的getSession().put(String "鍵", Object 值); //數據會自動映射到HttpSession中。
ActionContext提供的getApplication().put(String "鍵", Object 值); //數據會自動映射到ServletContext中。
b) 在JSP頁面取數據:struts2推薦使用OGNL來取ActionContext中的數據。
1. Struts2中的OGNL的使用。
2. OGNL:對象圖導航語言。通過OGNL表達式可以獲取對象的屬性,調用對象的方法,或構造出對象。
1) OGNL上下文中有一個根對象。這個根對象可以直接獲取。不需要#。
2)支持常量:
字符串常量、字符常量、
數值常量:int、long、float、double
布爾常量:true、false
Null常量 : null
支持操作符:支持Java的所有操作符,還支持特有的操作符: ,、 {}、in、not in;
Struts2中的OGNL:
1) Struts2將ActionContext設置爲OGNL上下文,並將值棧(ValueStack)作爲OGNL的根對象放置到ActionContext中。
2) Struts2總是把當前Action實例放置在值棧的棧頂。所以,在OGNL中引用Action中的屬性也可以省略“#”。
常用標籤
1) <s:property value="OGNL"/>
2) <s:date name="OGNL" format=""/>
3) <s:if test="OGNL"></s:if><s:elseif test="OGNL"></s:elseif><s:else></s:else>
★4) <s:iterator value="OGNL" status="vs">...</s:iterator>
5) <s:debug/>
struts2中#、%和$這三個符號的使用方法【摘自max struts2教程】
一、"#"的用法
1、 訪問OGNL上下文和Action上下文,#相當於ActionContext.getContext();下表有幾個ActionContext中有用的屬性:
parameters 包含當前HTTP請求參數的Map #parameters.id[0]作用相當於request.getParameter("id")
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屬性,直到找到爲止
2、用於過濾和投影(projecting)集合,如books.{?#this.price<100};
3、構造Map,如#{'foo1':'bar1', 'foo2':'bar2'}。
二、"%"的用法
“%”符號的用途是在標誌的屬性爲字符串類型時,計算OGNL表達式的值。例如在Ognl.jsp中加入以下代碼:
<h3>%的用途</h3>
<p><s:url value="#foobar['foo1']" /></p>
<p><s:url value="%{#foobar['foo1']}" /></p>
三、"$"的用法
1、用於在國際化資源文件中,引用OGNL表達式
2、在Struts 2配置文件中,引用OGNL表達式
例如:
<action name="AddPhoto" class="addPhoto">
<interceptor-ref name="fileUploadStack" />
<result type="redirect">ListPhotos.action? albumId=${albumId}</result>
</action>