Spring3学习

来源:互联网 发布:我帅不帅 知乎 编辑:程序博客网 时间:2024/05/20 04:26

Spring官网

http://spring.io


Spring framework

http://projects.spring.io/spring-framework/


Spring framework doc

http://docs.spring.io/spring/docs/3.2.9.RELEASE/spring-framework-reference/htmlsingle


学习书籍:Spring3.X企业应用开发实战

此学习笔记内容主要来自此书籍

Spring模块图

The Spring Framework consists of features organized into about 20 modules. These modules are grouped into Core Container, Data Access/Integration, Web, AOP (Aspect Oriented Programming), Instrumentation, and Test, as shown in the following diagram.


Spring XML-Schema-based configuration

Spring配置文件的Schema引入可参考Spring Refrence的附录部分,如下图:

IOC容器概述

依赖注入

DI与IOC概念

IOC即反转控制,是指类所依赖的对象的创建工作由类本身转变为由第三方来完成,从而达到降低类之间的偶合度及减少与业务逻辑不相关的依赖对象创建的代码。

DI是,指类的依赖对象是由第三方注入,以移除调用类对某一接口实现类的依赖,是IOC的另一种更容易理解的解释。

资源访问利器


BeanFactory与ApplicationContext


Bean生命周期



在IOC容器中装配Bean

依赖注入方式

属性注入

注意:属性注入Bean要求提供默认构造函数及setter方法,属性变量名的前两个字母要么全大写要么全小写,如brandName、IDCard。

构造方法注入

存在循环依赖问题:

因为Sping容器能够实例化以构造函数注入方式的Bean有一个前提:bean构造函数入参引用的对象必须已经准备就绪。由于这个机制,如果两个Bean都通过构造函数入参引用对方,就会发生类似于线程死锁的循环依赖问题。

工厂方法注入

包括静态工厂方法及非静态工厂方法

选择注入方式的考量

推荐使用构setter方法注入,

构造函数注入参数多时代码可读性差,如果有多个构造函数配置复杂,不利于子类继承,存在循环引用问题

工厂方法注入需编写额外的类及方法,Spring容器本身已经以优雅的方式实现了传统工厂模式的所有功能,我们大可不必再做这项重复性工作

注入参数详解

引用其它Bean时,<ref/>元素通过以下三个元素引用容器中其它bean,默认为bean范围:
bean:可以引用整个容器范围内bean,包括父容器
local:只能引用同一配置文件范围内的bean
parent:引用父容器内的bean。

Bean作用域

Spring可通过scope来配置其作用域,如:<bean id="car" class="org.lql.Car" scope="singleton"。
Spring包括如下作用域singleton(Spring默认作用域)、prototype、request、session、global session,此外允许自定义作用域。

