39. 经典 Spring 用法

本附录讨论了一些经典的 Spring 使用模式,以作为开发人员维护旧版 Spring 应用程序的参考。这些使用模式不再反映建议的使用这些功能的方式,并且当前的建议用法在参考手册的相应部分中进行了介绍。

39.1 经典 ORM 使用

本节介绍了在旧版 Spring 应用程序中可能遇到的经典使用模式。有关当前推荐的使用模式,请参阅ORM章。

39.1.1 Hibernate

有关 Hibernate 当前推荐的使用模式,请参见第 20.3 节“休眠”

The HibernateTemplate

用于模板的基本编程模型如下所示,这些方法可以是任何自定义数据访问对象或业务服务的一部分。对周围对象的实现没有任何限制,只需要提供一个 Hibernate SessionFactory即可。它可以从任何地方获取后者,但最好是通过简单的setSessionFactory(..) bean 属性设置器从 Spring IoC 容器中获取 bean 参考。以下代码片段显示了 Spring 容器中的 DAO 定义,引用了上面定义的SessionFactory,以及 DAO 方法实现的示例。

<beans>

    <bean id="myProductDao" class="product.ProductDaoImpl">
        <property name="sessionFactory" ref="mySessionFactory"/>
    </bean>

</beans>
public class ProductDaoImpl implements ProductDao {

    private HibernateTemplate hibernateTemplate;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.hibernateTemplate = new HibernateTemplate(sessionFactory);
    }

    public Collection loadProductsByCategory(String category) throws DataAccessException {
        return this.hibernateTemplate.find("from test.Product product where product.category=?", category);
    }
}

HibernateTemplate类提供了许多方法,这些方法与 Hibernate Session接口上公开的方法类似,此外还提供了许多便捷方法,例如上面显示的一种。如果您需要访问Session来调用未在HibernateTemplate上公开的方法,则始终可以像这样使用基于回调的方法。

public class ProductDaoImpl implements ProductDao {

    private HibernateTemplate hibernateTemplate;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.hibernateTemplate = new HibernateTemplate(sessionFactory);
    }

    public Collection loadProductsByCategory(final String category) throws DataAccessException {
        return this.hibernateTemplate.execute(new HibernateCallback() {
            public Object doInHibernate(Session session) {
                Criteria criteria = session.createCriteria(Product.class);
                criteria.add(Expression.eq("category", category));
                criteria.setMaxResults(6);
                return criteria.list();
            }
        };
    }

}

回调实现可以有效地用于任何 Hibernate 数据访问。 HibernateTemplate将确保正确打开和关闭Session实例,并自动参与 Transaction。模板实例是线程安全的并且可重用,因此可以将它们保留为周围类的实例变量。对于简单的单步操作(如单个查找,加载,saveOrUpdate 或 delete 调用),HibernateTemplate提供了可替代的便捷方法,可以代替此类单行回调实现。此外,Spring 提供了一个方便的HibernateDaoSupportBase Class,该 Base Class 提供了setSessionFactory(..)方法来接收SessionFactory以及getSessionFactory()getHibernateTemplate()供子类使用。结合起来,这允许针对典型需求的非常简单的 DAO 实现:

public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {

    public Collection loadProductsByCategory(String category) throws DataAccessException {
        return this.getHibernateTemplate().find(
            "from test.Product product where product.category=?", category);
    }

}

实现不带回调的基于 Spring 的 DAO

除了使用 Spring 的HibernateTemplate实现 DAO 以外,还可以以更传统的方式编写数据访问代码,而无需将 Hibernate 访问代码包装在回调中,同时仍然尊重并参与 Spring 的通用DataAccessException层次结构。 HibernateDaoSupportBase Class 提供了在这种情况下访问当前事务Session并转换异常的方法。 SessionFactoryUtils类也可以使用类似的方法作为静态助手。请注意,此类代码通常会将false作为getSession(..)方法allowCreate参数的值传递,以强制在事务内运行(这避免了关闭返回的Session的需要,因为其生命周期由事务 Management)。

public class HibernateProductDao extends HibernateDaoSupport implements ProductDao {

