Spring的事务管理难点剖析(2):应用分层的迷惑

阅读更多
    Web、Service及DAO三层划分就像西方国家的立法、行政、司法三权分立一样被奉为金科玉律,甚至有的开发人员认为如果要使用Spring的事务管理就一定要先进行三层的划分。这个看似荒唐的论调在开发人员中颇有市场。更有甚者,认为每层必须先定义一个接口,然后再定义一个实现类。其结果是:一个很简单的功能,也至少需要3个接口和3个类,再加上视图层的JSP和JS等,打牌都可以围上两桌了,这种误解贻害不浅。
   对将“面向接口编程”奉为圭臬,认为放之四海而皆准的论调,笔者深不以为然。是的,“面向接口编程”是Martin Fowler、Rod Johnson这些大师提倡的行事原则。如果拿这条原则去开发框架和产品,怎么强调都不为过。但是,对于我们一般的开发人员来说,做的最多的是普通工程项目,往往只是一些对数据库增、删、查、改的功能。此时,“面向接口编程”除了带来更多的类文件外,看不到更多其他的好处。
  Spring框架所提供的各种好处(如AOP、注解增强、注解MVC等)的唯一前提就是让POJO的类变成一个受Spring容器管理的Bean,除此以外没有其他任何的要求。下面的实例用一个POJO完成所有的功能,既是Controller,又是Service,还是DAO:
package com.baobaotao.mixlayer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

//①将POJO类通过注解变成Spring MVC的Controller
@Controller
public class MixLayerUserService {

    //②自动注入JdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;

    //③通过Spring MVC注解映射成为处理HTTP请求的函数,同时作为一个拥有事务性的方法
    @RequestMapping("/logon.do")
    @Transactional
    public String logon(String userName,String password){
        if(isRightUser(userName,password)){
            String sql = "UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?";
            jdbcTemplate.update(sql,20,userName);
            return "success";
        }else{
            return "fail";
        }
    }
    
    private boolean isRightUser(String userName,String password){
        //do sth
        return true;
    }
}

   通过@Controller注解将MixLayerUserService变成Web层的Controller,同时也是Service层的服务类。此外,由于直接使用JdbcTemplate访问数据,所以MixLayerUserService还是一个DAO。来看一下对应的Spring配置文件:


    
    


   在①处,通过事务注解驱动使MixLayerUserService的logon()工作于事务环境下,②处配置了Spring MVC的一些基本设施。要使程序能够运行起来还必须进行web.xml的相关配置:


	
		contextConfigLocation
		classpath:com/baobaotao/mixlayer/applicationContext.xml
	
	
		log4jConfigLocation
		/WEB-INF/classes/log4j.properties
	

	
		org.springframework.web.util.Log4jConfigListener
	
	
		org.springframework.web.context.ContextLoaderListener
	

	
		user
		org.springframework.web.servlet.DispatcherServlet
		
			contextConfigLocation
			classpath:com/baobaotao/mixlayer/applicationContext.xml
		
		1
	
	
		user
		*.do
	

 

   这个配置文件很简单,唯一需要注意的是DispatcherServlet的配置。默认情况下Spring MVC根据Servlet的名字查找WEB-INF下的-servlet.xml作为Spring MVC的配置文件,在此,我们通过contextConfigLocation参数显式指定Spring MVC配置文件的确切位置。
   将org.springframework.jdbc及org.springframework.transaction的日志级别设置为DEBUG,启动项目,并访问http://localhost:8088/chapter10/logon.do?userName=tom应用,MixLayerUserService#logon方法将作出响应,查看后台输出日志,如下所示:
引用
Returning cached instance of singleton bean 'transactionManager'
  Creating new transaction with name [com.baobaotao.mixlayer.MixLayerUserService.logon]:   
    PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''

(DataSourceTransactionManager.java:204) - Acquired Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver] for JDBC transaction
(DataSourceTransactionManager.java:221) - Switching JDBC Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver] to manual commit
(JdbcTemplate.java:810) - Executing prepared SQL update
(JdbcTemplate.java:569) - Executing prepared SQL statement [UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?]
(JdbcTemplate.java:819) - SQL update affected 0 rows
(AbstractPlatformTransactionManager.java:752) - Initiating transaction commit
(DataSourceTransactionManager.java:264) - Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver]

   日志中红色部分说明了MixLayerUserService#logon方法已经正确运行在事务上下文中。
   Spring框架本身不应是代码复杂化的理由,使用Spring的开发者应该是无拘无束的:从实际应用出发,去除那些所谓原则性的接口,去掉强制分层的束缚,简单才是硬道理。
  注:以上内容摘自《Spring 4.x企业应用开发实战》

你可能感兴趣的:(spring,事务管理)