JBoss 4.0 简化了中间件的开发(一)

日期: 2007-12-05 来源:TechTarget中国

  概要

  JBoss 应用程序服务器(AS)4.0不仅是一个通过J2EE认证的应用程序服务器,而且也是多种领先优势开源技术的一个融结点。这些技术简化了基于POJO的中间件应用的开发模型,而且将成为下一代J2EE标准。在这篇文章中,Dr. Michael 除了探究了JBoss AS 4.0中的新特性之外,还给你预览了明日将出现什么。(2005/02/21)

  在2004年的九月,JBoss应用服务器(AS)4.0 通过了J2EE 1.4的认证。对于JBoss的核心开发人员和JBoss早期的采用者,JBoss AS 4.0 最振奋的并不是J2EE的认证,而是目前J2EE无法涵盖的新技术和极大地简化Java中间件开发的目标。就是使用更加简单的,更加易于管理的POJO来替代已存在的EJB的理念。简化程序将提高开发人员的效率,更好的程序性能和更少的Bug.简单化(a.k.a 轻量级开发)将是服务器端Java社区下一个重大的事件,JBoss AS 4.0 将是第一个在该方面迈出坚实步伐的J2EE主流服务器。

  这篇文章中,我将用三个示例程序来展示JBoss AS 4.0中POJO中间件框架的简单性,以及他们是如何与当前和明日的J2EE规范关联起来的。如果你是一个JBoss的用户或者一个普通的J2EE开发人员,这篇文章将教你一些不仅在目前的JBoss AS 4.0和将来的JBoss 5.0 或者 J2EE 1.5 服务器上可以应用的轻便技巧。

  让我们从目前EJB 2.1中间件框架中固有的问题开始,来展示对一个更加简单的基于POJO框架的需要。

  (开源和J2EE规范――对于Java社区和开源社区来说,JBoss的官方J2EE认证是一个具有里程碑意义的事件。因为不久之前,由于高成本和所要求的大量的保证质量的工作,人们认为,如果Sun不发慈悲的话,任何开源的J2EE项目都不可能通过认证的。JBoss 仅仅依靠自己就获得了J2EE的认证,证明了开源开发模型在交付迅速的企业Java解决方案的正确性)

  (一)EJB 2.1出了什么问题了?

  开始的时候,J2EE 在开发具有伸缩性和分布性的服务器端应用市场获得了巨大的成功。然而,EJB,在J2EE里的一个核心的开发中间件的构件,却获得了一个太复杂和难用的名声,特别是对于中小型业务应用的开发。额外的EJB基础代码和部署描述符不仅使服务器资源承担不起,而且,更加重要的是降低了开发人员的效率。导致开发人员最终写更多了和需要维护更多的基础代码而不是业务逻辑。

  为了证明以上观点, 和提供一个JBoss AS 4.0 支持的更加简单的解决方案的选择性的比较,让我们来看一个基于EJB 2.1的示例程序。抵押金计算器Web应用程序先计算每个月每笔贷款的抵押金,将结果保存到一个关系数据库里。每次计算之后,该程序在数据库中之前的结果中搜索所需数额较低的抵押金。那些结果将在页面的底部显示。图1 展示了该程序如何工作的。当你初次使用该程序的时候,你将被要求填入一个用户名和密码。使用“user1” / “pass1”登录,如果你想看到显示给未授权用户的错误信息,请尝试 “user3/pass3”。

  图1 实战抵押金计算器web应用,

  从示例源码包ejb2文件夹可以获取该程序的源代码,在ejb2目录 (Windows)只要执行build.bat命令或者build.sh命令(Linux , Unix, 或者Mac OS X系统)来重建该应用。将生成的ejb2/build/jar/MortgageCalculatorEJB2.ear文件拷贝到JBoss 服务器的server/default/deploy/部署。访问该应用的URL将是

  http://localhost:8080/MortgageCalculatorEJB2/servlet/Calculator.

  为何要使用EJB?

  对一个如此简单的应用为什么使用EJB?EJB容器提供了一些有用的服务,无须我们写其他代码,这些服务可以立即增加一些企业性的特性给我们的web应用。例如,EJB会检查用户的信任度,对于所有的EJB方法的调用,容器将根据配置文件监控其相关的数据库事务。另外容器也管理者数据库的表,和数据库连接,所有这些都无须我们写任何的SQL或者JDBC代码。

  在该分支下嘛,Web应用有一个servlet接受用户的输入,同时产生HTML页面。Servlet将押金计算和数据库相关的工作分派给一个EJB模块来完成。

  该应用有两个EJB构件,Calculator bean是一个无状态的会话bean,它包含了计算押金的,保存结果到数据库的,搜索数据库的事务性的方法。这些方法都曝露给了servlet.

  Calculator bean使用了History实体bean访问数据库。在EJB的配置文件里,我们定义了History实体bean的数据域是如何映射到数据库的列的,对于一系列的History对象如何搜索数据库。由于History bean是一个EJB,它无法在EJB容器外使用,所以我们创建了HistoryList 值对象来保持任何的搜索操作,结果将返回给servlet.

  图2,阐明了EJB模块的结构。它展示了所有需要的EJB组成接口和部署描述符的要素。

  图2 EJB模块的重要构件。

  如你所看到的,在图2中展示的结构是复杂的,包括了几个紧耦合的Java接口,类,和史前的XML.如果一个框架允许开发人员专注于他们所擅长的—也就是写Java代码—而不是用一堆构件接口和XML转移他们的注意力,那该多好啊!好消息是,

  JBoss AS 4.0 中的POJO中间件框架使开发人员很好的利用EJB容器的服务而没有EJB2.1的包袱。

  (二)简单化的创新

  从使用EJB危险中,我们认识到一个成功的可选框架应该具有以下重要特性:

  该框架不应该给开发人员增加任意的构件模型,因为这些模型会打破面向对象的设计结构。换句话来说,该框架应该支持一些开发人员可以对其扩展和在应用容器内部或者外部重用的POJO.

  该框架应该摒弃需要手工编写大量冗余的EJB部署描述符。一个POJO应该可以以简单的声明它需要什么样的容器服务。

  该框架应该支持通过引用对POJO进行本地访问。Java对象序列化是缓慢的,在允许的时候应该避免使用,特别是对于小型到中型的应用更加需要避免。

  在过去的几年,Java开源社区并没有等待Java社区过程(JCP)去发明或者标准化一个基于POJO的轻量级的中间件框架。而是已经实验了无数的方法。这些开源的项目的例子包括了XDoclet, Hibernate, Spring和一些面向方面的编程(AOP)项目。JBoss 4.0 中的新的中间件框架补充了过去的研究和开发努力。

  两个JBoss赞助的开源项目在它的POJO中间件框架中扮演了重要的角色。

  JBoss AOP项目支持了通过Java注解把服务传输给POJO.

  Hibernate项目是对象关系映射和POJO持久化实际上的标准框架。

  为了支持企业级的应用,两个项目都集成和强化了经过检验的,和通过J2EE认证的JBoss AS容器服务。现在让我们重构抵押金计算器EJB构件成为POJO,看看这个强大的新开发模型是如何工作的。

  企业POJO的强大之处

  POJO抵押金计算器应用也包含在源码包里。就如我将要说明的,该应用的源代码和创建脚本对于J2SE 1.4和J2SE 1.5环境有稍微的不同。我准备了两个版本,分别存放在pojo-jdk14 pojo-jdk15目录里。创建和部署的命令跟ejb2样例应用都是一样的。一旦部署了,访问POJO样例的URL将是

  http://localhost:8080/MortgageCalculatorPOJO/servlet/Calculator.

  就如我讲论述的,我们的目标是使用POJO替代会话和实体bean.期间,该框架应该能够保持EJB服务器的一个关键的好处—声明应用容器服务给应用程序。这样的话,当我们今后需要改变容器服务的时候,我们仅仅需要修改声明而且无须修改大量的Java代码。但是,无须部署描述符,一个POJO如何告知JBoss容器它需要什么服务呢?这就是AOP和Java注解表演的地方了。

  重用注解

  注解是从J2SE 5.0开始引入为官方Java语言语义的一部分。JBoss AS 4.0定义了一套注解标签作为POJO访问J2EE容器服务的API,并且声明了容器服务是如何应用的。在该分支下,JBoss AS使用了JBoss AOP框架来动态改变被注解的对象和方法的行为。从JBoss AOP的用户指导和参考文档,你可以找到更多关于JBoss AOP的信息和它是如何与注解一道运作的。

  以下列出了替代了Calculator会话Bean的Calculator POJO类的基本骨架。
