速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

Java EE 6:EJB 3.1 的变化引人注目

  • 2010-02-17
  • 本文字数:4546 字

    阅读完需:约 15 分钟

Enterprise Java Bean 3.0(EJB 3)规范为 Java 企业级开发画上了浓墨重彩的一笔,规范的制订是非常透明的,很多想法都来源于社区。它代表了更加一致的服务模式,大量使用 POJO 的同时降低了复杂性。Java 5 的注解间接地成就了这种模式,在使其变得更加强大的同时也减少了开发者的工作量。对于各种新的解决方案来说,EJB 3 抛弃了那些差劲的决策,因此会对那些曾经回避 EJB 的开发者产生极大的吸引力。EJB Entity Beans 不见了,取而代之的是 JPA Entity。EJB 2.1 及更早版本所需的大量 Java 类和接口不见了。默认的“约定优于配置”得到了广泛的应用,这样人们上手就会非常快。EJB 3.0 是一场真正的革命。

如果说 EJB 3.0 是一场革命,那么 EJB 3.1 则是一种进化。EJB 3.1 的一些新特性似乎应该放到 EJB 3.0 中,大家应该原谅规范制定者所持有的谨慎态度——让 80% 的内容能够运转良好要比废弃掉规范中某个差劲的模式要好很多,宁缺毋滥。当然了,其发布时机还是不错的。虽然提供了大量的新特性,但 EJB 3.1 规范及所有的向后兼容和补充说明加起来一共才 626 页,比 10 年前的 EJB 2.1 规范少了 14 页。

我们将在本文介绍这些新特性及其使用方式。

EJB 3.1 的新特性

EJB 3.1 最大的变化并不是向平台增加了很多新特性,而是简化了平台的使用。其中一些扩大了用户所用 API 的范围以提升灵活性;另一些只是带来了更多的灵活性而已。

Singleton

Singleton 是一种新型的 Session Bean,提供了一个额外的保证:每个 JVM 上的 Bean 只会被创建一次。该特性在很多地方都有用武之地,比如说缓存。此外还保证每个资源只有一个共享视图存在,而应用服务器并没有提供该特性。无需持久化的有价值的数据可以通过简单存储实现,因为重新创建的代价可能会很大。来看一个具体的示例吧。假设其他地方已经创建好了 User 实体(或许使用了 JPA 2.0)。

复制代码
@javax.ejb.Singleton
public class ChatRoom {
private java.util.Map<User,Collection<String>> userComments;
@PostConstruct
public void setup (){
userComments = new java.util.concurrent.ConcurrentHashMap<User,Collection<String>>();
/* ...*/
}
public void join(User usr){ /* ...*/ }
public void disconnect(User usr){ /* ...*/ }
public void say(String msg){ /* ...*/ }
}

所有的客户端都可以通过 ChatRoom 实例的引用来更新同一个可变的状态。可以保证的是客户端所获得的实例是一样的。由于这是一个 Session Bean,因此与其他 Session Bean 一样都是线程安全的。

复制代码
@javax.ejb.EJB
private ChatRoom chatRoom ;
/* ... */
chatRoom.join(myUser) ;
chatRoom.say( "Hello, world!");
chatRoom.disconnect();

设计 Singleton 的目的就是用于并发访问。规范赋予了开发者复杂的控制手段来解决并发访问问题。我们可以通过容器以声明的方式指定某种访问类型,该行为是默认的;可以通过注解 @javax.ejb.ConcurrencyManagement(CONTAINER) 显式指定使用容器管理的并发手段。如果想对 Bean 进行更多的控制,请使用 @javax.ejb.ConcurrencyManagement(BEAN)。凭借容器管理的并发手段,我们可以在方法或类层次上指定访问类型。可以在默认情况下于类层次上使用 @javax.ejb.Lock(WRITE) 注解以保证所有的业务方法都是可序列化的,然后针对特定的“只读”方法再进行优化,这么做不会产生任何的副作用。对于只读方法需要使用注解 @Lock(READ)。对于 @Lock(WRITE) 注解所修饰方法的所有访问都是可序列化的,同时会阻塞客户端的访问直到前一个访问完成,或是出现超时的情况。可以通过 @AccessTimeout 注解指定超时的时间,该注解需要一个 java.util.concurrent.TimeUnit 值。现在我们可以使用这种并发控制了,先删除之前的 ChatRoom 实现代码。

