我对本文的一些看法:
Reza Rahman又出EJB3.1新文章了.本文作为该系列的第二部分,重点探讨了EJB3.1中的Timer Service和EJB打包机制.通过本文,你会了解到EJB3.1其实也在改变――越来越轻量级,越来越讲究最佳实践.比如说Timer Service就是在当前流行的开源scheduler(调度程序),比如说Quartz的基础上再接再厉.EJB打包机制也越来越看重轻量级的容器(比如说Tomcat),毕竟市场占有率说明了一切!!!最后稍带提到了Web Bean作为EJB的DI(依赖注入)补充,填补了EJB一直以来的诟病.但不管怎么样,EJB3.1究竟在JavaEE扮演什么样的角色,我们拭目以待.
正文
讨论
该系列文章预先给大家介绍了专家组们正在继续研究JavaEE下一版本的规范——EJB3.1所带来的改变。理想情况下,文章提到的这些变化,希望可以尽早的从你那里得到反馈从而使得专家组可以做出最好的选择。EJB3.0已经将笨重的JavaEE编程模型转变得简洁明了。EJB3.1的目标就是在此基础上通过添加一系列必须的特性,使得EJB走的更远。在应系列的每一篇文章中,我将告诉你专家组的进展情况。
在先前的第一篇文章中,我谈到了最早两个关于EJB3.1特性的讨论——可选的session bean接口和singleton bean。当然其它的特性也捎带谈了谈。在这第二篇文章里,我将详细讲述另外两个特性——增强的EJB Timer Service和更简化的packing(打包).请记住,尽管规范的草案很快会发布,但这里所说的一切至今仍然没有确定下来。我只是让你了解JCP内部在干什么,并且如果你有反馈意见,可以随时提出。
致谢
在第一篇文章里,我鼓励你直接给JCP邮箱jsr-318-comments@jcp.org或我的邮箱rrahman@tripodtech.net反馈。在介绍EJB3.1之前,我想致谢所有反馈意见的人们。我希望在我的每篇文章中,都能继续得到你们的思路与意见。我也为该系列文章广而好评而感激大家。
是时候改变Timer Service特性了
Scheduling(计划任务)是许多应用程序的重要组成部分,比如说报表生成,数据库维护,生成OLAP摘要以及数据同步等任务都涉及到Scheduling。如果你用过当前版本EJB Timer Service,我会发现它很有用,但也很受限制!最大的限制在于当前版本的EJB Timer Service并不十分灵活而且计划的作业只能硬编码实现,无法使用可声明式的处理办法。Timer Service的一些缺点早在EJB2.x时期,Richard Monson-Haefel就已经作了论述,下面就是Richard在TheServerSide上关于EJB的一些观点:http://www.theserverside.com/tt/articles/article.tss?l=MonsonHaefel-Column4.
好吧,现在我们来快速看看EJB3.0所支持的Timer Service。下面是一个EJB3的例子:
@Stateless
public class PlaceBidBean implements PlaceBid {
@Resource TimerService timerService;
public void addBid(Bid bid) {
... Code to add the bid goes here...
timerService.createTimer(15*60*1000, 15*60*1000, bid);
}
@Timeout
public void monitorBid(Timer timer) {
Bid bid = (Bid) timer.getInfo();
... Code to monitor the bid goes here...
}
}
上面的那个Stateless Session bean建立了一个每15分钟触发的timer,这样当一个bid(竞标)对象建立后,每过15分钟,addBid方法会被调用一次。当timer每次触发时,被标注为@Timeout annotation 的monitorBid方法被调用,查看bidder(竞标人)是否已经高于先前的价格了。
这个功能在PlaceBidBean中运作良好,现在我们稍微改变一下需求:需要为所有的ActionBazaar客户每月月初提供时事邮件。那么如果按照现在这样基于毫秒来控制TimerService的硬编程方式简直就是冒险!当然,不否认,在应用程序启动时,你可以写出一些相当笨重的代码来创建这样的timer。但何必呢,现在的JavaEE中,已经有几个现成的timer机制来实现相对灵活的声明式配置。你可以使用流行的开源scheduler(调度程序),比如说Quartz,也可以使用商业化的Flux,甚至还可以使用你应用程序服务器(比如说WebLogic或EAServer.)所特有的scheduler。现在的问题是,如果你需要一个类似于UNIX cron这么强大的声明式scheduler的话,那么以上解决方案就显得相当麻烦——所的以上方案都与各自的供应商绑定。还有更完美的解决方案吗?请随我一同走进EJB3.1中加强的Timer Service。
在Timer Service所增强的特性中,最重要的特性之一是也可以采用声明式方法来创建一个类似于cron的schedule来trigger(触发)EJB方法(当然还有更多特性,不过要等到最新规范的草案出来后,你们才能看到)。现在我们来解决先前提出的那个问题,这里你可以使用@Schedule annotation来标注EJB方法,实现为所有的ActionBazaar客户每月月初提供时事邮件:
@Stateless
public class NewsLetterGeneratorBean implements NewsLetterGenerator {
@Schedule(second="0", minute="0", hour="0",
dayOfMonth="1", month="*", year="*")
public void generateMonthlyNewsLetter() {
... Code to generate the monthly news letter goes here...
}
}
下面的表格显示了@Schedule annotation的一些默认值:
Attribute | Allowable Values | Default |
Second | [0,59] | 0 |
Minute | [0,59] | 0 |
Hour | [0,23] | 0 |
dayOfMonth | [1,31] | * |
Month | [1,12] or {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep","Oct", "Nov", Dec"} | * |
dayOfWeek | [0,7] or {"Sun", "Mon", "Tue","Wed", "Thu", "Fri", "Sat"} | * |
Year | A four-digit calendar year | * |
注意,所有的属性都支持cron风格的“*”号通配符,逗号分隔的列表(比如:用"Jan, Feb, Mar"来设置month的属性)还有“-”号分隔来描述时间跨度(比如说"Mon-Fri"表示week属性从星期一到星期五)。那么@Schedule表达式语法是否支持“/”操作呢?又是否支持缩写,比如用“Jan”代替“January”呢?同样,是否可以将@Schedule表达式再压缩的更简单些?上面的那个例子可以表示成:
@Schedule(expression="0 0 0 1 * * *")
有一些站起来嚷嚷说“纯cron风格的表达式”太晦涩了,又有一些人指出许多数开发人员已经习惯了这种风格,EJB应该支持它。现在的TimerService接口添加了新方法,用来支持编程版本的cron风格。编程版本中,支持为一个给定的schedule定义激活和取消日期。比如说,我们刚才提到的那些“每月初提供时事”将来可以在预定时间激活,而不是像以前那样当timer创建好了就处于激活状态。那么应该让@Schedule annotation也支持类似的功能吗?支持定义一个有限次数的cron trigger怎么样?你还能想到其它特性吗?
简化 EJB 打包机制
EJB3.0可选的XML布署描述符已经大大简化了JavaEE应用程序的打包和布署。尽管如此,JavaEE打包仍然死板的要求一个模块一个模块的。也就是说你必须分开为web和EJB模块打包成jar文件。在一个典型的JavaEE布署场合下,一个EAR文件包括一个war归档文件和一个EJB jar。图1描述了当前JavaEE打包的方案。概括的说,理想中的EJB的jar代表的是“模块化”的busines service,供 “客户端”的web模块使用.模块化思想是很重要,但问题是如果business service用不着客户端在多个JavaEE模块之间进行共享的话,这样反而会让简单的web应用程序过度浪费时间.