singleton(Spring默认作用域

该范围bean在SpringIOC容器中仅有一个该Bean实例,Bean以单实例方式存在,默认情况下SpringContext在启动时将实例化所有singleton的bean,但可通过设置lazy-init来设置是否需延迟初始化时机,但如果其它bean设置了依赖该bean,则即使配置了此策略也可能失效。

prototype

如果id=“car”的bean为prototype,则每个引用此bean的car实例都是一个新的实例,每次通过容器getBean("car")返回的也是一个新的car实例。默认情况下Spring容器启动时不实例化此类型bean,此外,Spring容器将此类型bean实例化交给调用者后就不再管理其生命周期。

Web应用环境相关Bean作用域

包括request、session、globalSession,其中globalSession类似于Session,仅在Porlet的Web应用中使用。web作用域很少使用。

基于注解的配置

使用注解定义bean

@Component:与<bean id="car" class="org.lql.Car" />等同
@Component("car")public class Car {    ...}
除了Component之外,Spring3还有3个基本功能与之等效的衍生注解,分别对应DAO、Service、及Web层的Controller进行注解:
@Repository
@Service
@Controller

使用注解配置信息启动Spring容器

在Spring配置文件中加上此配置:<context:component-scan base-package="org.lql"/>

自动装配Bean

使用@AutoWired进行自动注入

@Servicepublic class LoginService {    @AutoWired(required=true)    private CarDAO carDAO;        private UserDAO userDAO;    @AutoWired    @Qualifier("userDAO") //注入名为userDAO,类为UserDAO的bean    public getUserDAO(UserDAO userDAO) {        this.userDAO = userDAO;    }}

对标准注解的支持

@Resource:与@AutoWired默认按类型匹配注入不同,@Resource默认按名称匹配注入
@Inject:按类型匹配注入,但无required属性
此两个注解均功能均无@Autowire丰富,因此大可不必使用。

Bean作用范围及生命过程方法

@Scope:指定Bean的作用范围
@PostConstract、@PreDestory相当于Spring中的init-method、destory-method,不过注解可使用多个。

Spring AOP基础

AOP概述

AOP(面向切面编辑Aspect Oriented Programing)是一种编程技术,适用于那此具有横切逻辑的应用场景,如性能监测、事务管理、日志记录、访问控制等。

AOP术语

连接点(Joinpoint)

程序执行的某个特定位置,如类初始化前,类初始化后,方法调用前,方法调用后,方法抛出异常后,一个类或一段代码具有边界性质的特定点,这此代码中的特定点就称为“连接点”。
Spring AOP支持方法的连接点,即仅能在方法调用前,方法调用后,方法抛出异常时以及方法调用前后这些程序执行点织入增强。

切点(Pointcut)

切点是连接点的过滤条件,连接点相当于数据库中的记录,而切点相关于查询条件,一个切点可以匹配多个连接点。

增强(Advice)

增强(也有书籍翻译成通知)是织入到目标类连接点的一段程序代码,如事务控制代码。
在Spring中,增强除了用于描述一段程序代码外,还拥有另一个和连接点相关的信息,即执行点的方位。正因为增强即包含了用于添加到目标连接点的一段执行逻辑,又包含了用于定位连接点的方位信息,所以Spring提供的增强接口都是带方位名的,如:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。

目标对象(Target)

增加横切逻辑的织入目标类。

引介(Introduce)

引介是一类特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类没有实现某个接口,通过AOP的引介功能,我们可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。

织入(Weaving)

织入是将增强添加到目标类具体连接点上的过程,根据不同的实现技术,AOP有三种织入的方式:
1)编译期织入,这要求使用特殊的Java编译器;
2)类装载期织入,这要求使用特殊的类装载器;
3)动态代理织入,在运行期为目标类添加增加生成子类的方式。
Spring采用动态代理织入,而AspectJ采用编译期织入及类装载期织入。

代理(Proxy)

一个类被织入增强以后就产生了一个结果类,它是整个了原类和增强逻辑的代理类。根据不同的代理方式,代理类即可能是和原类具有相同接口的类,也可能就是原类的子类,所以我们可以采用调用相同的方式 调用代理类。

切面(Aspect)

切面由切点和增加(引介)组成,安既包括了横切逻辑定义,也包括 了连接点的定义,Spring AOP就是实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
AOP的工作重心在于如何将增加应用于目标对象的连接点上,这里首先就包括两个工作:第一,如何通过切点和增加定位到连接点上;第二,如何在增强中编写切面的代码。

Spring AOP基础知识

Spring AOP使用两种动态代理技术在运行期织入增强的代码:一种是基于JDK的动态代理;另一种是基于CGLib的动态代理。之所以城朵两种代理机制是因为JDK动态代理本身只提供接口的代理,而不支持类的代理。

JDK动态代理

JDK 1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例。
JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler,具体讲解可参考此文章
        由浅入深分析mybatis通过动态代理实现拦截器(插件)的原理

CGLib动态代理

CGLib可以动态创建类的代理实例,采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势织入横切逻辑。

基于@AspectJ和Schema的AOP

Spring AOP包括基于XML配置的AOP和基于@AspectJ注解的AOP,这两者的底层都是通过动态代理技术实现(JDK代理或CGLib代理)。Spring可以集成AspectJ,但AspectJ本身并不属于SpringAOP的ai范畴。
如果是JDK5.0(或以上版本)项目,建议使用注解@AspectJ方式,因为这种方式能以更简单直接的方式应用切面。
Spring AOP默认是通过JDK动态代理来实现,可通过设置<aop:config proxy-target-class="true"/>来指定使用CGLib实现代理,proxy-target-class默认为false,但对于非接口实现的连接点Spring AOP仍采用CGLib实现代理

