spring官网 http://spring.io/
目录
1.1.IoC 控制反转: 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部
1.3.DI 依赖注入: 在容器中建立bean与bean之间的依赖关系的整个过程
1.Spring核心概念
1.1.IoC 控制反转: 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部
1.spring提供了一个容器,成为IoC容器,用来充当IoC思想中的外部
2.IoC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC容器中称为Bean
1.2.Bean:被管理的对象称为Bean
1.3.DI 依赖注入: 在容器中建立bean与bean之间的依赖关系的整个过程
1.4.目标:充分解耦
1.使用IoC容器管理bean
2.在IoC容器内将有依赖关系的bean进行关系绑定
2.IoC入门案例
maven模块结构(此时的serviceImpl对象中,new了daoImpl对象)
可用此方法加载文件的绝对路径(了解)
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\workspace\\spring\\spring_10_container\\src\\main\\resources\\applicationContext.xml");
另外两种获取bean的方式(了解)
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
BookDao bookDao = ctx.getBean(BookDao.class);
导入坐标到pom.xml文件
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- 测试jar包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
applicationContext.xml配置文件(注:id不能重复,具有唯一性)
<?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">
<!--bean标签标示配置bean
id属性标示给bean起名字
class属性表示给bean定义类型-->
<bean id="bookDao" class="com.jinruidai.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.jinruidai.service.impl.BookServiceImpl"/>
</beans>
3.DI入门案例
3.1.基于IoC管理bean
3.2.不能保留new形式创建的dao对象
3.3.提供方法让dao对象进入到service中
3.4.配置实现
<?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">
<!--1.导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
<!--2.配置bean-->
<!--bean标签标示配置bean
id属性标示给bean起名字
class属性表示给bean定义类型-->
<bean id="bookDao" class="com.jinruidai.dao.impl.BookDaoImpl"/>
<!-- 变化在这里 -->
<bean id="bookService" class="com.jinruidai.service.impl.BookServiceImpl">
<!--7.配置server与dao的关系-->
<!--property标签表示配置当前bean的属性
name属性表示配置哪一个具体的属性,是对象中的属性名
ref属性表示参照哪一个bean-->
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
4.bean的基础配置
4.1.给bean起别名
异常NoSuchBeanDefinitionException: 要么是getBean("") 方法中名字写错,要么是配置文件中找不到“service44”该名字
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'service44' available
4.2.scope作用域
在bean标签中加 scope属性
修改后进行测试,发现两个对象的地址值不一致,表明不再是单例模式
问题.为什么bean默认单例? 防止类过多,不负责管理类的数量
4.3 开启懒加载
配置属性 lazy-init="true"
5.bean的实例化
5.1.构造方法实现
配置文件以及测试代码和"3.IoC注入”方式一致
异常BeanCreationException:表示无参构造方法不存在
Exception in thread "main" org.springframework.beans.factory.BeanCreationException:
5.2.静态工厂实现
xml配置如下,要把静态工厂方法名写在factory-method属性中
<!--方式二:使用静态工厂实例化bean-->
<bean id="orderDao" class="com.jinruidai.factory.OrderDaoFactory" factory-method="getOrderDao"/>
5.3.实例工厂初始化(了解)
属性配置如图
5.4.FactoryBean(掌握)
可选方法的内容
xml配置
<!--方式四:使用FactoryBean实例化bean-->
<bean id="userDao" class="com.jinruidai.factory.UserDaoFactoryBean"/>
6.bean的生命周期
- 创建对象(内存分配)
- 执行构造方法
- 执行属性注入(set操作)
- 执行bean初始化方法
- 使用bean
- 销毁bean
6.1配置方式
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
//表示bean初始化对应的操作
public void init(){
System.out.println("init...");
}
//表示bean销毁前对应的操作
public void destory(){
System.out.println("destory...");
}
}
xml配置
<!--init-method:设置bean初始化生命周期回调函数-->
<!--destroy-method:设置bean销毁生命周期回调函数,仅适用于单例对象-->
<bean id="bookDao" class="com.jinruidai.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
getBean()后发现,destory()方法并没执行。因为程序运行完,虚拟机直接退出了,没有给bean销毁的会,有以下两种方式解决:
6.1.1 close()方法:
用ClassPathXmlApplicationContext 接口里的close()方法,销毁bean。再次运行发现,destory()方法执行
public class AppForLifeCycle {
public static void main( String[] args ) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
//关闭容器
ctx.close();
}
}
6.1.2 设置关闭钩子
程序中设置一个标志,告诉虚拟机退出前记得关闭容器
public class AppForLifeCycle {
public static void main( String[] args ) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
//注册关闭钩子函数,在虚拟机退出之前回调此函数,关闭容器
ctx.registerShutdownHook();
//关闭容器
//ctx.close();
}
}
6.2 实现接口
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
//销毁前执行方法
public void destroy() throws Exception {
System.out.println("service destroy");
}
//属性注入完之后执行
public void afterPropertiesSet() throws Exception {
System.out.println("service init");
}
}
实现接口后 IoC容器中正常配置该对象即可
7.依赖注入方式
- 向一个类中传递数据的方式有2中
- 普通方法(setter)
- 构造方法
- setter注入(适合可选依赖注入,会出现null对象的情况,导致报错。自己开发的模块推荐使用)
- 简单类型
- 引用类型
- 构造器注入(倡导使用)
- 简单类型
- 引用类型
7.1 setter注入--简单类型
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
//setter注入需要提供要注入对象的set方法
public void setConnectionNum(int connectionNum) {
this.connectionNum = connectionNum;
}
//setter注入需要提供要注入对象的set方法
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
public void save() {
System.out.println("book dao save ..."+databaseName+","+connectionNum);
}
}
xml配置
<!--注入简单类型-->
<bean id="bookDao" class="com.jinruidai.dao.impl.BookDaoImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--value属性:设置注入简单类型数据值-->
<property name="connectionNum" value="100"/>
<property name="databaseName" value="mysql"/>
</bean>
7.2 setter注入--引用类型
public class BookServiceImpl implements BookService{
private BookDao bookDao;
private UserDao userDao;
//setter注入需要提供要注入对象的set方法
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
//setter注入需要提供要注入对象的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
userDao.save();
}
}
xml配置
<!--注入简单类型-->
<bean id="bookDao" class="com.jinruidai.dao.impl.BookDaoImpl"/>
<bean id="userDao" class="com.jinruidai.dao.impl.UserDaoImpl"/>
<!--注入引用类型-->
<bean id="bookService" class="com.jinruidai.service.impl.BookServiceImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--ref属性:设置注入引用类型bean的id或name-->
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
7.3 构造注入器--引用类型
public class BookServiceImpl implements BookService{
private BookDao bookDao;
private UserDao userDao;
public BookServiceImpl(BookDao bookDao, UserDao userDao) {
this.bookDao = bookDao;
this.userDao = userDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
userDao.save();
}
}
xml配置
<bean id="bookDao" class="com.jinruidai.dao.impl.BookDaoImpl"/>
<bean id="userDao" class="com.jinruidai.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.jinruidai.service.impl.BookServiceImpl">
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
7.4 构造器注入--简单类型
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
public BookDaoImpl(String databaseName, int connectionNum) {
this.databaseName = databaseName;
this.connectionNum = connectionNum;
}
public void save() {
System.out.println("book dao save ..."+databaseName+","+connectionNum);
}
}
xml配置
<bean id="bookDao" class="com.jinruidai.dao.impl.BookDaoImpl">
根据构造方法参数名称注入
<constructor-arg name="connectionNum" value="10"/>
<constructor-arg name="databaseName" value="mysql"/>
</bean>
第二种方式,根据参数类型来赋值,解耦(形参变名,不影响配置文件)
<bean id="bookDao" class="com.jinruidai.dao.impl.BookDaoImpl">
根据构造方法参数类型注入
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
</bean>
第三种方式,解决参数类型重复问题,使用位置解决参数匹配
<bean id="bookDao" class="com.jinruidai.dao.impl.BookDaoImpl">
<!--根据构造方法参数位置注入-->
<constructor-arg index="0" value="mysql"/>
<constructor-arg index="1" value="100"/>
</bean>
8. 依赖自动装配
IoC容器根据bean所依赖的资源在容器中自动查找并注入bean的过程;
自动装配只用于引用类型;自动装配优先级低于setter注入与构造器注入,同时出现自动装配配置失效
- 按类型
- 按名称
- 按构造方法(不推荐使用)
8.1按类型装配(主要用)
实体类如下,需提供setter方法
使用 autowire="byType" 属性。类型匹配必须唯一匹配,例class =com.jinruidai.dao.impl.BookDaoImpl 是唯一的一个bean
<bean class="com.jinruidai.dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.jinruidai.service.impl.BookServiceImpl" autowire="byType"/>
异常
NoUniqueBeanDefinitionException -- 类型不唯一
8.2 按名称装配
xml配置,设置属性 autowire="byName"。 id="bookDao" id需唯一
<bean id="bookDao" class="com.jinruidai.dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.jinruidai.service.impl.BookServiceImpl" autowire="byName"/>
NullPointerException -- id不唯一,报空指针异常
9. 集合等类型注入
实体类
package com.jinruidai.dao.impl;
import com.jinruidai.dao.BookDao;
import java.util.*;
public class BookDaoImpl implements BookDao {
private int[] array;
private List<String> list;
private Set<String> set;
private Map<String,String> map;
private Properties properties;
public void setArray(int[] array) {
this.array = array;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public void save() {
System.out.println("book dao save ...");
System.out.println("遍历数组:" + Arrays.toString(array));
System.out.println("遍历List" + list);
System.out.println("遍历Set" + set);
System.out.println("遍历Map" + map);
System.out.println("遍历Properties" + properties);
}
}
xml配置
<bean id="bookDao" class="com.jinruidai.dao.impl.BookDaoImpl">
<!--数组注入-->
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
<!--list集合注入-->
<property name="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</list>
</property>
<!--set集合注入-->
<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>
<!--map集合注入-->
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>
<!--Properties注入-->
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">kaifeng</prop>
</props>
</property>
</bean>
10. 第三方工具注入举例
此处用c3p0连接池举例;先导入对应的依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
xml中进行配置,导入第三方工具方式都大同小异,至于需要注入哪个类,设置类里的什么属性,需要自行查询文档
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_db"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
<property name="maxPoolSize" value="1000"/>
</bean>
测试类
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
System.out.println(dataSource);
}
}
10.1 加载properties文件
创建properties文件,写入以下内容
spring需要开启新的命名空间,在xml头部分内容中加入方框内的内容
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
在配置文件中添加新配置;location 属性识别源码以及resource文件夹下的文件;
system-properties-mode="NEVER" 属性,防止(若properties key和系统重名,则会优先读取系统的值)读取系统自带的值
<!-- 1.开启context命名空间-->
<!-- 2.使用context空间加载properties文件-->
<!-- classpath*:*.properties : 设置加载当前工程类路径和当前工程所依赖的所有jar包中的所有properties文件 -->
<context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
使用${}方式取properties里的值
此种读取方式也适用于类的属性
11. 注解开发
2.5版本的spring注解已比较完善。3.0时推出纯注解开发。配置文件中配置如下--开启扫描包
以下列出常用注解
<context:component-scan base-package="com.jinruidai"/>
注解 | 用途 |
---|---|
@Component | 定义bean;可加括号,里面写上bean的名字 |
@Controller | @Component的衍生类,功能一样,为了代码具有可读性。用在控制层 |
@Service | @Component的衍生类,功能一样,为了代码具有可读性。用在服务层 |
@Repository | @Component的衍生类,功能一样,为了代码具有可读性。用在持久层 |
@Configuration | 声明当前类为Spring配置类 |
@ComponentScan({"com.jinruidai.service", "com.jinruidai.dao"}) | 设置bean扫描路径,多个路径书写为字符串数组格式 |
@Scope(" ") | 设置bean的作用范围 singleton和prototype |
@PostConstruct | 加在方法上 设置bean的初始化方法 |
@PreDestroy | 加在方法上 设置bean的销毁方法 |
@Bean | 加在方法上 表示当前方法的返回值是一个bean,当前方法的形参根据类型自动装配 |
@Autowired | 加在属性上(不需要set方法) 注入引用类型,自动装配模式,默认按类型装配 |
@Qualifier | 自动装配bean时按bean名称装配,必须依赖@Autowired |
@Value("") | 注入简单类型(无需提供set方法) 可取properties文件值 |
@PropertySource({"jdbc.properties"}) | 加在配置类上 加载properties配置文件,可加多个 |
@Import({JdbcConfig.class}) | 在配置类中 导入配置信息 |
AnnotationConfigApplicationContext加载Spring配置类初始化Spring容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
11.1 注解方式管理第三方资源
此处用druid连接池举例
@Configuration
public class JdbcConfig {
//1.定义一个方法获得要管理的对象
@Value("com.mysql.jdbc.Driver")
private String driver;
@Value("jdbc:mysql://localhost:3306/spring_db")
private String url;
@Value("root")
private String userName;
@Value("root")
private String password;
//2.添加@Bean,表示当前方法的返回值是一个bean
//@Bean修饰的方法,形参根据类型自动装配
@Bean
public DataSource dataSource(BookDao bookDao){
System.out.println(bookDao);
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
}
12 AOP 面向切面编程
一种编程范式,指导开发者如何组织程序结构。在不惊动原始设计的基础上为其进行功能增强
12.1 概念介绍
当前有一个类里有save()方法
当我在测试类里调用不同的方法,改变save()方法里的内容,但不影响程序
分析以及概念
12.2 AOP入门案例
- 可以用xml或者注解实现,这里采用注解方法
- 业务:在接口执行前输出当前系统时间
- 思路:导入坐标,制作连接点方法,制作共性功能,制定切入点,绑定关系(切面)
执行update()方法时,也要输出系统时间
pom.xml导入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
</dependencies>
编写切面类
可以直接这样写:
@Before("execution(* com.example.spring04_aop.service.UserService.insert(..))")
配置类
测试类
12.3 AOP工作流程
- spring容器启动
- 读取所有切面配置中的切入点
- 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
- 匹配失败,创建对象
- 匹配成功,创建原始对象(目标对象)的代理对象
- 获取bean执行方法
- 获取bean,调用方法并执行,完成操作
- 获取的bean时代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作
12.4 AOP切入点表达式
标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)
- 动作关键字:如execution
- 访问修饰符:public等,可省略
- 异常名:可省略
//单个方法
@Pointcut("execution(void com.jinruidai.dao.BookDao.update())")
@Pointcut("execution(void com.jinruidai.dao.impl.BookDaoImpl.update())")
//表示方法要带有任意 一个 参数,返回值是任意类型
@Pointcut("execution(* com.jinruidai.dao.impl.BookDaoImpl.update(*))")
//*代表任意一个
@Pointcut("execution(void com.*.*.*.update())")
//..代表多个 任意包下的有任意参数的方法 即匹配所有
@Pointcut("execution(* *..*(..))")
//方法名是e结尾的
@Pointcut("execution(* *..*e(..))")
@Pointcut("execution(void com..*())")
@Pointcut("execution(* com.jinruidai.*.*Service.find*(..))")
12.5 AOP通知类型
前置通知,后置通知,环绕通知(重点),返回后通知(了解),抛出异常后通知(了解)
- 前置通知 @Before ,后置 @After
- 环绕通知@Around 如下:
注意事项:
- 必须依赖形参,未使用形参将直接跳过原始方法
- 原始方法返回值为void AOP类里的方法返回值可写void,但建议写成Object
- 环绕通知方法必须抛出异常
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(void com.jinruidai.dao.BookDao.update())")
private void pt(){}
@Pointcut("execution(int com.jinruidai.dao.BookDao.select())")
private void pt2(){}
//写上形参ProceedingJoinPoint pjp
@Around("pt2()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
//扩展方法
//获取执行的签名对象
Signature signature = pjp.getSignature();
String className = signature.getDeclaringTypeName();
String methodName = signature.getName();
System.out.println("around before advice ...");
//表示对原始操作的调用
Object ret = pjp.proceed();
System.out.println("around after advice ...");
return ret; //原方法有返回值需返回
}
}
- 返回后通知(了解)@AfterReturning----当方法正常结束时才会触发
- 抛出异常后通知(了解)@AfterThrowing
12.6 AOP获取数据
- 获取切入点方法的参数
- JointPoint:适用于前置,后置,返回后,抛出异常后
- ProceedJointPoint:适用于环绕通知
- 获取切入点方法的返回值
- 返回后通知
- 环绕通知
- 获取切入点方法运行异常信息
- 抛出异常后通知
- 环绕通知
//获取参数
// 如果是环绕通知获取就用ProceedingJoinPoint类型
//JoinPoint:用于描述切入点的对象,必须配置成通知方法中的第一个参数,可用于获取原始方法调用的参数
@Before("pt()")
public void before(JoinPoint jp) {
Object[] args = jp.getArgs(); //获取原方法的参数
System.out.println(Arrays.toString(args));
System.out.println("before advice ..." );
}
//获取返回值
//环绕可以直接获取返回值
//设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同
@AfterReturning(value = "pt()",returning = "ret")
public void afterReturning(JoinPoint jp,String ret) { //第一个参数必须时JoinPoint
System.out.println("afterReturning advice ..."+ret);
}
// 获取异常
//在环绕通知里直接 try-catch就能获取到异常
//返回异常后执行 获取异常如下
//设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同
@AfterThrowing(value = "pt()",throwing = "t")
public void afterThrowing(Throwable t) {
System.out.println("afterThrowing advice ..."+t);
}
13. 事务管理
13.1 事务简介
- 事务作用:在数据层保障一系列的数据库操作同成功同失败
- Spring事务作用: 在数据层或业务层保障一系列的数据库操作同成功同失败
举例
实现:在接口(方法)上配置注解 @Transactional(也可在实现类上写,不建议)
在相应的配置类中写transactionManager方法
注:事务的DataSource和mybatis的DataSource要是同一个,否则不能开启事务
在总配置类中开启注解 @EnableTransactionManagement
13.2 事务角色
原先的服务层中,加钱和减钱是两个独立的事务。转钱方法开启事务(事务管理员)后,总共是三个事务。不过加钱和减钱的两个事务(加入事务方)会自动被转钱方法(在它的控制范围内)管理,所以最后是一个事务
- 事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法
- 加入事务方:加入事务方,在spring中通常指代数据层方法,也可以是业务方法
13.3 事务属性
error错误,运行时异常,事务会自动回滚。其余异常不会,所以需要设置@Transactional(rollbackFor = IOException.class)
事务传播行为:事务协调员对事务管理员所携带事务的处理态度
说简单点就是控制 “加入事务方”到底加不加入 “事务管理员”方
在相应的方法上开启@Transactional(propagation = Propagation.REQUIRES_NEW),表示不属于加入事务方,另外开启一个