版权声明:任何获得matrix授权的网站,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
作者:bill siggelkow;ginge(作者的blog:http://blog.matrix.org.cn/page/ginge)
原文:http://www.javaworld.com/javaworld/jw-02-2005/jw-0221-jboss4.html
中文:http://www.matrix.org.cn/resource/article/44/44094_jboss.html
关键字:jboss
概要
jboss 应用程序服务器(as)4.0不仅是一个通过j2ee认证的应用程序服务器,而且也是多种领先优势开源技术的一个融结点。这些技术简化了基于pojo的中间件应用的开发模型,而且将成为下一代j2ee标准。在这篇文章中,dr. michael 除了探究了jboss as 4.0中的新特性之外,还给你预览了明日将出现什么。(2005/02/21)
编辑的话:请看dr. michael yuan的blog
http://www.michaelyuan.com/blog/java/?postid=127
来获得这篇文章的新动态。
by dr. michael juntao yuan
在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.
}
}
/**
* @@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>
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;
}
}<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>
<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>
<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>
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;
}
}@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);
}
@stateless
@securitydomain("other")
public class calculatorbean implements calculator {
@inject
private entitymanager manager;
public calculatorbean () { }
@methodpermissions({"authorizeduser"})
@tx(txtype.requiresnew)
public double getpayment (int principal, double rate, int term) {
rate = rate / 100.;
rate = rate / 12.;
double tmp = math.pow(1.+rate, term);
tmp = (principal * tmp * rate) / (tmp - 1.);
historybean history =
new historybean (principal, rate * 1200., term, tmp);
manager.create (history);
return tmp;
}
@methodpermissions({"authorizeduser"})
@tx(txtype.requiresnew)
public list gethistory (double payment) {
return manager.createquery(
"from historybean h where h.payment < :payment")
.setparameter("payment", new double(payment))
.listresults();
}
}
@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) {
this.principal = principal;
this.rate = rate;
this.term = term;
this.payment = payment;
}
@id(generate = generatortype.auto)
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;
}
}
Java Asp PHP .Net XML C/C++ CGI VB Jsp J2ee J2se J2me EJB Servlet Tomcat Resin Struts Weblogic Eclipse ANT GUI JMS Web servise IDEA Webphere Hibernate Spring Jboss Applet Swing Socket Javamail Perl Ajax P2P 安全 模式 框架 测试 开源 游戏
Windows XP Windows 2000 Windows 2003 Windows Me Windows 9.x Linux UNIX 注册表 操作系统 服务器 应用服务器