技术开发 频道

Bobby Woolf 谈 J2EE 体系结构和设计

  【IT168 技术文章】

         引言

  Java™ 2 企业版(Java™ 2 Enterprise Edition,J2EE)是开发并部署企业应用程序的环境。J2EE 平台由服务、应用程序编程接口(application programming interfaces,API)及协议所组成,该协议提供了开发多层的且基于 Web 的应用程序的功能。

  问: EJB 或 Web 服务的 BEA 控制及注释看上去是非常强大的并且易于使用这些技术。对此,您的看法是什么?在 WebSphere® 中使用这些技术有多容易?

  答: 您似乎希望获得 BEA® WebLogic Workshop 和 IBM WebSphere Studio Application Developer(以下简称 WebSphere Studio)的比较。我没有使用过 Workshop,所以不能直接评论它的特点。WebSphere Studio 确实包含许多对于元信息的编辑器,如部署描述符。使用这些编辑器比编辑原始的 XML 要容易得多(虽然 Source 记录页面也允许您那样做)。同样,在 WebSphere Studio V5 中重新设计了这些编辑器,使其与 J2EE 指定的和 WebSphere 指定的配置设置有更加明显的不同。

  问: 您知道如果启用了 WebSphere Application Server V5.1 Global Security,那么 Web 服务和 WebSphere Application Server 之间的传输是否可以自动地转换成 https?例如,使用 defaultSSLsettings 和 jmserver 认证。我不必在 Web 和 Application 服务器之间创建新的证书。同时,我已经具有 Web 和浏览器客户端之间的 https 了。目前,这仅是不完整的 Web<>WAS https。全局安全性还没有被启用。

  答: 在 WebSphere Application Server 5.1 中启用全局安全性会自动地进行 Web 服务的 HTTPS 转换吗?据我所知不是这样的。您需要自己手动完成。请见 WebSphere Application Server Information Center 中的“调用 HTTPS 上的 web 服务”和“使用 HTTP 基础认证来安全化安全的 Socket 层上的简单对象访问协议服务”。

  问: 在我们的项目中有一套 EJB。基本上,我们具备安装在 WebSphere Studio V5.1.2 上的 EAR 文件。当测试环境服务器运行时,对于 EJB 项目及客户端项目的代码变更都不会受到影响。每次我都需要重新启动测试环境服务器才能使得改变生效。那么,存在任何方案或配置装置来解决这个问题吗?

  答: 如果在您使用调试器编辑代码的时候(例如编辑 EJB 实现类,但不改变 EJB 的接口)启用了 hot-method 替换,那么将立即改变正在运行的应用程序的代码。请见 WebSphere Studio 的在线帮助中的“Hot-method 替换”。您也可以重启个别项目,这比重新启动整个项目要快得多。然而,许多变更需要重启测试服务器才能实现;请见 WebSphere Studio 的在线帮助中的“何时需要重启测试服务器”。也可参阅“调试服务器上的企业级 bean”和“调试服务器上的 servlet”。

  问: 在 Web 应用程序中,我们希望将 uid 和 pwd 传递到 EJB 层中,以便会话 EJB 能够通过 uid 和 pwd 连接到后端系统。我们不想将 uid 和 pwd 作为参数传递,或者存储到一些从 Web 层到 EJB 层传递的对象中。同时,存在不同的流程,它需要使用 uid 和 pwd 来连接后端。您能推荐完成该任务的解决方案吗?

  答: 对于 J2EE 应用程序,所有用户都使用同样的登录信息来访问后端系统。J2EE 不能使每个应用程序的用户都使用各自的登录信息。您的 EJB 层应当是无状态的服务,该服务由无状态的会话 bean 来负责管理,所以不能存储用户的数据,如用户名或密码。您可以在用户的 HTTPSession 中存储这些项目,但是这可能是巨大的潜在安全漏洞,并且登录信息可能不是非常有用的。所以,您需要使用 J2EE 服务来访问后端系统,它对于所有用户都使用同样的登录信息。

  如果您的后端系统是 JDBC 数据库,那么您应当将登录信息与数据源联系起来。例如,在 WebSphere Studio 中,在服务器配置编辑器中添加登录信息作为安全页面上的 JAAS 认证条目,然后将其指定为数据源容器管理的认证别名(在数据源页面上)。对于其他的后端系统,您应当获取或开发适合于 J2EE 连接器体系结构(J2EE Connector Architecture)的适配器,它也提供了登录信息。

  如果您有两种类型的用户,那么他们应当使用两种不同的登录信息,我的文章消除 J2EE 1.3 的服务探测器实现中的缓存中讨论了如何使用两个不同的 EJB 类来完成此项工作(登录)。显而易见,该技术不能使得成百上千的用户都用他们自己的登录信息登录。

  问: 我不是 EJB 专家,但是我已经学习了 IBM 提供的该方面的大量的教程和红皮书。我个人认为 WebSphere Studio 生成“管理的代码”(它被优化来管理容器)的能力激发了开发人员更大的创造力,远远超过不使用 EJB 开发的时候。这甚至适用于用不到 EJB 的情形。我的问题是:应当纠正它吗?我已经试着让我的公司从纯生产的角度来学习 WebSphere Studio 或 WebSphere Application Server 的 EJB 开发,但外面的顾问已经发现了所有 EJB 的负面案例。

  如果 IBM 有以小型或中型商店为对象而编写的白皮书,那么它将起到帮助作用。该商店展示了比“Banking”应用程序(使用 WebSphere Studio 和类似于 Spring/Hibernate/JDO/“whatever”解决方案的 WebSphere Application Server 来开发的应用程序)更复杂的基于 web 的 EJB 应用程序。我认为加快设计及开发应用程序的速度要与减少实际编写的代码量同时进行,通过使用 WebSphere Studio 能够使代码编写更高效。这假设工具能有效地支持您的向导生成的代码。

  花费的时间、创建的代码、应用程序的性能及每方面的耗费的具体细节都能证明通过使用 WebSphere Studio 或 WebSphere Application Server 使得 J2EE 或 EJB 开发比一行一行地推敲业务及管理代码更加高效且可维护。再一次,将与提高整体性能相关的硬件耗费同缩短开发时间及提高对于庞大的代码库的维护能力所需的耗费进行权衡之后,我认为前者可以忽略不计。

  答: 您对于向 SMB 市场提供更多的市场及技术材料的意见我十分赞同,但是在我的部门这并不实际。我所能做的是试着回答技术问题。

  您在正确的轨道上,也就是您应当最大限度地利用这些工具使得您的工作更简单,最大限度地利用容器使得代码的编写工作更容易。简短的代码更易于编写、测试、调试、维护及通过端口传输。您的代码应以您唯一的业务值为主,而不是寻找每个典型的应用程序的需求,这可以由容器来处理。

  我对 EJB 的负面案例没有强烈的兴趣。许多由于 EJB 1.0 的时间框架和忽视诸如本地接口和容器管理的关联(container-managed relationships,CMR)的改进。大多数负面案例包含实体 bean 并忽视会话及消息驱动的 EJB 的危险程度。

  即使使用实体 bean,O/R 映射还是非常困难的,那么您使用什么会好一些呢?我建议开发者开始使用 CMP 实体 bean 来将他们的域建模并且处理他们数据库的 O/R 映射。“然而 CMP 是非常慢的!”也许,O/R 映射是通用的,至少对于 CMP 而言,您使用容器来优化该流程。IBM 有许多开发人员致力于将 O/R 映射工作做得尽可能好。我想问每个客户:您真的认为您的团队能够开发更好的代码吗?(尤其是他们是否已经认为 EJB 太难了?)我认为首先尝试 CMP。如果证实它确实太慢了(或不能忍受),那么确定出现问题的特定的 bean 并替换它们。然后您需要再次运行您的加载测试,并且验证您的新代码真的优于它所取代的 CMP 代码。如果开发者严格地检查结果,并且不相信传闻,我认为他们会发现标准的 J2EE 的特点及 WebSphere 支持对于他们实际工作是非常有利的。

  问: 我们使用 WebSphere Studio 5.0 作为开发环境开发了一个 J2EE 应用程序。但是我们希望将其部署在 WebSphere Application Server 4.0 上。由于 EAR 文件是由 WebSphere Studio 5.0 生成的,所以它兼容 WebSphere Application Server 5.0。它给出了关于 DTD 的错误。考虑到相关的 EJB,我们仅使用了无状态会话 bean。需要哪些改变?在 WebSphere Application Server 4.0 中的哪些地方需要部署应用程序?

  答: 如果我正确地理解了您的问题,那您的意图是使用 WebSphere Studio 5.0(目前最新的版本是 5.1.2,所以您可能希望升级到该版本)来开发应用程序,并将其部署在 WebSphere Application Server 4.0 中。看上去您为 WebSphere Application Server 5.0 开发应用程序,这是 WebSphere Studio 5.0 的缺省模式,目前它不能部署而且 WebSphere Application Server 4.0 也是如此,所以您想知道原因及应该做什么。我希望正确地理解了您的问题。

  好消息!您一定可以使用 WebSphere Studio 5.0 来开发 WebSphere Application Server 4.0 应用程序。这是 WebSphere Studio 5.0 所特有的,以便开发者能升级到我们最新的工具,即使产品不准备升级到最新的 WebSphere Application Server 版本。您仅需要知道如何做。

  您需要做的是为 WebSphere Application Server 4.0 开发应用程序,而不是 WebSphere Application Server 5.0 默认的 WebSphere Studio 5.0。下面是关于在哪里及如何完成这项工作的两个例子:

  在 J2EE 透视图中,当您选择 New -> Enterprise Application Project 时,请选择创建 J2EE 1.2 项目。这样做是因为 WebSphere Application Server 4.0 支持 J2EE 1.2,不是 J2EE 1.3 默认的 WebSphere Studio 5.0。

  同样在 J2EE 透视图中,当您选择 New -> EJB Project 时,请选择创建 EJB 1.1 项目。这样做是因为 WebSphere Application Server 4.0 和 J2EE 1.2 支持 EJB 1.1,不是 EJB 2.0 默认的 WebSphere Studio 5.0。(当然,您不能获得最新的 EJB 2.0 特征,但是为此您需要升级到 WebSphere Application Server 5.0.2 或之后的版本。)

  在 Server 透视图中,当您选择 New -> Server and Server Configuration 时,请选择创建服务器配置类型为 WebSphere v4.0 Server Configuration 的服务器。您可以在 Server Type 列表的 WebSphere version 4.0 文件夹中找到它。这创建了测试服务器,它是 WebSphere Application Server 4.0 独立的服务器,为了能测试您部署在完整的 WebSphere Application Server 4.0 中的 J2EE 1.2 应用程序。

  在 Window -> Preferences 中,J2EE 下,对于部署中所用的最高的 J2EE 版本,您可以选择 J2EE 1.2。然后,WebSphere Studio 将认为您正在为 WebSphere Application Server 4.0(或另一个 J2EE 1.2 容器)进行部署。

  问: 这是有关事务管理和回滚的设计问题,它们被编码在单独的 DB2® 实例(非分布式事务)上。应用程序使用 DAO 模式来将数据存储到多个表中,一个应用程序使用的每个 DAO 都保存在一张表中。会话外观 EJB 业务方法调用了一个以上的 DAO 来将其保存在一张以上的表中。启用了管理事务的容器。我阅读了一些声明的事务管理的文章,您需要完成其中的一件事:

  使用业务方法中的 setRollBackOnly()

  使业务方法抛出运行时的异常

  目前,已经选择了第二项;构造 DAOException 来扩大 RuntimeException,并且当错误发生时所有的 DAO 类都抛出 DAOException 实例。请您给出建议,哪个选择是最好的?哪个能使我们的代码有更好的复用性?对于第一种选择,我认为您需要为每个业务方法添加额外的代码行 setRollBackOnly(),我认为这很麻烦。

  使用 WebSphere Studio V4 IDE 测试环境,事务管理及回滚会很好地进行,但是无论何时将应用程序部署到 WebSphere Application Server V4 上,都不会有回滚发生。存在任何特定的配置或设置来启用 WebSphere Application Server V4 上声明的事务管理吗?

  答: 您很好地利用了模式。数据访问对象(Data Access Object,DAO)及会话外观(Session Facade)都是非常值得一用的。由于您使用 EJB,所以进入了普遍存在的困境中:如何实现您的 EJB 代码及如何使用应用程序的异常?何时使用 setRollbackOnly()?怎样同时配合使用它们?

  应用程序异常表示发生了业务逻辑错误:取款超出了账单余额、预订已被订出的座位、获取已被冻结的信用卡的费用等等。这同系统异常不同,系统异常表示系统级别的错误,如耗尽内存或者数组越界。根据 EJB 规范(第 18 章)中指出的,应用程序异常必须是 Exception 的子类,并且不能是 RuntimeException 或 RemoteException 的子类,这是非常好的建议。同时,根据该规范,容器必须能够捕获系统异常,标记事务用于回滚,并向EJB 客户端抛出 RemoteException 或 EJBException 异常。容器必须将应用程序异常传递到 EJB 客户端并且不改变该异常以及事务。

  我认为这是非常矛盾的,系统异常引发了自动回滚而应用程序异常不能。已经抛出了异常,那么您真的要执行这些事务吗(哪个是自动的行为)?我不这么认为。然而,这是 EJB 的工作方式。

  因此,对于您的情况:您的应用程序异常类是 RuntimeException 的子类。这是不好的。当它自动执行回滚的同时,也将您有含义的描述的应用程序异常转换成了通用的异常。规范中指出不要这样做。

  您必须自己处理回滚。这意味着您在会话外观(您的 EJB 容器的最高层)中编写代码时需要养成这样的习惯——捕获应用程序异常、调用 setRollbackOnly() 方法并重新抛出同样的异常以便使 EJB 客户端知道发生了什么。这是编写优秀的 EJB 代码的方法:在向 EJB 容器外抛出异常之前,请不要使用 RuntimeException 的子类及 setRollbackOnly() 方法。

  问: 先前我的应用程序是基于 WebSphere 4.0 的 J2EE 1.2 标准开发的。目前,我试着使用 WebSphere Studio 5.0(WebSphere 5.0)的 J2EE 1.3 标准转换它。我将应用程序从 j2ee1.2 移植到 j2ee1.3 的级别。

  我有两个模块,一个是 EJB 模块(它包含所有的 EJB),另一个是 Web 模块。该 Web 模块被成功地转换成每个 J2EE 1.3 标准。该 EJB 模块在转换过程中出现了一些错误。日志文件如下:

1 Info: Project structure did not need migration: SCLS-ejb.
2
3   Info: J2EE version level did not need migration: SCLS-ejb.
4
5   Info: Migration was not required for META-INF/ejb-jar.xml.
6
7   Error: java.lang.NullPointerException
8
9   Error: Unable to migrate the Map and Schema file structure in project SCLS-ejb
10
11

  由于这个错误,我不能正常地运行应用程序。所有的实体 bean 都不起作用。我不能使用实体 bean 来插入记录。

  答: 您已经使用了 WebSphere Studio 5.0 中的 J2EE Migration Wizard 试图将您的 J2EE 1.2 应用程序移植到 J2EE 1.3,但是由于使用了 NullPointerException 而使其失败了。由于我不了解您的代码,所以判断工具出现的故障是很困难的。您或许需要求助于 IBM Support。至于您的 EJB 代码为什么不能正常地执行,可能与您移植工具中出现的故障有关,但也可能因为您需要移植代码。移植向导仅更新了您的部署描述符,而没有更新 EJB 实现中的代码。请见 WebSphere Studio 在线帮助中的“移植 J2EE 应用程序”和“从 J2EE 1.2 移植到 1.3”。

  问: 我真需要学习 EJB 吗?

  答: 我要讲一个在我出席 WebSphere Technical Exchange 会议时发生的故事。我提出了分布式(XA)事务上的会话及实验:如何将其编码及它们如何工作。该实验由一些简单的样例代码组成,主要是由 JDBC 数据库及 JMS 消息系统更新的无状态会话 bean(EJB),因此需要分布式的事务。该样例还包含被 SSB 简单调用的 servlet。该实验进行得非常好,事务回滚按预期的计划进行,所有都很顺利。实验之后,客户问我:“真是太棒了!我如何能够仅使用 servlet 来完成它?”回答是:您不能。我问他为什么不使用 EJB。他和他的合作者都感到 EJB 很难。即使 EJB 及容器管理的任何内容能使复杂的编程工作变得更简单,人们仍旧认为它们不够简单。

  该故事的含义是:如果您有完成复杂任务的工具,并且希望完成那些任务,那么您需要学习这些工具。我不想论证 EJB 是容易的,但是我不认为用它能非常容易地完成复杂的任务:事务、安全、池、远程调用——当然,由于实体 bean 具有容器管理的持久性。其他的哪些技术使您能够使用面向几百个用户的对象来支持上千个用户?其他的哪些技术使您能够声明您的事务模型、安全模型、持久性模型等在 XML 中的模型并且能在运行时运行那些模型?

  Java(J2SE)包含庞大的类库,使您开发应用程序时不能任意妄为。J2EE 包含服务及框架,它们完成了许多您应用程序所需的工作。使容器完成尽可能多的工作,这样您的应用程序就不必去做了。以尽可能少的代码编写您的应用程序,并且使容器来完成此项工作。代码量越少,开发进度就越快,维护越容易,且能更快地通过端口传输到 J2EE 和 WebSphere Application Server 的下一个版本中。因此,我的建议是尽可能多地掌握容器给您带来的益处,以及诸如 WebSphere Studio Application Developer 之类的开发工具使您能完成哪些工作,并且最大限度地利用它们。

  问: 我应当使用同步或异步的信息传递吗?

  答: 该问题一直存在。我们正在使用 RPC(例如,RMI、CORBA、具有远程接口的 EJB 等等);为什么我们应当使用 JMS 或消息传递?通常,当用户等候回答时,由于用户是同步的,所以可以同步地调用。当一个应用程序调用其它的应用程序时,进行异步调用。如果一个应用程序要向其他一些应用程序通告事件,那么也是异步调用。同步调用极易受破坏,所以为了使远程调用更稳固,要进行异步调用。

  存在这样的理解——消息传递比 RPC 慢且低效。然而,消息传递确实有比 RPC 更高的地方,仅有一点性能差异,除非您加入了高品质的服务,如持久性的消息传递。如果消息传递很慢,这由于您的网速很慢,在这种情况下 RPC 也会很慢或者根本不能工作。

  通常用户接口不能支持异步调用,因为 UI 阻碍了调用,可能需等候一段时间。一种技术是重载该页面,直到获得结果为止。更好的方法是重新设计 UI,使其不能同步。例如,电子商务站点当打包并传输订单的时候不会出现浏览器阻碍。它告知用户“谢谢订购”并且当订单传输的时候发送包含该包裹号码的电子邮件。这需要 UI 打破工作流,异步地执行,因此您需要诸如 Process Choreographer 之类的工作流引擎。然后,处理以异步的方式完成,并且用户可以随时检查它的进行情况。

  这是 Web 服务的缺陷,没有价值。目前,Web 服务(WS-I 基本概要 1.1)是同步的。调用者发出 HTTP 请求,并且阻止了等候 HTTP 响应。由于 Web 服务是用于应用程序到应用程序的信息传递,所以若它们能够支持异步的信息传递将会更加有用。

  问: SOA 和 ESB 之间的区别是什么?

  答: 面向服务的体系结构(Service Oriented Architecture,SOA)是一种方式或架构,用于具有自服务功能的应用程序,应用程序随后通过用户接口(UI)或经过工作流将其聚合成用户的功能。服务不仅是可复用代码的组件,更是运行程序的一部分,客户端可以不必合并它自己的代码直接调用该程序。事实上,应用程序的界限变得非常模糊了,它包括所有能被调用来执行它的功能的服务。

  企业服务总线(Enterprise Service Bus,ESB)是用于调节 SOA 中的调用者及服务提供者的机制。它使得调用者在不知道提供者或提供者使用的地址的情况下调用该服务。ESB 可在多个提供者、提供者的负载平衡及停止使用提供者(当失效时)之间进行选择,并且基于调用者的需求在提供者之间进行选择,这些提供者提供了各种质量级别的服务。ESB 能够调节同步或异步服务,事实上对于同一服务可以提供同步及异步的访问。

  因此 SOA 和 ESB 是相对应的。具备 SOA 的应用程序应当使用 ESB 来调用它的服务。SOA 和 ESB 不必用 Web 服务实现。然而,经常需要 ESB 来调用服务,该服务提供自我描述及发现的能力,这由 Web 服务帮助完成。在 SOA 中经常需要由一种技术实现的调用者,它们用于调用由其它技术实现的服务,这也由 Web 服务帮助完成。所以 SOA、ESB 和 Web 服务都集中于创建这样的领域——一个应用程序中的功能在其它应用程序中也是可用的,本质是复用性。

0
相关文章