@SecurityDomain ("other")
public class Calculator {

  @Unchecked
  public Calculator () {
  }

  @Permissions ({"AuthorizedUser"})
  @Tx (TxType.REQUIRED)
  public double getPayment (int principal, double rate,
                            int term) throws Exception {
                           
    // Calculate and save to database.
    // Code omitted for clarity.
  }

  @Permissions ({"AuthorizedUser"})
  @Tx (TxType.REQUIRED)
  public List getHistory (double payment) throws Exception {

    // Search the database.
    // Code omitted for clarity.
  }
}

  @SecurityDomain 注解声明了该POJO的安全域是other,该域告知了JBoss从classpath的users.properties 和roles.properties文件里寻找密码和用户角色列表。@Permissions注解指定了只有用户名为AuthorizedUser的用户才可以访问getPayment()和getHistory()方法。JBoss AS将在运行时进行权限检查。@Tx 注解将为getPayment()和getHistory()方法启动JBoss事务管理器,确保它们对数据库作出的任何修改都必须在整个方法成功地执行和返回的情况下才提交。

  但是,J2EE 1.4的用户又该如何做?他们也能使用简单的POJO作为EJB的选择?答案是一个响亮的“是!”你可以在J2EE源码里嵌入类似Javadoc风格注释的注解。JBoss AOP框架提供了一个注解编译器,该编译器后续执行那些Java注释并且把注解增加到字节码里。这个编译器的功能就类似于XDoclet.下面所列出的展示了在pojo-jdk14目录的J2EE版本的Calculator POJO类。
   /**

* @@org.jboss.aspects.security.SecurityDomain ("other")
*/
public class Calculator {

