概要
JBoss 应用程序服务器(AS)不仅是一个通过JEE认证的应用程序服务器而且也是多种领先优势开源技术的一个融结点这些技术简化了基于POJO的中间件应用的开发模型而且将成为下一代JEE标准在这篇文章中Dr Michael 除了探究了JBoss AS 中的新特性之外还给你预览了明日将出现什么(//)
在年的九月JBoss应用服务器(AS) 通过了JEE 的认证对于JBoss的核心开发人员和JBoss早期的采用者JBoss AS 最振奋的并不是JEE的认证而是目前JEE无法涵盖的新技术和极大地简化Java中间件开发的目标就是使用更加简单的更加易于管理的POJO来替代已存在的EJB的理念简化程序将提高开发人员的效率更好的程序性能和更少的Bug简单化(aka 轻量级开发)将是服务器端Java社区下一个重大的事件JBoss AS 将是第一个在该方面迈出坚实步伐的JEE主流服务器
这篇文章中我将用三个示例程序来展示JBoss AS 中POJO中间件框架的简单性以及他们是如何与当前和明日的JEE规范关联起来的如果你是一个JBoss的用户或者一个普通的JEE开发人员这篇文章将教你一些不仅在目前的JBoss AS 和将来的JBoss 或者 JEE 服务器上可以应用的轻便技巧
让我们从目前EJB 中间件框架中固有的问题开始来展示对一个更加简单的基于POJO框架的需要
(开源和JEE规范――对于Java社区和开源社区来说JBoss的官方JEE认证是一个具有里程碑意义的事件因为不久之前由于高成本和所要求的大量的保证质量的工作人们认为如果Sun不发慈悲的话任何开源的JEE项目都不可能通过认证的JBoss 仅仅依靠自己就获得了JEE的认证证明了开源开发模型在交付迅速的企业Java解决方案的正确性)
(一)EJB 出了什么问题了?
开始的时候JEE 在开发具有伸缩性和分布性的服务器端应用市场获得了巨大的成功然而EJB在JEE里的一个核心的开发中间件的构件却获得了一个太复杂和难用的名声特别是对于中小型业务应用的开发额外的EJB基础代码和部署描述符不仅使服务器资源承担不起而且更加重要的是降低了开发人员的效率导致开发人员最终写更多了和需要维护更多的基础代码而不是业务逻辑
为了证明以上观点 和提供一个JBoss AS 支持的更加简单的解决方案的选择性的比较让我们来看一个基于EJB 的示例程序抵押金计算器Web应用程序先计算每个月每笔贷款的抵押金将结果保存到一个关系数据库里每次计算之后该程序在数据库中之前的结果中搜索所需数额较低的抵押金那些结果将在页面的底部显示图 展示了该程序如何工作的当你初次使用该程序的时候你将被要求填入一个用户名和密码使用user / pass登录如果你想看到显示给未授权用户的错误信息请尝试 user/pass
图 实战抵押金计算器web应用
从示例源码包ejb文件夹可以获取该程序的源代码在ejb目录 (Windows)只要执行buildbat命令或者buildsh命令(Linux Unix 或者Mac OS X系统)来重建该应用将生成的ejb/build/jar/MortgageCalculatorEJBear文件拷贝到JBoss 服务器的server/default/deploy/部署访问该应用的URL将是
//localhost/MortgageCalculatorEJB/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
图阐明了EJB模块的结构它展示了所有需要的EJB组成接口和部署描述符的要素
图 EJB模块的重要构件
如你所看到的在图中展示的结构是复杂的包括了几个紧耦合的Java接口类和史前的XML如果一个框架允许开发人员专注于他们所擅长的—也就是写Java代码—而不是用一堆构件接口和XML转移他们的注意力那该多好啊!好消息是
JBoss AS 中的POJO中间件框架使开发人员很好的利用EJB容器的服务而没有EJB的包袱
(二)简单化的创新
从使用EJB危险中我们认识到一个成功的可选框架应该具有以下重要特性
该框架不应该给开发人员增加任意的构件模型因为这些模型会打破面向对象的设计结构换句话来说该框架应该支持一些开发人员可以对其扩展和在应用容器内部或者外部重用的POJO
该框架应该摒弃需要手工编写大量冗余的EJB部署描述符一个POJO应该可以以简单的声明它需要什么样的容器服务
该框架应该支持通过引用对POJO进行本地访问Java对象序列化是缓慢的在允许的时候应该避免使用特别是对于小型到中型的应用更加需要避免
在过去的几年Java开源社区并没有等待Java社区过程(JCP)去发明或者标准化一个基于POJO的轻量级的中间件框架而是已经实验了无数的方法这些开源的项目的例子包括了XDoclet Hibernate Spring和一些面向方面的编程(AOP)项目JBoss 中的新的中间件框架补充了过去的研究和开发努力
两个JBoss赞助的开源项目在它的POJO中间件框架中扮演了重要的角色
JBoss AOP项目支持了通过Java注解把服务传输给POJO
Hibernate项目是对象关系映射和POJO持久化实际上的标准框架
为了支持企业级的应用两个项目都集成和强化了经过检验的和通过JEE认证的JBoss AS容器服务现在让我们重构抵押金计算器EJB构件成为POJO看看这个强大的新开发模型是如何工作的
企业POJO的强大之处
POJO抵押金计算器应用也包含在源码包里就如我将要说明的该应用的源代码和创建脚本对于JSE 和JSE 环境有稍微的不同我准备了两个版本分别存放在pojojdk pojojdk目录里创建和部署的命令跟ejb样例应用都是一样的一旦部署了访问POJO样例的URL将是
//localhost/MortgageCalculatorPOJO/servlet/Calculator
就如我讲论述的我们的目标是使用POJO替代会话和实体bean期间该框架应该能够保持EJB服务器的一个关键的好处—声明应用容器服务给应用程序这样的话当我们今后需要改变容器服务的时候我们仅仅需要修改声明而且无须修改大量的Java代码但是无须部署描述符一个POJO如何告知JBoss容器它需要什么服务呢?这就是AOP和Java注解表演的地方了
重用注解
注解是从JSE 开始引入为官方Java语言语义的一部分JBoss AS 定义了一套注解标签作为POJO访问JEE容器服务的API并且声明了容器服务是如何应用的在该分支下JBoss AS使用了JBoss AOP框架来动态改变被注解的对象和方法的行为从JBoss AOP的用户指导和参考文档你可以找到更多关于JBoss AOP的信息和它是如何与注解一道运作的
以下列出了替代了Calculator会话Bean的Calculator POJO类的基本骨架
@SecurityDomain (other)
public class Calculator {
@Unchecked
public Calculator () {
}
@Permissions ({AuthorizedUser})
@Tx (TxTypeREQUIRED)
public double getPayment (int principal double rate
int term) throws Exception {
// Calculate and save to database
// Code omitted for clarity
}
@Permissions ({AuthorizedUser})
@Tx (TxTypeREQUIRED)
public List getHistory (double payment) throws Exception {
// Search the database
// Code omitted for clarity
}
}
@SecurityDomain 注解声明了该POJO的安全域是other该域告知了JBoss从classpath的usersproperties 和rolesproperties文件里寻找密码和用户角色列表@Permissions注解指定了只有用户名为AuthorizedUser的用户才可以访问getPayment()和getHistory()方法JBoss AS将在运行时进行权限检查@Tx 注解将为getPayment()和getHistory()方法启动JBoss事务管理器确保它们对数据库作出的任何修改都必须在整个方法成功地执行和返回的情况下才提交
但是JEE 的用户又该如何做?他们也能使用简单的POJO作为EJB的选择?答案是一个响亮的是!你可以在JEE源码里嵌入类似Javadoc风格注释的注解JBoss AOP框架提供了一个注解编译器该编译器后续执行那些Java注释并且把注解增加到字节码里这个编译器的功能就类似于XDoclet下面所列出的展示了在pojojdk目录的JEE版本的Calculator POJO类
/**
* @@orgjbossaspectssecuritySecurityDomain (other)
*/
public class Calculator {
/**
* @@orgjbossaspectssecurityUnchecked
*/
public Calculator () {
}
/**
* @@orgjbossaspectssecurityPermissions ({AuthorizedUser})
* @@orgjbossaspectstxTx (orgjbossaspectstxTxTypeREQUIRED)
*/
public double getPayment (int principal double rate
int term) throws Exception {
// Calculate and save to database
// Code omitted for clarity
}
/**
* @@orgjbossaspectssecurityPermissions ({AuthorizedUser})
* @@orgjbossaspectstxTx (orgjbossaspectstxTxTypeREQUIRED)
*/
public List getHistory (double payment) throws Exception {
// Search the database
// Code omitted for clarity
}
}
要使用JBoss AOP注解编译器你仅需要在Ant的建造脚本里增加一个任务以下列出了pojojdk/buildxml 脚本中相关的部分
<target name=prepare>
<taskdef name=annotationc
classname=orgjbossaopantAnnotationC
classpat />
</target>
<target name=annotate depends=compile>
<annotationc compilerclasspat
classpath=${builddir}/classes
bytecode=true>
<src path=${srcdir}/>
</annotationc>
</target>
有了Java注解我们用一个简单的POJO替代了会话bean和其相关的构件但是实体又怎么办?
轻量级的POJO持久化
容器管理持久化实体bean的主要功能是模拟应用数据和保持其对后端数据库的持续透明在JBoss AS 中轻量级的中间件框架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) {
thisprincipal = principal;
thisrate = rate;
thisterm = term;
thispayment = payment;
}
public int getId () {
return id;
}
public void setId (int id) {
thisid = id;
}
public int getPrincipal () {
return principal;
}
public void setPrincipal (int principal) {
thisprincipal = principal;
}
public double getRate () {
return rate;
}
public void setRate (double rate) {
thisrate = rate;
}
public int getTerm () {
return term;
}
public void setTerm (int term) {
thisterm = term;
}
public double getPayment () {
return payment;
}
public void setPayment (double payment) {
thispayment = payment;
}
}
现在我们需要一个名为Historyhbmxml 得Hibernate映射文件将History类映射到一个数据库表将数据字段映射到数据库表的列里
<hibernatemapping>
<class name=comjbossMortgageCalculatorpojoHistory
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>
</hibernatemapping>
但是我们还没指定用哪个后端数据库和如何与JBoss Server容器集成我们在hibernateservicexml里指定这些设置
<server>
<mbean code=orgjbosshibernatejmxHibernate
name=jbosshar:service=Hibernate>
<attribute name=DatasourceName>
java:/DefaultDS
</attribute>
<attribute name=Dialect>
netsfhibernatedialectHSQLDialect
</attribute>
<attribute name=SessionFactoryName>
java:/hibernate/SessionFactory
</attribute>
<attribute name=CacheProviderClass>
netsfhibernatecacheHashtableCacheProvider
</attribute>
<attribute name=HbmddlAuto>
createdrop
</attribute>
</mbean>
</server>
要在JBoss AS中部署Hibernate模块我们比较将其打包进一个har后缀的jar文件里hibernateservicexml文件必须要放到har压缩文件里的METAINF目录里以下列出了打包该har文件在Ant 建造脚本里的相关任务
<target name=packagehar depends=annotate>
<jar jarfile=${builddir}/jar/calculatorpojohar>
<metainf dir=dd/har includes=**/*xml />
<fileset dir=${builddir}/classes>
<include name=com/jboss/MortgageCalculator/pojo/**/>
<include name=*properties/>
</fileset>
</jar>
</target>
JBossHibernate集成的魔力
Hibernate模块模拟了业务数据透明的将Java数据对象和关系数据库表相互映射但是我们怎么在Calculator对象里使用该Hibernate POJO呢?
在一个正规的Hibernate应用里你必须获得一个SessionFactory创建一个Session启动一个事务然后在该事务内部干你自己的工作工作完成后你必须提交(或者回滚)该事务以及关闭该session
然而在JBoss AS内部你无须上述框架代码JBoss透明的处理所有Hibernate服务
对于抵押金计算器POJO应用我们仅仅需要从一个HibernateContext工厂类获得一个Hibernate Session对象该工厂类要通过使用配置在har模块hibernateservicexml文件中 JNDI(Java命名目录接口)名称获得就像我们看到的Hibernate事务是自动的与容器中的事务服务绑定的容器服务又是通过@Tx注解标签启动的你无须关闭该事务或者该会话JBoss将决定是否提交改变和清理会话
以下列出的展示了在Calculator POJO类里的Hibernate相关代码hsesssave()语句保存一个Hibernate POJO到数据库hsessfind()语句查询数据库获得一系列的History POJO
public class Calculator {
/**
* @@orgjbossaspectssecurityUnchecked
*/
public Calculator () {
}
/**
* @@orgjbossaspectssecurityPermissions ({AuthorizedUser})
* @@orgjbossaspectstxTx (orgjbossaspectstxTxTypeREQUIRED)
*/
public double getPayment (int principal double rate
int term) throws Exception {
rate = rate / ;
rate = rate / ;
double tmp = Mathpow(+rate term);
tmp = (principal * tmp * rate) / (tmp );
// 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 * term tmp);
Session hsess =
HibernateContextgetSession(
java:/hibernate/SessionFactory);
hsesssave (history);
} catch (Exception e) {
eprintStackTrace ();
// The new exception triggers the tx rollback
throw new Exception (Saving failed!);
}
return tmp;
}
/**
* @@orgjbossaspectssecurityPermissions ({AuthorizedUser})
* @@orgjbossaspectstxTx (orgjbossaspectstxTxTypeREQUIRED)
*/
public List getHistory (double payment) throws Exception {
List result = null;
try {
Session hsess =
HibernateContextgetSession(
java:/hibernate/SessionFactory);
result = hsessfind (
from History as h where hpayment < ?
new Double(payment) HibernateDOUBLE);
} catch (Exception e) {
eprintStackTrace ();
// The new exception triggers the tx rollback
throw new Exception (Finding failed!);
}
return result;
}
}
总结图阐明了抵押金计算器POJO应用的结构与图 EJB 模型比较形成鲜明对比的尽管对于一个像我们这样一个的小应用POJO模型是多么的简单
图 POJO模块中的关键构件
(三)前瞻EJB
基于POJO中间件的理念植根于开源项目现在它正朝着JEE的标准化迈进即将来临的由JSR(Java 规范要求)专家组开发的EJB 规范将使用POJO作为EJB的构件并将在诸多场合摒弃部署文件的需要作为一个开源的主角简单化POJO模型的倡议者JBoss是EJB 背后驱动力的一员
EJB 编程模型和API类似于JBoss AS 的POJO模型因而移植JBoss AS POJO应用和相关的开发技术到未来的EJB服务器将被证明是简单的
如果今日你想与明日的EJB 技术共舞你可以从JBoss 上下载和安装JBoss AS 的EJB 预览版模块JBoss EJB 预览实现是基于JBoss AOP和Hibernate的预览版软件基于EJB 的一份草案并将受将来变化的影响
核对你的JBoss版本
请确保你有安装正确JBoss版本支持EJB 模块例如JBoss EJB 预览模块仅仅与JBoss AS RC一起工作当新的EJB预览版本发行的时候这将受其影响你可以在EJB 模块的安装文档里找到最新的信息
为了展示EJB 的如何工作的我将使用EJB 的API重新实现抵押金计算器应用你可以在EJB源码目录建造一旦部署访问EJB 应用的URL将是
//localhost/MortgageCalculatorEJB/servlet/Calculator建造该EJB 应用JSE 是必须的
如下代码所示Calculator接口声明了基于POJO的会话Bean@local注解标签指定了EJB 会话Bean与其客户(即Servlet 模块)部署在同一个JVM上因此Servlet将通过它的Java引用访问该会话Bean也就避免了缓慢对象序列化操作如果会话Bean在不同的服务器上运行你可以在该接口上加注@remote标签
@Local
public interface Calculator {
public double getPayment (int principal double rate int term);
// Get previous queries that has payment lower than
// the current one
public List getHistory (double payment);
}
CalculatorBean类是会话Bean的实现我们用@stateless标签将其声明为一个无状态的会话Bean
@Stateless
@SecurityDomain(other)
public class CalculatorBean implements Calculator {
@Inject
private EntityManager manager;
public CalculatorBean () {}
@MethodPermissions({AuthorizedUser})
@Tx(TxTypeREQUIRESNEW)
public double getPayment (int principal double rate int term) {
rate = rate / ;
rate = rate / ;
double tmp = Mathpow(+rate term);
tmp = (principal * tmp * rate) / (tmp );
HistoryBean history =
new HistoryBean (principal rate * term tmp);
managercreate (history);
return tmp;
}
@MethodPermissions({AuthorizedUser})
@Tx(TxTypeREQUIRESNEW)
public List getHistory (double payment) {
return managercreateQuery(
from HistoryBean h where hpayment < :payment)
setParameter(payment new Double(payment))
listResults();
}
}
HistoryBean类是对数据库进行持久化的数据模型查看一下我们如何用注解来避免对对象数据库表映射文件的需要的另外EntityManager替代了POJO样例里的HibernateContext对象运行时注解将EntityManger对象注入HistoryBean对象因此我们无须与JNDI设置和命名纠缠
@Entity
@Table(name = history)
public class HistoryBean {
private int id;
private int principal;
private double rate;
private int term;
private double payment;
public HistoryBean () {
}
public HistoryBean (int principal double rate
int term double payment) {
thisprincipal = principal;
thisrate = rate;
thisterm = term;
thispayment = payment;
}
@Id(generate = GeneratorTypeAUTO)
public int getId () {
return id;
}
public void setId (int id) {
thisid = id;
}
public int getPrincipal () {
return principal;
}
public void setPrincipal (int principal) {
thisprincipal = principal;
}
public double getRate () {
return rate;
}
public void setRate (double rate) {
thisrate = rate;
}
public int getTerm () {
return term;
}
public void setTerm (int term) {
thisterm = term;
}
public double getPayment () {
return payment;
}
public void setPayment (double payment) {
thispayment = payment;
}
}
总的来说除了EJB 实体Bean模型相对基于Hibernate的POJO模型简单用EJB 写就的应用与JBoss AS 的POJO应用是相似的图 显示了基于 EJB 的抵押金计算器应用的结构
图 EJB 模块中的关键构件