您的位置:首页 > 其它

没有应用服务器的J2EE(2)

2008-04-01 17:55 239 查看
步骤4:增加Declarative 事务管理Spring允许我们往任何可配置的对象中加入declarative 事务管理。假设我们希望确定bank 对象永远被一个有效的事务上下文所调用。为了达到这个目的,我们在实际对象的基础上再配置一个代理类。这个代理类和实际的对象拥有同一个接口,所以客户端能够正确地使用它。这个代理类能够被配置来包装每一个BankDAO 对象的方法以调用事务。配置文件如下。不要被这个庞大的XML文件所吓倒,大部分的内容能够以拷贝和粘贴的方式重用到你自己的项目中去。                class="com.atomikos.jdbc.nonxa.NonXADataSourceBean">                    sa                            jdbc:hsqldb:SpringNonXADB                            org.hsqldb.jdbcDriver                            1                            60                            class="com.atomikos.icatch.jta.UserTransactionManager"/>                    class="com.atomikos.icatch.jta.UserTransactionImp"/>            class="org.springframework.transaction.jta.JtaTransactionManager">                                                                                                                class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">                                                                                                                PROPAGATION_REQUIRED, -Exception                                        这个XML文件告诉Spring去配置如下的对象:1.datasource 所需要的通过JDBC的连接2.添加jtaTransactionManager 和jtaUserTransaction 对象来准备Spring的JTA事务配置3.添加springTransactionManager 告诉Spring需要使用JTA事务4.将BankDAO 在重新命名为bankTarget (下面将解释其中的原因)5.bank对象被用来添加到所有的bankTarget的事务包装中来。我们配置bank对象来使用springTransactionManager,这意味着所有的事务是JTA事务。为每一个方法的事务设置PROPAGATION_REQUIRED,并且为所有的违例强迫使用会滚。对于所有的对象,你可以很容易的拷贝和粘贴jtaTransactionManager、jtaUserTransaction和springTransactionManager 到其他的项目。仅仅与每个应用相关的对象为:datasource、bankTarget和bank。bank对象是很有趣的:它实际上是bankTarget对象的一个代理,它们拥有相同的接口。诀窍是这样的:当你的应用请求Spring配置并且返回一个叫bank的对象,Spring实际上返回的是一个代理(这看起来对于应用来说一样使用),并且这个代理为我们启动和关闭事务。通过这种方法,应用和bank对象都不需要知道JTA。图4显示了这个问题:
                   