着手使用@AspectJ

使用前准备

1)JDK版本需1.5以上
2)需将Spring asm模块添加进类路径(Spring 3高版本中已经将此模块整合进Core模块中了,不需再额外添加)
3)需添加@AspectJ的注解类库及相应的解析类库aspectj.weaver及aspectj.tools
<dependency>            <groupId>org.aspectj</groupId>            <artifactId>aspectjweaver</artifactId>            <version>1.8.0</version>        </dependency>        <dependency>            <groupId>org.aspectj</groupId>            <artifactId>aspectjtools</artifactId>            <version>1.8.0</version>        </dependency>

一个简单的例子

通过编程的方式使用@AspectJ
package org.lql;/** * Created by Administrator on 14-6-29. */public class NaiveWaiter implements Waiter {    @Override    public void greetTo(String clientName) {        System.out.println("NaiverWaiter:greetTo " + clientName) ;    }    @Override    public void serveTo(String clientName) {        System.out.println("NaiverWaiter:serveTo " + clientName) ;    }}

package org.lql.aop.aspectj;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;/** * Created by Administrator on 14-6-29. */@Aspectpublic class PreGreetingAspect {    @Before("execution(* greetTo(..))") //定义切点及增强类型    public void beforeGreeting() { //增加横切逻辑        System.out.println("How are you ");    }}

package org.lql.aop.aspectj;import org.lql.NaiveWaiter;import org.lql.Waiter;import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;/** * Created by Administrator on 14-6-29. */public class AspectJProxyTest {    public static void main(String args[]) {        Waiter target = new NaiveWaiter();        AspectJProxyFactory factory = new AspectJProxyFactory();        factory.setTarget(target);        factory.addAspect(PreGreetingAspect.class);        Waiter proxy = factory.getProxy();        proxy.greetTo("lql");        proxy.serveTo("lql");    }}

输出结果:
How are you 
NaiverWaiter:greetTo lql
NaiverWaiter:serveTo lql

如何通过配置的方式使用@AspectJ切面

方式1.指定代理自动代理创建器
<?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:aop="http://www.springframework.org/schema/aop"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">    <!--1.目标ben -->    <bean id="waiter" class="org.lql.NaiveWaiter"/>    <!--2.使用了AspectJ注解的切面类-->    <bean class="org.lql.aop.aspectj.PreGreetingAspect"/>    <!--3.自动代理创建器,自动将@AspectJ注解的切面类织入到目标Bean中-->    <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/></beans>

方式2.使用基于Schema的aop命名空间进行配置
<?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:aop="http://www.springframework.org/schema/aop"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">    <!--3.基于@AspectJ切面的驱动器-->    <aop:aspectj-autoproxy proxy-target-class="true"/>    <!--1.目标ben -->    <bean id="waiter" class="org.lql.NaiveWaiter"/>    <!--2.使用了AspectJ注解的切面类-->    <bean class="org.lql.aop.aspectj.PreGreetingAspect"/>    <!--3.自动代理创建器,自动将@AspectJ注解的切面类织入到目标Bean中-->    <!--<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>--></beans>

@AspectJ语法基础

切点表达式函数

Spring支持9个@AspectJ切点表达式函数,它们跟据不同的方式扫描目标类的连接点,根据扫描对象的不同可大致分为4种类型:
方法切点函数:execution()及@annotion()
方法入参切点函数:args()及@args()
目标类切点函数:within()、target()及@within() 
目标类切点函数:@target()
代理类切点函数:this()

不同增强类型

@Before:前置增加
@AfterReturning:后置增加
@Aroud:环绕增加
@AfterThrowing:抛出增强
@After:Final增强
@DeclarePattern:引介增强

基于Schema配置切面

package org.lql;/** * Created by Administrator on 14-6-29. */public class NaiveWaiter implements Waiter {    @Override    public void greetTo(String clientName) {        System.out.println("NaiverWaiter:greetTo " + clientName) ;    }    @Override    public void serveTo(String clientName) {        System.out.println("NaiverWaiter:serveTo " + clientName) ;    }    @Override    public int clientCount(int clientNum) {        System.out.println("NaiverWaiter:clientCount " + clientNum) ;        return clientNum;    }    @Override    public void throwing() throws IllegalArgumentException {        throw new IllegalArgumentException();    }}
切面方法:
package org.lql.aop.aspectj;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.lql.Waiter;/** * Created by Administrator on 14-6-29. */public class SchemeAspectMethod {    public void beforeGreeting(JoinPoint jp) {        System.out.println("SchemeAspectMethod beforeGreeting: client Name: " + jp.getArgs()[0]);    }    public void afterCountReturning(int retVal) {        System.out.println("SchemeAspectMethod afterCountReturning: " + retVal);    }    public void aroudMethod(ProceedingJoinPoint pjp) throws Throwable {        System.out.println("SchemeAspectMethod round before: ");        pjp.proceed();        System.out.println("SchemeAspectMethod round after");    }    public void afterThrowingMethod(IllegalArgumentException iae) {        System.out.println("SchemeAspectMethod afterThrowingMethod executing");    }    public void after() {        System.out.println("SchemeAspectMethod final advice balalalala");    }}
schema配置:
<?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:aop="http://www.springframework.org/schema/aop"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">    <aop:config>        <aop:aspect ref="adviceMethod" >            <aop:before method="beforeGreeting" pointcut="execution(* greetTo(..))"/>            <aop:after-returning method="afterCountReturning" pointcut="execution(* afterCountReturning(..))" returning="retVal"/>            <aop:after-throwing method="afterThrowingMethod" pointcut="execution(* throwing(..))" throwing="iae"/>            <aop:around method="aroudMethod" pointcut="execution(* *greetTo(..))" />            <aop:after method="after" pointcut="execution(* greetTo(..))"/>        </aop:aspect>    </aop:config>    <!--1.目标ben -->    <bean id="waiter" class="org.lql.NaiveWaiter"/>    <!--2.增加方法定义bean-->    <bean id="adviceMethod" class="org.lql.aop.aspectj.SchemeAspectMethod"/></beans>
测试类:
package org.lql.aop.aspectj;import org.lql.Waiter;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/** * Created by Administrator on 14-6-29. */public class AspectJSchemaTest {    public static void main(String[] args) {        ApplicationContext context = new ClassPathXmlApplicationContext("application-aop-schema.xml");        Waiter waiter = (Waiter)context.getBean("waiter");        waiter.greetTo("lql");        waiter.clientCount(3);        //waiter.throwing();    }}

Spring AOP总结:Spring AOP共有4种定义切面方式

1)基于@AspectJ注解方式
2)基于<aop:aspect>的方式
3)基于<aop:advisor>的方式
4)基于Advisor类的方式

