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

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