最近看了比较多Spring的东西,脑中概念知识比较杂乱,借助周六周天的闲暇时间,写一些内容,梳理一下本身脑中的概念,也以此做为记录,但愿本身之后可以方便查阅,也但愿可以对各位看官有所帮助。
第一个Spring相关主题是Spring的事务管理,借助官方文档的一句话,Spring提供了一致的事物管理抽象模型,可以让你们在不一样的事务API(JTA、JDBC、Hibernate、JPA、JDO等)之间有一致的编程体验,Spring提供了声明式事务管理和编程事务管理两种事务管理方式,前者借鉴了EJB CMT中事务管理方式,后者则简化了JTA的事务异常编程模型,从这个角度来看,Spring事务提供了全面简洁的一致性事务管理方案。
熟悉Java EE编程的人应该知道,事务分为全局(global)和本地(local)两种事务;全局事务提供了在多个事务源之间操做保证原子性的能力,典型的事务源是数据库,在一些企业集成领域还包括MQ等中间件,在Java EE体系中经过JTA来对全局事务提供支持,事务管理功能由应用服务器提供支持,在实际操做时须要经过JNDI进行事务对象的引用,这就存在两种耦合绑定,特定查找协议和特定应用容器绑定,这是否是不良的组织形式要看应用的需求,但在大多数时候,大多数应用为了获取全局事务的能力而付出这种耦合的代价都是比较不合算的;本地事务是资源特定的,例如使用JDBC connection进行的事务操做,本地事务中的事物管理功能是应用程序进行控制的,非容器托管,所以其能摆脱容器绑定的反作用,但其局限性也很明显,由于是资源特定的,其不能支持跨事务源的操做,所幸咱们大部分应用都没有跨事务源的需求,本地事务在这种状况下比较适用;关于全局事务这里多补充一点,由于分布式事务自己的复杂性,其比较重量级,考虑的事务模型可能也比较复杂,除非必须使用,不然仍是敬而远之为好,即便由于水平扩展、高可用、负载均衡等需求而须要添置额外的事务源,也应该尽可能避免,例如使用Oracle RAC,将分布式事务的复杂性封装在数据库产品层面,简化应用级别的事务操做;实际上,全局事务操做不必定非须要绑定应用容器,借助独立的事务管理器如
Atomikos Transactions
和 JOTM也能提供JTA全局事务的功能,固然,仍是如上所述,并不推荐使用。
Spring提供了一致的事务抽象,而这抽象的核心就是PlatformTransactionManager接口,接口定义以下
该接口有三个方法,第一个方法根据TransationDefinition对象获取事务(TransactionStatus),TransationDefinition也是一个接口,定义以下
经过这个定义,咱们知道TransationDefinition提供了事务传播策略以及事务隔离级别的常量定义,同时提供了默认隔离和默认事务超时的常量定义,方法上提供了获取事务隔离级别、事务传播策略、事务超时时间、是否只读事务以及事务名称信息,基本上事务的基本概念都包含在了这个接口定义中;TransactionStatus提供了简单的控制事务以及查询事务状态的方法,定义以下 spring

