【AICon】探索RAG 技术在实际应用中遇到的挑战及应对策略!AICon精华内容已上线73%>>> 了解详情
写点什么

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

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

关注

评论

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

浅聊组合函数

掘金安东尼

前端 函数编程 8月月更

大咖说·图书分享 | Serverless工程实践:从入门到进阶

大咖说

Serverless 工程实践

制胜精细化运营时代 华为应用市场打出内容、场景、商业运营组合拳

Geek_2d6073

学好web前端培训课程方法推荐

小谷哥

研发了 5 年的时序数据库,到底要解决什么问题?

TDengine

数据库 tdengine

A tour of gRPC:06 - gRPC client straming 客户端流

BUG侦探

gRPC RPC

CWE4.8:2022年危害最大的25种软件安全问题

华为云开发者联盟

安全 后端 开发

开源一夏 | 数据结构课设:图书信息管理--顺序存储和链式存储

是Dream呀

开源

开源一夏 | 不会吧,十分钟就能上手Prometheus与Grafana监控SpringBoot项目

知识浅谈

开源 8月月更 SpringBoot实战

大数据培训如何部署一个健壮的Airflow

小谷哥

用 Lunchbox 在 vue3 中创建一个旋转的 3D 地球竟是如此简单

前端修罗场

3D 地球 ThreeJS

数字孪生园区场景中的坐标知识

ThingJS数字孪生引擎

数字孪生

开源一夏 | Python Web开发(八):后端开发中的增查改删处理

是Dream呀

开源

快速搞懂Seata分布式事务AT、TCC、SAGA、XA模式选型

知识浅谈

开源 8月月更

一文搞懂│php 中的 DI 依赖注入

设计模式 依赖注入 8月月更 高级编程

不改一行源码,实现 sentinel-dashboard 所有配置支持 apollo 持久化

铁匠

微服务 sentinel 流量控制 sentinel dashboard

开源一夏|数据结构课设:基于字符串模式匹配算法的病毒感染检测问题

是Dream呀

开源

全面认识二极管,一篇文章就够了

矜辰所致

ESD二极管 8月月更 二极管 电子设计基础 TVS二极管

Python 教程之输入输出(2)—— 输入和输出

海拥(haiyong.site)

Python 8月月更

大数据培训机构大概要花费多少钱

小谷哥

参加前端培训后程序员能找到工作吗?

小谷哥

Redis进阶之路:深度解析Redis单线程架构,图文并茂不能再清晰了

王小凡

Java redis 程序员 开发

融云「 IM 进阶实战高手课」系列直播上线

融云 RongCloud

IM 连接协议

绝对最直白的MySQL MVCC机制总结,免费拿走

知识浅谈

开源 8月月更

大数据培训班如何选

小谷哥

创新云集技术咖,工赋汇聚实战派:2022工赋开发者峰会

工赋开发者社区

工业 峰会

「全球数字经济大会」登陆 N 世界,融云提供通信云服务支持

融云 RongCloud

isc N世界

2022年值得尝试的7个MQTT客户端工具

EMQ映云科技

物联网 IoT mqtt 客户端 8月月更

电烙铁的基础知识

单宝华

电子技术 8月月更

什么是SVN(Subversion)?

龙智—DevSecOps解决方案

svn 版本控制 版本管理 版本控制软件

安全至上:落地DevSecOps最佳实践你不得不知道的工具

龙智—DevSecOps解决方案

DevOps DevSecOps

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