摘要:
這篇文章提供了一個對J2EE的簡化,展示了如何消除應用服務器的消耗和限制。特別地,這篇文章提到了:許多應用程序實際上并不需要運行應用服務器。
盡管J2EE平臺(應用程序服務器)及其編程模型(企業JAVA組件,簡稱EJB)擁有的眾所周知的復雜性,但是基于J2EE的應用程序仍然在企業領域里變得非常成功.我們要感謝應用于輕量級容器的控制反轉(IoC)和面向方面編程(AOP),比如Spring框架. 我們能夠更簡單地設計更大型的編程模型。然而,即使有了這些工具,應用服務器仍然是復雜度和消耗的一個重要瓶頸。這篇文章提供了一個對J2EE的簡化,展示了如何消除應用服務器的消耗和限制。特別地,這篇文章提到了:許多應用程序實際上并不需要運行應用服務器。這樣,J2EE應用組件將會變得:
·????????開發更容易:不再需要EJB運行代碼;
·????????更簡單: 繼承不需要EJB類或接口;
·????????測試更容易:你的應用程序及測試能在你的開發環境(IDE)中直接運行;
·????????更少的資源消耗:你只需要你的對象,不需要應用服務器,更不需要應用服務器的對象;
·????????安裝更容易:沒有運行應用服務專門的安裝軟件, 沒有加載額外的XML文件;
·????????維護更容易:所有的過程都更簡單,因此維護也更容易。
J2EE不必要的復雜度已經成為一個阻礙。今天,這種復雜度能夠通過在這篇文章中提到的方法來避免。另外,程序還能夠保留事務和安全這些典型的服務。J2EE程序從來沒有比這更有趣過。
版權聲明:任何獲得Matrix授權的網站,轉載時請務必保留以下作者信息和鏈接作者:Guy Pardon;
chmei83(作者的blog:
http://blog.matrix.org.cn/page/chmei83)
原文:
http://www.onjava.com/pub/a/onjava/2006/02/08/j2ee-without-application-server.html譯文:
http://www.matrix.org.cn/resource/article/44/44250_J2ee+Application+Server.html關鍵字:J2ee;Application;Server
例子:消息驅動Bank為了闡述我們的觀點,我們將開發和安裝一個完整的樣板應用程序:一個消息驅動的銀行系統. 通過(幸虧有Spring)改進的基于POJOs的編程模型和保留相同的事務,我們可以不需要EJB或者一個應用服務器來實現這個系統。在下一個部分,我們將從消息驅動架構產生到另一個架構.就像基于WEB的架構一樣.圖1展示我們的樣本應用程序的架構.??

Figure 1. Architecture of the message-driven bank
在我們的例子中,我們將處理來自Java消息服務隊列的銀行定單.一張定單的處理包括通過JDBC來更新當前帳戶的數據庫.為了避免信息的丟失和重復,我們將使用JTA和JTA/XA事務來配合更新:處理信息和更新數據庫將發生在一個原子事務里.資源部分可得到JTA/XA的更多信息.
編寫應用程序代碼該應用程序將由兩個JAVA類組成: Bank(一個DAO)和MessageDrivenBank.如圖2.

Figure 2. Classes for the message-driven bank
Bank是一個數據訪問對象,這個對象封裝數據庫訪問。MessageDrivenBank是一個消息驅動façade并且是DAO的委托.與典型的J2EE方法不同,這個應用程序不包括EJB類.
第一步:編寫Bank DAO如下, Bank源代碼是很直接和簡單的JDBC操作.
package jdbc;
import javax.sql.*;
import java.sql.*;
public class Bank
{
??private DataSource dataSource;
??public Bank() {}
??public void setDataSource ( DataSource dataSource )
??{
????this.dataSource = dataSource;
??}
private DataSource getDataSource()
??{
????return this.dataSource;
??}
??private Connection getConnection()
??throws SQLException
??{
????Connection ret = null;
????if ( getDataSource() != null ) {
????????ret = getDataSource().
??????????????getConnection();
????}
????return ret;
??}
??private void closeConnection ( Connection c )
??throws SQLException
??{
????if ( c != null ) c.close();
??}
????
??public void checkTables()
??throws SQLException
??{
????????
????Connection conn = null;
????try {
??????conn = getConnection();
??????Statement s = conn.createStatement();
??????try {
????????s.executeQuery (
????????"select * from Accounts" );
??????}
??????catch ( SQLException ex ) {
????????//table not there => create it
????????s.executeUpdate (
????????"create table Accounts ( " +
????????"account VARCHAR ( 20 ), " +
????????"owner VARCHAR(300), " +
????????"balance DECIMAL (19,0) )" );
????????for ( int i = 0; i < 100 ; i++ ){
??????????s.executeUpdate (
??????????"insert into Accounts values ( " +
??????????"'account"+i +"' , 'owner"+i +"', 10000 )"
??????????);
????????}
??????}
??????s.close();
??????}
??????finally {
????????closeConnection ( conn );
??????}
??????//That concludes setup
??}
????
??//
??//Business methods are below
??//
??public long getBalance ( int account )
??throws SQLException
??{
????????
????long res = -1;
????Connection conn = null;
????try {
??????conn = getConnection();
??????Statement s = conn.createStatement();
??????
??????String query =
??????"select balance from Accounts where account='"+
??????"account" + account +"'";
??????
??????ResultSet rs = s.executeQuery ( query );
??????if ( rs == null || !rs.next() )
????????throw new SQLException (
????????"Account not found: " + account );
??????res = rs.getLong ( 1 );
??????s.close();
????}
????finally {
????????closeConnection ( conn );
????}
????return res;
????????
??}
??public void withdraw ( int account , int amount )
??throws Exception
??{
????Connection conn = null;
????try {
??????conn = getConnection();
??????Statement s = conn.createStatement();
??????String sql =
??????"update Accounts set balance = balance - "+
??????amount + " where account ='account"+
??????account+"'";
??????
??????s.executeUpdate ( sql );
??????s.close();
????
????}
????finally {
????????closeConnection ( conn );
????}
??}
}
注意:代碼并沒有依賴EJB或任何專門的應用程序服務器.實際上,這是一個純JAVA代碼,這個JAVA代碼是能在任何J2SE環境下運行的.
你同時應注意:我們使用了來自JDBC的DataSource接口.這意味著我們的類是獨立于目前JDBC供應商提供的類. 你可能會疑惑,這怎么能與特定的數據管理系統(DBMS)提供商的JDBC實現緊密結合呢? 這里就是Spring框架幫你實現的. 這個技術被稱為依賴注入:在我們的應用程序的啟動期間,通過調用setDataSource方法,Spring為我們提供了相應的datasource對象.在后面幾部分我們會更多地提到Spring.如果我們在以前使用應用程序服務器,我們將不得不借助于JAVA命名綁定接口(JNDI)查詢.
除了直接使用JDBC,我們也可以使用Hibernate或者一個JDO工具來實現我們的持久層.這同樣不需要任何的EJB代碼.
第二步:配置BankDAO我們會將便用Spring框架來配置我們的應用程序.Spring不是必需的,但是使用Spring的好處是我們將可以簡單的添加服務,如:我們JAVA對象的事務和安全.這類似于應用服務器為EJB提供的東西,只是在我們的例子中Spring將變得更容易.
Spring也允許我們把我們的類從目前的JDBC驅動實現中分離出來:Spring能夠配置Driver(基于我們的XML配置數據)并把它提供給BankDAO對象(依賴注入原理).這樣可以保持我們的JAVA代碼的清淅和集中.這步的Spring配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="datasource"
class="com.atomikos.jdbc.nonxa.NonXADataSourceBean">
????<property name="user">
????????<value>sa</value>
????</property>
????<property name="url">
????????<value>jdbc:hsqldb:SpringNonXADB
????????</value>
????</property>
????<property name="driverClassName">
????????<value>org.hsqldb.jdbcDriver</value>
????</property>
????<property name="poolSize">
????????<value>1</value>
????</property>
????<property name="connectionTimeout">
????????<value>60</value>
????</property>
</bean>
<bean id="bank" class="jdbc.Bank">
????????<property name="dataSource">
????????????<ref bean="datasource"/>
????????</property>
</bean>
</beans>
這個XML文件包括兩個對象的配置:訪問數據庫的DataSource和使用這個DataSource的Bank對象.下面是由Spring維護的一些基本任務.
·????????創建應用程序(例: Bank和DataSource)需要的對象(“beans”).在XML文件中給出了這些對象的類名,并且在我們的例子中,這些對象需要有一個公共的無參數constructor (Spring也允許參數,但是配置語法上有所不同).這些對象都被命名(XML中的id屬性),所以我們后面能夠引用這些對象. id也允許我們的應用程序找回它需要的已配置對象.
·????????這些對象的初始化是通過在XML文件中的properties的值實現. 在XML文件中這些properties名 應與對應的類中的setXXX方法相對應.
·????????將對象連接在一起 :一個property可能是另一個對象(例如:在我們例子中的數據源)的引用,引用可以通過id創建.
注意:在我們下一步中, 我們將選擇配置一個JTA-enabled的數據源(由Atomikos Transactions提供,可用于企業和J2SE的JTA產品,我們將應用于我們的應用程序). 簡單起見,我們將使用HypersonicSQLDB,這個DBMS不需要專門的安裝步驟—它是在.jar文件里,就像JTA和Spring.
但是,考慮到漸增的可靠性需求,強列推薦你使用XA-capable的DBMS和JDBC驅動.沒有XA的支持, 在crash或重啟之后你的應用程序將不能恢復原有數據. 資源部分有鏈接到關于事務和XA的信息和一些例子.
作為一個練習,你可以試試從HypersonicSQLDB轉換到FirstSQL,一個易安裝XA-compliant的DBMS.換句話說,任何其他為企業準備的和XA-capable的DBMS也會做得很好.
第三步:測試BankDAO讓我們來測試我們的代碼,(使用極限編程的程序員會首先寫測試,但因開始不是很清淅,所以我們直到現在才開始寫測試.)下面是一個簡單的單元測試.這個測試可在你的的應用程序里運行:它通過Spring獲得一個BANK對象來進行測試(這在setUp方法中實現).注意:這個測試使用清楚的事務劃分:每一個測試開始之前開始一個事務,每個測試結束時強制進行事務回滾.這是通過手工的方式來減少測試對數據庫數據的影響.
package jdbc;
import com.atomikos.icatch.jta.UserTransactionImp;
import junit.framework.TestCase;
import java.io.FileInputStream;
import java.io.InputStream;
import org.springframework.beans.factory.xml.XmlBeanFactory;
public class BankTest extends TestCase
{
????private UserTransactionImp utx;
????private Bank bank;
????public BankTest ( String name )
????{
????????super ( name );
????????utx = new UserTransactionImp();
????????
????}
????protected void setUp()
????????throws Exception
????{
????????//start a new transaction
????????//so we can rollback the
????????//effects of each test
????????//in teardown!
????????utx.begin();
????????
????????//open bean XML file
????????InputStream is =
????????????new FileInputStream("config.xml");
????????//the factory is Spring's entry point
????????//for retrieving the configured
????????//objects from the XML file
????????XmlBeanFactory factory =
????????????new XmlBeanFactory(is);
????????bank = ( Bank ) factory.getBean ( "bank" );
????????bank.checkTables();
????}
????protected void tearDown()
????????throws Exception
????{
????????//rollback all DBMS effects
????????//of testing
????????utx.rollback();
????}
????public void testBank()
????throws Exception
????{
????????int accNo = 10;
????????long initialBalance = bank.getBalance ( accNo );
????????bank.withdraw ( accNo , 100 );
????????long newBalance = bank.getBalance ( accNo );
????????if ( ( initialBalance - newBalance ) != 100 )
????????????fail ( "Wrong balance after withdraw: " +
?????????????????? newBalance );
????}
????
}
我們將需要JTA事務來確保JMS和JDBC都是原子操作.一般來說,當經常都是兩個或多個連接的時候,你應考慮一下JTA/XA。例如,在我們例子中的JMS和JDBC. Spring本身不提供JTA事務;它需要一個JTA實現或者委派一個應用服務器來處理這個事務.在這里,我們使用了一個JTA實現,這個實現可以在任何J2SE平臺上工作.
最終架構如下面圖3.白色方框代表我們的應用程序代碼.

Figure 3. Architecture for the test
如你所看到的,當我們執行我們的測試,將會發生下面的情況:
1.????????BankTest開始一個新事務.
2.????????然后,這個test在Spring運行期間獲得一個BANK對象.這步觸發Sping的創建和初始化過程.
3.????????這個test調用BANK的方法.
4.????????BANK調用datasource對象,通過它自己的setDataSource 方法從Spring 獲取這個對像.
5.????????這個數據源是JTA-enabled,并且與JTA實現交互來注冊當前事務.
6.????????JDBC statements和帳戶數據庫交互.
7.????????當方法返回時, test調用事務回滾.
8.????????JTA記得住datasource對象,會命令它進行回滾.
第四步:添加聲明式事務管理Spring允許添加聲明式事務管理來管理java對象.假設我們想確認bank總是和一個有效的事務上下文一起被調用.我們通過在實際對象的上部配置一個proxy對象. Proxy和實際對象有相同接口,所以客戶通過完全相同的方式使用它. 配置Proxy wrap每個BankDAO方法到事務中.結果配置文件如下. 不要被XML的龐大嚇倒—大多數內容能通過復制和粘貼到你自己的工程中再使用.
<?xml version="1.0" encoding="UTF-8"?>
<beans>
????<!--
????????Use a JTA-aware DataSource
????????to access the DB transactionally
????-->
????<bean id="datasource"
????????class="com.atomikos.jdbc.nonxa.NonXADataSourceBean">
????????<property name="user">
????????????<value>sa</value>
????????</property>
????????<property name="url">
????????????<value>jdbc:hsqldb:SpringNonXADB</value>
????????</property>
????????<property name="driverClassName">
????????????<value>org.hsqldb.jdbcDriver</value>
????????</property>
????????<property name="poolSize">
????????????<value>1</value>
????????</property>
????????<property name="connectionTimeout">
????????????<value>60</value>
????????</property>
????</bean>
????<!--
????Construct a TransactionManager,
????needed to configure Spring
????-->
????<bean id="jtaTransactionManager"
????????class="com.atomikos.icatch.jta.UserTransactionManager"/>
????<!--
????Also configure a UserTransaction,
????needed to configure Spring??
????-->
????
????<bean id="jtaUserTransaction"
????????class="com.atomikos.icatch.jta.UserTransactionImp"/>
????<!--
????Configure the Spring framework to use
????JTA transactions from the JTA provider
????-->
????<bean id="springTransactionManager"
????class="org.springframework.transaction.jta.JtaTransactionManager">
????????<property name="transactionManager">
????????????<ref bean="jtaTransactionManager"/>
????????</property>
????????<property name="userTransaction">
????????????<ref bean="jtaUserTransaction"/>
????????</property>
????</bean>
????<!-- Configure the bank to use our datasource -->
????<bean id="bankTarget" class="jdbc.Bank">
????????<property name="dataSource">
????????????<ref bean="datasource"/>
????????</property>
????</bean>
????<!--
????Configure Spring to insert
????JTA transaction logic for all methods
????-->
????<bean id="bank"
????class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
????????<property name="transactionManager">
????????????<ref bean="springTransactionManager"/>
????????</property>
????????<property name="target">
????????????<ref bean="bankTarget"/>
????????</property>
????????<property name="transactionAttributes">
????????????<props>
????????????????<prop key="*">
????????????????????PROPAGATION_REQUIRED, -Exception
????????????????</prop>
????????????</props>
????????</property>
????</bean>
</beans>
這個XML文件告訴Spring去配置下面的對象:
1.????????需要通過JDBC連接的datasource.
2.???????? 添加jtaTransactionManager和jtaUserTransaction用于為JTA事務的Spring配置作準備.
3.????????springTransactionManager用于告訴Spring需要使用JTA.
4.???????? BankDAO被重命名為bankTarget (因如下解釋的原因).
5.????????bank對象被添加用于包裝事務和bankTarget的所有方法.我們通過配置bank對象來使用
springTransactionManager,這意味著所有事務將是JTA事務. 每個事務都被設置為PROPAGATION_REQUIRED,這將在任何異常下出現強制回滾.
對這些對象包含的內容,你都可以很容易的復制和粘貼jtaTransactionManager, jtaUserTransaction, springTransactionManager到其他工程.其他的是應用程序相關的對象:datasource, bankTarget, bank. Bank對象很有趣:事實上對于bankTarget它是一個proxy;他們擁有相同的接口. Trick如下:當我們的應用程序請求Spring去配置和返回bank對象,Spring實際上將返回proxy(看起來和我們的應用程序完全相同),隨后這個proxy將為我們開始/結束事務.這樣,應用程序和Bank類本身都不需要知道JTA!圖4闡述了在這步我們所得到的.??

Figure 4. Architecture with declarative JTA transactions in Spring
現在的工作如下:
1.????????應用程序調用bank對象.這將觸發Spring的初始化處理和返回proxy對象. 對應用程序而言,這個proxy行為和我們的Bank是一樣的.
2.????????當bank的一個方法被調用, 這個調用將會通過proxy進行.
3.????????proxy使用springTransactionManager創建一個新事務.
4.????????springTransactionManager被配置為使用JTA,因些它委派到JTA.
5.????????調用被forward到Bank的實際對象,bankTarget.
6.????????bankTarget使用從Spring中得到的datasource.
7.????????datasource對事務進行注冊.
8.????????通過規則的JDBC訪問數據庫.
9.????????在返回時, proxy終止事務:如果在先前的序列中沒有發生異常,那么將會提交終止指令.否則,它將會被回滾.
10.????????transaction 管理器與數據庫配合進行提交和回滾.
在這步中的測試怎樣進行?我們可重用BankTest 和它清晰的事務劃分:因為PROPAGATION_REQUIRED, proxy將和在BankTest中創建的事務上下文一起執行.
第五步:編寫PROPAGATION_REQUIRED在這步,我們將添加JMS處理邏輯.為了做到這樣,我們主要需要實現JMS MessageListener接口.我們也會添加公共的setBank方法使Spring的依賴注入起作用.源代碼如下:
package jms;
import jdbc.Bank;
import javax.jms.Message;
import javax.jms.MapMessage;
import javax.jms.MessageListener;
public class MessageDrivenBank
implements MessageListener
{
????private Bank bank;
????public void setBank ( Bank bank )
????{
????????this.bank = bank;
????}
????//this method can be private
????//since it is only needed within
????//this class
????private Bank getBank()
????{
????????return this.bank;
????}
????public void onMessage ( Message msg )
????{
????????try {
??????????MapMessage m = ( MapMessage ) msg;
??????????int account = m.getIntProperty ( "account" );
??????????int amount = m.getIntProperty ( "amount" );
??????????bank.withdraw ( account , amount );
??????????System.out.println ( "Withdraw of " +
??????????amount + " from account " + account );
????????}
????????catch ( Exception e ) {
??????????e.printStackTrace();
????????????
??????????//force rollback
??????????throw new RuntimeException (
??????????e.getMessage() );
????????}
????}
????
}
第六步:配置MessageDrivenBank這里我們配置MessageDrivenBank去監聽事務的QueueReceiverSessionPool.這樣給我們可以實現和EJB(沒有丟失信息和冗余信息)類似的消息機制,但在這里我們是用簡單的POJO對象實現.當向pool中插入一個MessageListener,這個會話池將確保用JTA/XA事務接收到消息.結合JTA/XA-capable 的JDBC數據源,我們可以實現可靠的消息機制.??Spring的配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<!--
????????NOTE: no explicit transaction manager bean
????????is necessary
????????because the QueueReceiverSessionPool will
????????start transactions by itself.
-->
<beans>
????<bean id="datasource"
????????class="com.atomikos.jdbc.nonxa.NonXADataSourceBean">
????????<property name="user">
????????????<value>sa</value>
????????</property>
????????<property name="url">
????????????<value>jdbc:hsqldb:SpringNonXADB</value>
????????</property>
????????<property name="driverClassName">
????????????<value>org.hsqldb.jdbcDriver</value>
????????</property>
????????<property name="poolSize">
????????????<value>1</value>
????????</property>
????????<property name="connectionTimeout">
????????????<value>60</value>
????????</property>
????</bean>
????<bean id="xaFactory"
????????class="org.activemq.ActiveMQXAConnectionFactory">
????????<property name="brokerURL">
????????????<value>tcp://localhost:61616</value>
????????</property>
????</bean>
????<bean id="queue"
????????class="org.activemq.message.ActiveMQQueue">
????????<property name="physicalName">
????????????<value>BANK_QUEUE</value>
????????</property>
????</bean>
????<bean id="bank" class="jdbc.Bank">
????????<property name="dataSource">
????????????<ref bean="datasource"/>
????????</property>
????</bean>
????<bean id="messageDrivenBank"
????????class="jms.MessageDrivenBank">
????????<property name="bank">
????????????<ref bean="bank"/>
????????</property>
????</bean>
????<bean id="queueConnectionFactoryBean"
????????class="com.atomikos.jms.QueueConnectionFactoryBean">
????????<property name="resourceName">
????????????<value>QUEUE_BROKER</value>
????????</property>
????????<property name="xaQueueConnectionFactory">
????????????<ref bean="xaFactory"/>
????????</property>
????</bean>
????<bean id="queueReceiverSessionPool"
????????class="com.atomikos.jms.QueueReceiverSessionPool"
????????init-method="start">
????????
????????<property name="queueConnectionFactoryBean">
????????????<ref bean="queueConnectionFactoryBean"/>
????????</property>
????????<property name="transactionTimeout">
????????????<value>120</value>
????????</property>
????????<!--
????????default license allows only limited
????????concurrency so keep pool small
????????-->
????????<property name="poolSize">
????????????<value>1</value>
????????</property>
????????<property name="queue">
????????????<ref bean="queue"/>
????????</property>
????????<property name="messageListener">
????????????<ref bean="messageDrivenBank"/>
????????</property>
????</bean>
</beans>
因為這篇文章需要一個便于安裝的JMS服務,所以這里我們使用ActiveMQ.如果你正在使用另一個JMS實現,那么你將仍然能使用這部分提出的技術.接下來除了datasource和bank對象,我們將增加下面的對象定義:
·????????xaFactory: 為建立JMS連接的connection工廠.
·????????queue: queue代表我們將使用的JMS隊列, 這個隊列被配置成ActiveMQ要求的形式.
·????????queueConnectionFactoryBean:一個JTA-aware的JMS連接器.
·????????A queueReceiverSessionPool for JTA-enabled message consumption:注意:我們同時指定了用來調用的初始化方法(例:start);這是Spring的另一個特性. Start方法在session pool類里定義,它是在Spring配置文件中進行配置的.
·????????messageDrivenBank:負責處理消息.
你可以問問自己事務管理是在哪里進行的.事實上, 在先前部分被添加的對象已消失.為什么呢?因為我們現在使用QueueReceiverSessionPool來接收來自JMS的消息,并且這個類也為每次接收啟動一個JTA事務.我們也可以保留JTA配置,另外添加JMS配置, 但是這樣可能會使XML文件更長. 現在session pool類將擔當事務管理角色.它和proxy方法的工作相似; 只是這個類需要JMS??MessageListener 為之添加事務. 通過這樣配置,在每個消息收接之前程序將啟動一個新事務 無論何時, 當我們的消息實例正常返回時, 這個事務將提交. 如果出現RuntimeException, 那么這個事務將回滾. 結構如下面圖5(可以清淅地看到一些JMS對象).

Figure 5. Architecture for message-driven applications in Spring
現在該架構工作如下:
1.????????應用程序調用bank對象和初始化數據庫表.
2.????????應用程序queueReceiverSessionPool, 因此觸發一個start方法的調用去監聽到達的消息.
3.????????queueReceiverSessionPool在隊列中偵察一個新消息.
4.????????queueReceiverSessionPool開始一個新事務,并且注冊這個事務.
5.????????queueReceiverSessionPool調用已注冊的MessageListener (messageDrivenBank).
6.????????這將觸發對bank 對象的調用.
7.????????bank 對象通過datasource訪問數據庫.
8.????????datasource注冊事務.
9.????????通過JDBC訪問數據庫.
10.????????當處理完成時, queueReceiverSessionPool會終止這個事務。然后進行commint(除非發生RuntimeException).
11.????????transaction manager開始消息隊列的兩階段提交.
12.????????transaction manager開始數據庫的兩階段提交.
第七步:編寫應用程序因為我們沒有使用容器,我們僅僅提供一個Java應用程序就可以啟動整個銀行系統.我們的Java應用程序是非常簡單: 它有能力找回配置的對象(Spring通過XML文件將他們放到一起). 這個應用程序能在任何兼容的JDK(Java Development Kit)上運行,并且不需要應用服務器.
package jms;
import java.io.FileInputStream;
import java.io.InputStream;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import com.atomikos.jms.QueueReceiverSessionPool;
import jdbc.Bank;
public class StartBank
{
??public static void main ( String[] args )
??throws Exception
??{
????//open bean XML file
????InputStream is =
????new FileInputStream(args[0]);
????
????//the factory is Spring's entry point
????//for retrieving the configured
????//objects from the XML file
????XmlBeanFactory factory =
????????new XmlBeanFactory(is);
????
????//retrieve the bank to initialize
????//alternatively, this could be done
????//in the XML configuration too
????Bank bank =
????????( Bank ) factory.getBean ( "bank" );
????
????//initialize the bank if needed
????bank.checkTables();
????//retrieve the pool;
????//this will also start the pool
????//as specified in the beans XML file
????//by the init-method attribute!
????QueueReceiverSessionPool pool??=
????????( QueueReceiverSessionPool )
????????factory.getBean (
????????"queueReceiverSessionPool" );
????//Alternatively, start pool here
????//(if not done in XML)
????//pool.start();
????System.out.println (
????????"Bank is listening for messages..." );
????????
??}
}
這就是J2EE!是不是認為J2EE也很容易呢?
對通用性的考慮這部分里我們看看更多的概念,這些概念在許多J2EE應用程序中是很重要的.我們同樣將看到對這些概念來說,一個應用服務器并不是必須的.
集群和可擴展性健壯的企業應用程序需要集群來分流負擔. 在消息驅動應用程序的例子中,這很容易:我們自動地從JMS應用程序繼承得來處理能力.如果我們需要更強大的處理能力,那么我們只需增加更多連接相同JMS服務器的進程.一個對服務性能有效的衡量標準是在隊列中停留的消息的數量. 在其他情況下,如基于web的 架構(如下)我們能很容易地使用web環境下的集群能力.
方法級別的安全一個典型的觀點是認為EJB能增加方法級的安全性.雖然并沒有在這篇文章中提到,但是在Sping中配置方法級別的安全是可能的.這種配置類似于我們增加方法級的事務劃分的方式.
對非消息驅動的應用程序的通用性在不改變源代碼的情況下(除了主應用程序類),我們使用的平臺能很容易地被整合到任何J2EE web 應用服務器. 換句話說 , 通過JMS進行后臺處理;這使得web服務器在面對后臺處理的延遲問題上更可靠和更獨立.在任何情況下, 為了實現容器管理的事務或容器管理的安全性,我們都不再需要依靠EJB容器來實現.
關于容器管理持久化?存在并被很多開發者檢驗過的技術例如:JDO或者Hibernate 都不一定需要一個應用服務器. 另外,這些工具已經占據了持久化市場.
結論今天,不需要應用服務器的J2EE已經成為可能,也很容易. 有人或許會說,沒有應用程序服務器,有一些應用程序仍不能實現:例如,如果你需要一般的JCA(Java Connectivity API) 功能性, 那么我們上面提供的平臺是不夠的. 但是,這可能會發生改變,因為不用使用一個應用程序服務器進行開發,測式和部署的好處實在是太大. 人們越來越相信: 將來得J2EE是一個模塊化的”選擇你所需要”架構. 這與我們之前的完全基于應用服務器的方法相反. 在這樣的情況下,J2EE開發者將從應用服務器和EJB中解放出來.
資源:
·????????
代碼下載·????????Guy Pardon's presentation on Transactions in Spring published at TheServerSide
·????????More information on Atomikos Transactions and message-driven functionality without EJB
·????????The home page of Spring
·????????More information on JUnit
·????????FirstSQL is an easy-to-install, XA-compliant DBMS
·????????More information on HSQLDB
·????????More information on ActiveMQ