回应:EJB 3 和 Spring 对比分析

工程 | Mark Fisher | 2007年11月9日 | ...

昨晚我参加了新英格兰 Java 用户组(NEJUG)的一次会议,Reza Rahman 在会上展示了关于EJB 3 和 Spring 的“对比分析”。Reza 是《EJB 3 in Action》的作者之一。我很高兴见到 Reza,也很敬佩他能就一个可能引起争议的话题进行演示。我也很赞赏他尝试分析 EJB 3 和 Spring 的优缺点。然而,我感到有必要澄清一些他对 Spring 的介绍中并非完全准确的观点,这些观点让我(以及其他与会者)认为本次演示带有偏向 EJB 3 的倾向。公平地说,与固定规范版本不同,Spring 正在不断发展,我在这里指出的一些内容是新特性。另一方面,有些是 Spring 2.0 的特性,已经存在一年多了。我个人认为,“对比分析”必须考虑所比较产品最新稳定版本的最新特性集。我想不言而喻的是,我也可能有一点偏见,但我在这里的动机是提供一个完全客观的回应,以便演示文稿可以修改,以反映更“同类相比”的比较。我将对演示文稿的 10 个“主题”提供简要回应。

1. EJB 使用注解表示元数据。Spring 使用 XML。

演示中提到,Spring 开始支持更多注解,但“还需要一段时间”。然而,Spring 2.0 版本提供了完整的 JPA 集成,使用 @PersistenceContext 注入 EntityManager,并支持基于注解的事务管理,使用 Spring 的 @Transactional 注解(支持与默认传播 REQUIRED 的 @Stateless EJB 相同的语义)。特别让我沮丧的是,本次比较没有在双方都包含 JPA(参见下面的第 3 点)。Spring 2.0 还引入了完整的基于注解的 AspectJ 支持(@Aspect、@Before、@After、@Around)和“stereotype”注解的概念。例如,@Repository 注解为直接使用 JPA 或 Hibernate API 的数据访问代码(不使用 Spring 模板)提供了非侵入式的异常转换功能。Spring 甚至早在 1.2 版本就提供了注解支持,例如 @ManagedResource,用于透明地将任何 Spring 管理的对象导出为 JMX MBean。

对我来说,这个问题成为第一位的主要原因是演示中提到“还需要一段时间”。作为 Spring 2.5 基于注解的配置支持的主要开发者之一,我必须说 Spring 的元数据模型极其灵活,因此我们能够比预期更快地提供一个全面的基于注解的模型。事实上,Spring 2.5 支持 JSR-250 注解:@Resource、@PostConstruct 和 @PreDestroy,以及 @WebServiceRef 和 @EJB。特别值得关注的是 @Resource,因为它是 EJB 3 中用于依赖注入的主要注解。在 Spring 中,@Resource 注解不仅支持 JNDI 查找(与 EJB 3 一样),还支持注入任何 Spring 管理的对象。这有效地将演示中提到的 Spring 主要优势(Spring 支持注入任何类型的对象)与 EJB 3 主要优势(使用注解而非 XML)结合起来。Spring 2.5 还引入了一种更细粒度的基于注解的依赖注入模型,该模型基于 @Autowired 和(可扩展的)@Qualifier 注解。Spring 2.5 还将“stereotype”注解扩展到包括 @Service 和 @Controller。每个 stereotype 注解通过将其作为元注解应用来扩展通用的 @Component 注解。通过应用相同的技术,@Component 注解为用户定义的 stereotypes 提供了一个扩展点。Spring 甚至可以自动检测这些带注解的组件,作为 XML 配置的替代方案。例如,以下摘录来自 PetClinic 示例应用的 2.5 版本:


   <context:component-scan base-package="org.springframework.samples.petclinic.web" />

对于 web 控制器来说,不需要额外的 XML 配置,因为它们使用了基于注解的依赖注入和请求映射注解。我之所以指出这一点,是因为演示特别强调了 web 层配置的冗长性。


@Controller
public class ClinicController {

   private final Clinic clinic;

