1. EJB 使用注解来表示元数据。Spring 使用 XML。
会上提到 Spring 开始支持更多注解,但“还需要一段时间”。然而,Spring 2.0 版本提供了完整的 JPA 集成,使用 @PersistenceContext 注解来注入 EntityManager,并提供了注解驱动的事务管理,使用 Spring 的 @Transactional 注解(支持与 @Stateless EJB 相同的语义,默认传播级别为 REQUIRED)。我特别感到失望的是,比较中没有在双方都包含 JPA(见下文第 3 点)。Spring 2.0 还引入了完整的基于注解的 AspectJ 支持(@Aspect、@Before、@After、@Around)以及“构造型”注解的概念。例如,@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 支持任何类型的对象 DI)和主要 EJB 3 优势(使用注解而不是 XML)。Spring 2.5 还引入了一个更细粒度的基于 @Autowired 和(可扩展的)@Qualifier 注解的注解驱动的依赖注入模型。Spring 2.5 还扩展了“刻板印象”注解,包括 @Service 和 @Controller。每个刻板印象注解都通过将其作为元注解来扩展通用的 @Component 注解。通过应用相同的技术,@Component 注解为用户定义的刻板印象提供了扩展点。Spring 甚至可以自动检测这些注解的组件,作为 XML 配置的替代方案。例如,这个片段摘自 2.5 版的 PetClinic 示例应用程序。
<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 允许您支持多种部署环境,但需要更多配置。
这实际上被 presenters 作为一个 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。同样,如果您想添加一些直接的 JDBC 进行数据访问,而 ORM 是过度的话,Spring 也支持这一点——即使是在与 Hibernate 或 JPA 数据访问相同的事务中调用。
4. Spring 不做任何假设,您必须提供配置。
一个具体的例子是事务管理器的定义。有人说您必须了解容器供应商级别的知识才能配置 Spring 集成。这是不正确的。例如,以下 bean 定义不包含任何特定于容器的信息,但 Spring 将自动检测所有 Java EE 应用服务器中的事务管理器。
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
如果您确实想利用特定于容器的功能,例如每个事务的隔离级别,那么 Spring 也提供了一些专门的实现:*WebLogicJtaTransactionManager*、*WebSphereUowTransactionManager* 和 *OC4JJtaTransactionManager*。切换这些实现之间只需更改这一个定义。
除此之外,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 可以轻松支持 Atomikos 和 JOTM 等独立的 JTA 提供商。当然,Spring 的事务管理器设置需要配置一个*单个* bean 定义,但这确实是一次性成本——但绝对值得。
5. Spring 没有有状态应用程序范例。
无状态服务层的优点作为最佳实践已经相当确立,Spring 也采纳了这一点。然而,Spring 确实提供了单例以外的范围。Spring 的“prototype”范围为每次注入或查找提供了一个独立的实例,Spring 2.0 引入了 Web 范围:“request”和“session”。范围机制本身甚至可以扩展;可以定义自定义范围并将其映射到对话的概念。Spring 还支持使用 CommonsPoolTargetSource 进行简单的对象池,但对象池很少是状态管理的最佳解决方案。
更重要的是,Spring 通过 Spring Web Flow 为 Web 应用程序提供了非常健壮、高度可配置的状态管理。在那里,对话状态是透明管理的,这与本次演讲中声称的开发人员必须直接与 HTTP Session 交互来管理 Spring 应用程序状态的说法相反。此外,存储库配置是可插拔的,因此可以使用各种策略来物理存储状态(session、client、后端缓存等)。最后,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…