复制代码
@javax.ejb.Singleton
@javax.ejb.Lock(WRITE)
public class ChatRoom {
private java.util.Map<User,Collection<String>> userComments;
@PostConstruct
public void setup (){
userComments = new java.util.concurrent.ConcurrentHashMap<User,Collection<String>>();
/* ...*/
}
public void join(User usr){ /* ...*/ }
public void disconnect(User usr){ /* ...*/ }
public void say(String msg){ /* ...*/ }
@javax.ejb.Lock(READ)
public int getCountOfActiveUsers(){ /* ... run through the map and count ... */ }
}

显然现在这个 ChatRoom 实现还是有问题的:一旦用户和帖子的数量超出了应用服务器所能承受的内存极限就会出现内存不足的问题。可以使用过期(expiration)机制来解决这个问题。为了能说的明白点,假设有一个垃圾收集器周期性地检查 ChatRoom 的帖子并根据 LRU 原则(或是使用 JPA 和 EntityManager.persists,然后再销毁)销毁过期的聊天数据。

EJB Timer

从 EJB 2.1 开始 EJB 就拥有一个 Timer 机制,然而遗憾的是该机制总是使用毫秒间隔数,用起来也十分不爽。EJB 3.0 对此进行了一些改进,但本质上却没有什么改变,还是基于时间间隔。因此,如果想在每周的开始执行一项活动就比较难办了。EJB 3.1 对此又进行了一些改进,提供了一种声明式、灵活的定时器服务并参考了其他调度器,如 Quartz 和 Flux。对于大多数简单的调度来说(包括 CRON 风格的调度),EJB 3.1 是一个完美的解决方案。再来看看 ChatRoom 吧,我们想周期性地回收旧数据。

复制代码
@javax.ejb.Singleton
public class ChatRoom {
private java.util.Map<User,Collection<String>> userComments;
@javax.ejb.Schedule(minute="1", hour="*")
public void cleanOutOldDataFromChatLogs() {
/** ... not reprinting all the existing code ... */
}
}

我们编写了一个方法来快速检查数据,如果必要的话就会删除旧的聊天记录,这样一切就尽在掌握之中,不会再出现内存吃紧的问题了。这里使用了声明式模型以保证方法每隔一小时都会运行,当然了,我们仍然从 EJB 3.0 中注入了 TimerService。

无接口的视图

EJB 3.0 中的 Bean 至少要实现一个接口(Local 或是 Remote 的业务接口),接口用作 Bean 对于客户端的视图。虽然间接地使用接口是个很强大的技术,但有时会使事情变得复杂。在 EJB 3.1 中可以编写没有接口的 Bean。客户端所看到的视图就是类所公开的 public 方法。

复制代码
@javax.ejb.Stateless
public class Calculator {
public float add ( float a , float b) {
return a + b;
}
public float subtract (float a , float b){
return a - b ;
}
}

该 Bean 的客户端可以通过正常的注入来获取 Bean 并调用其方法:

复制代码
@javax.ejb.EJB
private Calculator calculator;
...
float result = calculator.add( 10 , 10 ) ;
...

异步服务

处理可伸缩问题最简单的办法就是不去处理(直到容量不足时才考虑)!这种方式就是人尽皆知的 SEDA(staged event driven architecture)模式,可以通过排队来避免瓶颈的出现。通过这种方式为任务排队,同时客户端可以继续后面的处理。如果组件的处理需要花费很长时间,同时系统没有过载,那么这种模式可以保证处理时间较长的组件不会把系统搞糟。