由于接口定义比较明了,这里再也不展开赘述;
PlatformTransactionManager接口是Spring事务管理的核心,其类级结构以下
经过上面的结构图,咱们看到Spring为每一种经常使用的具体的事务操做API提供了具体的实现,例如咱们经常使用的基于纯JDBC的DataSourceTransactionManager和经常使用ORM hibernate的HibernateTransactionManager,JTA API特供了通用的实现以及针对Weblogic、WebSphere应用服务器提供的特定实现;
PlatformTransactionManager定义了基本的事务操做模型,而AbstractPlatformTransactionManager抽象类实现了该接口,该抽象类定义了一些全部具体实现类共有的属性和方法,同时定义了统一的事务处理流程,这是设计模式模板模式很是经典的应用,由于该类方法定义较多,这里咱们就不在贴出该类的定义,咱们来看看该类如何定义了统一的事务处理流程,该类实现了PlatformTransactionManager接口的commit方法,同时将该方法设为final,使其子类不可以重写,commit方法以下:
该方法定义了基本的提交处理,咱们看到实际处理是processXXX等方法,processCommit部分代码以下
咱们看到这个方法负责了具体事务提交的相关操做,最后实际提交操做是doCommit,而doCommit为一个抽象方法,这就是该类给子类实现留下的“钩子”,具体的提交操做由子类来去实现,这是一个典型的模板模式应用,接下来咱们找一个具体子类来看看doCommit的实际操做,咱们看看比较熟悉的HibernateTransactionManager,doCommit以下:
很明显,看到了Hibernate事务操做典型的用法,其余实现相似,这里咱们不在赘述,你们能够本身看看。
关于具体的Spring事务配置,虽然比较基础,但我仍是写在这里吧,在事务配置过程当中,我穿插着描述一些原理性的东西,由于头脑比较混乱,条理不清,还望你们海涵。
事务管理配置的核心是各类具体
PlatformTransactionManager的配置,其次就是各类事务管理器如何做用在具体操做方法上的切面配置,由于事务管理器到具体方法的切面配置都是一致的,这里咱们给出各类PlatformTransactionManager的配置,最后在来一个具体的事务切面配置,这里咱们提供DataSourceTransactionManager、JtaTransactionManager以及HibernateTransactionManager的配置,关于用XML配置仍是注解配置,这里咱们先提供XML的配置,而后在后面给出一个彻底注解的配置实例。
PlatformTransactionManager配置
1. DataSourceTransactionManager
首先须要配置JDBC的DataSource,毕竟这些本地事务是资源特定的嘛,DataSource定义以下
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
接着就是事务管理器的配置,DataSourceTransactionManager定义以下
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
这里事务管理器就配置完了,很是简单。
2. JtaTransactionManager
JtaTransactionManager配置更为简单,由于其是全局事务,为容器托管事务,其不须要知道特定的资源(DataSource),配置以下
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
可是注意JtaTransactionManager不须要知道DataSource,并不意味着不须要配置,Spring Data模块对资源的操做仍是须要DataSource的,在这种场景下DataSource由应用容器托管,因此使用JNDI进行查找引用,以下
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>
3.HibernateTransactionManager
Hibernate的配置比较多一些,首先是配置DataSource,和1中一致,这里再也不赘述,接着就是SessionFactory的配置,配置以下
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
</value>
</property>
</bean>
注意这里的配置比较老,若是使用基于注解的实体配置,请将mappingResources替换为
<property name="packagesToScan">
<list>
<value>com.app</value>
</list>
</property>
接着就是HibernateTransactionManager的配置了,配置以下
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
注意实际的持久化技术例如JDBC、JPA、JDO、Hibernate等和实际使用的事务管理器之间的区别,其没有必要一一对应,在使用JDBC时可使用JTA全局事务管理器,一样,使用Hibernate也能够,只需将最后一步的事务管理器替换为JtaTransactionManager便可。
事务管理器到特定方法配置
接下来就是具体事务管理器如何做用在具体方法上的配置了,不过在这以前咱们描述一下这个使用背景,如前所述,Spring提供了两种事务管理方式,一种是声明式事务管理(Declarative transaction management),另外一种则是编程式事务管理(Programmatic transaction management),这两种方式显然声明式比较简单,而在事务管理需求比较少的时候,想比较声明式比较繁琐的配置,编程式则比较简单,这里你们根据本身的需求进行选择,Spring官方推荐使用声明式事务管理,后面的论述也会分别给出声明式事务和编程式事务相关主题。
声明式配置
声明式事务管理的基本原理是利用在Spring中应用比较普遍的面向切面编程(AOP),经过将事务处理放在切面对象中来进行一致性的管理,减小冗余代码,提高简洁性,原理图大体以下 sql

