時間到了,繼續我心中的那個她,每天晚上堅持寫博客,感覺真的很好,我想把自己所想的寫出來。這兩天出奇的冷,北京的天氣真郁悶,是不是后天要來了,這種溫度讓我的大腦都結冰了,都語無倫次了,雖然很冷,但是,還是要用我那冰冷的手指敲打鍵盤。
昨天說到美女生氣了,她的沉默使我也很沉默,我也不是怎么會哄女孩的人,天生的呆板,屬于榆木腦袋那種,只能等她心情好的時候跟她多說幾句話。還好沒過幾天,她的心情多云轉晴,我也就自然的晴天了。過了段時間,作為我對她細心培訓的回報,她要請我吃飯。這頓飯可把我害苦了,就是從這頓飯后,每天腦子里都是她的身影(有點過了),但是每當想到她的時候,腦袋就亂,而且很不舒服。吃飯那天自己都不知道說了些什么,本來有很多話要說的,但是不知為什么,沒有說出口?;丶业臅r候她把我送到了車站,在回家的車上,腦子一片空白。還好自己知道在哪里下車,出站的時候突然感覺自己好像丟了什么,心里空蕩蕩的,原來把自己的心丟了,以前在學校的時候,知道沒有思想的痛苦,跟她吃了頓飯,知道了沒有心的痛苦,沒有心的感覺真好,不管身心如何痛苦,時間不會因為你而停止,生活還在繼續。以后的日子里,就不用說了,每天都在快樂并痛苦著,每天白天能有她相伴在身邊是快樂的,下班回家之后想她是痛苦的。
時間一點點的流逝,轉眼快到10.1了,大家都在討論放假回家什么的,說到這里有點對不起自己的父母了,我離家里比較近,動車也就3個小時的路程,但是每年只有過年的時候才回家一次,感覺跟家里人相聚的時間太短了,愧對自己的父母,沒辦法啊,我們這種在外面漂流的人...說她吧,那次聽到她要回家買票難什么的,我也沒打算幫她買,我也沒有那么大的本事,事情湊巧,以前的一個朋友打電話聊天,我想到他是做導游的應該很方便弄票,于是就跟兄弟說了一聲,兄弟是答應了,但是我當她說的時候,她卻說不用了(因為不知道做火車還是汽車),這是明顯的搪塞,那天我生氣了,生氣的不是因為她搪塞,是因為她那不冷不熱、莫不關心的態度,氣的我和她保持沉默了,但是工作上的關系,又不得不說話。還好我的天氣也不是那種南方的陰雨連綿,過幾天就好了,這樣我們還保持著普通朋友的關系。
明天就結束我們2個月的關系了,自己都沒想到,想一個人會是這個樣子,可能她不適合我,我也不適合她,朋友們都說,自己都做程序員了,還找個程序員做老婆。不合適,我也這么覺得。希望我們能成為好朋友,如果你給我當朋友的話,我也會視你為我的朋友。你只能活在我的記憶中了,不是我的心底。因為你沒有走進我。
時間真快,又一個小時了,明天早起去吃早餐,^_^,天氣涼了,胃口上來了。
Good night 我的那個她。
posted @
2009-11-03 00:15 王永慶 閱讀(260) |
評論 (3) |
編輯 收藏
會話Bean同其他企業Bean類型最主要的區別是生命周期的差異性。會話Bean實例是存活短暫的對象,會話Bean實例并不能夠在多客戶間共享。
通常,客戶會話的持續期決定了使用中的會話Bean的存活期,一旦應用服務器癱瘓,會話Bean實例也應該不復存在。因為,會話Bean僅僅是內存對象,一旦其生存的周邊環境遭到破壞,會話Bean也將不復存在。會話Bean并不是持久化的,因此,會話Bean并不會保存到持久化存儲源中,這同實體Bean不一樣,會話Bean能夠操作RDBMS,但是其本身并不是持久化對象。
會話指客戶同EJB組件的交互,它由客戶和EJB組件間的多次方法調用構成。會話Bean存在2種子類型:有狀態會話Bean和無狀態會話Bean,各自用于建模不同類型的會話。有狀態會話Bean是這樣一種EJB,即其服務的業務過程能夠延伸到多個方法請求或者事務中,為完成這種業務過程,有狀態會話Bean需要為單個客戶保存狀態信息。如果在方法調用期間有狀態會話Bean的狀態發生改變,則這種改變必須反映到同一客戶的隨后調用中。無狀態會話Bean是這樣一種EJB,即其服務的業務過程只需要單個業務方法即可完成。由于他們不需維護客戶多個方法調用間的會話狀態,因此它是無狀態的。在每次方法調用結束后,EJB容器可能會銷毀無狀態會話Bean實例,或者實例化新的實例,或者清楚掉上次方法調用中的相關信息。
無狀態意指不存在會話狀態。無狀態會話Bean能夠含有同特定客戶不相關的狀態信息,比如所有客戶將使用到數據庫鏈接工廠,開發者可以將它存儲在private變量中。如果開發者將數據存儲在private變量中,則將隨時丟失其中存儲的數據。
EJB容器將維護EJB實例池,而且這些EJB實例是可重用的。在每次方法調用時,都會有不同EJB實例或同一實例服務客戶。為了限制內存中運行的有狀態會話Bean實例的數量,EJB容器需要將有狀態會話Bean的會話狀態保存到硬盤或者其他存儲源中。該過程稱之為掛起。在掛起有狀態會話Bean后,會話狀態被安全的保存下來,而且其釋放的內存可以供其他應用使用。一旦被掛起的有狀態會話Bean實例的客戶再次調用它,被掛起的會話狀態將重新回到有狀態會話Bean實例中,該過程稱之為激活。
有狀態會話Bean實例的會話狀態必須遵循Java對象序列化設定的規則。在掛起有狀態會話Bean實例時,EJB容器借助于對象序列化將會話狀態轉換成二進制blob,然后將它寫入到硬盤中。在轉移會話狀態信息后,有狀態會話Bean實例(指刮起了會話狀態的那些EJB實例)還能夠服務于其他客戶,即同新的客戶進行新的會話過程。
一旦EJB中的成員變量符合如下條件,則可以認為它是會話狀態的組成部分之一。
1、成員變量是非transient類型的java原始類型。
2、成員變量是非transient類型的Java對象類型。
當容器將EJB實例掛起時,它需要將實例的會話狀態寫入到二級存儲源中,比如文件或者RDBMS中。通過調用EJB實例的ejbPassivate()回調方法,容器能夠完成實例的掛起工作。借助于ejbPassivate()方法能夠告知EJB實例:EJB容器需要掛起它,這使得釋放其持有的資源成為可能。比如EJB實例可能持有的資源有:RDBMS連接、已打開的Socket和文件或者其他任何資源。
在實際場合中,客戶調用了EJB對象中的某個方法,而當時在內存中暫時找不到該EJB對象,與此同時,EJB容器持有的企業Bean實例的個數已經到達了設定的上限。因此在處理客戶請求前,容器需要掛起最近未使用的EJB實例,在掛起它后,容器才能夠獲得所需的EJB對象。
有狀態會話Bean的部署描述符。
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="2.1" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd">
<enterprise-beans>
<session>
<ejb-name>Count</ejb-name>
<home>examples.CountHome</home>
<remote>examples.Count</remote>
<ejb-class>examples.CountBean</ejb-class>
<session-type>Stateful</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
服務端企業Bean:
package com.wyq.ejb02;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
/**
* 演示有狀態Bean
* 它會初始化val,并提供業務方法。
* 該實例演示了最簡單的有狀態會話Bean,并給出了掛起、激活的工作機理。
*/
public class CountBean implements SessionBean {
//會話狀態
public int val;
//業務方法
public int count(){
System.out.println("count");
return ++val;
}
public void ejbCreate(int val)throws CreateException{
this.val = val;
System.out.println("ejbCreate()");
}
public void ejbActivate() throws EJBException, RemoteException {
System.out.println("ejbActivate()");
}
public void ejbPassivate() throws EJBException, RemoteException {
System.out.println("ejbPassivate()");
}
public void ejbRemove() throws EJBException, RemoteException {
System.out.println("ejbRemove()");
}
public void setSessionContext(SessionContext ctx) throws EJBException,
RemoteException {
}
}
客戶端調用:
package com.wyq.ejb02;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import com.wyq.ejb01.HelloHome;
/**
* 1、獲得JNDI InitialContext上下文。
* 2、借助于JNDI,定義Home對象。
* 3、使用Home對象創建3個不同的CountEjb對象。因此,這將建立起3個不同的會話過程,而且模擬了3個不同的客戶。
* 4、由于內存中僅能存活2個EJB實例,因此在創建Count EJB實例期間,EJB容器需要完成實例的掛起操作。
* 5、調用各個EJB對象的count()方法。
* 6、最后,刪除所有的EJB對象。
*/
public class CountClient {
/**
* 客戶代碼示例
*
* 此時,創建了3個EJB對象。但我們規定容器:在內存中最多存在2個實例。
* 因為,能夠看到掛起操作的發生。
*/
public static void main(String[] args) {
try{
/*
* 獲得JNDI環境屬性
*/
Properties props = System.getProperties();
/*
* 獲得對Home對象的引用,Home對象是EJB對象的工廠
*/
Context ctx = new InitialContext(props);
Object obj = ctx.lookup("CountHome");
CountHome home =(CountHome)javax.rmi.PortableRemoteObject.narrow(obj,CountHome.class);
/*
* 能夠持有3個Count對象的數組
*/
Count count[] = new Count[3];
int countVal = 0;
/*
* 創建EJB實例,并調用各自的count()
*/
System.out.println("Instantiating beans
");
for(int i=0;i<3;i++){
/*
* 創建EJB對象,并初始化它們
*/
count[i] = home.create(countVal);
/*
* 加1,并打印出來
*/
countVal = count[i].count();
System.out.print(countVal);
/*
* 等待1/2秒
*/
Thread.sleep(500);
}
/*
* 調用各個EJB對象的count()方法,從而能夠瀏覽到EJB被掛起,并被成功激活
*/
System.out.println("Calling count() on beans
");
for(int i=0;i<3;i++){
/*
* 加1,并打印出來
*/
countVal = count[i].count();
System.out.println(countVal);
/*
* 等待1/2秒
*/
Thread.sleep(500);
}
/*
* 使用完后,銷毀它們
*/
for(int i=0;i<3;i++){
count[i].remove();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
會話Bean的聲明周期流程圖
1、起初,EJB實例并不存在。
2、EJB容器將決定是否需要實例化新的EJB實例。容器將何時實例化新的EJB實例,取決于容器使用的EJB實例池策略。
3、容器實例化EJB Bean類。EJB容器將調用Class.newInsatance("HelloBean.class");即動態創建HelloBean實例,這使得容器不會將EJBBean類的名字硬編碼在Java代碼中。最后,這使得容器更具通用性,能夠操控任何企業Bean.
4、容器調用setSessionContext()方法。這為EJB實例設置了上下文對象。最終,EJB實例將能夠訪問到EJB容器。
5、容器調用ejbCreate().這將初始化EJB實例。由于無狀態會話Bean的ejbCreate()方法并不存在參數,因此EJB客戶不可能為它提供任何啟動EJB實例的參數信息。
6、EJB容器調用EJB實例的業務方法。對于EJB實例提供的所有業務方法,EJB容器都可以使用。由于所有EJB實例間不存在區別,因此完全不同的客戶可以調用相同的業務方法。在業務方法調用結束后,各個無狀態會話Bean實例依然是相同的,因此,EJB容器能夠針對客戶請求,在每個方法級將各個EJB實例指定給客戶,即使同一客戶對同一業務方法的多次調用,都可以由不同的EJB實例響應它,當然,將EJB實例指定給客戶的具體實現策略取決于具體的EJB容器。
7、最后,容器調用ejbRemove()方法。
posted @
2009-11-02 16:35 王永慶 閱讀(192) |
評論 (0) |
編輯 收藏
剛洗了個澡,看了下時間,還不算太晚,23點剛過一點,突然想寫博客了,有點神經質了,o(∩_∩)o...
眼看就要辭職了,離開現在的公司了,馬上就要離開旁邊的那個她了,不知道離開之后,她還能不能想起旁邊的我,我們雖然不是一類人,我這么感覺,不知道離開之后,她的工作還順利不。我還在公司的時候,每當她遇到問題,還有耐心的我幫她解決,遇到問題的時候應該讓她自己去解決,感覺她還是比較獨立的,可能解決問題費勁一點。她不適合做程序員,現在太多人不適合做程序員了,現在的程序員已經不是程序員了。
認識她是在2個月前,那時我已經打算辭職了,但是還沒有提出來。這時的工作到了關鍵的時候,面臨著上線的壓力,公司還在不斷的招人,這時她進入了我們組,第一眼看到她的時候,沒什么感覺,咋一看去,很普通的一個女孩,算不上特別漂亮,但是也有幾分姿色,萬沒有想到就是這么一個有幾分姿色的女孩對我有這么大的殺傷力。不知道經理怎么分的,讓我去帶她,可能經理早就知道我要走。沒有過幾天,公司的座位開始調整,因為現在組員太分散了,比較難溝通,所以又重新排列了座位,不知道怎么回事,把我分到了她的旁邊,也可能比較容易培訓吧。教導她的時候可謂是盡心盡力了,雖然我人比較好,對任何人的請求來之不懼,但是不知不覺中我感覺對她的付出超出了我的義務。開始的時候,感覺她還真把我當了個哥哥,旁邊的幾個好兄弟也總是拿我們開玩笑,總是管她叫嫂子,他們叫嫂子的時候,我的心里可緊張,緊張她生氣,緊張她的心情,緊張她...,不過還好,她的性格很傻很天真的那種,一個耳朵進,一個耳朵出,今天生氣,明天就好的那種,不知道哪一天她真的生氣了,好嚴肅,:-),一臉的陰霾,呵呵,女人生起氣來也很可怕,她用她自己獨特的方式回擊了我們,沉默。沉默得讓我的心不能平靜,不知道從什么時候起,我的心中有了她的一席之地,一顆被栓著的心真的很痛苦,之后的幾天里,她的臉色依舊嚴肅著,感覺和她的距離拉遠了,之后和她說話也都是工作上的多。
0:00,今天先寫到這里吧,明天白天還要繼續技術博客呢,晚上繼續我的那個她,1個小時才寫了這么點,因為認真的回憶了,能讓我回憶的東西真的不多。現在的心中還有這個結,這個結打的好死,費了我好多腦細胞差不多打開了。
脫衣服,準備睡覺,晚安,我心中的那個她。
posted @
2009-11-02 00:03 王永慶 閱讀(345) |
評論 (2) |
編輯 收藏
分散配置:
有時你會發現將配置文件分成幾個分散的配置文件是很有益的。
將Data Source配置到Bean裝配文件中不是很適合。數據庫細節是一個發布信息。而Bean裝配文件的目的是定義如何裝配系統的各個模塊。如果使用ApplicationContext當作Spring容器,那么,在Spring中分離屬性配置是很簡單的。使用Spring的PropertyPlaceholderConfigurer告訴Spring從外部屬性文件裝載一些配置信息。
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url">
<value>jdbc:hsqldb:Training</value>
</property>
<property name="driverClassName">
<value>org.hsqldb.jdbcDriver</value>
</property>
<property name="username">
<value>appUser</value>
</property>
<property name="password">
<value>password</value>
</property>
</bean>
location屬性告訴Spring從哪里找到屬性文件。location屬性允許你使用單個配置文件。如果你想將配置信息分散到多個配置文件中,請使
用PropertyPlaceholderConfigurer的locations屬性設置文件列表,使用這種方式,可以使用占位符變量代替Bean裝
配文件中的硬編碼配置了。
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
<property name="location">
<value>jdbc.properties</value>
</property>
<property name="locations">
<list>
<value>jdbc.properties</value>
<value>security.properties</value>
<value>application.properties</value>
</list>
</property>
</bean>
<bean id="dataSources" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url">
<value>${database.url}</value>
</property>
<property name="driverClassName">
<value>${database.driver}</value>
</property>
<property name="username">
<value>${database.user}</value>
</property>
<property name="password">
<value>${database.password}</value>
</property>
</bean>
定制屬性編輯器:
java.beans.PropertyEditor接口提供了將字符串值映射成非String類型的方法。有一個好用的這個接口的實現-
java.beans.PropertyEditorSupport,它有2個方法我們會感興趣:
1、getAsText():方法返回一個表示屬性值的字符串。
2、setAsText(String value):將傳遞進來的字符串賦給Bean的屬性。
Spring帶了幾種建立在propertyEditorSupport之上的定制編輯器,包括
org.springframework.beans.propertyeditors.URLEditor,它是一個用于將字符串與java.net.URL相互轉換的定制編輯器。
Spring提供的其他定制編輯器包括:
1、ClassEditor-使用包含全稱類名的字符串設置java.lang.Class屬性。
2、CustormDateEditor-使用某種java.text.DateFormat對象將一個字符串設置給java.util.Date屬性。
3、FileEditor-使用包含文件路徑的字符串設置java.io.File屬性。
4、LocalEditor-使用包含地域信息的字符串設置java.util.Local屬性。
5、StringArrayPropertyEditor-將一個包含逗號的String轉換成String數組屬性。
6、StringTrimmerEditor-自動修正字符串屬性,可以選擇將空字符串轉變為null.
<bean id="costomEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="com.wyq.spring.PhoneNumber">
<bean id="phoneEditor" class="com.wyq.spring.PhoneEditor"></bean>
</entry>
</map>
</property>
</bean>
其中的map中的key表示要添加自定義屬性的類,value表示自定義屬性實現的類。
package com.wyq.spring;
import java.beans.PropertyEditorSupport;
public class PhoneEditor extends PropertyEditorSupport {
public void setAsText(String textValue) throws IllegalArgumentException {
String stripped = stripNonNumberic(textValue);
String areaCode = stripped.substring(0,3);
String prefix = stripped.substring(3,6);
String number = stripped.substring(6);
PhoneNumber phone = new PhoneNumber(areaCode,prefix,number);
setValue(phone);
}
private String stripNonNumberic(String original){
StringBuffer allNumberic = new StringBuffer();
for(int i=0;i<original.length();i++){
char c = original.charAt(i);
if(Character.isDigit(c)){
allNumberic.append(c);
}
}
return allNumberic.toString();
}
}
posted @
2009-10-31 17:54 王永慶 閱讀(320) |
評論 (0) |
編輯 收藏
為將企業Bean的中間件服務需求告知EJB容器,開發者還需生成部署描述符。
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="2.1" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd">
<!--
<ejb-name>:企業Bean的昵稱。在部署描述符的其他地方能夠引用它,供設置其他參數使用。
<home>:Home接口的全限定名。
<remote>:遠程接口的全限定名。
<local-home>:本地Home接口的全限定名。
<local>:本地接口的全限定名。
<ejb-class>:企業Bean類的全限定名。
<session-type>:標識企業Bean是有狀態的,還是無狀態的。
<transaction-type>:事務類型。
-->
<enterprise-beans>
<session>
<ejb-name>Hello</ejb-name>
<home>examples.HelloHome</home>
<remote>examples.Hello</remote>
<local-home>examples.HelloLocalHome</local-home>
<local>examples.HelloLocal</local>
<ejb-class>examples.HelloBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
Ejb-jar文件:
HelloWorld EJB組件準備好了之后只需將他們打包成Ejb-jar文件。手工創建jar文件
jar cf HelloWorld.jar *
注意:ejb-jar.xml文件必須放置在META-INF子目錄中。當EJB容器初次打開Ejb-jar文件時,將在ejb-jar.xml中查找Ejb-jar所包含的EJB組件信息。
如何調用EJB組件:
目前,存在2類客戶:
1、基于Java RMI-IIOP的客戶。這類客戶使用JNDI,即通過網絡查找對象。另外,它們使用JTA控制事務。
2、CORBA客戶。這類客戶村尋CORBA標準。這對于使用其他語言訪問EJB組件的客戶而言,意義深遠,CORBA客戶使用CORBA命名服務,即通過網絡查找對象。
訪問EJB組件的步驟:
1、查找Home對象
2、使用Home對象創建EJB對象
3、調用EJB對象的業務方法
4、銷毀EJB對象
查找Home對象:
開發EJB應用的最主要目標之一是,應用代碼能夠"一次編寫,到處運行".如果將已部署在機器A上的EJB應用遷移到機器B上,EJB應用代碼不需要改動,因為EJB實現了位置透明性。
借助于命名和目錄服務能夠實現EJB的位置透明性。命名和目錄服務是能夠在網絡中存儲和查找資源的產品。
在企業部署場景中,EJB服務器使用命名服務存儲位置信息,而這些位置信息是用于標識資源的,其中的資源涉及到EJB Home對象、企業Bean環境屬性、數據庫JDBC驅動、消息服務驅動和其他資源等。通過使用命名服務,應用代碼不用將具體機器或資源名硬編碼在代碼中,這就是EJB所具備的位置透明性,它使得代碼具有便攜性。
為實現位置透明性,EJB容器需要屏蔽掉Home對象的具體位置,使得EJB組件的客戶代碼感知不到其具體位置??蛻舨挥脤ome對象宿主的機器名硬編碼于代碼中。相反,使用JNDI就能夠查找到Home對象。物理上,Home對象可以存在于網絡上的任何地方,比如在運行EJB容器的同一進程地址空間中,其他機器上的EJB容器中。
客戶為定位Home對象,它必須提供企業Bean的Home對象的JNDI昵稱。客戶使用該昵稱標識所需的Home對象。一旦將EJB組件部署到容器中,容器會自動將HelloHome昵稱綁定到Home對象。因此,在對已部署EJB組件的物理機器位置不知情時,開發者能夠在任一機器上使用該昵稱查找到上述Home對象,借助于JNDI能夠查找到它。JNDI能夠在網絡上查找命名服務,或在JNDI樹中查找到Home對象,當然,Home對象也可能位于客戶同一進程中,最后一旦找到Home對象,客戶將獲得對它的引用。
package com.wyq.ejb;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
/**
* 客戶代碼實例,用于調用簡單、無狀態會話Bean中的方法。
*
*/
public class HelloClient {
public static void main(String[] args)throws Exception {
/*
* 設置屬性,用于JNDI初始化,從命令行讀入屬性。
*/
Properties props = System.getProperties();
/*
* 初始上下文是連接到JNDI樹的入口。
* 借助于環境屬性,能夠完成JNDI驅動、服務器的網絡地址等的設置
*/
Context ctx = new InitialContext(props);
/*
* 獲得對Home對象的引用。Home對象是創建EJB對象的工廠。
*/
Object obj = ctx.lookup("HelloHome");
/*
* Home對象是RMI-IIOP對象。因此,需要借助于特殊的RMI-IIOP造型操作將它們造型成RMI-IIOP對象
*/
HelloHome home =(HelloHome)javax.rmi.PortableRemoteObject.narrow(obj,HelloHome.class);
/*
* 使用工廠,來創建Hello EJB對象。
*/
Hello hello = home.create();
/*
* 調用EJB對象的hello()方法。它會將調用委派給EJB Bean類實例。一旦接收到響應結果,它便會返回。
*/
System.out.println(hello.hello());
/*
* 在使用完EJB對象后,需要銷毀它。EJB容器負責銷毀EJB對象。
*/
hello.remove();
}
}
posted @
2009-10-31 12:36 王永慶 閱讀(130) |
評論 (0) |
編輯 收藏
為創建和銷毀EJB對象,開發者需要使用Home接口。Home接口的實現是Home對象,而Home對象由EJB服務器提供的工具生成。
package com.wyq.ejb;
import javax.ejb.EJBHome;
/**
* Hello EJB組件的Home接口。它由EJB服務器提供的工具實現。Home接口實現
* 稱之為Home對象。Home對象充當了創建EJB對象的工廠。
*
* 在該Home接口中,存在create()方法。它對應于HelloBean中的ejbCreate()方法。
*/
public interface HelloHome extends EJBHome {
/**
* 創建EJB對象,
* @return 新建的EJB對象。
*/
Hello create() throws java.rmi.RemoteException,javax.ejb.CreateException;
/**
* 1、為獲得對EJB對象的引用,客戶需要使用create方法。其中,create方法還能夠完成
* EJB組件的初始化工作。
* 2、create方法拋出了如下異常:RemoteException和CreateException。由于Home對象是網絡
* 使能的基于RMI-IIOP的遠程對象,因此要拋出RemoteException異常。
*/
}
本地Home接口是Home接口的高性能版本。
package com.wyq.ejb;
import javax.ejb.EJBLocalHome;
/**
* Hello EJB組件的本地Home接口。它由EJB服務器提供的工具實現。
* 本地Home接口實現稱之為本地Home對象。本地Home對象充當了創建EJB本地對象的工廠。
*
*/
public interface HelloLocalHome extends EJBLocalHome {
/**
* 創建EJB本地對象
* @return 返回新創建的EJB本地對象。
*/
HelloLocal create()throws javax.ejb.CreateException;
}
創建企業Bean類
package com.wyq.ejb;
import java.rmi.RemoteException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
/**
* 演示無狀態會話Bean.
*
*/
public class HelloBean implements SessionBean {
private SessionContext ctx;
//EJB規范定義的方法
public void ejbCreate(){
System.out.println("ejbCreate()");
}
public void ejbRemove() throws EJBException, RemoteException {
System.out.println("ejbRemove()");
}
public void ejbActivate() throws EJBException, RemoteException {
System.out.println("ejbActivate()");
}
public void ejbPassivate() throws EJBException, RemoteException {
System.out.println("ejbPassivate()");
}
/**
* 由于企業Bean宿主在受管容器中,因此容器能夠在任何時候調用企業Bean的方法。但如果企業Bean
* 需要查詢容器的當前信息的話,需要為EJB提供何種信息呢?比如在EJB中,組件本身可能需要查詢當前用戶
* 的安全性憑證信息。
*
* 容器將這些信息包含在稱之為"EJB上下文"的對象中。EJB上下文對象是通往EJB容器的關口。其中,EJB上
* 下文是容器的組成部分,開發者能夠在企業Bean中訪問到它。因此,在某種程度上,EJB上下文是為企業Bean
* 訪問容器提供的回調。該回調有助于企業Bean探知其自身的狀態,并修改他們。
*
* EJB上下文對于會話Bean、實體Bean、消息驅動Bean而言很有用。比如會話Bean含有會話上下文、實體Bean
* 含有實體上下文、消息驅動Bean含有消息驅動上下文。
* 依據EJB類型的不同,開發者能夠分別通過調用setSessionContext、setEntityContext、setMessageDivenContext
* 方法設置EJB上下文信息。
*/
public void setSessionContext(SessionContext ctx) throws EJBException,
RemoteException {
this.ctx = ctx;
}
//業務方法
public String hello(){
System.out.println("hello()");
return "Hello,World!";
}
}
posted @
2009-10-30 13:01 王永慶 閱讀(159) |
評論 (0) |
編輯 收藏
首先開發遠程接口,遠程接口支持EJB組件暴露的所有業務方法。
package com.wyq.ejb;
import javax.ejb.EJBObject;
/**
* Hello EJB組件的遠程接口
*
* 在客戶同EJB對象交互時,需要使用這一接口。容器廠商會實現這一接口,而相應的實現對象
* 就是EJB對象。EJB對象會將客戶請求委派給實際的EJB Bean類。
*
*/
public interface Hello extends EJBObject {
/**
* 返回歡迎信息給客戶。
*/
public String hello()throws java.rmi.RemoteException;
/**
* 注意:遠程接口繼承于javax.ejb.EJBObject.其含義是:容器生成的EJB對象實現了遠程接口(EJBObject)
* 即javax.ejb.EJBObject接口中定義的各個方法。其中包括比較不同EJB對象的方法,刪除EJB對象的方法等。
* 僅實現了單個業務方法,即hello().HelloWorld EJB組件的Bean類需要實現hello方法。另外,由于Hello
* 接口是RMI-IIOP類型的遠程接口,因此必須拋出遠程異常。這也是企業Bean類中hello方法簽名同遠程接口中
* hello方法簽名的區別。
*/
}
為訪問EJB組件的業務方法,本地客戶應該使用本地接口,而不是遠程接口。
package com.wyq.ejb;
import javax.ejb.EJBLocalObject;
/**
* Hello EJB組件的本地接口。
*
* 當本地客戶同EJB本地對象交互時,需要使用這一接口。容器廠商會實現這一接口。
* 而相應的實現對象就是EJB本地對象。EJB本地對象會將客戶請求委派給實際的EJB Bean類。
*
*/
public interface HelloLocal extends EJBLocalObject {
/**
* 返回歡迎信息給客戶,本地接口繼承EjbLocalObject接口,并且不需要拋出RemoteException.
*/
public String hello();
}
posted @
2009-10-30 13:00 王永慶 閱讀(127) |
評論 (0) |
編輯 收藏
由于EJB對象可以運行在與客戶不同的機器上,因此客戶不能夠直接實例化EJB對象。EJB推崇位置透明性,因此從這個角
度考慮客戶不應該對EJB對象的位置信息進行關注。
為獲得對EJB對象的引用,客戶代碼需要從EJB對象工廠中請求EJB對象。該工廠負責實例化EJB對象。EJB規范將這種工廠
稱之為Home對象。Home對象的職責主要有:
1、創建EJB對象
2、查找現有的EJB對象
3、刪除EJB對象
同EJB對象一樣,Home對象專屬于特定的EJB容器。Home對象含有容器特定的邏輯,如負載均衡邏輯、借助于圖形化管理
控制臺追蹤信息等。與此同時,Home對象也是EJB容器的組成部分,通過容器提供的工具能夠自動創建它。
Home對象是創建EJB對象的工廠。但是Home對象是如何實例化EJB對象的呢?為創建Home對象,EJB容器需要掌握這方面的
信息。通過指定Home接口給容器即可完成這方面信息的注入。Home接口簡單的定義了用于創建、銷毀和查找EJB對象的方法。
容器的Home對象實現了Home接口。
使用Home接口存在一個問題,即通過Home接口創建EJB實例速度很慢,而且,借助于遠程接口調用EJB實例也是如此。當
訪問EJB對象時,通常會依次觸發如下內容:
1、客戶調用本地存根
2、存根將參數壓包成適合網絡傳輸格式
3、存根借助于網絡將參數傳遞給骨架
4、骨架將參數解包成適合Java的格式。
5、骨架調用EJB對象。
6、EJB對象獲得所需的中間件服務,如連接池、事務、安全性和生命周期服務。
7、EJB對象調用企業Bean實例,Bean實例處理客戶請求。
從EJB2.0開始,客戶能夠通過本地對象(而不是EJB對象)快速、高效的訪問企業Bean組件。具體過程如下:
1、客戶訪問本地對象
2、本地對象獲得所需的中間件服務
3、一旦企業Bean實例處理完客戶請求,則將結果返回給本地對象,最終傳回給客戶。
EJB對象指請求攔截器,遠程接口指供請求攔截器使用的接口,Home對象指工廠,Home接口指工廠接口。
本地接口存在的缺點:
1、只在同一進程中有效。如在同一應用服務器中存在訪問銀行賬號實體Bean的銀行出納會話Bean,如果EJB組件代碼本身依賴
于本地接口實現,則不能夠通過遠程訪問到它。
2、通過引用(傳址),而不是傳值來marshal參數。
部署描述符:借助于部署描述符文件,EJB組件能夠聲明其依賴的中間件服務。然后,EJB容器將通過部署描述符了解到組件
待使用的中間件服務。
posted @
2009-10-29 15:56 王永慶 閱讀(205) |
評論 (0) |
編輯 收藏
構造函數注入:
Set注入法的缺點是,它無法清晰的表示出哪些屬性是必須的,哪些是可選的。而構造函數注入法的優勢是通過構造函數來強制依賴關系。
使用Set注入時,我們通過<property>元素來注入屬性的值。構造函數注入也一樣,只不過是通過<bean>元素下的<constructor-arg>元素來指定實例化這個bean的時候需要傳遞的參數。constructor-arg沒有name屬性。
解決構造函數參數的不確定性:
有2種方法可以用來處理構造函數的不確定性:通過序號和類型。<constructor-arg>元素有一個可選的index屬性,可以用它來指定構造函數的順序。
<bean id="foo" class="com.springinaction.Foo">
<constructor-arg index="1">
<value>http://www.manning.com</value>
</constructor>
<constructor-arg index="0">
<value>http://www.springinaction.com</value>
</constructor>
</bean>
另一種方法是使用type屬性??梢酝ㄟ^type屬性確定參數的類型
<bean id="foo" class="com.springinaction.Foo">
<constructor-arg type="java.lang.String">
<value>http://www.manning.com</value>
</constructor>
<constructor-arg type="java.net.URL">
<value>http://www.springinaction.com</value>
</constructor>
</bean>
使用構造函數注入的理由:
1、構造函數注入強制使用依賴契約。就是如果沒有提供所有需要的依賴,一個Bean就無法被實例化。
2、由于Bean的依賴都通過它的構造函數設置了,所以沒有必要再寫多余的Set方法。
3、因為只能通過構造函數設置類的屬性,這樣你有效的保證了屬性的不可變性。
Set注入的依據:
1、如果Bean有很多依賴,那么構造函數的參數列表會很長。
2、構造函數只能通過參數的個數和類型來區分。
3、如果構造函數的參數中有2個以上是相同類型的,那么很難確定每個參數的用途。
4、構造函數注入不利于自身的繼承。
自動裝配:
你可以讓Spring自動裝配,只要設置需要自動裝配的<bean>中的autowire屬性。
<bean id="foo" class="com.springinaction.Foo" autowire="autowire type"/>
byName-視圖在容器中尋找和需要自動裝配的屬性名相同的Bean.
byType-視圖在容器中尋找一個與需要自動配置的屬性類型相同的Bean.
constructor-視圖在容器中查找與需要自動裝配的Bean的構造參數一致的一個或多個Bean.
autodetect-首先嘗試使用congstructor來自動裝配,然后使用byType方式。
使用Spring的特殊Bean
編寫一個后處理Bean,Spring為你提供了2次機會,讓你切入到Bean的生命周期中,檢查或者修改它的配置,這叫做后處理,后處理實在Bean實例化以及裝配完成之后發生的。postProcessBeforeInitialization()方法在Bean初始化之前被調用。postProcessAfterInitialization()方法在初始化之后馬上被調用。
package com.wyq.hibernate;
import java.lang.reflect.Field;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class Fuddifier implements BeanPostProcessor {
/**
* postProcessAfterInitialization()方法循環Bean的所有屬性,尋找java.lang.String類型的屬性。對于每個String屬性,把它傳遞給
* fuddify()方法,這個方法將String將變成嘮叨用語。
*/
public Object postProcessAfterInitialization(Object bean, String name)
throws BeansException {
Field[] fields = bean.getClass().getDeclaredFields();
try{
for(int i=0;i<fields.length;i++){
if(fields[i].getType().equals(java.lang.String.class)){
fields[i].setAccessible(true);
String original = (String)fields[i].get(bean);
fields[i].set(bean,fuddify(original));
}
}
}catch(IllegalAccessException e){
e.printStackTrace();
}
return bean;
}
/**
* postProcessBeforeInitialization()方法沒有做任何有意義的工作,它只是簡單的返回沒有修改過的Bean.
*/
public Object postProcessBeforeInitialization(Object bean, String name)
throws BeansException {
return bean;
}
private String fuddify(String orig){
if(orig==null)return orig;
return orig.replaceAll("(r|l)", "w").replaceAll("(R|L)", "W");
}
}
注冊后處理Bean:如果你的應用系統運行在Bean工廠中,你需要調用工廠的addBeanPostProcessor()方法來注冊BeanPostProcessor.
BeanPostProcessor fuddifier = new Fuddifier();
factory.addBeanPostProcessor(fuddifier);
如果你是使用應用上下文,你只需要像注冊其他Bean那樣注冊后處理Bean.
posted @
2009-10-29 14:59 王永慶 閱讀(259) |
評論 (0) |
編輯 收藏
傳統建立應用系統對象之間關聯關系的方法會導致復雜的代碼,使它們很難被服用,很難做單元測試。在Spring中,組件無需自己負責與其他組件的關聯。取而代之的是,容器負責把協作左鍵的引用給予各個組件。創建系統組件之間協作關系的這個動作叫做裝配。
容器是Spring框架的核心。Spring容器使用IOC管理所有組成應用系統的組件。這包括在協作組件之間建立關聯。Spring實際上有2種不同的容器:Bean工廠(org.springframewor.bens.factory.BeanFactory接口定義)是最簡單的容器,提供了基礎的依賴注入支持。應用上下文(由org.springframework.contextApplicationContext接口定義)建立在Bean工廠基礎之上,提供了系統架構服務。
(1)、BeanFactory這個類負責創建和分發Bean.由于Bean工廠知道應用系統中的很多對象,所以它可以在實例化這些對象的時候,創建協作對象間的關聯關系。這樣就把配置的負擔從Bean自身以及Bean的調用者中脫離出來。在Spring中有幾種BeanFactory的實現。其中最常使用的是org.springframework.beans.factory.xml.XmlBeanFactory,它根據XML文件中的定義裝載Bean.
例如:BeanFactory factory = new XmlBeanFactory(new FileInputStream("beans.xml"));
這行代碼告訴Bean工廠從XML文件中讀取Bean的定義信息。但是,現在Bean工廠還沒有實例化Bean.Bean是被延遲載入到Bean工廠中的,就是說Bean工廠會立即把Bean定義信息載入進來,但是Bean只有在被需要的時候才被實例化。
MyBean myBean = (MyBean)factory.getBean("myBean");
當getBean()方法被調用的時候,工廠就會實例化Bean并且使用依賴注入開始設置Bean的屬性。
(2)、ApplicationContext的諸多實現中,有3個實現經常用到:
ClassPathXmlApplicationContext-從類路徑中的XML文件載入上下文定義信息,把上下文定義文件當成類路徑資源。
FileSystemXmlApplicationContext-從文件系統中的XML文件載入上下文定義信息。
XmlWebApplicationContext-從web系統中的XML文件載入上下文定義信息。
例如:ApplicationContext context = new FileSystemXmlApplicationContext("c:/foo.xml");
ApplicationContext context = new ClassPathXmlApplicationContext("foo.xml");
FileSystemXmlApplicationContext只能在指定的路徑中尋找foo.xml文件,而ClassPathXmlApplicationContext可以在整個類路徑中尋找foo.xml.
應用上下文與Bean工廠的另一個重要區別是關于單實例Bean是如何被載入的。Bean工廠延遲載入所有的Bean,知道getBean()方法被調用時Bean才被創建。應用上下文啟動后預載入所有的單實例Bean.
Spring中的Bean缺省情況下是單例模式。在容器分配Bean的時候,它總是返回同一個實例。如果想每次得到不同的實例你需要將Bean定義為原型模式。定義為原型模式意味著你是定義一個Bean藍圖,而不是一個單一的Bean.<bean>的singleton屬性告訴這個bean是否是單例的,如果是true表示是單例的,如果是false表示是原型的。
<bean id="connectionPool" class="com.springinaction.chapter02.MyConnectionPool" init-method="initialize" destroy-method="close"/>
按這樣配置,MyConnectionPool被實例化后initialize()方法馬上被調用,給Bean初始化池的機會。在Bean從容器中刪除之前,close()方法將釋放數據庫連接。
設值注入:它是一種基于標準命名規范的設置Bean屬性的技術。JavaBean規范規定使用對應的set和get方法來設置和獲得Bean的屬性值。<property>元素的子元素<value>用來設置普通類型的屬性,子元素<ref bean="">用來設置要關聯的Bean.
內部Bean:另一種不常使用的裝配Bean引用的方法是在<property>元素中嵌入一個<bean>元素。
<bean id="courseService" class="com.springinaction.service.training.CourseServiceImpl">
<property name="studentService">
<bean class="com.springinaction.service.training.StudentServiceImpl"/>
</property>
</bean>
這種裝配Bean引用的方式的缺點是你無法在其他地方重用這個StudentServiceImpl實例,因為它是一個專門的courseServiceBean建立的實例。
注入的是對象集:如果你有一個屬性是一個列表或是一個Bean引用集合,Spring支持多種類型的集合作為屬性。
<list><set><map><props>
裝配List和數組:裝配一個List屬性,List里的元素可以是任何一種元素,包括<value><ref>甚至是其他<list>
<property name="barList">
<list>
<value>bar1</value>
<ref bean="bar2"/>
</list>
</property>
裝配Set:和List一樣,Set可以包含任何類型的屬性。
<property name="barSet">
<set>
<value>bar1</value>
<ref bean="bar2"/>
</set>
</property>
<set>的使用方法和<list>是一樣的。唯一不同的地方是它被裝到什么樣的屬性中。<list>是向java.util.List或數組里裝配數據,而<set>是向java.util.Set中裝配數據。
裝配Map:
<property name="barMap">
<map>
<entry key="key1">
<value>bar1</value>
</entry>
<entry key="key2">
<value>bar2</value>
</entry>
</map>
</property>
Map中的<entry>的數值和<list>以及<set>的一樣,可以是任何有效的屬性元素。重申一邊,包括<value>、<ref>、<list>甚至是另一個<map>。注意,配置<entry>時,屬性key的值只能是String.這對于java.util.Map的功能來說有一點限制,java.util.Map是允許任何類型的對象作為主鍵的。
裝配properties:java.util.Properties集合是最后一個能在Spring中裝配的集合類。使用<props>元素來裝配它。使用<prop>元素表示每條屬性。
<property name="barProps">
<props>
<prop key="key1">bar1</prop>
<prop key="key2">bar2</prop>
</props>
</property>
設置null
為了將一個屬性設為null,你只要使用<null/>元素就行了。
例如:
<property name="foo"><null/><property>
posted @
2009-10-29 10:28 王永慶 閱讀(1598) |
評論 (2) |
編輯 收藏