使用LTW织入切面

LTW(LoadTimeWeaver)是类加载期通过字节码编辑技术将切面织入到目标类中的一种技术。

Spring事务管理

数据库事务基础知识

何为数据库事务

数据库事务是指多个SQL语句要全部执行成功,要么全部执行失败,必须满足4个特性:原子性(Atomic)、一致性(Consistency)、隔离性(Isolation)及持久性(Durabiliy)。
原子性指事务内的SQL操作是一个整体,只有全部执行成功才能提交事务,否则必须回滚,让数据库返回到初始状态。
一致性指事务提交成功后数据库所处的状态与业务规则一致,即数据不会破坏。如A帐户转帐100块到B帐户,不管转帐是否成功,总额均应不变。
隔离性指多个事务之间相互不产生影响。
持久性指一旦事务提交成功,事务中所有数据库操作都必须被持久化到数据库中,即使提交后数据库崩溃,在数据库重启时也必须能够保证能通过某种机制恢复数据。
数据库一般采用重新执行日志文件保证原子性、一致性和待久性。通过锁机制来保证隔离性。

数据库并发问题

脏读(dirty read):A事务可读取B事务尚未提交的更改数据,并在这个数据基础上进行了操作,若B事务回滚,则会引发脏读。
不可重复读(unrepeatable read):A事务读了B事物修改的数据。
幻象读(phantom read):A事务读取了B事务提交的新增数据,这时A数据将出现比幻象读写问题。
幻象读与的不可重复读区别:前者是指读到了其它事务新增加的数据,后者是指读到了其它事务更改(修改或删除)的数据,为了避免这两种情况采取的策略是不一样的,前者需要表级锁将整个表锁住(Oracle采用的是多版本数据的方式实现)后者需要行级锁。
第一类更新丢失问题:
第二类更新丢失问题:

数据库锁机制

数据库锁机制是用来解决数据库并发操作问题的。
按锁对象划可划分为行级锁表级锁,前者对整个表锁定,后者对特定的表记录锁定。
从并发事务锁定的关系上看,可以分为共享锁独占锁,共享锁会防止独占锁,但允许其它的共享锁,独占锁既防止独占锁又防止共享锁。
为了更新数据,数据库必须在更改的行上施加行级独占锁,insert、update、delete及select for update语句都会隐式采用必要的行级锁。
Oracle数据库常用的5种锁:
行共享锁:一般通过select for update隐式获取行共享锁,也可用以下语句显示获取,如:LOCK TABLE EMP IN ROW SHARE MODE。
行独占锁:insert、update、delete会隐式获取行排他锁,LOCK TABLE IN ROW EXCLUSIVE MODE或显示获取行独占锁。
表共享锁:通过LOCK TABLE IN SHARE MODE显示获取。
表独占锁:通过LOCK TABLE IN EXCLUSIVE MODE显示获取。
表共享行独占锁:通过LOCK TABLE IN SHARE ROW EXCLUSIVE MODE显示获取

事务隔离级别

尽管数据库提供了锁的DML操作方式,但直接使用锁管理是非常麻烦的,因此数据库为用户提供了自动锁机制。只要用户指定会话的事务隔离级别,数据库就会分析事务中SQL语句并为自动为事物操作的数据资源添加相应的锁。此外数据库还会自动维护这些锁,当锁过多时数据库会自动进行锁升级以提高系统运行性能。
ANSI/IOS SQL92标准定义了4个等级的事务隔离级别,不同事务隔离级别能够解决的数据并发问题的能力是不同的,如下图:

SQL92定义READ UNCOMMITED主要是为了提供非阻塞读的能力,Oracle虽然也支持READ UNCOMMITED,但它不支持脏读,类为Oracle使用多版本机制彻底解决了非阻塞读时读到脏数据的问题并保证读的一致性,所以Oracle的READ COMMITED(Oracle默认隔离级别)就已经满足了SQL92标准的REAPEATEABL READ 隔离级别。

JDBC对事务的支持

用户可以通过Connection#getMetaDate()方法获取DatabaseMetaData对象,并通过该对象的supportsTransactions()、supportsTransactionIsolationLevel(int level)查看底层数据库的事务支持情况。
connection默认是自动提交事务的,可以通过connection.setAutoCommit(false)阻止自动提交;
JDBC2.0 事务只支持提交入回滚操作,JDBC 3.0(jdk1.4以后版本)引入了保存点特性(savepoint),允许将事务分割为多段,可指定回滚至指定的Savepoint。

ThreadLocal基础知识

Spring使用ThreadLocal解决线程安全问题

Spring对事务管理的支持

Spring为事务管理提供了统一的编程模板(TransactionTemplate),在高层次建立了统一的事务抽象,因此不管我们是用Spring JDBC、Hibernate、JPA,Spring都可以让我们用统一的编程模型进行事务管理。
Spring事务管理的亮点在于声明式事务管理

事务管理关健抽象

Spring事物管理的抽象层主要包括3个接口,分别是PlatformTransactionManager、TransactionDefiniton和TransactionStatus。
TransactionDefinition定义了Spring兼容的事务属性,包括事物隔离、事务传播、事务超时、只读状态。
TransactionStatus代表一个事务具体的运行状态。
PlatformTansactionManager是Spring定义的高层次事物管理接口方法,主要包括如下三个方法:
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;void commit(TransactionStatus status) throws TransactionException;void rollback(TransactionStatus status) throws TransactionException;

