一、认识Spring【了解】
Spring概述
Spring是分层的全栈式的轻量级开发框架,以IOC和AOP为核心,官网是https://spring.io
Spring优势
-
便解耦,简化开发
Spring通过容器,将对象的创建从代码中剥离出来,交给Spring控制,避免直接编码造成模块之间的耦合度高,用户也不必自己编码处理对象的单例和多例控制,主要关注接口功能即可,不用关注具体使用哪个实现类和实现细节问题 -
AOP切面编程
AOP切面编程是程序设计的一种概念,Spring对该概念实现的比较好,通过切面编程我们可以在不修改原有代码的情况下实现功能的增加,通常用于 事务控制,日志记录,性能检测,权限控制等等 -
声明式事务
事务的控制可以托管给Spring,我们通过注解或者配置文件声明事务的处理方式即可,不用我们自己去编码处理 -
整合JUNIT,方便测试
spring整合JUNIT单元测试,对于项目的功能都可以进行轻松快速的测试,便于我们调试程序 -
方便整合各种优秀的框架
SSM> Spring+SpringMVC +MyBatis
SSH> Spring+Hibernate +Strust
各种其他框架 -
丰富的功能封装
spring对JAVAEE(JDBC ,JAVAMail,)都进行了一系列的封装,简化我们对于API的使用,提高程序的开发效率 -
规范的源码学习样本
spring的源码设计巧妙,结构清晰,大量使用了设计模式,是java代码规范编写的典范,也是高级程序员面试中经常会问到的源码
Spring的体系结构
- Data Access/Integration(数据访问/集成)
数据访问/集成层包括 JDBC、ORM、OXM、JMS 和 Transactions 模块,具体介绍如下。
JDBC 模块:提供了一个 JDBC 的抽象层,大幅度减少了在开发过程中对数据库操作的编码。
ORM 模块:对流行的对象关系映射 API,包括 JPA、JDO、Hibernate 和 iBatis 提供了的集成层。
OXM 模块:提供了一个支持对象/XML 映射的抽象层实现,如 JAXB、Castor、XMLBeans、JiBX 和 XStream。
JMS 模块:指 Java 消息服务,包含的功能为生产和消费的信息。
Transactions 事务模块:支持编程和声明式事务管理实现特殊接口类,并为所有的 POJO。
- Web 模块
Spring 的 Web 层包括 Web、Servlet、Struts 和 Portlet 组件,具体介绍如下。
Web 模块:提供了基本的 Web 开发集成特性,例如多文件上传功能、使用的 Servlet 监听器的 IoC 容器初始化以及 Web 应用上下文。
Servlet模块:包括 Spring 模型—视图—控制器(MVC)实现 Web 应用程序。
Struts 模块:包含支持类内的 Spring 应用程序,集成了经典的 Struts Web 层。
Portlet 模块:提供了在 Portlet 环境中使用 MVC实现,类似 Web-Servlet 模块的功能。
- Core Container(核心容器)
Spring 的核心容器是其他模块建立的基础,由 Beans 模块、Core 核心模块、Context 上下文模块和 Expression Language 表达式语言模块组成,具体介绍如下。
Beans 模块:提供了 BeanFactory,是工厂模式的经典实现,Spring 将管理对象称为 Bean。
Core 核心模块:提供了 Spring 框架的基本组成部分,包括 IoC 和 DI 功能。
Context 上下文模块:建立在核心和 Beans 模块的基础之上,它是访问定义和配置任何对象的媒介。ApplicationContext 接口是上下文模块的焦点。
Expression Language 模块:是运行时查询和操作对象图的强大的表达式语言。
- 其他模块
Spring的其他模块还有 AOP、Aspects、Instrumentation 以及 Test 模块,具体介绍如下。
AOP 模块:提供了面向切面编程实现,允许定义方法拦截器和切入点,将代码按照功能进行分离,以降低耦合性。
Aspects 模块:提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。
Instrumentation 模块:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
Test 模块:支持 Spring 组件,使用 JUnit 或 TestNG 框架的测试。
二、Spring IOC
什么是IOC
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做**依赖注入(Dependency Injection,简称DI**),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
简单的说就是,创建对象的权利,或者是控制的位置,由JAVA代码转移到spring容器,由spring的容器控制对象的创建,就是控制反转,spring创建对象时,会读取配置文件中的信息,然后使用反射给我们创建好对象之后在容器中存储起来,当我们需要某个对象时,通过id获取对象即可,不需要我们自己去new.
一句话:创建对象交给容器
项目搭建
Spring提供了两个方式实现Bean管理,XML和注解。接下来分别对两种方式进行实现,先搭建项目。
-
创建一个maven项目,导入依赖
<!--spring核心依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.9</version> </dependency>
-
准备一个要交给Spring容器管理的类
@Data @AllArgsConstructor @NoArgsConstructor public class Student { //基础类型 private Integer sid; private String name; private Integer age; //引用类型 private Date admissionDate;//入学日期 //集合类型 private String[] books; private List<String> bookList; private Set<String> bookSet; private Map<String,String> bookMap; private List<Book> bookList2; }
-
在resources中创建spring的配置文件spring-config
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
XML方式实现
创建对象
<!--
autowire:引用属性的自动注入
scope:值为singleton时,容器创建对象就创建
值为prototype时,获取对象时才创建
lazy-init:获取对象时才创建出对象
parent:可以继承父对象的属性值
depends-on:bean的依赖,先实例化依赖的对象
abstract:声名为抽象对象,不能实例化
init-method:指定bean的生命周期方法,初始化,在类中添加指定的方法
destroy-method:指定bean的生命周期方法,销毁,在类中添加指定的方法
-->
<bean id="studentParent" class="com.lzf.bean.Student"/>
<bean id="student" class="com.lzf.bean.Student"
autowire="byType"
scope="singleton"
lazy-init="true"
parent="studentParent"
depends-on="studentParent"
abstract="false"
init-method="initUser"
destroy-method="destroyUser"
>
</bean>
通过set方法给对象属性赋值。可以用p命名空间简化
<!--set注入-->
<bean id="student" class="com.lzf.bean.Student">
<property name="sid" value="1"/>
<property name="name" value="lzf"/>
<property name="age" value="18"/>
</bean>
通过有参构造给对象属性赋值。可以用c命令空间简化
<!--构造器注入-->
<bean id="student" class="com.lzf.bean.Student">
<constructor-arg name="sid" value="1"/>
<constructor-arg name="name" value="lzf"/>
<constructor-arg name="age" value="18"/>
</bean>
其他类型的属性注入
<!--1.引用类型注入-->
<bean id="admissionDate" class="java.util.Date"/>
<bean id="student" class="com.lzf.bean.Student">
<!--1.引用外部bean-->
<property name="admissionDate" ref="admissionDate"/>
</bean>
<!-- <bean id="student2" class="com.lzf.bean.Student">-->
<!-- <!–2.引用内部bean–>-->
<!-- <property name="admissionDate">-->
<!-- <bean class="java.util.Date"/>-->
<!-- </property>-->
<!-- </bean>-->
<!--3.引用类型自动注入
autowire 属性控制自动将容器中的对象注入到当前对象的属性上
byName 根据目标id值和属性值注入,要保证当前对象的属性值和目标对象的id值一致
byType 根据类型注入,要保证相同类型的目标对象在容器中只有一个实例
-->
<bean id="student" class="com.lzf.bean.Student" autowire="byType"/>
<!--2.集合类型注入-->
<bean id="student" class="com.lzf.bean.Student">
<!--数组-->
<property name="books">
<array>
<value>1</value>
<value>2</value>
</array>
</property>
<!--list-->
<property name="bookList">
<list>
<value>1</value>
<value>2</value>
</list>
</property>
<!--set-->
<property name="bookSet">
<set>
<value>1</value>
<value>2</value>
</set>
</property>
<!--map-->
<property name="bookMap">
<map>
<entry key="1" value=""></entry>
</map>
</property>
<!--list中放对象-->
<property name="bookList2">
<list>
<bean class="com.lzf.bean.Book"></bean>
</list>
</property>
<!--Properties-->
<property name="properties">
<props>
<prop key="key">1</prop>
</props>
</property>
</bean>
测试获取bean
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("spring-config.xml");
Student student = applicationContext.getBean("student", Student.class);
System.out.println(student);
工厂方式获取bean
实现FactoryBean,工厂模式创建bean
public class StudentFactory implements FactoryBean<Student> {
@Override
public Student getObject() throws Exception {
//返回一个对象
return new Student();
}
@Override
public Class<?> getObjectType() {
//返回对象类型
return Student.class;
}
@Override
public boolean isSingleton() {
//是否单例
return false;
}
}
配置文件中配置
<bean id="student" class="com.lzf.factory.StudentFactory"/>
获取Student对象
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("spring-config.xml");
Student student = applicationContext.getBean("student", Student.class);
System.out.println(student);
使用外部文件
spring容器可以读取.properties属性配置文件,可以将文件中的信息注入给bean
<!--1.添加约束context-->
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
<!--2.引入-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--3.获取-->
${jdbc_username}
注解方式实现
创建对象
- @Component 放在类上,用于标记,告诉spring当前类需要由容器实例化bean并放入容器中。 该注解有三个子注解
- @Controller 用于实例化controller层bean
- @Service 用于实例化service层bean
- @Repository 用于实例化持久层bean
当不确定是哪一层,就用Component,这几个注解互相混用其实也可以,但是不推荐。
第一步:spring-config.xml中添加包扫描
<!--扫描包,记得添加context约束-->
<context:component-scan base-package="com.lzf.bean"/>
第二步:在需要被Spring容器管理的类上添加注解
//value不指定时,默认就是类的首字母小写
@Component(value = "book")
public class Book {
}
第三步:测试
@Test
public void testGetBean(){
ApplicationContext context =new ClassPathXmlApplicationContext("spring-cofig.xml");
Book book = context.getBean("book", Book.class);
System.out.println(book);
}
依赖注入
-
@Autowired
根据属性数据类型自动装配
-
@Qualifier
根据属性名称注入依赖
-
@Resources
JDK提供,如果不配置name 那么就是根据类型注入,配置name,就是根据名称注入。
-
@Value
注入普通数据类型(8+String),可以使用${}这种表达式获取系统的变量值,或者是.properties属性配置文件中的值。
三、Spring AOP
什么是AOP
AOP切面编程一般可以帮助我们在不修改现有代码的情况下,对程序的功能进行拓展,往往用于实现 日志处理,权限控制,性能检测,事务控制等。AOP实现的原理就是动态代理,在有接口的情况下,使用JDK动态代理,在没有接口的情况下使用cglib动态代理
AOP中的术语辨析
- 连接点 Joint point
类里面那些可以被增强的方法,这些方法称之为连接点
表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point
- 切入点 Pointcut
实际被增强的方法,称之为切入点
表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方
- 通知 Advice
实际增强的逻辑部分称为通知 (增加的功能)
Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
通知类型: 1 前置通知 2 后置通知 3 环绕通知 4 异常通知 5 最终通知
- 目标对象 Target
被增强功能的对象(被代理的对象)
织入 Advice 的目标对象
- 切面Aspect
表现为功能相关的一些advice方法放在一起声明成的一个Java类
Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
- 织入 Weaving
创建代理对象并实现功能增强的声明并运行过程
将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
切入点表达式
通过一个表达式来确定AOP要增强的是哪个或者那些方法
语法结构:execution([权限修饰符][返回值类型][类的全路径名][方法名](参数 列表) )
例子
execution(* com.lzf.dao.UserDaoImpl.add(..))//指定切点为UserDaoImpl.add方法
execution(* com.lzf.dao.UserDaoImpl.*(..)) //指定切点为UserDaoImpl.所有的方法
execution(* com.lzf.dao.*.*(..)) //指定切点为dao包下所有的类中的所有的方法
execution(* com.lzf.dao.*.add(..)) // 指定切点为dao包下所有的类中的add的方法
execution(* com.lzf.dao.*.add*(..)) // 指定切点为dao包下所有的类中的add开头的方法
注解实现
导入依赖
<!--切面-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.5</version>
</dependency>
在spring-config.xml中开启注解扫描和AOP切面编程自动生成代理对象
<!--扫描包,记得添加context约束-->
<context:component-scan base-package="com.lzf"/>
<!-- 自动生成代理对象,记得添加aop约束-->
<aop:aspectj-autoproxy/>
目标对象
@Repository
public class StudentDao {
public void insert(){
System.out.println("insert student...");
}
}
定义切面类
@Component
@Aspect
public class DaoAspect {
@Before("execution(* com.lzf.dao.StudentDao.*(..))")
public void methodBefore(){
System.out.println("before");
}
}
测试
public static void main(String[] args) {
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("spring-config.xml");
StudentDao dao = applicationContext.getBean("studentDao", StudentDao.class);
dao.insert();
}
五个通知
//前置通知(目标方法前执行)
@Before("execution()")
public void methodBefore(JoinPoint joinPoint){
//获取方法参数
Object[] args = joinPoint.getArgs();
}
//后置通知(目标方法后执行,无论是否出现异常都会执行)
@After("execution(* com.lzf.dao.impl.*.add*(..))")
public void methodAfter(JoinPoint joinPoint){
}
//返回通知(在目标方法后执行,出现异常不执行。可以获取目标方法的返回值)
@AfterReturning(value = "execution(* com.lzf.dao.impl.*.add*(..))",returning = "res")
public void methodReturning(JoinPoint joinPoint,Object res){
System.out.println(res);
}
//异常通知(目标方法异常时执行)
@AfterThrowing(value = "execution(* com.lzf.dao.impl.*.add*(..))",throwing = "e")
public void methodAfterThrowing(JoinPoint joinPoint,Exception e){
System.out.println(e.getMessage());
}
//环绕通知(目标方法之前和之后进行增强)
@Around("execution(* com.lzf.dao.impl.*.add*(..))")
public Object methodAround(ProceedingJoinPoint point) throws Throwable {
System.out.println("Before");
Object res = point.proceed();//切点方法执行
System.out.println("After");
return res;
}
有多个增强类对同一个方法进行增强,通过@Order注解设置增强类优先级
数字越小,优先级越高
数字越小,其代理位置越靠近注入位置
XML实现【了解】
1、创建两个类,增强类和被增强类,创建方法
见之前的代码
2、在spring配置文件中创建两个类对象
<!--创建对象-->
<bean id="studentDao" class="com.lzf.studentDao"></bean>
<bean id="daoAspect" class="com.lzf.aspect.DaoAspect"></bean>
3、在spring配置文件中配置切入点
<!--配置aop增强-->
<aop:config>
<!--切入点-->
<aop:pointcut id="pointCutAdd" expression="execution(* com.lzf.dao.StudentDao.*(..))"/>
<!--配置切面-->
<aop:aspect ref="daoAspect">
<!--增强作用在具体的方法上-->
<aop:before method="methodBefore" pointcut-ref="pointCutAdd"/>
<aop:after method="methodAfter" pointcut-ref="pointCutAdd"/>
<aop:around method="methodAround" pointcut-ref="pointCutAdd"/>
<aop:after-returning method="methodAfterReturning" pointcut-ref="pointCutAdd" returning="res"/>
<aop:after-throwing method="methodAfterThrowing" pointcut-ref="pointCutAdd" throwing="ex"/>
</aop:aspect>
</aop:config>
四、Spring声明式事务
事务回顾
1.事务概念
事务(Transaction)指的是一个操作序列,该操作序列中的多个操作要么都做,要么都不做,是一个不可分割的工作单位,是数据库环境中的逻辑工作单位,由DBMS中的事务管理子系统负责事务的处理。
目前常用的存储引擎有InnoDB(MySQL5.5以后默认的存储引擎)和MyISAM(MySQL5.5之前默认的存储引擎),其中InnoDB支持事务处理机制,而MyISAM不支持。
2.事务的特性
事务处理可以确保除非事务性序列内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的序列,可以简化错误恢复并使应用程序更加可靠。
但并不是所有的操作序列都可以称为事务,这是因为一个操作序列要成为事务,必须满足事务的原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。这四个特性简称为ACID特性。
1)原子性
原子是自然界最小的颗粒,具有不可再分的特性。事务中的所有操作可以看做一个原子,事务是应用中不可再分的最小的逻辑执行体。
使用事务对数据进行修改的操作序列,要么全部执行,要么全不执行。通常,某个事务中的操作都具有共同的目标,并且是相互依赖的。如果数据库系统只执行这些操作中的一部分,则可能会破坏事务的总体目标,而原子性消除了系统只处理部分操作的可能性。
2)一致性
一致性是指事务执行的结果必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库中只包含事务成功提交的结果时,数据库处于一致性状态。一致性是通过原子性来保证的。
例如:在转账时,只有保证转出和转入的金额一致才能构成事务。也就是说事务发生前和发生后,数据的总额依然匹配。
3)隔离性
隔离性是指各个事务的执行互不干扰,任意一个事务的内部操作对其他并发的事务,都是隔离的。也就是说:并发执行的事务之间既不能看到对方的中间状态,也不能相互影响。
例如:在转账时,只有当A账户中的转出和B账户中转入操作都执行成功后才能看到A账户中的金额减少以及B账户中的金额增多。并且其他的事务对于转账操作的事务是不能产生任何影响的。
4)持久性
持久性指事务一旦提交,对数据所做的任何改变,都要记录到永久存储器中,通常是保存进物理数据库,即使数据库出现故障,提交的数据也应该能够恢复。但如果是由于外部原因导致的数据库故障,如硬盘被损坏,那么之前提交的数据则有可能会丢失。
3.事务的并发问题
脏读(Dirty read)
当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
不可重复读(Unrepeatableread)
:指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
幻读(Phantom read)
幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
不可重复度和幻读区别:不可重复读的重点是修改,幻读的重点在于新增或者删除。
4.事务的隔离级别
事务的隔离级别用于决定如何控制并发用户读写数据的操作。数据库是允许多用户并发访问的,如果多个用户同时开启事务并对同一数据进行读写操作的话,有可能会出现脏读、不可重复读和幻读问题,所以MySQL中提供了四种隔离级别来解决上述问题。
事务的隔离级别从低到高依次为READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ以及SERIALIZABLE,隔离级别越低,越能支持高并发的数据库操作。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED | × | × | × |
READ COMMITTED | √ | × | × |
REPEATABLE READ | √ | √ | × |
REPEATABLE READ | √ | √ | √ |
注解实现
添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.18</version>
</dependency>
spring-config.xml中添加
<!--包扫描-->
<context:component-scan base-package="com.lzf"/>
<!--引入外部文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc_driver}"/>
<property name="url" value="${jdbc_url}"/>
<property name="username" 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>
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>
在Service层中添加事务的注解
//@Transactional //加在类上,代表类中的所有方法都添加了事务控制
public class AccountServiceImpl implements AccountService {
@Transactional// 放在方法上,就是仅仅对当前方法增加了事务控制
public int transMoney(int from, int to, int money) {
}
}
XML实现【了解】
spring-config.xml中,通过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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
">
<context:component-scan base-package="com.lzf"/>
<!--读取jdbc配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置德鲁伊连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc_username}"></property>
<property name="password" value="${jdbc_password}"></property>
<property name="url" value="${jdbc_url}"></property>
<property name="driverClassName" value="${jdbc_driver}"></property>
</bean>
<!--配置一个事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--将数据源注入事务管理器-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置通知-->
<tx:advice id="txAdvice">
<!--配置事务参数-->
<tx:attributes>
<tx:method name="transMoney" isolation="DEFAULT" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置AOP-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt" expression="execution(* com.lzf.service.AccountService.transMoney(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
</aop:config>
</beans>
事务的传播行为
多事务方法之间调用,事务是如何管理的
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择(默认)。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
:advisor>
</aop:config>
### 事务的传播行为
多事务方法之间调用,事务是如何管理的
| 事务传播行为类型 | 说明 |
| ------------------------- | ------------------------------------------------------------ |
| PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择(默认)。 |
| PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
| PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
| PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
| PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
| PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
| PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |