`
jonsvien
  • 浏览: 11492 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

揭开Spring事务处理

阅读更多
Spring事务处理你未关注过的原理
转自:http://blog.csdn.net/jdream314/article/details/12647449
本文对Spring实现事务处理的真正原理进行追究,从而从中提炼出一些见解。其中讲解内容可能会存在一定的误导,还希望指出,内容仅供参考!(经过本人后期继续研读Spring关于Mybatis的事务处理,其实在mybatis的里面调用了spring的方法来获取Connection,所以本文所提供的一种实现,是另一种Spring的实现猜想,仅供参考!)
  说到Spring事务原理,百度一下最多的就是Spring的AOP了,本文当然不是给你将AOP的原理,如果是这样,我也就没必要写这篇文章了,直接转载一篇就行了。借助Spring的AOP的原理,提出一个问题。
此处先粘贴出Spring事务需要的配置内容:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />……</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
<property name="dataSource" ref="dataSource" />.....</bean>

上面两段配置文件一个是Spring事务管理器的配置文件,另一个是一个普通的JPA框架(此处是mybatis)的配置文件,这两个里面都配置了datasource,而且这个datasource的对象是在Spring的容器里面。一下提几个问题:
            1、当JPA框架对数据库进行操作的时候,是从那里获取Connection?
            2、jdbc对事务的配置,比如事务的开启,提交以及回滚是在哪里设置的?
            3、Spring是通过aop拦截切面的所有需要进行事务管理的业务处理方法,那如何获取业务处理方法里面对数据库操作的事务呢?
           现在我来对上面的问题来一一回答
           1、这个问题很简单,既然在JPA的框架里面配置了datasource,那自然会从这个datasource里面去获得连接。
           2、jdbc的事务配置是在Connection对消里面有对应的方法,比如setAutoCommit,commit,rollback这些方法就是对事务的操作。
           3、Spring需要操作事务,那必须要对Connection来进行设置。Spring的AOP可以拦截业务处理方法,并且也知道业务处理方法里面的DAO操作的JAP框架是从datasource里面获取Connection对象,那么Spring需要对当前拦截的业务处理方法进行事务控制,那必然需要得到他内部的Connection对象。整体的结构图如下:

上图是一个标准的业务处理在一个线程上的基本流程。JPA框架在需要对数据库进行操作的时候,就会从Datasource里面去获取Connection对象,那么Spring是怎么样拿到在JPA内部调用的Connection并且加上用户配置的事务处理规则的呢?现在我来揭开这个谜底。
           上面也看到注入到Spring的事务管理的datasource和注入到第三方JPA框架的datasource都是在Spring容器里面的,并且是同一个对象。既然Spring可以拿到你这个datasource对象,那它为什么不进行一下封装呢?不管是哪家的Datasource他们都会实现javax.sql.DataSource这个接口,这个接口里面主要有两个方法
public interface DataSource  extends CommonDataSource,Wrapper {  
  
  /** 
   * <p>Attempts to establish a connection with the data source that 
   * this <code>DataSource</code> object represents. 
   * 
   * @return  a connection to the data source 
   * @exception SQLException if a database access error occurs 
   */  
  Connection getConnection() throws SQLException;  
        
  /** 
   * <p>Attempts to establish a connection with the data source that 
   * this <code>DataSource</code> object represents. 
   * 
   * @param username the database user on whose behalf the connection is  
   *  being made 
   * @param password the user's password 
   * @return  a connection to the data source 
   * @exception SQLException if a database access error occurs 
   * @since 1.4 
   */  
  Connection getConnection(String username, String password)   
    throws SQLException;

这两个方法均是获取Connection对象的。Spring有没有可能对这个接口创建一个代理呢?通过spring的AOP。然后偷偷将Spring容器里面的datasource的bean指向这个代理对象(此处称该对象为datasourceproxy,替换之前的叫datasource)。于是不管是从哪里调用Datasource,那必然会被Spring拦截。下面是模拟了一个简单实现:
public class DatasourceHandler implements InvocationHandler {  
  
    private DataSource dataSource;  
    /** 
     * @param dataSource 
     */  
    public DatasourceHandler(DataSource dataSource) {  
        super();  
        this.dataSource = dataSource;  
    }  
    /* (non-Javadoc) 
     * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) 
     */  
    @Override  
    public Object invoke(Object proxy, Method method, Object[] args)  
            throws Throwable {  
        if(method.getName().equals("getConnection")){  
              
            if(ResourceHolder.getResource(proxy)!=null){  
                Connection connection = (Connection) method.invoke(this.dataSource, args);  
                ResourceHolder.addResource(proxy, connection);  
            }  
            return ResourceHolder.getResource(proxy);  
        }else{  
            return method.invoke(this.dataSource, args);  
        }  
    }  
}

上面这个类是一个InvocationHandler的实现,假设这个就是Spring Aop拦截Datasource的实现。那么这个对象里面有一个datasource对象,这个对象是Spring替换代理Datasource之前的那个对象(datasource)。看到invoke的实现,其实就是将代理(datasourceproxy)调用的类发转到datasource去调用,其实还是调用了datasource,但是这里就加入了一些特殊的东西,那就是ResourceHolder
public class ResourceHolder {  
  
    private static ThreadLocal<Map<Object,Object>> resources= new ThreadLocal<Map<Object,Object>>();  
      
    public static void addResource(Object key,Object value){  
        if(resources.get()==null){  
            resources.set(new HashMap<Object,Object>());  
        }  
        resources.get().put(key, value);  
    }  
      
    public static Object getResource(Object key){  
          
        return resources.get().get(key);  
    }  
      
    public static void clear(){  
        resources.remove();  
    }  
}

上面是这个对象的实现,里面有一个ThreadLocal静态属性,用于存放一些数据信息。ThreadLocal用过的人都知道他是线程的局部变量,在整个线程过程中都是有效的。那么在invoke里面当每次调用的时候,判断调用的方法是不是getConnection,如果是,则进行如下操作
if(ResourceHolder.getResource(proxy)!=null){
Connection connection =(Connection) method.invoke(this.dataSource, args);
ResourceHolder.addResource(proxy, connection);
}
return ResourceHolder.getResource(proxy);
其中proxy就是Spring自动生成的datasourceproxy,将proxy和connection的关系添加到ResourceHolder里面去,而ResourceHolder又是将这个关系添加到ThreadLocal<Map<Object,Object>> resources这个静态变量里面,添加到这个里面,那么以后如果在当前线程从datasourceproxy获取connection对象,都将是一个对象,这就保证了一个业务方法里面进行多次dao操作,调用的都是一个connection对象,同时保证了多个dao都是在一个事务里面。既然这样,那么Spring的事务管理就可以在调用业务方法之前,先从datasource里面先获得一个connection对象,并且对connection添加上用户配置的事务规则,由于这个connection对象会自动添加到ThreadLocal里面,那么后面的业务处理方法将会是调用已经添加好事务规则的connection对象,当业务方法处理完毕,那么spring事务就可以对这个connection进行回滚或者提交了。经过这样一个过程,那么在一个处理某个业务的线程里面执行流程应该是这样的:

总结一下:这里首选是对DataSource生成一个代理类,从而可以监控获取Connection的过程,在通过ThreadLocal对Connection线程级别的缓存从而促使在同一个业务处理方法相对于某个DataSource都是在一个Connection中,从而保证处于同一事务中,因为这些执行都是在一个线程中的。这里处理Spring的AOP之外,还有一个ThreadLocal的使用。在实践编程中,有时候你会发现ThreadLocal会带来很大的帮助。
        比如,你要在某个操作中的每个处理流程都要知道操作人信息,而且这个流程可能不是在一个方法或者一个类中处理完,如果在session环境中,你可能会考虑用session,但不是所有的开发都是在Session环境中的,那么此时ThreadLocal边是最好的帮手,可以在用户触发这个操作时候将用户信息放在ThreadLocal中,那么后面的每个流程都可以从ThreadLocal中获取,而且这个是线程范围的,每个线程中的ThreadLocal是不相干的,这样也防止了多线程的操作。
分享到:
评论

相关推荐

    Tom_深入分析Spring源码

    《Spring源码分析》这份资料深入探讨了Spring框架的核心机制,尤其聚焦于Spring5版本。...Tom老师的这份资料无疑是一份宝贵的资源,它将引导我们逐步揭开Spring的奥秘,让我们在编程旅程中更进一步。

    教你阅读Spring源码资源.zip

    从IoC到AOP,从数据访问到Web开发,通过调试代码和实例,我们可以逐步揭开Spring的神秘面纱,提升我们的编程技巧和问题解决能力。在阅读源码的过程中,不仅要看懂代码,更要理解设计模式和架构思想,这样才能真正将...

    Spring 4.2源码

    Spring 4.2是Java开发领域中极为重要的一个版本,它是Spring框架的里程碑...通过探索`spring-framework-4.1.2.RELEASE`这个压缩包中的文件,我们可以一步步揭开Spring框架的神秘面纱,从而成为一名更优秀的Java开发者。

    Spring官方 源码

    Spring框架是Java开发中不可或缺的一部分,它以其模块化、易用性和灵活性著称。源码是理解任何软件系统核心的...在阅读源码的过程中,可以结合官方文档和社区资源,逐步揭开Spring的神秘面纱,从而成为更优秀的开发者。

    Spring的源代码

    Spring框架是Java开发中最广泛应用的轻量级框架之一,它以其强大的依赖注入(Dependency Injection,DI)和面向切面编程...而《Spring源码深度解析》这本书则是一个很好的指南,引导读者逐步揭开Spring的神秘面纱。

    spring技术内幕

    这本书深入探讨了Spring的核心架构和设计原则,帮助读者揭开Spring框架的神秘面纱,从而能够更高效、更灵活地使用Spring进行开发。 Spring作为Java企业级应用中的主流框架,其强大的功能和灵活的配置吸引了无数...

    spring源码3.2.4

    《Spring框架3.2.4源码解析》 Spring框架是Java开发中不可或缺的重要组成部分,其3.2.4版本更...通过对`spring-framework-3.2.4.RELEASE`中的各个模块进行分析,开发者可以逐步揭开Spring的神秘面纱,领略其设计之美。

    spring batch 英文文档

    根据给定的“spring batch 英文文档”的标题...以上对Spring Batch的介绍仅仅揭开了冰山一角,深入学习和实践才能真正掌握其强大功能。无论是进行大数据处理、数据迁移还是定时任务,Spring Batch都是值得信赖的选择。

    hibernate源码,struts2源码,spring源码

    - **事务管理**:Spring提供声明式和编程式事务管理,简化了事务处理。 源码分析可以帮助你深入理解这些框架的工作原理,例如查看Struts2的拦截器实现、Hibernate的SQL生成逻辑以及Spring的IoC和AOP实现。通过...

    SpringCloud微服务之揭开微服务的面纱.pptx

    4. **数据一致性**:在分布式环境下保证事务和数据一致性可能会成为挑战。 总结来说,微服务是一种强大的架构模式,它带来了许多好处,但也需要谨慎评估,以确保适合项目的需求和约束。理解微服务的原理、实践及其...

    spring-analysis:Spring源码阅读

    六、Spring事务管理 Spring的事务管理提供了声明式和编程式两种方式,可以统一处理本地事务和分布式事务。声明式事务管理通过@Transactional注解实现,简单易用;编程式事务管理则提供了PlatformTransactionManager...

    JAVA高并发高性能高可用高扩展架构视频教程

    spring事务处理 课程文档 高并发之基础数据MySql调优 mongodb 三级联动课程资料 应用架构之灵魂设计模式 应用架构之魂设计模式实战演练应用架构之魂设计模式实战演练 揭开springAOP神秘面纱(动态代理) Mysql性能优化...

    spring-analizy:spring源码分析

    《Spring源码深度解析》 在Java开发领域,Spring框架无疑是使用最为广泛的轻量级应用框架之...通过研究这些资料,开发者可以逐步揭开Spring框架的神秘面纱,提升自己的技术水平,从而在实际工作中更有效地运用Spring。

    trance_spring_cource:阅读spring原始码辅助工程

    4. 事务管理:Spring提供声明式和编程式的事务管理,使得事务处理变得简单且易于维护。 5. 数据访问抽象:Spring通过JdbcTemplate、HibernateTemplate等工具类,对各种ORM框架进行了统一的抽象,简化了数据访问层的...

    【Java架构师】Spring 5.x开源框架讲解

    此外,Spring AOP(Aspect-Oriented Programming,面向切面编程)提供了声明式事务管理、日志记录等功能,简化了代码的编写。你可以定义切面,使用@Aspect注解,然后在切点上应用通知(advice),如@Before、@After...

    基于SpringBoot+Vue的在线外卖系统

    数据库事务处理也是关键,确保数据的一致性和完整性。 在部署方面,Spring Boot应用可以被打包成可执行的JAR文件,直接在服务器上运行。配合Docker容器化技术,可以轻松实现环境隔离和弹性扩展,确保系统的稳定性和...

    J2EE扫盲之-揭开J2EE集群的神秘面纱

    ### J2EE扫盲之-揭开J2EE集群的神秘面纱 #### 1. 前言 随着互联网业务的迅速发展与企业信息化水平的不断提升,越来越多的关键性应用程序选择基于J2EE(Java 2 Platform, Enterprise Edition)平台进行构建。例如,...

    基于springboot线上买菜系统.zip

    Spring负责依赖注入和事务管理,SpringMVC处理HTTP请求并转发到相应的控制器,MyBatis则作为持久层框架,提供SQL映射和执行,实现了数据库操作的便捷性。通过这三者的配合,可以高效地处理用户请求,实现数据的增删...

    struts的各种功能小例子

    Struts遵循Model2架构,这是一种改进的MVC模式,强调了业务模型与表现层的分离,以及数据校验和事务管理的集成。 10. **与Spring的集成**: Struts可以无缝集成Spring框架,这样可以利用Spring的依赖注入、AOP等...

    IDEA版本淘淘商城zip.zip

    2. **Spring框架**:Spring是Java企业级应用开发的事实标准,它提供了依赖注入、AOP(面向切面编程)、事务管理等功能,使得项目能够轻松地实现组件化和解耦。 3. **MyBatis**:MyBatis是一个优秀的持久层框架,它...

Global site tag (gtag.js) - Google Analytics