处理可伸缩性问题的另一个方法就是不要在单向消息交换时阻塞客户端的调用。此外还可以进行异步的处理,让客户端继续后面的处理直到结果返回。所有这些方法都加到了 EJB 3.1 中新的异步服务支持当中。我们可以在 Bean 或是单个方法上使用注解 @javax.ejb.Asynchronous 以告诉容器在调用结果返回前不要阻塞客户端的执行,这样客户端就可以继续后续的处理,从理论上来说,这种方式可以让容器缓存待处理的任务直到其可以处理为止。

如果使用 @Asynchronous 来注解 Bean 类或是业务接口,那么该注解会应用到 Bean 上的所有方法;否则只有使用了 @Asynchronous 注解的方法才会变成异步的。异步方法可以返回 void 或是 java.util.concurrent.Future的实例。客户端可以通过 Future实例来查询返回的结果,但方法调用后客户端可以继续后续的处理而不会被阻塞。在这种方式下,即便 EJB 花费了 1、2 个小时的处理时间也无所谓,因为客户端并不会受到任何影响。从概念上来看,这与向 JMS 队列发送请求来调用服务的方式是一致的。

Future实例可用于取消任务或是等待结果。客户端的事务上下文不会被传播到异步方法中,这样对于异步方法来说,REQUIRED 要比 REQUIRES_NEW 更具效率。

看个具体的示例吧:要构建的服务需要与几个 Web Services 通信并综合使用调用的结果。我们需要结果,但却不能挂起客户端的请求(或许是个网页)。如下代码展示了该服务:p>

复制代码
@javax.ejb.Stateless
public CarHotelAndAirLineBookingServiceBean implements CarHotelAndAirLineBookingService {
@javax.ejb.Asynchronous
public Future<BookingConfirmation> bookCarHotelAndAirLine( Car rental, Hotel hotel, AirLine airLine) {
/** ... */
}
}

我们在客户端(一个 JSF action)这样调用服务:

复制代码
@Named
public BookingAction {
@javax.ejb.EJB private CarHotelAndAirLineBookingServiceBean bookingService;
private Future<BookingConfirmation> confirmation;
public String makeRequest(){
Hotel hotelSelection = ... ;
Car rentalSelection = ... ;
AirLine airLineSelection = ... ;
confirmation = bookingService.bookCarHotelAndAirLine(
rentalSelection, hotelSelection, airLineSelection ) ;
return "showConfirmationPending";
}
public Future<BookingConfirmation> getConfirmation(){ /* ... */ }
/* ... */
}

简化的部署

EJB 3.1 还首次提供了动态、更加简化的部署方式,支持.WAR 文件内的部署。在打包到 WEB-INF/classes 或是 WEB-INF/classes 下的.jar 文件中时,具有组件定义(component-defining)注解的类将成为 Enterprise Bean 组件。此外,还可以通过 WEB-INF/ejb-jar.xml 文件来定义 Enterprise Bean。打包到.WAR 中的 Bean 共享单独的命名空间并成为.WAR 环境的一部分。这样,将.jar 打包到 WEB-INF/lib 下就与将 class 文件放到 WEB-INF/classes 中是一样的了。

新规范另一个值得注意的特性就是 EJB Lite。对于很多应用来说,EJB 技术显得过于庞大了。EJB Lite 提供了 EJB 的一套子集,关注于 Session Bean 的使用。它提供了一种方式以嵌入式风格来使用 EJB 组件,这简化了单元测试。EJB Lite 支持无状态、有状态以及单例的 Session Bean。Bean 可以有 Local 接口,也可以没有接口。他们可以与拦截器协同工作并使用事务和安全等容器服务。