  /**
    * @@org.jboss.aspects.security.Unchecked
    */
  public Calculator () {
  }

  /**
    * @@org.jboss.aspects.security.Permissions ({"AuthorizedUser"})
    * @@org.jboss.aspects.tx.Tx (org.jboss.aspects.tx.TxType.REQUIRED)
    */
  public double getPayment (int principal, double rate,
                            int term) throws Exception {
                           
    // Calculate and save to database.
    // Code omitted for clarity.
  }

  /**
    * @@org.jboss.aspects.security.Permissions ({"AuthorizedUser"})
    * @@org.jboss.aspects.tx.Tx (org.jboss.aspects.tx.TxType.REQUIRED)
    */
  public List getHistory (double payment) throws Exception {

    // Search the database.
    // Code omitted for clarity.
  }
}

  要使用JBoss AOP注解编译器,你仅需要在Ant的建造脚本里增加一个任务。以下列出了pojo-jdk14/build.xml 脚本中相关的部分。
<target name="prepare">
   
    … …

    <taskdef name="annotationc"
           classname="org.jboss.aop.ant.AnnotationC"
           classpathref="build.classpath" />
  </target>
 
  … …
 
  <target name="annotate" depends="compile">
    <annotationc compilerclasspathref="build.classpath"
                 classpath="${build.dir}/classes"
                 bytecode="true">
      <src path="${src.dir}"/>
    </annotationc>
  </target>

  有了Java注解,我们用一个简单的POJO替代了会话bean和其相关的构件。但是实体又怎么办?

  轻量级的POJO持久化

  容器管理持久化实体bean的主要功能是模拟应用数据和保持其对后端数据库的持续透明。在JBoss AS 4.0 中轻量级的中间件框架,Hibernate完成了数据的模拟和持久化。

  History类是一个在计算事务中模拟数据的简单POJO.它仅包含了数据域和JavaBean风格的访问方法:
public class History {

  private int id;
  private int principal;
  private double rate;
  private int term;
  private double payment;

  public History () {
  }

  public History (int principal, double rate,
                  int term, double payment) {
    this.principal = principal;
    this.rate = rate;
    this.term = term;
    this.payment = payment;
  }

  public int getId () {
    return id;
  }

  public void setId (int id) {
    this.id = id;
  }

  public int getPrincipal () {
    return principal;
  }

  public void setPrincipal (int principal) {
    this.principal = principal;
  }

  public double getRate () {
    return rate;
  }

  public void setRate (double rate) {
    this.rate = rate;
  }

  public int getTerm () {
    return term;
  }

  public void setTerm (int term) {
    this.term = term;
  }

  public double getPayment () {
    return payment;
  }

  public void setPayment (double payment) {
    this.payment = payment;
  }
}

  现在我们需要一个名为,History.hbm.xml 得Hibernate映射文件将History类映射到一个数据库表,将数据字段映射到数据库表的列里。
<hibernate-mapping>
  <class name="com.jboss.MortgageCalculator.pojo.History"
         table="history">
    <id name="id" type="int" column="id">
      <generator class="increment" />
    </id>

    <property name="principal"
              type="int" column="principal"/>

    <property name="rate"
              type="double" column="rate"/>

    <property name="term"
              type="int" column="term"/>

    <property name="payment"
              type="double" column="payment"/>
  </class>
</hibernate-mapping>

  但是我们还没指定用哪个后端数据库和如何与JBoss Server容器集成。我们在hibernate-service.xml里指定这些设置。