   @Autowired
   public ClinicController(Clinic clinic) {
      this.clinic = clinic;
   }
   ...

要获取 Spring 注解支持的最新信息,请参阅:The Server Side 上的 Spring 2.5 介绍,或 Spring 参考手册的最新版本——特别是基于注解的配置部分。另外,请继续关注本博客和Spring Framework 主页,我们将很快发布一些关于 2.5 版本的文章和博客。

2. Spring 允许您支持多种部署环境,但需要更多配置。

这一点实际上被作为 Spring 的一个优点提出,但强调了配置开销。事实是,任何认真对待测试和敏捷开发的项目都需要支持“多种部署环境”。换句话说,这个话题常常被扭曲,仿佛只适用于多个生产环境。实际上,在每个开发和测试周期都必须部署到应用服务器,这是敏捷性的主要障碍。通常,Spring 用户会将配置模块化,以便“基础设施”配置(例如 DataSource、TransactionManager、JMS ConnectionFactory)是分开的,并且动态属性是外部化的。由于 Spring 支持根据外部化属性替换 '${placeholders}',因此包含不同的属性文件通常会成为一个透明的问题。

3. EJB 使用 JPA,Spring 使用 Hibernate

我必须承认这一点最让我困扰。在对比幻灯片中,EJB 3 的示例展示了通过 entityManager 进行数据访问的 JPA,并且 entityManager 实例是通过 @PersistenceContext 注解提供的。另一方面,Spring 的示例使用了 Hibernate,并展示了 Hibernate SessionFactory 的 setter 注入。在我看来,这违反了真正的“对比分析”的第一条规则:在比较的双方使用最相似的功能。在这个特定的案例中,Spring 确实支持直接使用 JPA API(例如,JpaTemplate 是完全可选的;直接使用 'entityManager' 仍然参与 Spring 事务等),并且 Spring 也识别 @PersistenceContext 注解。这项支持自 Spring 2.0 版本以来就已提供(最终版本发布于一年多前),所以我不知道为什么比较中没有在 Spring 侧也使用 JPA。比较的其他部分显然是基于 Spring 2.0 的,因此这给人的印象是选择性地过时并暴露了偏见。如果这个特定的例子被修改为“同类相比”,它就会破坏一个主要的主题:即 Spring 需要更多配置而 EJB 3 依赖标准注解。

现在,即使我认为在 Spring 侧使用 Hibernate 而不是 JPA 扭曲了比较,它同时也揭示了 Spring 的一个优势。如果您确实想直接使用 Hibernate API 而不是依赖 JPA API,Spring 支持这样做,并且在 Spring 事务管理和异常转换方面以一致的方式进行。这为使用超出 JPA 限制的 Hibernate 特性提供了机会,例如 Hibernate 的“criteria”查询 API。同样地,如果您想在 ORM 过于繁重的地方添加一些直接的 JDBC 进行数据访问,Spring 也支持这样做——即使在与 Hibernate 或 JPA 数据访问相同的事务中调用。

4. Spring 不做任何假设,您必须提供配置。

一个具体的例子是事务管理器的定义。演示中提到,您必须了解容器供应商级别的细节才能配置 Spring 集成。这是不正确的。例如,以下 bean 定义不包含任何容器特定的信息,但 Spring 将在所有 Java EE 应用服务器中自动检测事务管理器:


   <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

如果您确实想利用容器特定功能,例如按事务隔离级别,那么 Spring 也提供了一些专门的实现:WebLogicJtaTransactionManagerWebSphereUowTransactionManagerOC4JJtaTransactionManager。在这些实现之间切换只需更改这个单一的定义即可。

此外,Spring 配置的幻灯片过于冗长。我担心这可能也是为了强调 EJB 不同于 Spring,它依赖智能默认设置的目标所驱动的。例如,幻灯片显示:


   <tx:annotation-driven transaction-manager="transactionManager"/>

实际上,如果在 Spring 上下文内定义了单个 'transactionManager',那么 'annotation-driven' 元素上就不需要显式提供该属性。该属性仅用于在同一个应用中如有必要启用使用多个事务管理器。这些“自动检测”和“智能默认”技术贯穿于 Spring 中,例如用于消息监听器的 JMS 'connectionFactory'(在下面的第 6 点示例中是隐含的)以及自动定位现有的 MBean 服务器或 RMI 注册表。

从积极的方面看,演示中确实将 Spring 允许“本地”事务管理作为一个优点提到。虽然 EJB 需要 JTA 进行事务管理,但许多应用不需要跨支持两阶段提交的资源的分布式事务。在这种情况下,Spring 允许使用开销更小的更简单的事务管理器:DataSourceTransactionManager(用于 JDBC)、HibernateTransactionManager 或 JpaTransactionManager。如果目标是准确描述优缺点,我本期望听到更多关于这个特定 Spring 优势的细节。例如,这对于在容器外部进行测试或在 Eclipse 或 IDEA 等轻量级 IDE 环境中进行开发是一个巨大的好处。

此外,如果您确实需要 JTA 进行分布式事务,但想在 Tomcat 或 Jetty 等轻量级容器中运行,Spring 轻松支持独立的 JTA 提供商,如 Atomikos 和 JOTM。诚然,Spring 的事务管理器设置需要配置一个单一的 bean 定义,但这确实是一次性的开销——而且非常值得。

5. Spring 没有有状态的应用范式。

无状态服务层的好处作为最佳实践已经相当成熟,Spring 也接受这一点。然而,Spring 确实提供了除 singleton 之外的其他作用域。Spring 的“prototype”作用域为每次注入或查找提供一个不同的实例,Spring 2.0 引入了 web 作用域:“request”和“session”。作用域机制本身甚至是可以扩展的;可以将自定义作用域定义并映射到会话概念。Spring 也支持使用 CommonsPoolTargetSource 进行简单的对象池化,但对象池化很少是状态管理的最佳解决方案。

更重要的是,Spring 通过 Spring Web Flow 为 Web 应用提供了非常健壮、高度可配置的状态管理功能。在那里,会话状态是透明管理的,这与本次演示中声称开发者必须直接与 HTTP Session 交互来管理 Spring 应用中的状态的说法是矛盾的。此外,存储库配置是可插拔的,因此可以使用各种策略来物理存储状态(会话、客户端、后端缓存等)。最后,Spring Web Flow 的最新进展包括对扩展持久化上下文的支持以及对 JSF 的完全集成支持。

6. Spring 需要为每个 MessageListener 配置一个容器。

Spring 2.5 提供了一个新的 'jms' 命名空间,极大地简化了消息监听器的配置。请注意,不需要为每个监听器单独配置一个容器。多个监听器共享配置,并且广泛使用了智能默认设置:


<jms:listener-container>
	<jms:listener destination="queue.confirm" ref="logger" method="log"/>
	<jms:listener destination="queue.order…

下载《Spring 在生产环境中的应用》白皮书

工程 | Adrian Colyer | 2007年11月9日 | ...

我们最近举办了一个主题为“Spring 在生产环境中的应用”的网络研讨会。当时我承诺将网络研讨会的录音和配套幻灯片放在我们的网站上。不幸的是,为我们制作网络研讨会的工程师忘记设置“录制”标志,所以我需要为您重新录制会话 :(。我现在正在旅途中,但我会尽快尝试完成并使其可用。

好消息是,与此同时您不必错过。我撰写了一篇关于“Spring 在生产环境中的应用”的白皮书,涵盖了网络研讨会的内容以及更多…

Spring Java 配置向前发展

工程 | Rod Johnson | 2007年11月5日 | ...

一些用户询问我们是否致力于 Spring Java 配置,以及它与 Spring 2.5 中引入的注解配置选项如何共存。答案是肯定的,我们致力于 Java 配置;这两种方法并非相互排斥。

这两种配置方法截然不同:Spring Framework 中的 @Autowired 注解使用业务对象中的注解配置组件,而 Spring Java Config 采用了一种独特的方法,将注解外部化到专门的配置类中。这两种方法都没有绝对的对错…

Spring 2.5 RC1 发布 - 引入新的配置方法

工程 | Juergen Hoeller | 2007年10月24日 | ...

正如 你们中一些人已经注意到的那样,Spring 2.5 RC1 已于周一发布,正等着您进行测试!在很多方面,Spring 2.5 是一个完成 Spring 2.0 使命的版本:为 Java 1.4 和 Java 5 提供最灵活、最全面的配置模型。Spring 2.5 专注于对 Java 5 的特别全面的支持,引入了各种进一步的注解选项。我想借此机会指出本次版本背后的统一主题

Spring 2.5 允许方便的外部化配置,同时保持其尽可能简洁。这建立在 Spring 2.0 对 XML Schema 命名空间的支持之上,Spring 2.5 引入了新的“context”和“jms”配置命名空间。后者是 Spring 配置命名空间可以提供的增值的一个特别好的例子——如果您正在使用 Spring 2.0 风格的消息驱动对象,绝对值得采用!此外,Spring 也允许不涉及 XML 的编程方式启动

Spring 工具套件

工程 | Adrian Colyer | 2007年10月16日 | ...

您可能已经看到了关于 Interface21 与 Tasktop 合作创建“Spring 工具套件”的宣布一些近期新闻报道。该套件将整合 Spring IDEAspectJ 开发工具 (AJDT)AspectJMylyn,以创建一种以任务为中心的方法来开发基于 Spring 的企业应用。我们希望在即将到来的 The Spring Experience 会议上提供集成套件的预览版供您分享,但与此同时,您将看到许多改进正在融入现有的 Spring IDE、AJDT、AspectJ 和 Mylyn 开源项目…

Gartner 精辟分析创新与颠覆

工程 | Neelan Choksi | 2007年10月12日 | ...

在上个月的 Gartner 开源大会上,分析师宣称开源已经渗透到全球软件市场的很大一部分。详细信息在最近的 Matt Asay 博客中得到了强调,该博客引用了eWeek 文章。eWeek 写道:“开源产品在 2006 年占据了 927 亿美元软件市场份额的 13%,但预计到 2011 年,当收入预计达到 1692 亿美元时,将占据市场份额的 27%。”

与此同时,Gartner 分析师 Massimo Pezzini 和 Yefim Natis 发表了一份报告,强调了当前正在进行的中间件和事务处理市场中的一个重要颠覆性趋势。这份 2007 年 9 月 24 日发布的报告,题为“平台中间件趋势:颠覆在望,”,强调了十多个“将颠覆看似静态的应用服务器和事务处理市场”的趋势,并警告说…

我们对 JCP 的态度

工程 | Rod Johnson | 2007年9月30日 | ...

正如我之前发布的,Interface21 正在参与 Java EE 6 的工作,包括我在内的 Juergen Hoeller、Keith Donald 和 Rob Harrop 等多人将参与多个专家组。

这意味着我们总体上更多地参与了 JCP。我们尊重 JCP 的保密规定及其他条款,因此不会谈论任何非公开内容。然而,我想谈谈我们参与 JCP 的目标以及我们将带来的基本方法。当然,我们只是众多公司和个人中的一员,所以我们只是…

新的 bean() 切点

工程 | Ramnivas Laddad | 2007年9月24日 | ...

Spring 2.5 提供了一个新的切点指示符 -- bean(),它允许选择与名称模式匹配的 bean 中的连接点。现在可以使用自动代理机制结合 Spring-AspectJ 集成来选择特定的 bean,即使同类型存在多个 bean。之前,您可以使用 BeanNameAutoProxyCreator 实现类似的结果;然而,该机制不适用于 Schema 风格或 @AspectJ 切面。

除了选择特定的 bean 外,如果您遵循适当的命名约定,此切点指示符还提供了两种有趣的方式来选择 bean:

  1. 选择 bean 的垂直切片:如果您遵循一个约定,即 bean 名称包含一个表示其业务角色的字符串,则 bean() 切点可以根据其业务角色选择 bean。例如,如果 bean 名称以表示其业务功能的字符串开头,您可以使用 bean(account*) 切点来选择所有与会计相关的 bean,例如 accountRepositoryaccountServiceaccountController
  2. 选择 bean 的水平切片:如果您遵循一个约定,即 bean 名称包含一个表示其架构角色的字符串,则 bean() 切点可以根据其架构角色选择 bean。例如,如果 bean 名称以表示其架构角色的字符串结尾,您可以使用 bean(*Repository) 来选择所有存储库 bean。如果没有 bean() 切点,您必须依赖于包结构或基于类型的切点,这有时可能会过于严格。
The bean() Pointcut Designator

图 1:使用 bean() 切点根据 bean 名称选择水平和垂直切片

此切点代表了 AspectJ 切点表达式语言的一个 Spring 特定扩展,因此仅在基于 Spring 的应用中有用。名称模式遵循 AspectJ 对名称模式的匹配规则,其中 '*' 是唯一允许的通配符。下表展示了一些示例切点及其选择的 bean:
切点 选择的连接点
bean(accountRepository) 名称为 "accountRepository" 的 bean
!bean(accountRepository) 除 "accountRepository" bean 外的任何 bean
bean(*) 任何 bean
bean(account*) 名称以 "account" 开头的任何 bean
bean(*Repository) 名称以 "Repository" 结尾的任何 bean
bean(accounting/showaccount) 名称为 accounting/showaccount 的 bean(例如,指定处理该 URL 的控制器)
bean(accounting/*) 名称以 "accounting/" 开头的任何 bean(例如,指定处理会计相关 URL 的任何控制器)
bean(accounting/*/edit) 名称以 "accounting/" 开头并以 "/edit" 结尾的任何 bean(例如,指定处理与会计相关的编辑操作功能的任何控制器)
bean(*dataSource) || bean(*DataSource) 名称以 "dataSource" 或 "DataSource" 结尾的任何 bean
bean(service:name=monitoring) 名称为 "service:name=monitoring" 的 bean

关于开源的更多废话

工程 | Rod Johnson | 2007年9月22日 | ...

在一篇标题贴切的 关于 Interface21 的废话 文章中,SourceLabs 的一名员工不同意我的论点,即提交权限对于提供可信的开源支持是必需的。

在我回应之前:我想再次完全明确一些我已经在上篇博客中说过、但似乎被一些人误解的事情:Interface21 无意阻止其他人从 Spring 中赚钱。我们的过往记录证明了这一点。我们欢迎其他人撰写关于 Spring 的文章并提供 Spring 服务。或者基于 Spring 开发产品,例如 Matt Raible 的 AppFuse。我们祝愿他们成功。Spring 之所以能有今天的成就,部分归因于…

回应关于开源的废话

工程 | Rod Johnson | 2007年9月20日 | ...

我几个月前写的关于开源商业模式的博客似乎引起了共鸣。我收到了许多积极回应,并因此接到了一个名为“软件如何构建”的网站的采访请求。我的采访在这里

最后,OpenLogic 的某人发表了一篇有趣的回应。Bryan Noll 在对我博客的回应中留下了一些评论,值得认真回应。

首先,我认为您提出的论点,即当对特定项目没有实际投入的人为其提供支持时,这对项目或整个开源生态是不健康的,这是个有趣的观点……我之前从未听过。我认为这个论点有足够的合理性,足以让像我们这样的公司认真考虑,并真正审视我们对所支持的开源项目的责任。在我看来,这次审视的结果将是 OpenLogic 为缓解您提出的潜在顾虑而采取的一项可证明的政策。我确定我不知道具体会是什么,所以请允许我在此刻保持模糊。然而,这一点与我对您所说的一些问题非常契合。
我认为找到这样一项“可证明的政策”是相当简单的。OpenLogic 需要理解 Stormy 的帖子中的开篇评论“在开源软件上工作的开发者通常有不错收入的全职工作……所以他们在空闲时间免费为开源软件工作,白天则为了丰厚报酬写代码”在很大程度上是错误的,他们需要理解他们希望从中获利的开源软件来自何处,恰当地建立伙伴关系,并设定一个允许提供真正支持的价格点。另一种选择是停止声称提供企业级支持,并明确所提供的只是某种随叫随到的开发协助,不能保证能够解决关键问题。这又回到了为什么我对 Stormy 的帖子有强烈感受并对其进行解构的原因。

我将聚合模型视为超市模式的业务。当我在超市购物时,我期望他们从我购买的所有商品中抽取(少量)提成,以换取他们处理众多供应商,并将所有商品汇集…

获取 Spring 新闻邮件

订阅 Spring 新闻邮件,保持联系

订阅

领先一步

VMware 提供培训和认证,助力您的进步。

了解更多

获取支持

Tanzu Spring 在一项简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

近期活动

查看 Spring 社区的所有近期活动。

查看全部