`

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配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
    …
    <!--①事务管理配置->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          p:dataSource-ref="dataSource"/>
    <tx:annotation-driven/>

        
    <!--②启动Spring MVC的注解功能-->
    <bean class="org.springframework.web.servlet.mvc.annotation.
                  AnnotationMethodHandlerAdapter"/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:prefix="/WEB-INF/jsp/" p:suffix=".jsp"/>
</beans>

   在①处,通过事务注解驱动使MixLayerUserService的logon()工作于事务环境下,②处配置了Spring MVC的一些基本设施。要使程序能够运行起来还必须进行web.xml的相关配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:com/baobaotao/mixlayer/applicationContext.xml</param-value>
	</context-param>
	<context-param>
		<param-name>log4jConfigLocation</param-name>
		<param-value>/WEB-INF/classes/log4j.properties</param-value>
	</context-param>

	<listener>
		<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
	</listener>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<servlet>
		<servlet-name>user</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:com/baobaotao/mixlayer/applicationContext.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>user</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>
</web-app>
 

   这个配置文件很简单,唯一需要注意的是DispatcherServlet的配置。默认情况下Spring MVC根据Servlet的名字查找WEB-INF下的<servletName>-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企业应用开发实战》
分享到:
评论
4 楼 骑着蜗牛超F1 2018-03-30  
huang_yong 写道
我的经验是,只需定义三层:

1.entity 实体
2.service 服务
3.controller 控制器

至于dao这一层,可定义一个泛型的dao类,并将其注入到相应的service中,可提高service的开发效率。

每个service需定义一个接口。

请作者点评,谢谢!

PS:
看到很多ActiveRecord框架,貌似比较轻量级,但我个人认为:易使用、易维护、易扩展的框架才是好框架,其它都是浮云!

我们无需纠结在分层架构上,将更多的精力放在业务上,技术永远都是为业务而服务的!

作者在前面说的更准确,如果不是做基础平台或框架的研发是不用关心这些,也就是上层业务系统是可以按照这种方式来做。基础平台或框架不是为了具体业务而服务的,而是为上层业务系统提供支撑。
3 楼 janwen 2012-06-08  

<context-param>  
        <param-name>log4jConfigLocation</param-name>  
        <param-value>/WEB-INF/classes/log4j.properties</param-value>  
    </context-param>  
  
    <listener>  
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>  
    </listener>  


这个配置有神马用处?
2 楼 huang_yong 2012-04-14  
我的经验是,只需定义三层:

1.entity 实体
2.service 服务
3.controller 控制器

至于dao这一层,可定义一个泛型的dao类,并将其注入到相应的service中,可提高service的开发效率。

每个service需定义一个接口。

请作者点评,谢谢!

PS:
看到很多ActiveRecord框架,貌似比较轻量级,但我个人认为:易使用、易维护、易扩展的框架才是好框架,其它都是浮云!

我们无需纠结在分层架构上,将更多的精力放在业务上,技术永远都是为业务而服务的!
1 楼 kanny87929 2012-03-13  
我一直提倡,又简单又易阅读又易管理的代码

相关推荐

    Spring事务管理Demo

    在Spring框架中,事务管理是核心特性之一,它允许开发者以声明式或编程式的方式处理应用中的事务。Spring事务管理的目的是确保数据的一致性和完整性,尤其是在多操作、多资源的环境中。本Demo将深入探讨Spring如何...

    Spring事务管理开发必备jar包

    2. **Spring事务管理**:Spring提供了两种事务管理方式,即编程式事务管理和声明式事务管理。编程式事务管理通过TransactionTemplate或直接调用PlatformTransactionManager接口的方法来管理事务,而声明式事务管理则...

    Spring框架的事务管理应用分析.doc

    【Spring框架的事务管理应用分析】 Spring框架是一个在2003年推出的开源项目,它的核心设计理念源于Rod Johnson的《Expert One-on-One J2EE Design and Development》一书中的实用主义J2EE思想。Spring框架主要包括...

    Spring 框架的事务管理及应用

    ### Spring框架的事务管理及应用 #### Spring框架概述 Spring框架是一个开源项目,最早出现在2003年2月,其起源可以追溯到Rod Johnson在2002年末出版的书籍《Expert One-on-One J2EE Design and Development》中的...

    spring事务案例分析.zip

    本主题将深入探讨“Spring事务案例分析.zip”中的关键知识点,包括Spring事务管理及其在实际项目中的应用。 首先,我们来了解什么是Spring事务管理。在分布式系统或数据库操作中,事务管理是确保数据一致性和完整性...

    spring事务管理

    Spring框架提供了强大的事务管理功能,使得开发者能够更方便地管理应用程序中的事务。Spring事务管理主要包括两种类型:编程式事务管理和声明式事务管理。 - **编程式事务管理**:通过编写代码来控制事务的开始、...

    Spring事务流程图

    Spring事务管理是Spring框架的核心特性之一,主要用于处理应用程序中的数据一致性问题。在Spring中,事务管理分为编程式和声明式两种方式。本篇文章将详细解释Spring事务管理的流程,以及如何通过时序图来理解这一...

    Struts2+Hibernate+Spring整合开发深入剖析与范例应用03

    3. 配置Struts2:设置Struts2的配置文件,定义Action类,这些Action类通常是Spring管理的Bean,这样可以利用Spring的依赖注入功能。 4. 配置Struts2-Spring插件:这个插件允许Struts2的动作类直接在Spring容器中...

    spring 事务管理的理解

    2. 声明式事务管理:这是Spring 的主要推荐方式,它通过在配置或者注解中声明事务边界。Spring 使用AOP(面向切面编程)来实现这一功能。我们可以使用@Transactional注解在方法级别声明事务,Spring AOP代理会在方法...

    Spring事务管理的jar包

    在Java企业级应用开发中,Spring框架以其强大的功能和灵活性被广泛应用,特别是在事务管理方面。Spring提供了全面的事务管理解决方案,使得开发者可以方便地控制事务的边界,保证数据的一致性和完整性。本篇将深入...

    实验 spring 声明事务

    实验 "Spring 声明事务" 是 Java 高级编程中的一个重要环节,旨在让学生掌握 Spring 框架中声明式事务管理的配置和使用。在实际应用中,事务管理是确保数据一致性、完整性和可靠性的关键组件。Spring 提供了声明式...

    spring事务管理5种方法

    在IT行业中,Spring框架是Java企业级应用开发的首选,其强大的事务管理功能是它的一大亮点。本篇文章将深入探讨Spring事务管理的五种方法,旨在帮助开发者更好地理解和运用这一核心特性。 首先,我们来了解什么是...

    Spring的事务管理小案例

    Spring是一个广泛应用的Java企业级应用开发框架,它提供了强大的事务管理功能,使得开发者可以方便地控制事务的边界,保证数据的一致性和完整性。 首先,理解事务(Transaction)是数据库操作的基础概念。事务具有...

    全面分析 Spring 的编程式事务管理及声明式事务管理

    本文将全面分析Spring中的编程式事务管理和声明式事务管理,旨在帮助开发者深入理解这两种事务管理方式,并在实际项目中合理选择。 **编程式事务管理** 编程式事务管理是通过代码直接控制事务的开始、提交、回滚等...

    全面分析_Spring_的编程式事务管理及声明式事务管理

    本教程将深入探讨 Spring 的编程式事务管理和声明式事务管理,帮助你理解这两种方式的差异与应用场景。 首先,编程式事务管理依赖于编程的方式显式地控制事务的开始、提交、回滚等操作。它通过实现 `...

    spring事务操作试验

    在Spring框架中,事务管理是核心功能之一,它允许开发者以声明式或编程式的方式处理应用中的事务。本文将深入探讨在"spring事务操作试验"中涉及的关键知识点,并结合提供的资源进行详细阐述。 首先,Spring事务管理...

    spring-tx事务管理实例

    Spring事务管理是企业级Java应用中不可或缺的一部分,它确保了数据的一致性和完整性,尤其是在多线程和分布式环境中。本实例将深入探讨Spring事务管理的实现与应用。 首先,Spring事务管理分为编程式事务管理和声明...

    深入理解spring的事务管理机制

    Spring框架的事务管理机制是在Java开发环境中非常重要的一个组成部分,它能够帮助开发者简化事务处理的复杂度,提高应用程序的一致性和可靠性。Spring事务管理的核心是基于AOP(面向切面编程)来实现的。 **Spring...

    Spring事务小demo

    2. **Spring事务管理**: - **AOP(面向切面编程)**:Spring使用AOP来实现声明式事务管理。当一个方法被标记为@Transactional时,Spring会自动在方法开始时开启一个事务,并在方法结束时根据返回情况决定提交或...

    spring_事务管理(实例代码)

    Spring 事务管理是Java开发中一个非常重要的概念,特别是在企业级应用开发中,它确保了数据的一致性和完整性。在Spring框架中,事务管理分为编程式事务管理和声明式事务管理两种方式。 一、编程式事务管理 编程式...

Global site tag (gtag.js) - Google Analytics