其中的AOP proxy,Spring定义了TransactionInterceptor类,该类结构以下 数据库
经过这个结构,咱们看到实际的方法都在TransactionInterceptor的父类TransactionAspectSupport中,TransactionInterceptor的关键方法是invoke方法,而这个方法内部调用了父类的invokeWithinTransaction方法,这是这里事务处理的核心,invokeWithinTransaction方法片断以下
这看起来是一个典型的切面代理方法,咱们按照顺序讲下主要流程,首先270行获取该方法的事务属性,这些属性包括事务隔离级别、事务传播策略、只读、回滚等属性信息,而后271行获取平台事务管理器,由于Spring支持多事务管理器特性,这里须要配合实际配置来决定具体的事务管理器,注意276行,这一行获取了事务信息,同时又在必要状况下开启了事务,后面281行是具体方法执行,291行提交事务。
上面说了些原理,这里给出声明式事务的一些具体应用配置,在Spring中声明式事务大致有两种用法,一种是纯粹的切面配置,一种是基于@Transactional的注解配置,这里都会给出具体应用实例。
1.基于切面的配置
基于界面的配置比较简单,核心是Transaction 的Advice的配置,而后将这Advice配置在具体pointcut上就能够了,咱们先看看Advice的配置,配置实例以下:
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
如上,这里的配置比较清晰明了,引用具体的事务管理器(若是事务管理器名称为transactionManager,那么能够不须要明确引用事务管理器),而后通配符匹配特定方法,配置特定事务属性;配置完Advice,接下来是将Advice关联到pointcut的配置,以下
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
这里的配置比较简单,不在赘述
关于事务属性的配置,可配置属性以下
属性 express |
是否必须 apache |
默认值 编程 |
描述 设计模式 |
name 性能优化 |
是 服务器 |
|
方法名,可适用通配符(*) session |
propagation |
否 |
REQUIRED |
事务传播策略 |
isolation |
否 |
DEFAULT |
事务隔离级别 |
timeout |
否 |
-1(无限制) |
事务超时时间 |
read-only |
否 |
false |
是不是只读事务 |
rollback-for |
否 |
|
事务回滚的异常 |
no-rollback-for |
否 |
|
事务不回滚的异常 |
除方法名以外,其余属性都是可选的,根据具体的应用场景进行定制,事务传播策略定义了不一样事务的存在关系,可选属性列表以下(注意Spring事务概念中逻辑事务和物理事务的区别,物理事务就是指数据库层面的事务,而逻辑事务是应用层面的,能够具备更丰富的行为特性,Spirng中的事务就是特指逻辑事务,要注意)
传播策略 |
描述 |
REQUIRED |
支持当前上下文中存在事务,若是没有事务就建立一个新的,若是有,则在当前事务下执行 |
SUPPORTS |
支持当前上下文中存在事务,若是没有事务,就在无事务上下文下执行 |
MANDATORY |
支持当前上下文中存在事务,若是没有事务就抛出异常 |
REQUIRES_NEW |
产生一个新的事务,若是当前上下文中有事务就挂起原来事务 |
NOT_SUPPORTED |
不支持当前上下文存在事务,老是在无事务上下文下执行,存在的事务会进行挂起操做 |
NEVER |
不支持当前上下文存在事务,若是存在事务则抛出异常 |
NESTED |
若是当前上下文中存在事务就执行一个内嵌的事务 |
事务隔离级别这个属性是数据库层面的,可选属性列表以下
传播策略 |
避免问题 |
不能避免问题 |
READ_UNCOMMITTED |
第一更新丢失 |
脏读、不可重复读、幻读 |
READ_COMMITTED |
第一更新丢失、脏读 |
不可重复读、幻读 |
REPEATABLE_READ |
第一更新丢失、脏读、不可重复读 |
幻读
|
SERIALIZABLE |
第一更新丢失、脏读、不可重复读、幻读 |
|
具体这些问题以及每一个隔离级别的具体特征有机会在进行叙述吧,否则太多了,这里注意不一样的数据库对不一样隔离级别支持是不一样的,例如Oracle只能支持读提交和序列化,不可重复读的问题经过额外的乐观锁实现,其中存在只读事务,隔离级别实际是序列化,在配置事务只读时要注意这里的特性;Mysql支持比较丰富,但其可重复读可以解决部分幻读问题,这是与其实现有关的,等有机会把它展开叙述,通常应用场景下设置读提交就能知足要求,读未提交隔离太低,而其余两个隔离级别又过重量级,使用的话会严重下降应用性能,对于一些并发问题的容忍性,第一更新丢失、脏读、不可重复读(特殊场景为第二更新丢失)是不能容忍的,这里可使用读提交隔离级别+乐观锁来屏蔽第二更新丢失的问题,这是一个权衡场景须要和性能状况下作出的综合性方案。
事务超时设置能够根据须要设置,通常状况下,为了防止数据库锁阻塞,全部方法都应该有事务超时时间。
只读事务设置注意其性能优化的价值,通常状况下,只读操做可能都不须要事务,这时候下降事务隔离级别或者干脆不要事务都可以提高应用性能。
事务异常回滚,关于受检异常和非受检异常的争论这里就不在论述了,Spring事务采用了JTA的惯例,默认状况下只用运行时异常及其子类会致使事务回滚,受检异常不会致使事务回滚,后面两个选项就是关于回滚与不回滚异常设置的,这里能够根据须要进行设置。
2.基于@Transactional的事务管理
说完了切面设置的事务管理,这里说一下基于@Transactional的事务管理,咱们看下该注解的一些定义,以下
经过上面的定义,咱们知道这个注解能够放在类上和特定方法(只能为public方法,protected、private、package-visible都不能够)上,放在类上的话,该类全部的public方法都有了事务,放在特定方法上能够定制这个方法执行的事务属性,为了启用这种注解式的事务管理方式,须要在xml中添加以下信息
<tx:annotation-driven transaction-manager="txManager"/>
这时候就能够把以前事务配置中切面的部分删掉了,关于该注解上的属性,由于和上面切面形式中相似,这里就不在赘述了
上面的配置仍是基于XML的,这里给出一个彻底基于注解的实例
注解配置类以下
这里开启bean扫描,开启基于@Transactional的事务管理
其中的一些Bean定义,DataSource
SessionFactory
TransactionManager
在初始化的部分使用AnnotationConfigApplicationContext初始化就能够了
编程式事务管理
说完了声明式的事务管理,接下来说讲编程式事务管理,Spring提供了两种编程方式,一种是使用TransactionTemplate,一种是直接使用具体PlatformTransactionManager,官方推荐第一种方式,接下来咱们分别进行叙述。
1. 使用TransactionTemplate
TransactionTemplate采用一种回调的方式将要执行方法包含在事务中,其初始化须要指定特定的事务管理器,实际使用代码片断以下
transactionTemplate.execute(new TransactionCallback() {
// the code in this method executes in a transactional context
public Object doInTransaction(TransactionStatus status) {
updateOperation1();
return resultOfUpdateOperation2();
}
})
上例是有返回结果的,能够不须要返回结果,以下
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
updateOperation1();
updateOperation2();
}
})
能够在方法体内回滚事务,设置status.setRollbackOnly()便可
TransactionTemplate提供了一些事务属性设置接口,能够根据须要进行设置。
2.使用PlatformTransactionManager
这个比较简单,关键是定义TransactionDefinition,代码以下
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can only be done programmatically
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
// execute your business logic here
}
catch (MyException ex) {
txManager.rollback(status);
throw ex;
}
txManager.commit(status);
代码很清晰明了,这里就不在赘述了。
写不下去了,算是作了一次梳理吧,有时间在进行详细叙述。