EJB 3.1 是个强大的开发者工具集,能满足应用 80% 的需要。规范的未来是光明的,同时也首次明确地提及了未来 Java SE 的裁剪机制将要移除的特性。未来可能会移除的特性包括老式的容器管理和 Bean 管理的持久化、Entity Bean 的 EJB 2.1 客户端视图、EJB QL(EJB 2.1 的查询语言)以及基于 JAX-RPC 的 Web Service 支持(包括 J2EE 1.4 引入的端点和客户端视图)。显然,EJB 3.1 是个引人注目、向后兼容的升级,它代表了 5 年前开始的 JSR 220(EJB 3.0)的新进展。

查看英文原文: Java EE6: EJB3.1 Is a Compelling Evolution

2010-02-17 07:256102
用户头像

发布了 88 篇内容, 共 263.0 次阅读, 收获喜欢 8 次。

关注

评论

发布
暂无评论
发现更多内容

京东前端二面常考手写面试题(必备)

helloworld1024fd

JavaScript 前端

手写一个react,看透react运行机制

goClient1992

React

BNB Chain 2023年40佳DAPP评选,Zebec赫然在列

威廉META

从recat源码角度看setState流程

flyzz177

React

从深度学习框架到开发工具,百度飞桨携最新成绩单亮相 GTC

飞桨PaddlePaddle

英伟达 百度飞桨 GTC

你的聊天室该升级啦!融云平滑迁移方案助你「无感换乘」

融云 RongCloud

通讯

ChunJun 1.16 Release版本即将发布,bug 捉虫活动邀您参与!

袋鼠云数栈

技术写作的“坎”

码猿外

程序员 写作

DockQuery 天狼 v1.2.0 正式发布

BinTools图尔兹

#数据库

极光笔记 | 极光PUSH服务助力企业提升抢单速度

极光JIGUANG

技术干货 移动推送 智能推送

自动化测试工具加入黑科技带来新纪元

石臻臻的杂货铺

人工智能

从零手写react-router

helloworld1024fd

JavaScript 前端

适合开发团队的文档管理系统盘点

爱吃小舅的鱼

文档管理软件 团队协作管理

云原生消息队列Pulsar浅析——实践类

阿里云AIoT

阿里云 物联网 IoT

一次配置,设备就可实现毫秒级的全球就近接入——实践类

阿里云AIoT

阿里云 物联网 IoT

【明晚直播】KunlunBase 1.1 版本发布:完善MySQL 兼容性,OLAP性能提升

KunlunBase昆仑数据库

MySQL 数据库 PgSQL 线上直播

滴滴前端一面常考手写面试题整理

helloworld1024fd

JavaScript 前端

从源码角度看React-Hydrate原理

flyzz177

React

轻量易部署!Coolbpf 发布不依赖 Clang 的脚本化编程特性 lwcb | 龙蜥技术

OpenAnolis小助手

开源 rust ebpf coolbpf lwcb

深度分析React源码中的合成事件

goClient1992

React

从react源码看hooks的原理

flyzz177

React

Unity 荣膺 2022 鲸鸣奖“影响力出海品牌”及“新势力出海服务商”两项大奖

Geek_2d6073

从计费出账加速的设计谈周期性业务的优化思考

鲸品堂

运营商 业务流程优化 企业号 3 月 PK 榜

深入React源码揭开渲染更新流程的面纱

goClient1992

React

BNB Chain 2023年40佳DAPP评选,Zebec赫然在列

鳄鱼视界

墨天轮2022年度数据库获奖名单

墨天轮

数据库 opengauss TiDB oceanbase 国产数据库

假如面试官要你手写一个promise

helloworld1024fd

JavaScript 前端

基于开源IM即时通讯框架MobileIMSDK:RainbowChat-iOS端v6.2版已发布

JackJiang

网络编程 即时通讯 IM

基于大规模边缘计算的千万级聊天室技术实践

环信

聊天室 大规模边缘计算 千万级

Java EE 6:EJB 3.1的变化引人注目_Java_Josh Long_InfoQ精选文章