EJB3的功能很是強大,但中小企業(yè)很少使用它做開發(fā),一般應(yīng)用SSH足以應(yīng)付。JAVAEE是做什么的?她是分布式企業(yè)級應(yīng)用的規(guī)范,那EJB就是為實現(xiàn)這樣的應(yīng)用而開發(fā)的。
什么是分布式應(yīng)用?聽著名字很大。比如咱們的在線支付系統(tǒng),淘寶、china-pub和amazon等都支持支付寶在線支付。難道它們分別都要將支付寶模塊放在自己的服務(wù)器上?完全沒必要。支付寶模塊單獨放在一個服務(wù)器上,其他服務(wù)器使用(調(diào)用)那一個服務(wù)器上的支付寶就可以了。在現(xiàn)實應(yīng)用中還有一些類似的將某一應(yīng)用模塊單獨取出放到另一個服務(wù)器上,供其他應(yīng)用使用。這就是分布式應(yīng)用了。(分布在不同的服務(wù)器上的應(yīng)用)
一、JAVAEE回顧
JAVAEE到底是什么呢?一圖更清(JavaEE5.0):

昨天我們實現(xiàn)的EJB組件接口使用的是“@Local”注解,此注解只提供給本地調(diào)用。外部機器無法訪問。因為外部用戶與本地不在同一個虛擬機中。我們編寫的EJB組件不主要是給外部用戶訪問而使用分布試應(yīng)用嗎?正是。所以為了讓外部機器可以使用,我們需要將“@Local”注解修改為“@Remote”注解。在外部調(diào)用遠程的EJB組件時,應(yīng)該這樣使用:
InitialContext ctx = new InitialContext(); HelloWorld hw = (HelloWorld)ctx.lookup("HelloWorldBean/remote"); String str = hw.sayHello("changcheng"); |
二、EJB3中的EntityBean
EJB3中的EntityBean就是POJO(Plain old java object)+注解,這方面的知識我們已經(jīng)在JPA中學(xué)習(xí)了。
三、EJB3中的SessionBean
SessionBean分為有狀態(tài)SessionBean(StateFulSessionBean-sfsb)和無狀態(tài)SessionBean(StateLessSessionBean-slsb)。
1.StateLessSessionBean
我們在之前是做的練習(xí)中,一般都有一個DAO層。DAO層不記錄用戶的狀態(tài),所以我們在使用EJB開發(fā)時,可以直接將DAO層定義為無狀態(tài)Bean。
如何定義無狀態(tài)Bean,我們在昨天的練習(xí)中使用的就是無狀態(tài)Bean。直接在類上添加“@Stateless”注解,即可將類定義為無狀態(tài)的Bean。
每次產(chǎn)生一個新的會話,EJB容器需要為會話創(chuàng)建相關(guān)的無狀態(tài)Bean對象,無疑這會為服務(wù)器帶來極大的資源開銷。我們在學(xué)習(xí)JDBC時,使用了線程池專門用來處理用戶與數(shù)據(jù)庫的連接。因為無狀態(tài)Bean不記錄用戶的狀態(tài),所以EJB3中也定義了無狀態(tài)的Bean池。這樣大大節(jié)省了用戶訪問的時間與服務(wù)器資源的開銷。
無狀態(tài)Bean具有兩個事件,創(chuàng)建后和銷毀前。我們在方法上添加“@PostConstruct”注解,此方法將在無狀態(tài)Bean創(chuàng)建后調(diào)用,我們在方法上添加“@PreDestroy”注解,此方法將在無狀態(tài)Bean銷毀后被調(diào)用。
2.StateFulSessionBean
有狀態(tài)Bean的典型案例是購物車,購物車需要記錄用戶購買的商品,所以它是有狀態(tài)的Bean。我們知道無狀態(tài)Bean有Bean池,那有狀態(tài)Bean可否有Bean池呢?當(dāng)然不可以。所以有狀態(tài)Bean會給服務(wù)器帶來極大的負擔(dān),能不用有狀態(tài)Bean就不要使用有狀態(tài)Bean。
但EJB3中也盡量減少有狀態(tài)Bean所帶來的負載,所以EJB3為有狀態(tài)Bean添加了鈍化和激活的功能。比如用戶在超市購物,用戶剛進入超市時推起購物車(CartBean對象,這個有狀態(tài)Bean被創(chuàng)建)。當(dāng)用戶選擇商品并謝謝購物車時(向CartBean對象添加狀態(tài))。突然用戶有急事要離開一會,工作人員將購物車推到“等候區(qū)”(CartBean對象被鈍化)。用戶處理完事物之后回來繼續(xù)購物,到“等候區(qū)”推出自己的購物車(CartBean對象被激活)。用戶結(jié)賬工取消購物時(CartBean對象被刪除)。
EJB3中正是使用方法減少頻繁創(chuàng)建有狀態(tài)Bean所給服務(wù)器帶來的負擔(dān),同時也為用戶提供了方便。
EJB3中有狀態(tài)Bean的事件:
1).有狀態(tài)Bean被創(chuàng)建后:在某一方法上添加“@PostConstruct”注解,有狀態(tài)Bean被創(chuàng)建后會調(diào)用此方法。
2).有狀態(tài)Bean被鈍化:在某一方法上添加“@PrePassivate”注解,調(diào)用此方法后,有狀態(tài)Bean被鈍化。EJB3容器會將此Bean序列化后,保存到硬盤上,并從內(nèi)存中刪除。有狀態(tài)Bean需要實現(xiàn)“Serializable”接口。
3).有狀態(tài)Bean被激活:在某一方法上添加“@PostActivate”注解,調(diào)用此方法后,有狀態(tài)Bean被激活。EJB3容器會將此Bean反序列化后,放到內(nèi)存中,并刪除硬盤上的內(nèi)容。
4).有狀態(tài)Bean被銷毀前:在某一方法上添加“@PreDestroy”注解,無狀態(tài)Bean被銷毀前會調(diào)用此方法。
5).有狀態(tài)Bean被刪除:在某一方法上添加“@Remove”注解,調(diào)用此方法后,通知EJB3容器刪除有狀態(tài)Bean。如果不添加“@Remove”注解,客戶端將沒有任何方式告訴服務(wù)器終止會話,結(jié)果將導(dǎo)致SFSB在超時后進行鈍化至磁盤,再次超時后將對象銷毀。在高并發(fā)的系統(tǒng)當(dāng)中,無疑會有嚴重的性能問題。內(nèi)存開銷會居高不下,更不要提占用cpu和磁盤空間了。
四、EJB3中的消息驅(qū)動Bean
EJB3中的消息驅(qū)動分為隊列消息(queue)和主題消息(Topic),是一種異步消息驅(qū)動。
1.隊列消息
發(fā)送消息:
import javax.jms.*; import javax.naming.InitialContext; /** * 隊列消息驅(qū)動bean */ public class MDBQueueApp { public static void main(String[] args) throws Exception { InitialContext ctx = new InitialContext(); //通過JNDI查找隊列連接工廠,EJB自身包含的。 QueueConnectionFactory qcf = (QueueConnectionFactory) ctx.lookup("QueueConnectionFactory"); //通過連接工廠創(chuàng)建隊列連接 QueueConnection qc = qcf.createQueueConnection(); //通過隊列連接創(chuàng)建隊列會話 QueueSession qs = qc.createQueueSession(false,Session.AUTO_ACKNOWLEDGE); //通過JNDI查找可用隊列,EJB3自身包含的。 Queue queue = (Queue) ctx.lookup("queue/testQueue"); //通過隊列會話創(chuàng)建到指定隊列的消息生產(chǎn)者 MessageProducer mp = qs.createProducer(queue); //通過隊列回話創(chuàng)建文本消息 TextMessage msg = qs.createTextMessage(); msg.setText("Hi,你好嗎?(隊列消息)"); //通過消息的生產(chǎn)者發(fā)送消息 mp.send(msg); //釋放資源 qs.close(); qc.close(); System.out.println("隊列消息發(fā)送完畢!"); } } |
接收消息:
import javax.ejb.*; import javax.jms.*; @MessageDriven(activationConfig={@ActivationConfigProperty(propertyName="destinationType",propertyValue="javax.jms.Queue"),@ActivationConfigProperty(propertyName="destination",propertyValue="queue/testQueue")}) public class MyQueueMDB implements MessageListener { public void onMessage(Message arg0) { if(arg0 instanceof TextMessage){ try { System.out.println("收到消息隊列消息 : " + ((TextMessage)arg0).getText()); System.out.println("I'm fine,Thank you!"); } catch (Exception e) { e.printStackTrace(); } } } } |
隊列消息只能被一個接收都接收到,隊列消息一旦被某個接收者接收到后,消息將從隊列中消失。所以其他接收同一隊列消息的接收者無法收到。這個第一個接收到隊列消息的接收者是最后一次被添加的接收者。
2.主題消息
發(fā)送消息:
import javax.jms.*; import javax.naming.InitialContext; /** * 主題消息驅(qū)動bean */ public class MDBAppTopic { public static void main(String[] args) throws Exception { InitialContext ctx = new InitialContext(); //通過JNDI查找主題連接工廠,EJB3自身包含的。 TopicConnectionFactory tcf = (TopicConnectionFactory) ctx.lookup("TopicConnectionFactory"); //通過連接工廠創(chuàng)建主題連接 TopicConnection tc = tcf.createTopicConnection(); //通過主題連接創(chuàng)建主題會話 TopicSession ts = tc.createTopicSession(false,Session.AUTO_ACKNOWLEDGE); //通過JNDI查找可用主題,EJB3自身包含的。 Topic topic = (Topic) ctx.lookup("topic/testTopic"); //通過主題會話創(chuàng)建到指定主題的消息生產(chǎn)者 MessageProducer mp = ts.createProducer(topic); //通過主題回話創(chuàng)建文本消息 TextMessage msg = ts.createTextMessage(); msg.setText("Hi,你好嗎?(主題消息)"); //通過消息的生產(chǎn)者發(fā)送消息 mp.send(msg); //釋放資源 ts.close(); tc.close(); System.out.println("主題消息發(fā)送完畢!"); } } |
接收消息:
import javax.ejb.*; import javax.jms.*; @MessageDriven(activationConfig={@ActivationConfigProperty(propertyName="destinationType",propertyValue="javax.jms.Topic"),@ActivationConfigProperty(propertyName="destination",propertyValue="topic/testTopic")}) public class MyTopicMDB implements MessageListener { public void onMessage(Message arg0) { if(arg0 instanceof TextMessage){ try { System.out.println("收到主題消息 : " + ((TextMessage)arg0).getText()); System.out.println("我很好,謝謝!"); } catch (Exception e) { e.printStackTrace(); } } } } |
主題消息是一種共享模式,相當(dāng)于群。每個消息被當(dāng)做主題,群內(nèi)的所有成員都可以收到。
五、ANT
我們編寫EJB組件和WEB應(yīng)用時,需要手動打成JAR和WAR包并放置到JBOSS的deploy目錄中。這顯然是不理想的操作方式,我們每測試一個功能都這樣做嗎?ANT可以幫助我們解決這個問題。
當(dāng)一個代碼項目大了以后,每次重新編譯,打包,測試等都會變得非常復(fù)雜而且重復(fù),因此c語言中有make腳本來幫助這些工作的批量完成。在Java 中應(yīng)用是平臺無關(guān)性的,當(dāng)然不會用平臺相關(guān)的make腳本來完成這些批處理任務(wù)了,ANT本身就是這樣一個流程腳本引擎,用于自動化調(diào)用程序完成項目的編譯,打包,測試等。除了基于JAVA是平臺無關(guān)的外,腳本的格式是基于XML的,比make腳本來說還要好維護一些。
《ANT IN ACTION (第2版)》是一本專門調(diào)解ANT的書籍,在此就不多總結(jié)了。