1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦
2.可以使用容易提供的众多服务,如事务管理,消息服务等
3.容器提供单例模式支持
4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能
5.容器提供了众多的辅助类,能加快应用的开发
6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等
7.spring属于低侵入式设计,代码的污染极低
8.独立于各种应用服务器
9.spring的DI机制降低了业务对象替换的复杂性
10.Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring的部分或全部
什么是DI机制?
依赖注入(Dependecy Injection)和控制反转(Inversion of Control)是同一个概念,具体的讲:当某个角色
需要另外一个角色协助的时候,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在spring中
创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者的工作由spring来完成,然后注入调用者
因此也称为依赖注入。
spring以动态灵活的方式来管理对象 , 注入的两种方式,设置注入和构造注入。
设置注入的优点:直观,自然
构造注入的优点:可以在构造器中决定依赖关系的顺序。
什么是AOP?
面向切面编程(AOP)完善spring的依赖注入(DI),面向切面编程在spring中主要表现为两个方面
1.面向切面编程提供声明式事务管理
2.spring支持用户自定义的切面
面向切面编程(aop)是对面向对象编程(oop)的补充,
面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面。
AOP从程序运行角度考虑程序的结构,提取业务处理过程的切面,oop是静态的抽象,aop是动态的抽象,
是对应用执行过程中的步骤进行抽象,,从而获得步骤之间的逻辑划分。
aop框架具有的两个特征:
1.各个步骤之间的良好隔离性
2.源代码无关性
Spring @Transactional原理及使用
本文主要讨论Spring声明式事务中使用注解@Transactional的方式、原理及注意事项,主要包括以下内容:
- Spring @Transactional的配置使用;
- Spring @Transactional的传播行为和隔离级别;
- Spring @Transactional的工作原理;
- Spring @Transactional的注意事项;
- Spring @Transactional自我调用中的问题。
1、Spring @Transactional的配置
步骤一、在Spring配置文件中引入命名空间
1
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
步骤二、xml配置文件中,添加事务管理器bean配置
<!-- 事务管理器配置,单数据源事务 -->
<bean id="pkgouTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="pkGouDataSource" />
</bean>
<!-- 使用annotation定义事务 -->
<tx:annotation-driven transaction-manager="pkgouTransactionManager" />
步骤三、在使用事务的方法或者类上添加 @Transactional(“pkgouTransactionManager”)注解
2、 Spring @Transactional的传播行为和隔离级别
1> 事务注解方式: @Transactional
- 标注在类前:标示类中所有方法都进行事务处理
- 标注在接口、实现类的方法前:标示方法进行事务处理
2> 事务传播行为介绍:
事务传播行为 | 说明 |
---|---|
@Transactional(propagation=Propagation.REQUIRED) | 如果有事务, 那么加入事务, 没有的话新建一个(默认情况) |
@Transactional(propagation=Propagation.NOT_SUPPORTED) | 容器不为这个方法开启事务 |
@Transactional(propagation=Propagation.REQUIRES_NEW) | 不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务 |
@Transactional(propagation=Propagation.MANDATORY) | 必须在一个已有的事务中执行,否则抛出异常 |
@Transactional(propagation=Propagation.NEVER) | 必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反) |
@Transactional(propagation=Propagation.SUPPORTS) | 如果其他bean调用这个方法,在其他bean中声明事务,那就用事务。如果其他bean没有声明事务,那就不用事务 |
3> 事务超时设置:
@Transactional(timeout=30) //默认是30秒
4> 事务隔离级别:
事务隔离级别 | 说明 |
---|---|
@Transactional(isolation = Isolation.READ_UNCOMMITTED) | 读取未提交数据(会出现脏读, 不可重复读),基本不使用 |
@Transactional(isolation = Isolation.READ_COMMITTED)(SQLSERVER默认) | 读取已提交数据(会出现不可重复读和幻读) |
@Transactional(isolation = Isolation.REPEATABLE_READ) | 可重复读(会出现幻读) |
@Transactional(isolation = Isolation.SERIALIZABLE) | 串行化 |
- 脏读 : 一个事务读取到另一事务未提交的更新数据
- 不可重复读 : 在同一事务中, 多次读取同一数据返回的结果有所不同, 换句话说, 后续读取可以读到另一事务已提交的更新数据。相反,”可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是后续读取不能读到另一事务已提交的更新数据
- 幻读 : 一个事务读到另一个事务已提交的insert数据
@Transactional的属性:
3、 Spring @Transactional的工作原理
- 自动提交
默认情况下,数据库处于自动提交模式。每一条语句处于一个单独的事务中,在这条语句执行完毕时,如果执行成功则隐式的提交事务,如果执行失败则隐式的回滚事务。
事务管理,是一组相关的操作处于一个事务之中,因此必须关闭数据库的自动提交模式。这点,Spring会在org/springframework/jdbc/datasource/DataSourceTransactionManager.java中将底层连接的自动提交特性设置为false。
1
|
// switch to manual commit if necessary。 this is very expensive in some jdbc drivers,
|
- spring事务回滚规则
Spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。Spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。
默认配置下,Spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚)。而抛出checked异常则不会导致事务回滚。
Spring也支持明确的配置在抛出哪些异常时回滚事务,包括checked异常。也可以明确定义哪些异常抛出时不回滚事务。
还可以编程性的通过setRollbackOnly()方法来指示一个事务必须回滚,在调用完setRollbackOnly()后你所能执行的唯一操作就是回滚。
在上一篇文章中,我们使用了声明式事务来配置事务,使事务配置从service逻辑处理中解耦出来。但它还存在一些缺点:
1. 我们只针对方法名的特定进行拦截,但无法利用方法签名的其它信息定位,如修饰符、返回值、方法入参、异常类型等。如果我们需要为同名不同参的同载方法配置不同事务就会出问题了。
2. 事务属性的配置串虽然能包含较多信息,但配置较易出错。
针对这些问题,我们可以基于Schema,引入tx和aop的命名空间来改进我们的配置:
- 引入命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
- tx\aop核心配置
<!-- 配置事务属性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRES_NEW" />
<tx:method name="update*" propagation="REQUIRES_NEW" />
<tx:method name="delete*" propagation="REQUIRES_NEW" />
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务切入点,以及把事务切入点和事务属性关联起来 -->
<aop:config proxy-target-class="true">
<aop:pointcut expression="execution(* com.yc.service.*.*(..))"
id="ServicePointcut" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="ServicePointcut" />
</aop:config>
这里需要特别注意的是,我们需要在标签中将proxy-target-class配置成true,否则会出现和上一篇文章相同的错误:我们定义的类无法转换成代理类
这里我们通过来配置我们的事务增强属性。在标签中,常见属性及其说明如下,其中,除了name属性是必选外,其他都是可选的:
属性 | 说明 | 默认 | 允许值 |
---|---|---|---|
name | 匹配方法名 | 必须声明,至少为* | 可使用*通配符 |
propagation | 事务传播行为 | REQUIRED | REQUIRED,SUPPORTS和MANDATORY和REQUIRES_NEW和NOT_SUPPORTED和NEVER和NESTED |
read-only | 设置当前事务是否只读 | false | true,false |
isolation | 事务隔离级别 | DEFAULT | READ_UNCOMMITTED和READ_COMMITTED和REPEATABLE_READ和SERIALIZABLE |
timeout | 设置事务的超时时间 | -1 | 默认由顶层事务系统决定 |
rollback-for | 内容为异常名,表示当抛出这些异常时事务回滚,可以用逗号分隔配置多个 | 无默认值 | 可以使用异常名称的片段进行匹配如ception等 |
no-rollback-for | 内容为异常名,表示当抛出这些异常时继续提交事务,可以用逗号分隔配置多个 | 无默认值 | 可以使用异常名称的片段进行匹配如ception等。 |