图4:带有declarative JTA事务的Spring架构
 现在事情象下面所示的方向发展:1.应用获取bank对象,启动了Spring的启动过程并且返回一个代理。对于应用来说,这个代理看起来和行为都跟bank对象一样2.当调用一个bank的方法时,这种调用都是通过代理的3.代理使用springTransactionManager 来产生一个新的事务4.springTransactionManager 被配置为使用JTA,所以它的任何事务都委派到JTA5.调用现在指向实际的bank,被命名为bankTarget6.bankTarget 使用从Spring里取得的datasource 7.datasource 注册了一个事务8.通过正常的JDBC来访问数据库9.返回之后,代理终止了事务:如果在前面的过程没有违例,则终止机制为commit;否则,为rollback10.事务管理和数据库协调commit(或者rollback)有关测试这个过程的情况,我们可以重用BankTest 的外部的事务机制:由于PROPAGATION_REQUIRED的设置,代理将执行BankTest产生的事务上下文。 步骤5:对MessageDrivenBank编码在这一步骤,我们加入JMS处理逻辑。为了达到这个目的,我们使用了JMS的MessageListener 接口。同样,我们也添加一个公有的setBank 方法来使的Spring的依赖注入工作。源代码如下:package jms; import jdbc.Bank;import javax.jms.Message;import javax.jms.MapMessage;import javax.jms.MessageListener; public class MessageDrivenBankimplements 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() );        }    }    } 步骤6:配置MessageDrivenBank我们配置MessageDrivenBank 来监听QueueReceiverSessionPool事务(基于JTA)。这给与我们与EJB一样的保证(没有信息丢失和信息重复),但是这里我们使用了POJO代替。当一个MessageListener 对象加入到池里,池会确定是否收到了有关JTA/XA事务的消息。和基于JTA/XA的JDBC的数据源组合起来,我们能得到可信赖的消息。关于Spring的配置如下:             class="com.atomikos.jdbc.nonxa.NonXADataSourceBean">                    sa                            jdbc:hsqldb:SpringNonXADB                            org.hsqldb.jdbcDriver                            1                            60                        class="org.activemq.ActiveMQXAConnectionFactory">                    tcp://localhost:61616                        class="org.activemq.message.ActiveMQQueue">                    BANK_QUEUE                                                            class="jms.MessageDrivenBank">                                            class="com.atomikos.jms.QueueConnectionFactoryBean">                    QUEUE_BROKER                                                    class="com.atomikos.jms.QueueReceiverSessionPool"         init-method="start">                                                        120                                    1                                                                    因为本文所用的JMS要求易于安装,这里我们将使用ActiveMQ 。如果你使用另外的一个JMS实现,那么这个实现也需要满足本文这个部分所列出的一些技术要求。就像增加datasource对象和bank一样,下面所定义的一些对象也需要被添加进来:。xaFactory:用来建立JMS连接的连接工厂。queue:表示我们所要用到的JMS对列,用来配置ActiveMQ 所需要的线路。queueConnectionFactoryBean:基于JTA的JMS连接。queueReceiverSessionPool:基于JTA的消息池。注意:我们也给定一个初始化方法(例如:start)来被调用。这也是Spring的一个特性。这个start方法在session池里被定义,并且在Spring配置文件里被XML属性所引用。messageDrivenBank:作用是处理消息你可能会问自己,事务管理在哪里执行的啊?实际上,前面部分所添加的对象又一次不见了。为什么?因为我们现在使用QueueReceiverSessionPool来从JMS里获取消息,并且这些对象在每一个receive的事务中被启动。我们可以丝毫不改动JTA配置,而仅仅增加JMS元素。但这会使得XML文件变得有的长。session池会确定在步骤5里所增加的事务管理任务。它运行起来有点像代理方法一样,仅仅这一个类需要JMS MessageListener来增加事务。通过这个配置,在每一次产生消息之前就会开始一个新的事务。无论onMessage实现什么时候正常的返回,这个事务将会提交。如果是一个RuntimeException,那么事务将会被会滚。这个架构显示在图5(一些JMS对象被忽略掉)

               图5:消息驱动的应用在Spring里的架构现在这个架构运行如下:1。应用获取bank对象,并且在需要的情况下初始化数据库的表2。应用获取queueReceiverSessionPool,因而触发了一个start方法的调用,开始监听输入的消息3。queueReceiverSessionPool从消息队列中监测到一个新的消息4。queueReceiverSessionPool开始一个新的事务并且注册5。queueReceiverSessionPool调用已经注册了的MessageListener(在我们的例子中是messageDrivenBank)6。触发对于bank的调用7。bank使用datasource访问数据库8。bank注册事务9。数据库通过JDBC被访问10。当整个过程被处理完以后,queueReceiverSessionPool结束事务,除非是抛出RuntimeException,期望的结果是commit11。在消息队列里,事务管理器初始化两个状态的commit 12。在数据库里,事务管理器初始化两个状态的commit  步骤7:给应用编码既然我们不使用容器,我们仅仅提供一个Java应用来启动这个银行系统。我们的Java应用很简单:它仅仅用来取得配置对象(在读取XML文件的期间和Spring绑定)。这个应用能够运行在所有的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应用很重要的概念。同时,我们也将看到,对于这些应用来说,应用服务器并不是必需的。群集和可测性一个鲁棒性好的企业级应用需要群集能力来大批量的启动和关闭。在消息驱动的应用的案例中,这很简单:我们自动的取得JMS应用固有的可测性特性。如果我们需要更强的处理能力,只需要增加更多的过程连接到同一个JMS服务器上。一个有用的服务器性能测试手段是测量队列中有用的消息数。在其他的像基于WEB的架构中(如下),我们可以很容易的使用WEB环境的群集能力。方法级的安全宠爱EJB的一个典型的证据是能够增加方法级的安全。虽然没有在这个应用的演示,但是在Spring中增加方法级的安全应该是可能的,就像我们增加方法级的事务一样。推广到非消息驱动的应用我们所用的平台能够很容易的集成到所有的J2EE WEB服务器上,不需要修改源代码(main application class除外)。作为一个选择,后台的处理也可以直接调用JMS,这样能够使得WEB服务器能够响应潜在的后台处理,并且保持和后台处理的独立。不管怎么说,使用一个EJB容器可以不需管容器管理的事务,或者做容器管理的安全。容器管理的持久层怎样呢?现有的已经得到证明了的技术,如JDO或Hibernate对于一个应用服务器来说不是必需的。实际上,这些工具已经主导了可管理的持久层市场。  结论没有应用服务器,J2EE会变得更加简单;而且现在这也是可能的。有人说,有一些应用在没有应用服务器的情况下,无法实现。例如,如果你需要一个普通的JCA(Java Connectivity API),那么我们在这里提到的平台是不够用的。然而,这种情况很有可能改变,因为不使用应用服务器来开发、测试和发布一个应用所获得的利益是巨大的。越来越多的人相信:未来的J2EE应用将是一个模块化的“按需获取”的架构,而不是今天的一整块的基于应用-服务的架构。根据这个假设,J2EE开发人员将从应用服务器和EJB强加的一些限制中解放出来。 资源 The complete source code for this article
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
 
关于作者
Guy PardonAtomikos公司的首席架构师。在公司里,他领导了开发既有传统的也有现在的(面向WEB服务)的事务技术
 
原文链接
http://www.onjava.com/pub/a/onjava/2006/02/08/j2ee-without-application-server.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: