1、OGNL表達式語言簡介
OGNL是Object Graphic Navigation Language(對象圖導航語言)的縮寫,它是一個開源項目。Struts2框架使用OGNL作爲默認的表達式語言。
相對於EL表達式,它提供了平時我們需要的一些功能,如:
* 支持對象方法調用,如xxx.sayHello();
* 支持類靜態方法調用和直接訪問,表達式的格式爲@[類全名(包括包路徑)]@[方法名|值名],例如:@java.lang.String@format("foo %s","bar")或@com.rlcc.Constant@APP_NAME;
* 操作集合對象。
2、上下文中對象的訪問
ognl有一個上下文(Context)概念,說白了上下文就是一個MAP結構,它實現了java.util.Map接口,在Struts中上下文(Context)的實現爲ActionContext。下面爲ognl上下文結構示意圖:
OGNL Context(在Struts中的實現爲ActionContext):
ValueStack(值棧,它是根對象)
Parameters
request
session
application
attr
當struts2接收一個請求時,會迅速創建ActionContext,ValueStack,action。然後把action存放進ValueStack,所以action的實例變量可以被OGNL訪問。
訪問上下問(Context)中的對象需要使用 #符號標註命名空間,如#application、#session
另外OGNL會設定一個根對象(root對象),在Struts2中根對象就是ValueStack(值棧)。如果要訪問根對象(即ValueStack)中的屬性,則可以省略#命名空間,直接訪問該對象的屬性即可。
在struts2中,根對象ValueStack的實現類爲OgnlValueStack,該對象不是我們想像的只存放單個值,而是存放一組對象。在OgnlValueStack類裏有一個List類型的root變量,就是使用他存放一組對象
context-----------------request
application
OgnlValueStack root變量[action,OgnlUtil,...]
session
attr
parameters
在root變量中處於第一位的對象叫棧頂對象。通常我們在OGNL表達式裏直接寫上屬性的名稱即可訪問root變量裏對象的屬性,搜索順序是從棧頂對象開始尋找,如果棧頂對象不存在該屬性,就會從第二個對象尋找,如果沒有找到就從第三個對象尋找,依次往下訪問,直到找到爲止。
注意:Struts2中,OGNL表達式需要配合Struts標籤纔可以使用。如:<s:property value="name"/> ,value屬性的值爲OGNL表達式
由於ValueStack(值棧)是Struts2中OGNL的根對象,如果用戶需要訪問值棧中的對象,在JSP頁面可以直接通過下面的EL表達式訪問ValueStack中對象的屬性:
${foo}//獲得值棧中某個對象的foo屬性
如果訪問其他Context中的對象,由於他們不是根對象,所以在訪問時,需要添加#前綴。
* application對象:用於訪問ServletContext,例如#application.userName或者#application['userName'],相當於調用ServletContext的getAttribute("userName")。
* session對象:用於訪問HttpSession,例如#session.userName或者#session['userName'],相當於調用session.getAttribute("userName")。
* request對象:用於訪問HttpServletRequest,例如#request.userName或者#request['userName'],相當於調用request.getAttribute("userName")。
* parameters對象:用於訪問HTTP的請求參數,例如#parameters.userName或者#parameters['userName'],相當於調用request.getParameter("userName")。
* attr對象:用於按page->request->session->application順序訪問其屬性。
3、爲何使用EL表達式能夠訪問ValueStack中對象的屬性
原因是Struts2對HttpServletRequest作了進一步的封裝。簡略代碼如下:
public class StrutsRequestWrapper extends HttpServletRequestWrapper{
public StrutsRequestWrapper(HttpServletRequest req){
super(req);
}
public Object getAttribute(String s){
...
ActionContext ctx = ActionContext.getContext();
Object attribute = super.getAttribute(s);//先從request範圍獲取屬性值
if(ctx != null){
if(attribute == null){//如果從request範圍沒有找到屬性值,即從ValueStack中查找對象的屬性值
......
ValueStack stack = ctx.getValueStack();
attribute = stack.findValue(s);
......
}
}
return attribute;
}
}
4、採用OGNL表達式創建List/Map集合對象
如果需要一個集合元素的時候(例如List對象或者Map對象),可以使用OGNL中同集合相關的表達式。使用如下代碼直接生成一個List對象:
<s:set name="list" value="{'zhangming','xiaoi','liming'}"/>
<s:iterator value="#list">
<s:property/><br>
</s:iterator>
Set標籤用於將某個值放入指定範圍。
scope:指定變量被放置的範圍,該屬性可以接收application、session、request、page或action。如國沒有設置該屬性,則默認放置在OGNL Context中(即該變量和ValueStack、request...等對象並列,使用時可以在變量名前加前綴#)。
value:賦給變量的值。如果沒有設置該屬性,則將ValueStack棧頂的值賦給變量。
生成一個Map對象:
<s:set name="foobar" value="#{'foo1':'bar1','foo2':'bar2'}"/>
<s:iterator value="#foobar">
<s:property value="key"/>=<s:property value="value"/><br>
</s:iterator>
5、採用OGNL表達式判斷對象是否存在於集合中
對於集合類型,OGNL表達式可以使用in和not in兩個元素符號。其中,in表達式用來判斷某個元素是否在指定的集合對象中;not in判斷某個元素是否不在指定的集合對象中,如下所示:
in表達式:
<s:if test="'foo' in {'foo','bar'}">
在
</s:if>
<s:else>
不在
</s:else>
not in表達式:
<s:if test="'foo' not in {'foo','bar'}">
不在
</s:if>
<s:else>
在
</s:else>
6、OGNL表達式實現投影功能
除了in和not in之外,OGNL還允許使用謀個規則獲得集合對象的子集,常用的有以下3個相關操作符:
:獲得所有復合邏輯的元素。
^:獲得符合邏輯的第一個元素。
$:獲得符合邏輯的最後一個元素。
例如代碼:
<s:iterator value="books.{#this.price>35}">
<s:property value="title"/>-$<s:property value="price"/><br>
</s:iterator>
在上面代碼中,直接在集合後緊跟.{}運算符表明用於去除該集合的子集,{}內的表達式用於獲取符合條件的元素,this指的是爲了從大集合books篩選數據到小集合,需要對大集合books進行迭代,this代表當前迭代的元素。本例的表達式用於獲取集合中價格大於35的書集合。
public class BookAction extends ActionSupport{
private List<Book> books;
......
@Override
public String execute(){
books = new LinkedList<Book>();
books.add(new Book("A735619678","spring",67));
books.add(new Book("B435555322","ejb3.0",15));
}
}
二十、Struts2標籤
1、property標籤
property標籤用於輸出指定值:
<s:set name="name" value="kk"/>
<s:property value="#name"/>
default:可選屬性,如果需要輸出的屬性值爲null,則顯示該屬性指定的值。
escape:可選屬性,指定是否格式化HTML代碼。
value:可選屬性,指定需要輸出的屬性值,如果沒有指定該屬性,則默認輸出ValueStack棧頂的值。
id:可選屬性,指定該元素的標識。
2、iterator迭代標籤
iterator標籤用於對集合進行迭代,這裏的集合包括List、Set和數組。該標籤使用時會把集合放在ValueStack的棧頂,所以可以直接用property標籤來輸出集合的元素值。
<s:set name="list" value="{'zhangsan','lisi','wangwu'}"/>
<s:iterator value="#list" status="st">
<font color=<s:if test="#st.odd">red</s:if><s:else>blue</s:else>
><s:property/></font><br>
</s:iterator>
value:可選屬性,指定被迭代的集合,如果沒有設置該屬性,則使用ValueStack棧頂的集合。
id:可選屬性,指定集合理元素的id(已被標註爲過時)
status:可選屬性,該屬性指定迭代時的IteratorStatus實例。該實例包含如下幾個方法:
int getCount,返回當前迭代了幾個元素。
int getIndex(),返回當前被迭代元素的索引。
boolean isEven(),返回當前被迭代元素的索引是否是偶數。
boolean isOdd(),返回當前被迭代元素的索引是否是奇數。
boolean isLast(),返回當前被迭代元素是否是最後一個 元素。
3、if/elseif/else標籤
<s:set name="age" value="21"/>
<s:if test="#age==23">
23
</s:if>
<s:elseif test="#age==21">
21
</s:elseif>
<s:else>
都不等
</s:else>
4、url標籤
<s:url action="helloworld_add" namespace="/test">
<s:param name="personId" value="23"/> //value屬性裏的值默認爲OGNL表達式的值,如果找不到該OGNL表達式值,則原樣輸出,例如:<s:param name="personId" value="#person.id"/>
</s:url>
生成類似如下路徑:
${pageContext.request.contextPath}/test/helloworld_add.actionpersonId=23
當標籤的屬性value值作爲字符串類型處理時,“%”符號的用途是計算OGNL表達式的值。
<s:set name="myurl" value=" 'http://www.foshanshop.net' "/>
<s:url value="#myurl"/><br>
<s:url value="%{#myurl}"/>
輸出結果:
#myurl
http://www.foshanshop.net
二十一、Struts2表單標籤
1、checkboxlist複選框
a、如果集合爲list
<s:checkboxlist name="list" list="{'Java','.Net','RoR','PHP'}" value="{'java','.net'}"/>
生成代碼如下html代碼:
<input type="checkbox" name="list" value="Java" checked="checked"/><label>Java</label>
<input type="checkbox" name="list" value=".Net" checked="checked"/><label>.Net</label>
<input type="checkbox" name="list" value="RoR"/><label>RoR</label>
<input type="checkbox" name="list" value="PHP"/><label>PHP</label>
b、如果集合爲MAP
<s:checkboxlist name="map" list="#{1:'Java',2:'.Net',3:'RoR',4:'PHP'}" listKey="key" listValue="value" value="{1,2,3}"/>
生成代碼如下html代碼:
<input type="checkbox" name="map" value="1" checked="checked"/><label>Java</label>
<input type="checkbox" name="map" value="2" checked="checked"/><label>.Net</label>
<input type="checkbox" name="map" value="3" checked="checked"/><label>RoR</label>
<input type="checkbox" name="map" value="4"/><label>PHP</label>
注:strut2生成這些html代碼時,會先生成一個<tr><td class="tdLable"></td></tr>主題標籤,如果我們不希望strut2給我們生成這個多餘的主題標籤,則可以在常量裏面配置主題視圖常量:
<constant name="struts.ui.theme" value="simple"/>
c、如果集合裏存放的是javabean
<%
Person person1 = new Person(1,"第一個");
Person person2 = new Person(1,"第二個");
List<Person> list = new ArrayList<Person>();
list.add(person1);
list.add(person2);
request.setAttribute("persons",list);
%>
<s:checkboxlist name="beans" list="#request.persons" listKey="personid" listValue="name"/>
personid和name爲Person的屬性
生成如下html代碼:
<input type="checkbox" name="beans" value="1"/><label>第一個</label>
<input type="checkbox" name="beans" value="2"/><label>第二個</label>
2、radio單選框
使用方法和複選框相同
a、如果集合爲list
<s:radio name="list" list="{'Java','.Net','RoR','PHP'}" value=" 'java' "/>
生成代碼如下html代碼:
<input type="radio" name="list" value="Java" checked="checked"/><label>Java</label>
<input type="radio" name="list" value=".Net" /><label>.Net</label>
<input type="radio" name="list" value="RoR"/><label>RoR</label>
<input type="radio" name="list" value="PHP"/><label>PHP</label>
b、如果集合爲MAP
<s:radio name="map" list="#{1:'Java',2:'.Net',3:'RoR',4:'PHP'}" listKey="key" listValue="value" value="1"/>
生成代碼如下html代碼:
<input type="radio" name="map" value="1" checked="checked"/><label>Java</label>
<input type="radio" name="map" value="2"/><label>.Net</label>
<input type="radio" name="map" value="3"/><label>RoR</label>
<input type="radio" name="map" value="4"/><label>PHP</label>
c、如果集合裏存放的是javabean
<%
Person person1 = new Person(1,"第一個");
Person person2 = new Person(1,"第二個");
List<Person> list = new ArrayList<Person>();
list.add(person1);
list.add(person2);
request.setAttribute("persons",list);
%>
<s:radio name="beans" list="#request.persons" listKey="personid" listValue="name"/>
personid和name爲Person的屬性
生成如下html代碼:
<input type="radio" name="beans" value="1"/><label>第一個</label>
<input type="radio" name="beans" value="2"/><label>第二個</label>
3、select下拉列表框
和複選框使用方法一樣
<s:select name="list" list="{'Java','.Net','RoR','PHP'}"/>
<s:select name="beans" list="#request.persons" listKey="personid" listValue="name"/>
<s:select name="map" list="#{1:'Java',2:'.Net',3:'RoR',4:'PHP'}" listKey="key" listValue="value" value="3"/>
4、<s:token/>標籤防止重複提交
<s:token/>標籤防止重複提交,用法如下:
第一步:在表單中加入<s:token/>
<s:form action="helloworld_other" method="post" namespace="/test">
<s:textfield name="person.name"/><s:token/><s:submit/>
</s:form>
第二步:
<action name="helloworld_*" class="com.rlcc.action.HelloWorldAction" method="{1}">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="token"/>
<result name="invalid.token">/WEB-INF/page/message.jsp</result>
<result>/WEB-INF/page/result.jsp</result>
</action>
以上配置加入了“token”攔截器和“invalid.token”結果,因爲“token”攔截器會話的token與請求的token不一致時,將會直接返回“invalid.token”結果。
在debug狀態,控制檯會出現下面信息,是因爲Action中並沒有struts.token和struts.token.name屬性,我們不用關心這個錯誤:
嚴重:ParametersInterceptor-[setParameters]:Unexpected Exception caught setting 'struts.token' on 'class xxx:Error setting expression 'struts.token' with value '[Ljava.lang.String;@19f16f]' '
嚴重:ParametersInterceptor-[setParameters]:Unexpected Exception caught setting 'struts.token.name'
二十二、Struts2+Spring2.5+Hibernate3.3整合開發
1、整合開發時Struts2、hibernate、Spring需要的JAR。
Struts2:
--------------------------------------------------------------------
struts2-core-2.xx.jar:Struts2框架的核心類庫
Xwork-core-2.x.x.jar:XWork類庫,Struts2在其上構建
ognl-2.6.x.jar:對象圖導航語言(Object Graph Navigation Language),struts2框架通過其讀寫對象的屬性
frermarker-2.3.x.jar:Struts2的UI標籤的模版使用FreeMarker編寫
commons-fileupload-1.2.x.jar:文件上傳組件,2.1.6版本後需要加入此jar文件
commons-io-1.3.2.jar:文件上傳組件依賴到該jar文件
struts2-spring-plugin-2.x.x.jar:用於struts2集成Spring的插件
hibernate
----------------------------------------------------------------------
核心包
hibernate3.jar
lib\bytecode\calib\hibernate-cglib-repack-2.1_3.jar
lib\required\*.jar
註解包
hibernate-annotations.jar
lib\ejb3-persistence.jar、hibernate-commons-annotations.jar
針對JPA的實現包(使用hibernate枚舉類型的映射時需要該實現包)
hibernate-entitymanager.jar
lib\test\log4j.jar、slf4j-log4j12.jar
Spring
----------------------------------------------------------------------------
dist\spring.jar
lib\c3p0\c3p0-0.9.1.2.jar
lib\aspectj\aspectjweaver.jar、aspectjrt.jar
lib\calib\cglib-nodep-2.1_3.jar
lib\j2ee\common-annotations.jar
lib\log4j\log4j-1.2.15.jar
lib\jakarta-commons\commons-logging.jar
數據庫驅動jar
注:建議先整合spring+hibernate,然後再整合struts2
2、Spring配置模版
<xml version="1.0" encoding="UTF-8">
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
</beans>
3、在Spring中配置數據源
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="org.gjt.mm.mysql.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/dbNameuseUnicode=true&characterEncoding=UTF-8"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
<!--初始化時獲取的連接數,取值應在minPoolSize與maxPoolSize之間。Default:3-->
<property name="initialPoolSize" value="1"/>
<!--連接池中保留的最小連接數-->
<porperty name="minPoolSize" value="1"/>
<!--連接池中保留的最大連接數。Default:15-->
<property name="maxPoolSize" value="300"/>
<!--最大空閒時間,60秒內未使用則連接被丟棄。若爲0則永不丟棄。Default:0-->
<property name="maxIdleTime" value="60"/>
<!--當連接池中的連接耗盡時,c3p0一次同時獲取的連接數。Default:3-->
<property name="acquireIncrement" value="5"/>
<!--每60秒檢查所有連接池中的空閒連接。Default:0-->
<property name="idleConnectionTestPeriod" value="60"/>
</bean>
4、集成hibernate
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>com/rlcc/bean/buyer.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.hbm2ddl.auto=update
hibernate.show_sql=false
hibernate.format_sql=false
</value>
</property>
</bean>
5、使用Spring容器管理事務服務
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!--使用基於註解方式配置事務-->
<tx:annotation-driven transaction-manager="txManager"/>
6、配置hibernate實體映射文件buyer.hbm.xml
<xml version="1.0" encoding="UTF-8">
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.rlcc.bean">
<class name="Buyer" table="buyer">
<id name="username" length="20"/>
<property name="password" length="20" not-null="true"/>
<property name="gender" not-null="true" length="5">
<type name="org.hibernate.type.EnumType">
<param name="enumClass">com.rlcc.bean.Gender</param>
<!--12爲java.sql.Types.VARCHAR常量值,即保存枚舉的字面值到數據庫。如果不能指定type參數,保存枚舉的索引值(從0開始)到數據庫-->
<param name="type">12</param>
</type>
</property>
</class>
</hibernate-mapping>
7、在web容器中使用Listener實例化spring容器和配置struts2
<!--指定spring的配置文件,默認從web根目錄尋找配置文件,我們可以通過spring提供的classpath:前綴指定從類路徑下尋找-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans.xml</param-value>
</context-param>
<!--對spring容器進行實例化-->
<listener>
<listener-class>org.springframework.context.ContextLoaderListener</listener-class>
</listener>
配置struts2
<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>
8、struts2的配置文件模版
struts.xml如下:
常量struts.objectFactory=spring明確指出將由Spring負責創建Action實例
<xml version="1.0" encoding="UTF-8" >
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<!--默認的視圖主題-->
<constant name="struts.objectFactory" value="spring"/>
<package name="person" namespace="/person" extends="struts-default">
<global-results>
<result name="message">/WEB-INF/page/message.jsp</result>
</global-results>
<action name="action_*" class="personList" method="{1}">
<result name="list">/WEB-INF/page/person.jsp</result>
</action>
</package>
</struts>
爲了能從spring容器中尋找到Action bean ,要求action配置的class屬性值和spring中的bean名稱相同。