QCon北京「鸿蒙专场」火热来袭!即刻报名,与创新同行~ 了解详情
写点什么

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:256144
用户头像

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

关注

评论

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

物超所值!5.7w字在GitHub标星120K的Java面试知识点总结

Java 架构 面试 程序人生 编程语言

后端选择 java, 还是 python?

cdhqyj

十大算法

wudaxue

百度智能云全面升级金融AI中台解决方案, 打造软硬一体AI开发全栈能力

百度大脑

人工智能 金融

防火防盗防内卷!阿里的24W字Java面试复盘指南,在Github上已标星98K+

Java 架构 面试 程序人生 编程语言

企业如何通过图数据库及知识图谱形成业务壁垒

星环科技

夸克APP端智能:文档关键点检测实践与应用

阿里巴巴终端技术

算法 移动开发 客户端 端智能

《数据安全法》实施后,企业如何依法进行数据安全加固及创新

星环科技

数据安全 数据安全法

第6章-《Linux一学就会》- Centos8 用户管理

学神来啦

Linux 运维 linux学习 linux云计算

小程序下一破局点?钉钉小程序卡片,应用与平台的深度集成

阿里巴巴终端技术

小程序 ios android App 移动开发

想要入职阿里P8?至少是要啃完这本500页Java并发多线程源码笔记!

Java 架构 面试 程序人生 编程语言

开发上云,化繁为简 | CIF 论坛精彩看点

CODING DevOps

腾讯云 DevOps 云原生 云开发 CIF

如何处理各种「陨石开发」的紧急要求?

LigaAI

敏捷开发

大模型时代的AI之变与开发之根

脑极体

列举出常见的Java面试题100+,我靠这个在十月拿到了阿里的offer

Java 程序员 编程语言

当支付宝 App 遇见 AndroidX......

阿里巴巴终端技术

android App 移动端 AndroidX

2022前端react高频面试题

buchila11

React

常用的 分布式事务 都有哪些?我该用哪个?

Java 程序员 面试 后端 计算机

不愧是阿里高工出产的《Java面试手册》,实战命中率竟高达“80%”

Java 架构 面试 后端

低代码的自动化工作流靠谱吗?对企业有何帮助?

优秀

自动化 低代码

AI技术在漫画阅读体验上的应用

快看工程技术中心

深度学习 AI 漫画

云拨测助力节卡机器人 全面优化海外网站性能

阿里巴巴云原生

阿里云 云原生 拨测 成功案例

GraphQL 快速入门【4】GraphQL 组件

码语者

Rest graphql

基于星环大数据云平台 TDC 的一站式数据湖解决方案

星环科技

大数据 云平台

未来5年,虚拟化5个非常有前景的就业方向

hanaper

技术干货 | Native 页面下如何实现导航栏的定制化开发?

蚂蚁集团移动开发平台 mPaaS

大前端 H5 移动开发 mPaaS

大数据presto作业

Clarke

这份阿里P8手写的JDK发展史源码剖析手册,竟获GitHub热门榜第一

Java 架构 面试 程序人生 编程语言

财经大课:从效率公平看“共同富裕”

石云升

学习笔记 9月日更 共同富裕

内含(基础+进阶+高级+调优)的神仙级的阿里巴巴“MySQL”教程限时开源!

Java 架构 面试 程序人生 编程语言

DCEP:真正的“无现金新时代”!现已完成技术对接!

CECBC

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