图 1: 当前JavaEE的打包情况.
为简化web应用程序的EJB打包机制针对的就是解决这个问题.在新的方案中,没有必要再分开建立EJB模块.EJB(尤其是采用标注annotation形式的Pojo)可以直接放在WEB-INF/classes文件夹下,作为WAR布署的一部分.类似的,如果你要使用ejb-jar.xml布署描述符,你也可以直接将其同web.xml文件一起放在WEB-INF文件夹下.将来也有可能可以把EJB jar包直接放在WEB-INF/lib文件夹下(你觉得这重要吗?).新的打包方案请看图2:
图 2: 为简化web应用程序的EJB打包方案
对我个人而言,一个有趣的含意是:简化后的打包方案与先前所要求严格定义的JavaEE EAR文件相比,EJB变成更加模糊了.(毕竟EJB也可以直接布署在WEB-INF文件夹及其子文件夹WEB-INF\classes下了)这对使用XML配置文件和JNDI look-ups而非100%采用Annotation和DI(依赖注入)的我们来说,确实是一个不小的麻烦.现在所有的EJB引用,资源的引用以及任何环境下定义的实体现在都可以在WAR中进行共享.因为整个WAR文件只有一个local componet environment(本地组件环境,将JDNI绑定在java:comp/env命名空间上).也就是说,你可以在web.xml这么定义一个数据源的引用:
<resource-ref> <description>My data source</description> <res-ref-name>jdbc/mydb</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref>
现在你不仅可以在web容器组件(比如说servlet)使用下列代码来查找数据源,也可以在WAR里的EJB中直接使用.
// Looking up my data source.
DataSource ds = (DataSource) envCtx.lookup("java:comp/env/jdbc/mydb");
你是怎么看待这个的?真正使我喜欢简化后的打包机制的一个原因是——它与EJB Lite(注:EJB Lite是EJB的一个子集,但最后是否会推出,还需要等JSR的最后决定)配合的很好.EJB Lite是EJB的一个最小子集,专门用于简化后的应用程序.在以后的文章中,我会谈到更多关于EJB Lite的内容.一种有趣的假设就是,很有可能许多供应商会先从实现EJB Lite开始,将EJB直接布署到WAR文件里,这样就完全绕过了JavaEE的EAR文件,可以运行于各种Servlet容器(比如说Tomcat或Jetty).我感觉JBoss AS Express,GlassFish Express以Tomcat+OpenEJB很可能不会忽略EJB Lite的,尤其假定在JavaEE 6基础上.而你是怎么看待这些可能发生的事情呢?
更多主题
不管你相信与否,这些讨论的特性在这两篇文章中仍然只是冰山一角.下面一些有趣的话题我将在后续的系列文章中再给大家探讨:
1. EJB以EJB Lite形式支持最低要求的容器.这也许与现在开源的Embedded Jboss,OpenEJB和EasyBeans作为Tomcat的一个插件形式类似.
2. 支持异步Session Bean的调用
3. 通过Stateful Session Bean的web service终端,支持stateful web service.
4. 标准化的JNDI映射来代替目前各个供应商四分五裂的局面已经有了初步的讨论.
除了EJB3.1外,我还想谈谈从EJB到Web Beans的使用.也许你已经知道,Web Beans是一个非常强大的综合应用程序框架,它所得EJB可以拥有一些非常有意义的DI(依赖注入)特性,而此前JavaEE一直备受批评.Web Beans规范由Gaving King领导着,并且其中的思想来自于JBoss Seam和Google Guice(外号“疯狂的Bob” Bob Lee因Guice而名声大振,目前也致力于WebBeans开发).最后,祝希望专家组好运吧,并且希望他们及时将反馈的信息进行处理.
相关连接
1. JSR 318: Enterprise JavaBeans 3.1, http://jcp.org/en/jsr/detail?id=318.
2. JSR 299: Web Beans, http://jcp.org/en/jsr/detail?id=299.
按照Linux约定:0或7都可以代表星期天
(第二部分结束)