Spring事务管理器实现类

org.springframework.orm.jpa.JpaTransactionManager
org.springframework.orm.hibernate3.HibernateTransactionManager
org.springframework.orm.datasource.DataSrouceTransactionManager
使用JDBC或Mybatis等基于DataSource数据源持久化技术时,使用此事务管理器
org.springframework.orm.jdo.JdoTransactionManager
org.springframework.orm.jta.JtaTransactionManager

一个基于配置的事务配置例子

<?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:aop="http://www.springframework.org/schema/aop"       xmlns:tx="http://www.springframework.org/schema/tx"       xmlns:context="http://www.springframework.org/schema/context"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.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       http://www.springframework.org/schema/context http://www.springframework.org/schema/tx/spring-context-3.0.xsd">    <context:property-placeholder location="classpath*:app.properties"/>    <bean id="dataSource" class=" org.springframework.jdbc.datasource.SimpleDriverDataSource">        <property name="driver" value="${db.driver}"/>        <property name="url" value="${db.url}"/>        <property name="username" value="${db.username}"/>        <property name="password" value="${db.password}"/>    </bean>    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="dataSource"/>    </bean>    <tx:advice id="txAdvice" transaction-manager="transactionManager">        <tx:attributes>            <tx:method name="get*" read-only="true"/>            <tx:method name="is*" read-only="true"/>            <tx:method name="find*" read-only="true"/>            <tx:method name="*" isolation="DEFAULT" rollback-for="Exception"/>        </tx:attributes>    </tx:advice>    <aop:config proxy-target-class="true">        <aop:advisor advice-ref="txAdvice" pointcut="(execution(* org.lql..*.*(..)))"/>    </aop:config></beans>

事务同步管理器

Spring将JDBC的Connection、Hibernate的Session待访问数据库的连接或者会话对象资源。这此资源在同一时刻需是不能多线程共享的,为了让DAO、Service类能做到Singleton,Spring的同步事务管理器类org.springframework.transaction.support.TransactionSynchronizationManager采用ThreadLocal为不同的线程提供了独立的资源副本,同时维护扩展事务配置的属性及运行状态信息。事务同步管理器类是Spring事务管理的基石,不管是编程式事务管理还是声明式事务管理,都离不开同步事务管理器。
public abstract class TransactionSynchronizationManager {private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);private static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<Map<Object, Object>>("Transactional resources");private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =new NamedThreadLocal<Set<TransactionSynchronization>>("Transaction synchronizations");private static final ThreadLocal<String> currentTransactionName =new NamedThreadLocal<String>("Current transaction name");private static final ThreadLocal<Boolean> currentTransactionReadOnly =new NamedThreadLocal<Boolean>("Current transaction read-only status");private static final ThreadLocal<Integer> currentTransactionIsolationLevel =new NamedThreadLocal<Integer>("Current transaction isolation level");private static final ThreadLocal<Boolean> actualTransactionActive =new NamedThreadLocal<Boolean>("Actual transaction active");
Spring为不同的持久化技术提供了不同的工具类用于从TransationSynchronizationManager中获取对应线程绑定资源的工具类,这些工具类提供了静态方法用于获取当前线程绑定的资源,如:
Spring JDBC或Ibatis:org.springframework.jdbc.datasource.DataSourceUtils.
Hibernate3:org.springframework.orm.hibernate3.SessionFactoryUtils
JPA:org.springframework.orm.jpa.EntityManagerFactoryUtils
Spring也为不同的持久化技术提供模板类(如:JdbcTempate),模板类在内部通过资源获取工具类获取当前线程绑定的资源。

事务传播行为

