Global JNDI names(统一的全局JNDI命名)
该特性已经渴望很久了,终于在EJB3.1 中得以实现。原来EJB的全局JNDI命名方式都是供应商各自的实现版本,在布署的时候有很多问题。同一个应用程序中的那些session beans在不同供应商的容器中很可能JNDI命名就不同,造成客户端的调用代码必须得调整修改。除此之外,支持EJB3的某些供应商将允许将本地业务接口配置在全局的JDNI中,而另一些供应商却将此特性排队在外,这也导致了兼容性问题。
规范定义了全局JNDI命名方式,采用统一的方式来获取注册的session beans。也就是说,我们终于可以使用兼容性的JNDI命名了。
每个兼容的全局JNDI命名都有如下语法规则:
java:global[/<app-name>]/<module-name>/<bean-name>[!<fully-qualified-interface-name>]
下面的表格是对不同的元素的解释:
名称 |
描述 |
必选 |
app-name |
应用程序的名称。如果没有在application.xml中指定,则默认的名称就是EAR的打包名称。 |
否 |
module-name |
模块的名称。如果没有在ejb-jar.xml中指定,则默认的名称就是bundle文件名 |
是 |
bean-name |
Bean的名称。如果没有使用标注@Stateless,@Stateful,@Singleton或其它布署描述符,则默认的名称就是该session bean的类的完全限定名称。 |
是 |
Fully-qualified-interface-name |
暴露接口的限定名称。如果是一个no-interface view,则它的值为应该bean类的完全限定名称。 |
是 |
如果一个bean只想对客户端暴露一个对外接口,那么容器不公必须保证该bean在JNDI命名中是可用的,而且必须采用以下格式:
java:global[/<app-name>]/<module-name>/<bean-name>
为了简化JDNI的使用,容器分别也提供了java:app 和 java:module 两种命名方式:
java:app[/<module-name>]/<bean-name>[!<fully-qualified-interface-name>]
java:module/<bean-name>[!<fully-qualified-interface-name>]
示例1:
Bean A 可以通过下面合法的JNDI命名来取得这个可用的no-interfaceview:
示例2:
将下列代码打包到mybeans.jar中,但不放在任何ear包中。同样,我们没有使用任何布署描述符:
Blocal接口可以通过以下JNDI命名获得:
BRemote接口可以通过以下JNDI命名获得:
Timer-Service(调度服务)
有相当一部分企业应用程序或多或少的有“时间驱动(time-driven)”的需求。长久以来,EJB规范却一直忽略了这一点,于是开发人员被迫去采纳非标准的解决方案——Quartz或Flux。早在EJB2.1时就引入了Timer Service,容器提供Timer服务,允许EJBs在特定情况下使用timer回调从而实现任务调度。除外之外,任务调度还可以在事务上下文中完成。
虽然大家都知道Timer服务对某些应用是非常重要的环节,但EJB的专家组们考虑的非常有限,比如:
到EJB3.1版本时,有两种方式来创建timer:
@Schedule可用于自动创建一个timer,里面可以加入参数来限制调度时间。当一个方法被标注@Schedule后,到时间了就会自动被容器回调。如果采用编程式来创建timer,对一个bean来说,在哪个方法中调用timer都无所谓(原文没有给出编程式的例子,我在网上找了一个)。 如果是声明式的创建方式,只局限于被@Schedule标注过的方法才可以任务调度。在接下来的两个timer例子中,一个定义了每周一的午夜开始调度;另一个其是每个月的最后一天开始调度。注意看itIsMonday和itIsEndOfMonth上面的annotation:
现在无论是声明式还是编程式都可以持久化(默认选项)或非持久化。非持久化的timer在应用程序关闭或容器宕机时,并不会存活下来。可以使用annotation的持久化属性来实现持久化需求。对于编程式的话,可以将TimerConfig对象作为参数传递给TimerService接口的createTimer方法。Timer接口提供了新的isPersistent方法,判断是否允许持久化。
每个被持久化的timer相当于一个单独的timer,在应用程序分布式布署时,也不用考虑JVM的数量。这对集群的应用影响非常重大。现在我们假设一下这个场景,如何让一个应用程序更新一个现有的timer。在EJB3.1之前,如果是布署在单个JVM的应用程序中,很容易做到,直接替换即可。但在在多个JVM的环境下,如果只是其中某个JVM的timer被创建或更新了,对其它JVM来说是不可见的,自然容易出很多奇怪的问题。这就意味着必须采用某种策略允许现有的所有timers对所有的JVM都是可见的。每次在出问题后,才知道是布署的应用程序不一致或其它低级错误造成的,这是一种非常差的实践方式。到EJB3.1时,开发人员再也不用关心布署时的跨JVM问题,这个工作留给容器去实现。
一个自动创建的非持久化timer在每次跨越JVM时,会在每个JVM中创建一个新的timer实例。
Timeout的回调方法有两个可选的重载函数:void <METHOD> (Timer timer) 和 void <METHOD> ()。
对于定时调度而言,有了很大的改进。表达式采用了模仿UNIX cron的日历语法格式。有8个主要属性可以按照下列的规则使用:
属性 |
属性值 |
示例 |
second |
[0, 59] |
second = "10" |
minute |
[0, 59] |
minute = "30" |
hour |
[0, 23] |
hour = "10" |
dayOfMonth |
- [1, 31] - day of the month - Last - last day of the month - -[1, 7] - number of days before end of month - {"1st", "2nd", "3rd", "4th", "5th", ..., "Last"} {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}- identifies a single occurrence of a day of the month |
dayOfMonth = "3" dayOfMonth = "Last" dayOfMonth = "-5" dayOfMonth = "1st Tue" |
month |
- [1, 12] - month of the year - {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}- month name |
month = "7" month = "Jan" |
dayOfWeek |
- [0, 7]- day of the week where both 0 and 7 refer to Sunday - {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}- day's name |
dayOfWeek = "5" dayOfWeek = "Wed" |
year |
Four digit calendar year |
year = "1978" |
timezone |
Id of the related timezone |
timezone = "America/New_York" |
每个属性值还有不同形式:
表达式类型 |
描述 |
示例 |
Single Value |
限制属性只有一个值 |
dayOfWeek = "Wed" |
Wild Card |
对于给定的属性,允许任意合法值 |
month = "*" |
List |
限制属性允许两个或两个以上的值,中间用逗号隔开 |
DayOfMonth = "3,10,23" dayOfWeek = "Wed,Sun" |
Range |
限制属性在一个封闭的区间段内 |
year = "1978-1984" |
Increments |
定义一个 x/y 的表达式。限制属性在每 y 秒调度一次,并且在 x 时开始。 |
second = "*/10" - every 10 seconds hour = "12/2"- every second hour starting at noon |
再来多看一些示例吧:
每周二上午7:30开始调度:
@Schedule(hour = "7", minute ="30", dayOfWeek ="Tue")
每周从周一到周五的,7点,15点,20点开始调度:
@Schedule(hour = "7, 15, 20",dayOfWeek = "Mon-Fri")
每周日的每个小时调度一次:
@Schedule(hour = "*", dayOfWeek ="0")
Last Friday of December, at 12
每年12月的最后一个周五12时调用一次:
@Schedule(hour = "12", dayOfMonth= "Last Fri", month="Dec")
2009年每个月的最后三天的20点开始调用:
@Schedule(hour = "20", dayOfMonth= "-3", year="2009")
从下午三点开始,每个小时的第5分钟开始调用:
@Schedule(minute = "*/5", hour ="15/1")
TimerService接口对编程式也进行了增强,可以使用类似于cron的表达式。这些表达式可以看作是ScheduleExpression类的实例,并在创建timer时,作为参数传递进去。
EJB Lite
EJB必须按照规范去实现一系列API。从EJB3.1开始,这组API分成了两——最小配置和完整配置。最小配置就是EJB3.1 Lite,它的作为EJB3.1的子集基本足够应用程序的开发,没有必须实现整套API规范。这将带来很多好处:
提高了性能。削减大量API,为容器减了不少肥,自然变得更加轻快了,提供的服务也更加出色。
学习曲线降低。成为一名EJB开发人员可不是一件很简单的事,要求开发人员学习大量的知识。如果只是开发EJBLite应用程序,开发人员就的学习曲线明显轻松很多,提高了生产率。
降低了开销。常常听到人们抱怨高昂的EJB容器价格,以及一大堆根本用不上的API。一个应用程序几乎没有用啥statelessbean,也根本用不着很多EJBAPI或特性,却还要承担这笔可观的费用。现在有了EJB完事版和EJB Lite版,供应商可以采用不同的许可费用,应用程序可以按需付费了。
EJB 3.1 Lite包括下列特性:
简化的EJB打包机制
ejb-jar文件为enterprise beans的打包模块。在EJB3.1前,所有Beans都必须打包在该包下。那时考虑到的是所有JavaEE应用程序都由web 前端和 EJB后端组成,自然ear的目的就是分别将这两个模块 war 和 ejb-jar打包成一个整体。将结构分为前端和后端感觉上是一个不错的最佳实践,但其实于于简单的应用程序来说反而难以忍受。
EJB 3.1 允许企业enterprise beans打包到war包中去。这些类可以放在WEB-INF/classes目录下,或者打成jar包扔到WEB-INF/lib中去。一个war包最多只能包含一个ejb-jar.xml,该文件可放在WEB-INF/ejb.jar.xml下,也可放在WEB-INF/lib某个ejb-jar中的 META-INF/ejb-jar.xml下。
这种简化的打包方式必须是用在简单的应用程序布署环境中,如果你有更多的需求,还是切换回传统的ear包吧。
Embeddable EJB Containers(嵌入式的EJB容器)
传统意义上,EJB总是同一大堆笨重的Java EE容器联系在一起,很难使用:
EJB3.1最具意义的特性之一就是提供了embeddable container。现在就连JavaSE客户端就可以在自己的JVM和classloader,实例化EJB容器。嵌入式的容器提供了一系列基本服务,允许客户端在享受EJB和同时,还不需要那些完整版的JavaEE容器。
embeddable container会扫描classpath从而找到EJB模块。有两种方式来限定是否为EJB模块:
同样一个bean,无论是跑在embeddable container还是标准的Java EE容器,都没有什么区别,也不要求你的代码做任何修改。这一点绝对保证是透明的。
embeddable container原则上是应该至少实现EJB 3.1 Lite子集的。但仍然允许供应商去扩展EJB3.1其它更完整的功能。
EJBContainer类在embeddable container中扮演了一个非常核心的角色。它的static方法createEJBContainer用于实例化一个新的容器;而当close方法调用时,先遍历所有bean的PreDestroy回调方法,最后关闭容器。最后一项的要点是,getContext方法用于返回一个context,然后客户端可以通过这个context将布署在embeddable container中的session bean都给lookup出来使用。
输出结果:
Log output
Starting client...
ByeEJB is being initialized...
Container created...
ByeEJB is saying bye...
Got the following message: Bye!
ByeEJB is being destroyed...
Finishing client...
接下来的热点
除了上述的新的特性外,还有一些细微的改进。比如说简化现有的功能。下面列出的就是相关话题:
结论
Java EE即将发布最终版,上面提到的绝大多数特性非常接近最终版本。2009必将是JavaEE火爆的一年。
EJB 3.1提供了更出色的架构,同时还为开发人员提供了更丰富的功能集,允许你去扩展现有设计和实现。这次发布的版本非常成熟完整,会使用Java服务端开发更加牢固。
由于技术总是在不断的自我完善,总难免会遗漏些更新的特性。计划是在下个版本推出下列特性: