1. EJB 使用注释进行元数据。Spring 使用 XML。
有人提到 Spring 正在开始支持更多注释,但这“需要一段时间”。但是,Spring 2.0 版本提供了与 @PersistenceContext 的完全 JPA 集成,用于注入 EntityManager 和使用 Spring 的 @Transactional 注释进行注释驱动的事务管理(支持与具有 REQUIRED 默认传播的 @Stateless EJB 相同的语义)。我特别沮丧的是,比较中没有在双方都包含 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还将“stereotype”注解扩展到包括@Service和@Controller。每个stereotype注解都通过将其用作元注解来扩展通用的@Component注解。通过应用相同的技术,@Component注解为用户定义的stereotype提供了扩展点。Spring甚至可以自动检测这些带注解的组件,作为XML配置的替代方案。例如,这段摘录取自2.5版本的PetClinic示例应用程序。
<context:component-scan base-package="org.springframework.samples.petclinic.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框架主页,以了解即将发布的一些文章和博客,内容涵盖2.5版本。
2. Spring允许您支持多个部署环境,但需要更多配置。
这实际上被认为是Spring的一个优势,但强调了配置开销。事实是,任何认真对待测试和敏捷开发的项目都需要支持“多个部署环境”。换句话说,这个特定主题经常被歪曲,好像它只适用于多个_生产_环境。实际上,在每个开发和测试周期中都必须部署到应用服务器是敏捷性的一大障碍。通常,Spring用户会将他们的配置模块化,以便“基础设施”配置(例如DataSource、TransactionManager、JMS ConnectionFactory)是独立的,并且外部化动态属性。由于Spring支持根据外部化的属性替换'${占位符}',因此包含不同的属性文件通常成为一个透明的问题。
3. 使用JPA的EJB,使用Hibernate的Spring
我必须承认,这让我最困扰。在比较幻灯片中,EJB 3示例显示了使用_entityManager_通过JPA进行数据访问,并且使用@PersistenceContext注解提供_entityManager_实例。另一方面,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这个特定优势的细节。例如,这对于在容器外部进行测试或在轻量级IDE环境(如Eclipse或IDEA)中进行开发来说是一个巨大的好处。
此外,如果您确实需要JTA进行分布式事务,但想在像Tomcat或Jetty这样的轻量级容器中运行,Spring很容易支持独立的JTA提供程序,如Atomikos和JOTM。当然,Spring的事务管理器设置需要配置_单个_bean定义,但这确实是一次性成本——并且非常值得。
5. Spring没有状态应用程序范例。
无状态服务层的好处作为最佳实践已被广泛认可,Spring也接受了这一点。但是,Spring提供的范围不仅仅是单例。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…