1.什么是spring?
Spring是 SE/EE 开发的一站式框架。(一站式框架:有EE开发的每一层解决方案。)
- WEB层 :SpringMVC
- Service层 :Spring的Bean管理,Spring声明式事务
- DAO层 :Spring的Jdbc模板,Spring的ORM模块
Spring的核心是控制反转(IOC)和面向切面(AOP)。
2. 什么是控制反转(IOC)和依赖注入(DI)?
什么是控制反转(IOC)?
将对象的创建权反转给(交给)Spring。就是将原本在程序中手动创建对象的控制权,交由Spring框架管理。
什么是依赖注入(DI)?
是指在Spring创建对象的过程中,将这个对象所依赖的属性注入(设置)进来。
3.Spring的工厂类
BeanFactory(老版本的工厂类):调用getBean的时候,才会生成类的实例
ApplicationContext (新版本的工厂类):加载配置文件的时候,就会将Spring管理的类都实例化。(ApplicationContext 接口继承BeanFactory接口)
ApplicationContext有两个实现类:
ClassPathXmlApplicationContext :加载类路径下的配置文件
FileSystemXmlApplicationContext :加载文件系统下的配置文件
代码:
在ApplicationContext.xml中配置bean:
<beans> <!-- Spring的入门的配置 --> <bean name="userDAO" class="com.ztt.spring.demo1.UserDAOImpl" > <property name="name" value="李东"/> </bean> </beans>
【注】:<bean>标签的 id 和 name 的配置:
- id :使用了约束中的唯一约束。里面不能出现特殊字符。
- name :没有使用约束中的唯一约束(理论上可以出现重复的,但是实际开发不能出现的)。里面可以出现特殊字符。
【注】:Bean的作用范围的配置 (scope:Bean的作用范围)
- singleton :默认scope的值是singleton,即产生的对象是单例的,singleton对象会在容器启动时被创建。
- prototype :多例模式。spring容器启动的时候并不会创建对象,而是在调用getBean()方法时才会创建对象。
- request :应用在web项目中,Spring创建这个类以后,将这个类存入到request范围中。
- session :应用在web项目中,Spring创建这个类以后,将这个类存入到session范围中。
- globalsession :应用在web项目中,必须在porlet环境下使用。但是如果没有这种环境,相对于session。
测试:
public void demo(){ // 创建Spring的工厂 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); //获取bean对象 UserDAO userDAO = (UserDAO) applicationContext.getBean("userDAO"); userDAO.save(); }
4.请介绍一下Spring框架中Bean的生命周期和作用域
(1)bean定义
在配置文件里面用<bean></bean>来进行定义。
(2)bean初始化
- 在配置文件中通过指定init-method属性来完成
(3)bean调用
有三种方式可以得到bean实例,并进行调用
(4)bean销毁
使用配置文件指定的destroy-method属性来完成。
需要注意的是
1)destroy-method 只对 scope="singleton" (单例)有效;
2)销毁方法,必须关闭ApplicationContext对象(手动调用),才会被调用。
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); applicationContext.close();
## 作用域
singleton
单例模式。当一个bean的作用域为singleton, 那么Spring IoC容器中只会存在一个共享的bean实例。并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。
prototype
多例模式。每次通过容器的geuBean方法获取prototype定义的beans时都会产生一个新的bean实例。
根据经验,对所有有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用 singleton作用域。
request
对于每次HTTP请求中,使用request定义的bean都会产生一个新的实例。该作用 域仅在基于web的Spring ApplicationContext情形下有效。
session
对于每次HTTP Session,使用session定义的都将产生一个新的实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
global session
每个全局的HTTP Session,使用session定义的bean都将产生一个新的实例。该作用域仅在基于 web的Spring ApplicationContext情形下有效
5.IOC
5.1 spring配置bean 实例化有几种方式?(3种)
(1)无参构造方法方式
编写类:
public class Bean1 { //无参构造方法 public Bean1() { 。。。。。。 } }
在 applicationContext.xml 中编写配置:
<!-- 无参数构造方法 --> <bean id="bean1" class="com.ztt.spring.Bean1"></bean>
(2)使用静态工厂方法实例化
编写类Bean2的静态工厂
public class Bean2Factory { public static Bean2 createBean2(){ return new Bean2(); } }
在 applicationContext.xml 中编写配置:
<!-- 静态工厂实例化 --> <bean id="bean2" class="com.ztt.spring.Bean2Factory" factory-method="createBean2"/>
(3)使用实例工厂方法实例化
编写Bean3的实例工厂:
public class Bean3Factory { public Bean3 createBean3(){ return new Bean3(); } }
在 applicationContext.xml 中编写配置:
<!-- 实例工厂实例化 --> <bean id="bean3Factory" class="com.ztt.spring.Bean3Factory"></bean> <bean id="bean3" factory-bean="bean3Factory" factory-method="createBean3"></bean>
5.2 Java中实现依赖注入的三种方式?Spring的属性注入方式有哪几种(2种)?
Java中实现依赖注入有3种方式:构造方法方式、set方法方式、接口注入方式。
spring支持构造方法的属性注入和set方法的属性注入:
(1)构造方法的属性注入 (通过 <constructor-arg> 完成注入)
<!-- 构造方法的方式 --> <bean id="car" class="com.ztt.spring.Car"> <constructor-argname="name" value="宝马"/> <constructor-arg name="price" value="800000"/> </bean>
(2)set方法的属性注入 (通过<property> 完成注入)
<!-- set方法的方式 --> <bean id="car2" class="com.ztt.spring.Car2"> <property name="name" value="奔驰"/> <property name="price" value="1000000"/> </bean>
【注】如果注入属性不是普通类型,而是类的话,不用value,用ref。 (value:设置普通类型的值,ref:设置其他的类的id或name)
<!-- set方法注入对象类型的属性 --> <bean id="employee" class="com.ztt.spring.Employee"> <!-- value:设置普通类型的值,ref:设置其他的类的id或name --> <property name="name" value="涛哥"/> <property name="car2" ref="car2"/> </bean>
(3)P名称空间的属性注入(Spring2.5以后)
通过引入p名称空间完成属性的注入:
写法:
普通属性 p:属性名=”值”
对象属性 p:属性名-ref=”值”
首先引入P名称空间:
使用p名称空间:
<!-- p名称空间的方式 --> <bean id="car2" class="com.ztt.spring.Car2" p:name="奇瑞QQ" p:price="30000"></bean> <bean id="employee" class="com.ztt.spring.Employee" p:name="王东" p:car2-ref="car2"></bean>
5.3 Spring基于注解的配置
5.3.1 入门演示:
要想在类上使用注解,首先需要在Spring配置文件中applicationContext.xml中配置组件扫描。
<!-- 使用IOC的注解开发,配置组件扫描(哪些包下的类使用IOC注解) --> <!-- 注意:扫描是为了扫描类上的注解 --> <context:component-scan base-package="com.ztt.spring"></context:component-scan>
然后在类上添加注解:
利用注解方式设置属性值:
- 属性如果有set方法,需要将属性注入的注解加到set方法上;
- 属性如果没有set方法,需要将属性注入的注解加到该属性上。
//@Component("UserDAO") //相当于<bean id="UserDAO" class="com.ztt.spring.dao.impl.UserDAOImpl"/> @Repository("UserDAO") //因为修饰DAO层的类,所以使用@Component的衍生类@Repository,代替@Component public class UserDAOImpl implements UserDAO { //利用注解设置属性的值:如果属性有set方法,将注解加到set方法上;如果没有set方法,将注解加到该属性上 @Value("zhang") private String name; /*@Value("zhang") public void setName(String name) { this.name = name; }*/ @Override public void save() { System.out.println("UserDao的save方法执行了。。。" + name); } }
测试:
@Test //使用spring的注解方式 public void test1(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); UserDAO userDAO = (UserDAO) applicationContext.getBean("UserDAO"); userDAO.save(); }
5.3.2 Spring的IOC的注解:
1、@Component :组件。 修饰一个类,将这个类交给Spring管理。
这个注解有3个衍生注解(功能类似):
@Controller :修饰web层的类
@Service :修饰service层的类
@Repository:修饰DAO层的类
2、属性注入的注解
普通属性:
@Value:设置普通属性的值
对象类型属性:
@Autowired:设置对象类型的属性的值。但是按照类型完成属性注入。
我们习惯按照名称完成属性注入:必须让@Autowired和@Qualifier一起使用完成按照名称属性注入,
@Resource:完成对象类型的属性的注入,按照名称完成属性注入。
@Service("UserService") //相当于<bean id="UserService" class="com.ztt.spring.demo1.UserServiceImpl"/> public class UserServiceImpl implements UserService { //注入DAO //*@Autowired @Qualifier(value="UserDAO") //联合使用@Resource(name="UserDAO") private UserDAO userDAO; @Override public void save() { System.out.println("UserService的save方法执行了。。。"); userDAO.save(); } }
3、Bean的其他注解
生命周期相关的注解:
@PostConstruct :初始化方法
@PreDestroy :销毁方法
Bean作用范围的注解:
@Scope :作用范围
singleton :默认单例
prototype :多例
request
session
globalsession
@Service("CustomerService") //<bean id="CustomerService" init-method="init" destroy-method="destroy"/> @Scope("prototype") public class CustomerService { @PostConstruct //相当于init-method="init" public void init(){ System.out.println("CustomerService被初始化了"); } public void save(){ System.out.println("Service的save方法执行了。。。"); } @PreDestroy //相当于destroy-method="destroy" public void destroy(){ System.out.println("CustomerService被销毁了"); } }
【注】有时会用xml与注解整合开发,即用xml管理Bean,注解完成属性注入。
这时类上不再加注解了,只在属性上加注解。applicationContext.xml里面不再配置组件扫描而改为<context:annotation-config/>:
<!-- xml中只管理Bean,属性注入交给注解 -->
<!-- 上面的扫描context:component-scan可以关闭,只打开属性上的注解 --> <!-- 在没有扫描的情况下,使用属性注入的注解 --> <context:annotation-config/>
<bean id="productService" class="com.ztt.spring.demo3.ProductService"/> <bean id="productDAO" class="com.ztt.spring.demo3.ProductDAO"/>
6. AOP
6.1 什么是AOP?
面向切面编程。AOP是OOP(面向对象编程)的扩展和延伸,解决OOP开发遇到的问题。当我们需要为分散的对象引入公共的行为时,OOP则显得无能为力,她会引入大量重复代码。也就是说,OOP允许定义从上到下的关系,但不适合定义从左到右的关系。
AOP利用一种成为“横切”的技术,将那些影响了多个类的公共行为封装到一个可重用模块,将其命名为“Aspect”(方面),便于减少系统的重复代码,降低模块间的耦合度。
使用“横切”技术,AOP将系统分为两部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,横切关注点的一个特点是疆场发生在核心关注点的多处,并且各处都基本相似。比如权限认证、日志、事务处理。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
AOP解决的问题往往可以用代理模式来解决。代理模式分静态代理和动态代理,静态代理的代理类需要手工编写,在编译器生成;而动态代理的代理类是在运行时才生成的,动态代理有JDK和cglib两种实现方式。AOP的实现原理是动态代理。
- JDK动态代理:只能对实现了接口的类产生代理。
- cglib动态代理:对没有实现接口的类产生代理对象。生成子类对象。
Spring底层,如果实现了接口,自动用JDK动态代理;如果没有实现接口,自动用cglib动态代理。
6.2 AOP的相关术语:
Joinpoint(连接点) :指那些可以被拦截到的点(方法)。 在Spring 中,这些点指的是方法,应为spring只支持方法类型的连接点。
Pointcut(切入点) :指我们要对哪些 Jointpoint 进行拦截的定义。
Advice(通知/增强) :指拦截到 Jointpoint 之后要做的动作就是Advice。通知分为前置通知、后置通知、异常通知、最终通知、环绕通知。
- 前置通知 :在目标方法执行之前执行的通知。
- 后置通知 :在目标方法执行之后执行的通知。如果方法没有正常返回(例如抛出异常),则后置通知不会执行。
- 环绕通知 :在目标方法执行之前和之后都可以执行的通知。 【注】1)环绕通知需要返回返回值,否则真正调用者将拿不到返回值,只能得到一个nul;2)只有环绕通知可以接收ProceedingJoinPoint,而其他通知只能接收JoinPoint。
- 异常通知:在目标方法抛出异常时执行的通知。
- 最终通知:是在目标方法执行之后执行的通知。无论代码是否有异常都会执行。
Introduction(引介):是一种特殊的通知,在不修改类代码的前提下,Introduction 可以在运行期为类动态的添加一些方法或Field。。
Target(目标对象) : 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(advised) 对象。
Weaving(织入) :把通知(Advice)应用到目标对象来创建新的代理对象的过程。 Spring采用动态代理织入,而AspectJ采用编译器织入和类装载期织入。
Proxy(代理) :一个类被AOP织入增强后,就产生一个结果代理类。
Aspect(切面) :切入点和通知的结合。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @Aspect 注解(@AspectJ风格)来实现。
6.3 Spring的AOP基于xml开发
功能:我们想在类 ProductDAOImpl 中的 save() 方法前加入权限验证
(1)目标对象(被增强的对象)
public class ProductDAOImpl implements ProductDAO { @Override public void save() { System.out.println("保存商品。。。"); } @Override public void update() { System.out.println("修改商品。。。"); } @Override public void find() { System.out.println("查找商品。。。"); } @Override public String delete() { System.out.println("删除商品。。。"); return "zhangsan"; } }
(2)切面类MyAspectXML:
public class MyAspectXML { /** * 前置通知 */ public void checkPre(JoinPoint joinPoint){ //传入此参数可以打印切入点信息 System.out.println("权限校验=======" + joinPoint); } /** * 后置通知 */ public void writeLog(Object result){ //传入此参数可以打印切入点信息 System.out.println("日志记录=======" + result); } /** * 性能监控 * @throws Throwable */ public Object around(ProceedingJoinPoint joinPoint) throws Throwable{ //传入此参数可以打印切入点信息 System.out.println("环绕前通知======="); //相当于执行目标程序,并得到一个返回值 Object object = joinPoint.proceed(); System.out.println("环绕后通知======="); return object; } /** * 异常抛出通知 */ public void afterThrowing(Throwable ex){ //传参的目的是获得异常抛出信息 System.out.println("异常抛出通知========" + ex); } /** * 最终通知 */ public void after(){ //传参的目的是获得异常抛出信息 System.out.println("最终通知========"); } }
(3)applicationContext.xml:
<!-- 配置目标对象:被增强的对象 --> <bean id="ProductDAO" class="com.ztt.spring.demo1.ProductDAOImpl"/> <!-- 将切面类交给Spring管理 --> <bean id="MyAspectXML" class="com.ztt.spring.demo1.MyAspectXML"/> <!-- 通过AOP的配置完成对目标类产生代理 --> <aop:config> <!-- 切入点,通过表达式配置哪些类的哪些方法需要增强 --> <aop:pointcutexpression="execution(* com.ztt.spring.demo1.ProductDAOImpl.save(..))" id="pointCut1"/> <aop:pointcut expression="execution(* com.ztt.spring.demo1.ProductDAOImpl.delete(..))" id="pointCut2"/> <aop:pointcut expression="execution(* com.ztt.spring.demo1.ProductDAOImpl.update(..))" id="pointCut3"/> <aop:pointcut expression="execution(* com.ztt.spring.demo1.ProductDAOImpl.find(..))" id="pointCut4"/> <!-- 配置切面 --> <aop:aspectref="MyAspectXML"> <!-- 前置通知=========== --> <aop:beforemethod="checkPre" pointcut-ref="pointCut1"/> <!-- 后置通知=========== --> <aop:after-returningmethod="writeLog" pointcut-ref="pointCut2" returning="result"/> <!-- 环绕通知=========== --> <aop:around method="around" pointcut-ref="pointCut3"/> <!-- 异常抛出通知========= --> <aop:after-throwingmethod="afterThrowing" pointcut-ref="pointCut4" throwing="ex"/> <!-- 最终通知 --> <aop:aftermethod="after" pointcut-ref="pointCut4"/> </aop:aspect> </aop:config>
总结:
1)使用<aop:config>进行配置:proxy-target-class=“true”时使用cglib代理,如果不声明,Spring会自动选择JDK代理。
2)<aop:pointcut>切入点,配置哪些类的哪些方法需要增强。
3)<aop:advisor>特殊的切面,只有一个通知和一个切入点。
4)切入点表达式:基于execution 函数完成。
语法:[访问修饰符] 方法返回值 包名.类名.方法名(参数)
【注】访问修饰符可不写,*表示任意返回值/包名/类名等,..表示任意返回值
例 : public void com.ztt.spring.ProductDAOImpl.save(..)
例 : * *.*.*.*DAO.save(..)
5)Spring AOP的具体加载步骤:
1. 当 spring 容器启动的时候,加载了 spring 的配置文件。
2. 为配置文件中的所有bean创建对象。
3. Spring容器会解析aop:config的配置。
解析切入点表达式,用切入点表达式和纳入Spring容器中的bean做匹配,如果匹配成功,则会为该bean创建代理对象,代理对象的方法=目标方法+通知;如果匹配不成功,则不会创建代理对象。
4. 在客户端利用context.getBean()获取对象时,如果该对象有代理对象,则返回代理对象,否则返回目标对象。
6.3 Spring的AOP基于注解开发
配置文件中只需要声明bean,不用进行AOP声明。配置文件中需要配置扫描注解类。配置文件中还需要配置AOP注解识别。
(1)目标对象还是上面的ProductDAOImpl。
(2)切面类MyAspectAnno:
@Aspect public class MyAspectAnno { @Before(value = "MyAspectAnno.pointcut_save()") public void checkPre(){ System.out.println("前置权限校验======="); } @AfterReturning(value="MyAspectAnno.pointcut_delete()",returning="result") public void writeLog(Object result){ //传入此参数可以打印切入点信息 System.out.println("后置日志记录=======" + result); } @Around(value="MyAspectAnno.pointcut_update()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable{ //传入此参数可以打印切入点信息 System.out.println("环绕前通知======="); //相当于执行目标程序,并得到一个返回值 Object object = joinPoint.proceed(); System.out.println("环绕后通知======="); return object; } @AfterThrowing(value="MyAspectAnno.pointcut_find()",throwing="ex") public void afterThrowing(Throwable ex){ //传参的目的是获得异常抛出信息 System.out.println("异常抛出通知========" + ex); } @After(value="MyAspectAnno.pointcut_find()") public void after(){ System.out.println("最终通知========"); } //切入点注解: @Pointcut(value="execution(* com.ztt.spring.demo1.ProductDAO.find(..))") private void pointcut_find(){} @Pointcut(value="execution(* com.ztt.spring.demo1.ProductDAO.update(..))") private void pointcut_update(){} @Pointcut(value="execution(* com.ztt.spring.demo1.ProductDAO.delete(..))") private void pointcut_delete(){} @Pointcut(value="execution(* com.ztt.spring.demo1.ProductDAO.save(..))") private void pointcut_save(){} }
(2)applicationContext.xml: 配置文件中只需配置AOP注解识别<aop:aspectj-autoproxy/> 和扫描类。
<!-- 开启AOP注解的自动代理 --> <aop:aspectj-autoproxy/> <!-- 配置目标类 --> <bean id="ProductDAO" class="com.ztt.spring.demo1.ProductDAO"></bean> <!-- 配置切面类 --> <bean id="MyAspectAnno" class="com.ztt.spring.demo1.MyAspectAnno"></bean>
7. Spring的事务管理
7.1 事务的概念和特性
什么是事务?
事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态。
比如向数据库插入多条数据,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作。这样可以防止出现脏数据,防止数据库数据出现问题。
事务的四大特征?
原子性:事务不可分割。事务的原子性确保动作要么全部完成,要么完全不起作用。
一致性:事务执行前后数据完整性保持一致。一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。
隔离性:一个事务的执行不应该受到其他事务的干扰。(可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。)
持久性:一旦事务结束,数据就持久化到数据库。(一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。)
如果不考虑隔离性引发安全性问题?
1、 读问题
- 脏读 :一个事务读到另一个事务未提交的数据。
- 不可重复读 :一个事务读到另一个事务已经提交的 update 的数据,导致一个事务中多次查询结果不一致。
- 虚读、幻读 :一个事务读到另一个事务已经提交的 insert 的数据,导致一个事务中多次查询结果不一致。
2、写问题
- 丢失更新
解决读问题:设置事务的隔离级别:
- Read uncommitted :未提交读,任何读问题解决不了。
- Read committed :已提交读,解决脏读,但是不可重复读和虚读有可能发生。
- Repeatable read :重复读,解决脏读和不可重复读,但是虚读有可能发生。
- Serializable :解决所有读问题
7.2 介绍Spring的事务管理
Spring的事务管理的API:1、PlatformTransactionManager:平台事务管理器
平台事务管理器:接口,是Spring用于管理事务的真正的对象。
DataSourceTransactionManager :底层使用JDBC管理事务
HibernateTransactionManager :底层使用Hibernate管理事务
2、TransactionDefinition :事务定义信息
事务定义:用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读
3、TransactionStatus:事务的状态
事务状态:用于记录在事务管理过程中,事务的状态的对象。
事务管理的API的关系:
Spring进行事务管理的时候,首先平台事务管理器(PlatformTransactionManager)根据事务定义信息(TransactionDefinition)进行事务的管理,在事务管理过程中,产生各种状态,将这些状态的信息记录到事务状态(TranscationStatus)的对象中。
l Spring中提供了七种事务的传播行为:
n 保证多个操作在同一个事务中
u
PROPAGATION_REQUIRED
:默认值,如果A中有事务,使用A中的事务,如果A没有,创建一个新的事务,将操作包含进来
u
PROPAGATION_SUPPORTS
:支持事务,如果A中有事务,使用A中的事务。如果A没有事务,不使用事务。
u
PROPAGATION_MANDATORY
:如果A中有事务,使用A中的事务。如果A没有事务,抛出异常。
n 保证多个操作不在同一个事务中
u
PROPAGATION_REQUIRES_NEW
:如果A中有事务,将A的事务挂起(暂停),创建新事务,只包含自身操作。如果A中没有事务,创建一个新事务,包含自身操作。
u
PROPAGATION_NOT_SUPPORTED
:如果A中有事务,将A的事务挂起。不使用事务管理。
u
PROPAGATION_NEVER
:如果A中有事务,报异常。
n 嵌套式事务
u
PROPAGATION_NESTED
:嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置一个保存点,执行B中的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到保存点。
spring的事务管理机制,一般是使用TransactionMananger进行管理,可以通过Spring的注入来完成此功能。spring提供了几个关于事务处理的类:
TransactionDefinition :事务属性定义
TranscationStatus :代表了当前的事务,可以提交,回滚。
PlatformTransactionManager :这个是spring提供的用于管理事务的基础接口,其下有一个实现的抽象类 AbstractPlatformTransactionManager,我们使用的事务管理类例如 DataSourceTransactionManager等都是这个类的子类。
一般事务定义步骤:
TransactionDefinition td = new TransactionDefinition(); TransactionStatus ts = transactionManager.getTransaction(td); try{ //do something transactionManager.commit(ts); }catch(Exception e){ transactionManager.rollback(ts); }
7.2 Spring的两类事务管理(编程式、声明式)
所谓编程式事务指的是通过编码方式实现事务,允许用户在代码中精确定义事务的边界。即类似于JDBC编程实现事务管理。管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
声明式事务管理是建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
7.2.1 编程式事务 (需手动写代码,了解)
(1)applicationContext.xml
<!-- 配置Service============= --> <bean id="accountService" class="com.ztt.tx.demo1.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> <!-- 第三步:在业务层注入事务管理的模板 --> <property name="trsactionTemplate" ref="transactionTemplate"/> </bean> <!-- 配置DAO================= --> <bean id="accountDao" class="com.ztt.tx.demo1.AccountDaoImpl"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置连接池和JDBC的模板 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 配置C3P0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- 第一步:配置平台事务管理器============================= --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入连接池,获得连接 --> <property name="dataSource" ref="dataSource"/> </bean> <!-- 第二步:配置事务管理的模板 --> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"/> </bean>
(2)编写事务管理的代码:
public class AccountServiceImpl implements AccountService { // 注入DAO: private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } // 注入事务管理的模板 private TransactionTemplate trsactionTemplate; public void setTrsactionTemplate(TransactionTemplate trsactionTemplate) { this.trsactionTemplate = trsactionTemplate; } @Override /** * from:转出账号 * to:转入账号 * money:转账金额 */ public void transfer(final String from, final String to, final Double money) { //编写事务管理代码 trsactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { accountDao.outMoney(from, money); accountDao.inMoney(to, money); } }); } }
7.2.2 声明式事务管理 (通过配置实现,AOP)
(1)xml方式的声明式事务管理
applicationContext.xml
<!-- 第一步:配置事务管理器 --> <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="save*" propagation="REQUIRED" isolation="DEFAULT"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="delete*" propagation="REQUIRED"/> <tx:method name="find*" read-only="true"/> --> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 第三步:AOP的配置 --> <aop:config> <aop:pointcut expression="execution(* com.ztt.tx.demo2.AccountServiceImpl.*(..)))" id="pointcut1"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/> </aop:config>
(2)注解方式的声明式事务管理
applicationContext.xml:
<!-- 第一步:配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 第二步:开启注解事务 --> <tx:annotation-driventransaction-manager="transactionManager"/>
第三步:在业务层添加注解:
@Transactional public class AccountServiceImpl implements AccountService { // 注入DAO: private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override /** * from:转出账号 * to:转入账号 * money:转账金额 */ public void transfer(String from, String to, Double money) { accountDao.outMoney(from, money); accountDao.inMoney(to, money); }