<server>
    <mbean code="org.jboss.hibernate.jmx.Hibernate"
           name="jboss.har:service=Hibernate">
        <attribute name="DatasourceName">
          java:/DefaultDS
        </attribute>
        <attribute name="Dialect">
          net.sf.hibernate.dialect.HSQLDialect
        </attribute>
        <attribute name="SessionFactoryName">
          java:/hibernate/SessionFactory
        </attribute>
        <attribute name="CacheProviderClass">
          net.sf.hibernate.cache.HashtableCacheProvider
        </attribute>
        <attribute name="Hbm2ddlAuto">
          create-drop
        </attribute>
    </mbean>
</server>

  要在JBoss AS中部署Hibernate模块,我们比较将其打包进一个。har后缀的jar文件里。hibernate-service.xml文件必须要放到。har压缩文件里的META-INF目录里。以下列出了打包该。har文件在Ant 建造脚本里的相关任务。
<target name="package-har" depends="annotate">
    <jar jarfile="${build.dir}/jar/calculator-pojo.har">
      <metainf dir="dd/har" includes="**/*.xml" />
     
      <fileset dir="${build.dir}/classes">
        <include name="com/jboss/MortgageCalculator/pojo/**"/>
        <include name="*.properties"/>

      </fileset>
    </jar>
  </target>

  JBoss-Hibernate集成的魔力

  Hibernate模块模拟了业务数据,透明的将Java数据对象和关系数据库表相互映射。但是我们怎么在Calculator对象里使用该Hibernate POJO呢?

  在一个正规的Hibernate应用里,你必须获得一个SessionFactory,创建一个Session,启动一个事务,然后在该事务内部干你自己的工作。工作完成后,你必须提交(或者回滚)该事务以及关闭该session.

  然而,在JBoss AS内部,你无须上述框架代码,JBoss透明的处理所有Hibernate服务。

  对于抵押金计算器POJO应用,我们仅仅需要从一个HibernateContext工厂类获得一个Hibernate Session对象,该工厂类要通过使用配置在。har模块hibernate-service.xml文件中 JNDI(Java命名目录接口)名称获得。就像我们看到的,Hibernate事务是自动的与容器中的事务服务绑定的,容器服务又是通过@Tx注解标签启动的。你无须关闭该事务或者该会话。JBoss将决定是否提交改变和清理会话。

  以下列出的展示了在Calculator POJO类里的Hibernate相关代码。hsess.save()语句保存一个Hibernate POJO到数据库,hsess.find()语句查询数据库获得一系列的History POJO.
public class Calculator {

  /**
    * @@org.jboss.aspects.security.Unchecked
    */
  public Calculator () {
  }

  /**
    * @@org.jboss.aspects.security.Permissions ({"AuthorizedUser"})
    * @@org.jboss.aspects.tx.Tx (org.jboss.aspects.tx.TxType.REQUIRED)
    */
  public double getPayment (int principal, double rate,
                            int term) throws Exception {
    rate = rate / 100.;
    rate = rate / 12.;
    double tmp = Math.pow(1.+rate, term);
    tmp = (principal * tmp * rate) / (tmp – 1.);

    // Save this calculation into the database.
    // Notice that it is automatically associated
    // with the AOP transaction. We do not even need
    // to close the session!
    try {
      History history = new History (principal,
          rate * 1200., term, tmp);
      Session hsess =
          HibernateContext.getSession(
              "java:/hibernate/SessionFactory");
      hsess.save (history);

    } catch (Exception e) {
      e.printStackTrace ();
      // The new exception triggers the tx rollback.
      throw new Exception ("Saving failed!");
    }

    return tmp;
  }

  /**
    * @@org.jboss.aspects.security.Permissions ({"AuthorizedUser"})
    * @@org.jboss.aspects.tx.Tx (org.jboss.aspects.tx.TxType.REQUIRED)
    */
  public List getHistory (double payment) throws Exception {

    List result = null;
    try {
      Session hsess =
          HibernateContext.getSession(
              "java:/hibernate/SessionFactory");
      result = hsess.find (
          "from History as h where h.payment < ?",
          new Double(payment), Hibernate.DOUBLE);

    } catch (Exception e) {
      e.printStackTrace ();
      // The new exception triggers the tx rollback.
      throw new Exception ("Finding failed!");
    }
    return result;
  }
}

  总结:图3阐明了抵押金计算器POJO应用的结构。与图2 EJB 2.1模型比较形成鲜明对比的,尽管对于一个像我们这样一个的小应用,POJO模型是多么的简单。

图3 POJO模块中的关键构件。

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