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 还扩展了“原型”注解以包含 @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 允许您支持多个部署环境,但需要更多配置。
这个实际上被呈现为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的“原型”作用域为每次注入或查找启用一个不同的实例,而Spring 2.0引入了web作用域:“请求”和“会话”。作用域机制本身也是可扩展的;可以将自定义作用域定义和映射到会话的概念。Spring还支持使用CommonsPoolTargetSource进行简单的对象池化,但是对象池化很少是状态管理的最佳解决方案。
更重要的是,Spring通过Spring Web Flow为web应用程序提供了非常强大、高度可配置的状态管理。与本演示文稿声称的开发人员必须直接与HTTP会话交互以管理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…