Spring Framework之最佳实践

| No Comments | No TrackBacks

Spring Framework从诞生之日起,受到了越来越多的关注。最近,新的开源项目大多支持Spring Framework。国内目前也有专门的网站(http://spring.jactiongroup.net/)。那它为什么如此受欢迎呢?

我想最重要的是,EJB让每个人都痛恨。要编写一个EJB,需要写LocalHome, RemoteHome, Bean, LocalInterface, RemoteInterface,需要一个标准描述符,一个特殊厂商描述符(Weblogic、WebSphere都不一样),如果是Entity Bean,还需要Mapping文件。如此之多,实在麻烦。但EJB最重要的是解决Transaction问题,没有Spring之前,没有其他方法能够 描述式的解决它。每个人、每个公司为了解决Transaction的问题,编程的写法都不一样,百花齐放。于是,在最需要它的时候,Spring出现了。

Spring的功能非常多。但对于一个产品,最重要的是如何用好它的精华。Spring包含AOP、ORM、DAO、Context、Web、 MVC几个部分组成。Web、MVC暂不用考虑,用成熟的Struts、JSP或Webwork更好。DAO由于目前Hibernate、JDO的流行, 也可不考虑。因此最需要用的是AOP、ORM、Context。

Context中,最重要的是Beanfactory,它是将接口与实现分开,非常重要。以前我们写程序,如一个接口IDocument,一个实现 类Document1。在写程序时,需写成IDocument doc = new Document1(),一旦我们的实现类需改变时,变为Document2,则程序需写成IDocument doc = new Document2(),所有用到的地方全需改。Beanfactory帮我们解决了这个问题,用context后,写法变为IDocument doc=(IDocument)beanFactory.getBean("doc")。如果实现类从Document1改为Document2,直接在 配置文件改就可以了。Context是Bean factory的进一步抽象。很多人都喜欢用ApplicationConext,用Servlet把它Load。这样就把Bean Factory与Web绑定在一起。如果是Fat Client或Remote调用,则这些Bean factory就很难调用,实际是将表现层与业务层绑定的太紧。推荐的方法是SingletonBeanFactoryLocator。具体为:

   BeanFactoryLocator bfLocator = SingletonBeanFactoryLocator.getInstance();
   BeanFactoryReference bf = bfLocator.useBeanFactory("beanFactory");
   // now use some bean from factory
   return bf.getFactory().getBean(name);

 

 <beans>

     <bean id="beanFactory" class="org.springframework.context.support.ClassPathXmlApplicationContext">
     <constructor-arg>
      <list>
       <value>dataAccessContext.xml</value>
       <value>securityContext.xml</value>
       <value>...</value>
      </list>
     </constructor-arg>
    </bean>

</beans>


这样,就可随时动态扩展,实现组件式的开发。


Spring Framework最得以出名的是与Hibernate的无缝链接,基本上用Spring,就会用Hibernate。可惜的是Spring提供的 HibernateTemplate功能显得不够,使用起来也不是很方便。我们编程序时,一般先写BusinessService,由 BusinessService调DAO来执行存储,在这方面Spring没有很好的例子,造成真正想用好它,并不容易。

我们的思路是先写一个BaseDao,仿照HibernateTemplate,将基本功能全部实现:

public class BaseDao extends HibernateDaoSupport{

    private Log log = LogFactory.getLog(getClass());

    public Session openSession() {
        return SessionFactoryUtils.getSession(getSessionFactory(), false);
    }

    public Object get(Class entityClass, Serializable id) throws DataAccessException {
        Session session = openSession();
        try {
            return session.get(entityClass, id);
        }
        catch (HibernateException ex) {
            throw SessionFactoryUtils.convertHibernateAccessException(ex);
        }
    }

    public Serializable create(Object entity) throws DataAccessException {
        Session session = openSession();
        try {
            return session.save(entity);
        }
        catch (HibernateException ex) {
            throw SessionFactoryUtils.convertHibernateAccessException(ex);
        }
    }

...

其它的DAO,从BaseDao继承出来,这样写其他的DAO,代码就会很少。

从BaseDao继承出来EntityDao,专门负责一般实体的基本操作,会更方便。

public interface EntityDao {

    public Object get(Class entityClass, Serializable id) throws DataAccessException;

    public Object load(Class entityClass, Serializable id) throws DataAccessException;

    public Serializable create(Object entity) throws DataAccessException;
...}

/**
 * Base class for Hibernate DAOs.  This class defines common CRUD methods for
 * child classes to inherit. User Sping AOP Inteceptor
 */
public class EntityDaoImpl extends BaseDao implements EntityDao{

}

为了Transaction的控制,采用AOP的方式:

public interface EntityManager {

    public Object get(Class entityClass, Serializable id);

    public Object load(Class entityClass, Serializable id);

    public Serializable create(Object entity);
...

}

/**
 * Base class for Entity Service. User Sping AOP Inteceptor
 */
public class EntityManagerImpl implements EntityManager {

    private EntityDao entityDao;

    public void setEntityDao(EntityDao entityDao) {
        this.entityDao = entityDao;
    }

    public Object get(Class entityClass, Serializable id) {
        return entityDao.get(entityClass, id);
    }

    public Object load(Class entityClass, Serializable id) {
        return entityDao.load(entityClass, id);
    }
...

}

这样我们就有了一个通用的Hibernate实体引擎,可以对任何Hibernate实体实现基本的增加、修改、删除、查询等。

其它的BusinessService就可以继承EntityManager,快速实现业务逻辑。

具体XML配置如下:

 <!-- Oracle JNDI DataSource for J2EE environments -->
 <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName"><value>java:comp/env/jdbc/testPool</value></property>
 </bean>

 <!-- Hibernate SessionFactory for Oracle -->
 <!-- Choose the dialect that matches your "dataSource" definition -->
 <bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
  <property name="dataSource"><ref local="dataSource"/></property>
  <property name="mappingResources">
   <value>user-hbm.xml</value>
  </property>
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">net.sf.hibernate.dialect.OracleDialect</prop>
    <prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.Provider</prop>
    <prop key="hibernate.cache.use_query_cache">true</prop>
                  <prop key="hibernate.show_sql">false</prop>
   </props>
  </property>
 </bean>

 <!-- AOP DAO Intecepter -->
        <bean id="hibernateInterceptor" class="org.springframework.orm.hibernate.HibernateInterceptor">
          <property name="sessionFactory">
            <ref bean="sessionFactory"/>
          </property>
        </bean>

        <bean id="entityDaoTarget" class="com.gpower.services.entity.dao.EntityDaoImpl">
          <property name="sessionFactory">
            <ref bean="sessionFactory"/>
          </property>
        </bean>

        <bean id="entityDao" class="org.springframework.aop.framework.ProxyFactoryBean">
          <property name="proxyInterfaces">
            <value>com.gpower.services.entity.dao.EntityDao</value>
          </property>
          <property name="interceptorNames">
            <list>
              <value>hibernateInterceptor</value>
              <value>entityDaoTarget</value>
            </list>
          </property>
        </bean>

 <!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
 <bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
  <property name="sessionFactory"><ref local="sessionFactory"/></property>
 </bean>

 <!-- Transaction manager that delegates to JTA (for a transactional JNDI DataSource) -->
 <!--
 <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
 -->

 <!-- Transactional proxy for the Application primary business object -->
        <bean id="entityManagerTarget" class="com.gpower.services.entity.EntityManagerImpl">
          <property name="entityDao">
            <ref bean="entityDao"/>
          </property>
        </bean>

        <bean id="entityManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
          <property name="transactionManager">
            <ref bean="transactionManager"/>
          </property>
          <property name="target">
            <ref bean="entityManagerTarget"/>
          </property>
          <property name="transactionAttributes">
     <props>
       <prop key="get*">PROPAGATION_SUPPORTS</prop>
       <prop key="*">PROPAGATION_REQUIRED</prop>
     </props>
          </property>
</bean>

No TrackBacks

TrackBack URL: http://www.wujianrong.com/mt-tb.cgi/1550

Leave a comment

About this Entry

This page contains a single entry by kevinwu published on December 29, 2006 11:57 AM.

Wiring Your Web Application with Open Source Java was the previous entry in this blog.

联通彩e接口开发 is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.