Spring在TransactionDefinition中定义了7种事务传播行为,它们事务方法以及事务方法发生嵌套调用时事务如何进行传播。
PROPAGATION_REQUIRED    Spring默认的事务传播级别,如果当前没有事务,创建事务,如果已经有,则加入事务中,
PROPAGATION_SUPPORTS    支持当前事务,如果当前没有,则以非事务方式运行
PROPAGATION_MANDATORY    使用当前事务,如果当前没有,就抛出异常
PROPAGATION_REQUIRES_NEW    新建事务,如果当前已有事务,把此事务挂起
PROPAGATION_NOT_SUPPORTED    以非事务方式运行,若当前已有事务,就把事务挂起
PROPAGATION_NEVER    以非事务方式运行,若已经存在事务则抛出异常
PROPAGATION_NESTED    如果当前存在事务,则嵌套在此事务中运行,若当前没有,则与PROPAGATION_REQUIRED执行类似操作。此传播级必须在底层数据源为JDBC3.0以上才行。

Spring事务管理难点剖析

DAO和事务管理在牵绊

很少有人使用Spring面不用Spring的事务管事,但是否必须使用Spring事务管理,否则数据就无法进行持入化操作呢,答案是否定的。因为DataSource默认的事务提交机制为自动提交。
JdbcTemplate.getDataSource().getDefaultAutoCommit();
对于强调读速度的应用,数据库本身可能就不支持事务,如使用MYISAM引擎的MYSQL数据库,这时即使在Spring中是配置了事务管理,也是没有实际用处的。

特殊方法成漏网之鱼

哪些方法不能实施Spring AOP事务

因为Spring AOP是基于接口代理或者动态字节码技术实施增强的(AspectJ LTW类装载时实施增强很少使用,不考虑)。
所以基于Java语言的特性,基于接口的动态代理要求实现类方法必须为public的,同时不能使用static修饰,所以,可实施接口动态代理的方法只能是public或public final修饰的方法,其它的方法不能被动态代理也就不能实施AOP增加,即不能进行Spring事务增加。
基于CGLib字节码动态代理是通过扩展被增强类,动态创建其子类的方式进行AOP增强植入的,由于final、static及private修饰的方法都不能被子类覆盖,相应的这些方无法实施AOP增加,所以方法签名必须特别注意这些修饰符的使用,以免使方法不小必成为漏网之鱼。

不过,需要特别指出的是,这此不能被Spring AOP增强的方法并非不能工作在事务环境下,只要它们被外层的事务方法调用了,由于Spring的事务管理的传播级别,内部的方法也可以工作在外部方法启动的事务上下文中。我们说这些方法不能被Spring AOP进行事务增加是指这些方法不能启动事务,但外层方法的事务依然可以顺利的传播到这些方法中。

数据连接泄漏

Spring DAO支所有支持的数据访问技术框架都使用了模板化技术进行了薄层封装,只要你使用Spring DAO的模板(如JDBCTemplate)进行数据访问,一定不会存在数据连接泄漏的问题,如果我们使用模板,我们无须关注数据库连接(Connection)及其衍生产品(Hibernate的Session等)的获取及释放操作,模板类在其内部流程替我们完成了,且对开发人员是透明的。
当Spring事务方法运行时,会产生一个事务上下文,该上下文在事务执行线程中会针对同一个数据源绑定同一个数据库连接,所有被事务上下文传播的方法都共享这个数据库连接。这个数据库连接的创建及释放都在Spring的控制之内,不会产生任务问题,使用者可以放心使用,不用关心数据库连接释放的问题。
那么如何获取Spring控制的数据库连接呢?Spring提供了两种方式:
1、使用数据源工具类获取,DataSourceUtils的getConnection方法。
2、对数据源进行代理

Spring JDBC数据连接泄漏

如果不使用Spring控制的数据库连接,直接从数据源获取连接,而后又调用Connection.close()方法释放连接,将造成数据库连接泄漏,如:jdbcTemplate.getDataSource().getConnection()

通过DataSourceUtils获取数据连接

使用DataSourceUtils获取数据库连接,如果Spring事务上下文中使用此方法,则不会造成连接泄漏,否则,同样会造成数据连接泄漏问题!

其它数据访问技术造价类

Spring为每种数据访问技术提供了获取事务上下文绑定的数据库连接的工具类或数据源的代理类,如下图:




0 0
原创粉丝点击