    public Collection loadProductsByCategory(String category) throws DataAccessException, MyException {
        Session session = getSession(false);
        try {
            Query query = session.createQuery("from test.Product product where product.category=?");
            query.setString(0, category);
            List result = query.list();
            if (result == null) {
                throw new MyException("No search results.");
            }
            return result;
        }
        catch (HibernateException ex) {
            throw convertHibernateAccessException(ex);
        }
    }
}

这种直接的 Hibernate 访问代码的优点是,它允许在数据访问代码中抛出* any *检查的应用程序异常。与此形成对比的是HibernateTemplate类,该类仅限于在回调中仅引发未经检查的异常。请注意,您通常可以将相应的检查和应用程序异常的引发推迟到回调之后进行,这仍然允许使用HibernateTemplate。通常,HibernateTemplate类的便捷方法在许多情况下更简单,更便捷。

39.2 JMS 使用

Spring 的 JMS 支持的好处之一是使用户免受 JMS 1.0.2 和 1.1 API 之间的差异的影响。 (有关这两个 API 之间差异的描述,请参阅“域统一”上的边栏)。由于现在通常只遇到 JMS 1.1 API,因此在 Spring 3.0 中不建议使用基于 JMS 1.0.2 API 的类。本节描述了对 JMS 1.0.2 不推荐使用的类的 Spring JMS 支持。

Domain Unification

JMS 规范有两个主要版本:1.0.2 和 1.1.

JMS 1.0.2 定义了两种类型的消息传递域,即点对点(队列)和发布/订阅(主题)。 1.0.2 API 通过为每个域提供并行的类层次结构来反映这两个消息传递域。结果,Client 端应用程序在使用 JMS API 时成为特定于域的。 JMS 1.1 引入了域统一的概念,该概念最小化了两个域之间的功能差异和 Client 端 API 差异。作为已消除功能差异的一个示例,如果您使用 JMS 1.1 提供程序,则可以使用相同的Session来事务处理来自一个域的消息并在另一个域上产生消息。

Note

JMS 1.1 规范于 2002 年 4 月发布,并于 2003 年 11 月成为 J2EE 1.4 的一部分。因此,仍在广泛使用的常见 J2EE 1.3 应用程序服务器(例如 BEA WebLogic 8.1 和 IBM WebSphere 5.1)基于 JMS。 1.0.2.

39.2.1 JmsTemplate

JmsTemplate102位于包org.springframework.jms.core中,提供了 JMS 章节中描述的JmsTemplate的所有功能,但是基于 JMS 1.0.2 API 而不是 JMS 1.1 API。因此,如果您使用的是 JmsTemplate102,则需要设置布尔属性pubSubDomain以使用所使用的 JMS 域的知识来配置JmsTemplate。默认情况下,此属性的值为 false,指示将使用点对点域 Queues。

39.2.2 异步消息接收

MessageListenerAdapter's与 Spring 的消息侦听器容器结合使用,通过将几乎任何类都公开为消息驱动的 POJO 来支持异步消息接收。如果使用的是 JMS 1.0.2 API,则将要使用 1.0.2 特定的类,例如MessageListenerAdapter102SimpleMessageListenerContainer102DefaultMessageListenerContainer102。这些类提供与基于 JMS 1.1 的对应类相同的功能,但仅依赖于 JMS 1.0.2 API。

39.2.3 Connections

ConnectionFactory接口是 JMS 规范的一部分,并且是使用 JMS 的入口点。 Spring 基于 JMS 1.0.2 API 提供了ConnectionFactory接口SingleConnectionFactory102的实现,该接口将在所有createConnection()调用上返回相同的Connection并忽略对close()的调用。您将需要设置布尔属性pubSubDomain,以指示使用哪个消息传递域,因为SingleConnectionFactory102将始终明确区分javax.jms.QueueConnectionjavax.jmsTopicConnection

39.2.4TransactionManagement

在 JMS 1.0.2 环境中,类JmsTransactionManager102为 Management 单个 Connection Factory 的 JMS 事务提供支持。有关此功能的更多信息,请参阅JMSTransactionManagement上的参考文档。