Spring
1.概述
spring framework:最早,设计型,底层框架
spring boot:加速开发
spring cloud:分布式开发
2.Spring Framework架构的基本学习内容
学习内容:
1.核心容器Ioc
2.数据继承Mybatis
3.面向切面AOP
4.事务:事务实用开发
3.IOC原理
为了降低耦合,提供一个IOC容器,来充当new对象的作用
被IOC管理的对象,称为Bean。
但是service创建对象需要用到DAO层呀,所以他们俩是相互依赖的。IOC容器就顺便把service和dao依赖绑定了。我们称为依赖注入,也称为DI
最终效果:使用对象时,1可以从IOC容器中获取,2而且获取的Bean3已经注入好依赖了
4.IOC案例思路分析
1.导入spring和spring frame的坐标pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
2.写好要作为Bean调用的接口和实现类(bean标签 id定义名字 class代表实现类)
package com.itheima.dao.impl;
import com.itheima.dao.BookDao;
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
package com.itheima.dao;
public interface BookDao {
public void save();
}
3.再resourse里面创建iml,来进行spring的配置
<!--2.配置bean-->
<!--bean标签标示配置bean
id属性标示给bean起名字
class属性表示给bean定义类型-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl"/>
4.利用IOC来进行调用Bean
//3.获取IoC容器
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
//4.获取bean(根据bean配置id获取)
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
BookService bookService = (BookService) ctx.getBean("bookService");
bookDao.save();
IOC实例的应用:
5.DI
不能new 对象,要从外面传参。所以要写一个set方法,从外面传进去
//5.删除业务层中使用new的方式创建的dao对象
private BookDao bookDao;
//6.提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
配置Dao和Service的关系,在大的里面配置
第一个name对于的java代码里面的变量名
第二个对应着xml里面的bean的id
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!--7.配置server与dao的关系-->
<!--property标签表示配置当前bean的属性
name属性表示配置哪一个具体的属性
ref属性表示参照哪一个bean-->
<property name="bookDao" ref="bookDao"/>
<!-- <property name="bookDao" ref="bookDao"></property>-->
</bean>
6.Bean的基础配置
name和id的效果一样,都可以写在ref里面进行引用;也可以再getBean里面引用
id里面只能写一个,name里面可以写多个(可以用, ; 空格三种来进行分割)
spring默认ioc创建的对象是单例的(scope 单例:singleon 多例:prototype)
单例适合那些通用的;多例适合那些有状态的,每个都不一样的
7.bean如何实例化对象的
构造方法
无参构造(public,private)都可以
报错的话,从后面往前看,一般先看最后一句,不行再往上看
如果没有写无参构造方法,会抛出异常BeanCreationException
public BookDaoImpl(){
System.out.println("BookDaoImpl ...");
}
<!--方式一:构造方法实例化bean-->
<!-- <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>-->
Bean静态工厂(了解即可)
造对象不要自己new,要用工厂new,可以一定程度上解耦。接口,实现类,静态工厂是三件套。
要的不是factory对象,是要里面的getOrderDao方法造出来的对象
package com.itheima.factory;
import com.itheima.dao.OrderDao;
import com.itheima.dao.impl.OrderDaoImpl;
//静态工厂创建对象
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
System.out.println("factory setup....");
return new OrderDaoImpl();
}
}
主注意看这里的bean标签中的factory-method是怎么配置的
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>
import com.itheima.dao.UserDao;
import com.itheima.factory.OrderDaoFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppForInstanceOrder {
public static void main(String[] args) {
//通过静态工厂创建对象
OrderDao orderDao = OrderDaoFactory.getOrderDao();
orderDao.save();
// ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//
// OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");
//
// orderDao.save();
}
}
Bean实例工厂初始化(了解)
不是静态的,没有static,所以必须要先new一个对象,再调用方法
package com.itheima.factory;
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
//实例工厂创建对象
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
package com.itheima;
import com.itheima.dao.OrderDao;
import com.itheima.dao.UserDao;
import com.itheima.factory.OrderDaoFactory;
import com.itheima.factory.UserDaoFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppForInstanceUser {
public static void main(String[] args) {
//创建实例工厂对象
UserDaoFactory userDaoFactory = new UserDaoFactory();
//通过实例工厂对象创建对象
UserDao userDao = userDaoFactory.getUserDao();
userDao.save();
}
}
<bean/>标签怎么写?
先把工厂对象造出来,再调用工厂对象把需要的bean造出来
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
FactoryBean(重点)
泛型里面想写谁就写谁,要造谁写谁
FactoryBean是Spring提供的一个抽象方法,我们要对接口中的方法进行实现
public class UserDaoFactoryBean implements FactoryBean<UserDao>{
@Override
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
@Override
public Class<?> getObjectType() {
return UserDao.class;
}
}
默认单例
如果不想要单例,可以重写接口方法
public class UserDaoFactoryBean implements FactoryBean<UserDao>{
@Override
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
@Override
public Class<?> getObjectType() {
return UserDao.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
在xml里面配置一下即可:
<!--方式四:使用FactoryBean实例化bean-->
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
7.Bean的生命周期
用标签控制
在创建以后,摧毁之前做一些事情。
手动写一个init() 和destory()。取什么名字无所谓,但是必须要是public void
public void init(){
System.out.println("init...");
}
//表示bean销毁前对应的操作
public void destory(){
System.out.println("destory...");
}
ctrl+点击:点进一个方法。
然后再点击alt+7,就能看到所有方法;
ctrl+h:查看一个接口,实现类
原本写的ApplicationContext里面没有close,但是它的子类ClassPathXmlApplicationContext中有 。close是关闭虚拟机,在关闭前释放容器
public class AppForLifeCycle {
public static void main( String[] args ) {
ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
// ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
ctx.close();
}
}
还可以设置关闭钩子:在java虚拟机停止前,先关闭容器
public class AppForLifeCycle {
public static void main( String[] args ) {
ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
// ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
ctx.registerShutdownHook();
// ctx.close();
}
}
最后想让init()和destory()和生命周期绑定,就要在xml里面配置一下
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
直接用Spring配置好的接口
还有一种方法,不用在<bean> 标签处配置init-method方法。
继承DispoableBean,InitializingBean接口,再重写destroy,afterPropertiesSet.。前者很好理解,就是销毁前做的,后者是数据刚刚set好,执行的
public class BookServiceImpl implements BookService ,DisposableBean ,InitializingBean {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
System.out.println("set .....");
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
@Override
public void destroy() throws Exception {
System.out.println("service destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("service init");
}
}
但是无论哪种init和destroy方法
都要用close或者钩子,要不然destroy看不到。close注意放置的位置,钩子可以随便放
完整的生命周期:
1.创建对象2.进行构造方法3.set配置数据4.执行Bean的init()方法
使用Bean执行业务(调用save等等方法)
进行销毁操作:用close;钩子
8.依赖注入
setter注入
引用注入
简单注入
构造器注入
引用注入
简单注入
setter
setter注入引用类型
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<!--注入引用类型-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--ref属性:设置注入引用类型bean的id或name-->
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
setter注入简单类型
配置的先后顺序无所谓
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
public void setConnectionNum(int connectionNum) {
this.connectionNum = connectionNum;
}
public void save() {
System.out.println("book dao save ..."+databaseName+","+connectionNum);
}
}
<!--注入简单类型-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--value属性:设置注入简单类型数据值-->
<property name="connectionNum" value="10"/>
<property name="databaseName" value="mysql"/>
</bean>
构造器
用构造引用类型
private BookDao bookDao;
private UserDao userDao;
public BookServiceImpl(BookDao bookDao, UserDao userDao) {
this.bookDao = bookDao;
this.userDao = userDao;
}
标签换成<constuction-arg 但是,这个name,就不是变量名了,而是构造方法里面传的形参名。
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!-- <constructor-arg name="userDao" ref="userDao"/>-->
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
用构造方法,简单方法
<!--解决参数类型重复问题,使用位置解决参数匹配-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!--根据构造方法参数位置注入-->
<constructor-arg index="0" value="mysql"/>
<constructor-arg index="1" value="100"/>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
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);
}
}
总结:给你构造器,就用构造器注入;自己写用setter
依赖自动装配
自动装配用于引用,不用于简单
一般用bytype,装配的bean要唯一
byname耦合性比较高
自动装配的优先级低于setter和构造器注入,同时注入的话,自动装配可能会失败(手写优先)
给好set方法
public class BookServiceImpl implements BookService{
private BookDao bookDao;
// public BookServiceImpl(BookDao bookDao) {
// this.bookDao = bookDao;
// }
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
给唯一的bean 甚至可以省略name
aurowire选byType
<bean class="com.itheima.dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
9.集合注入
一般都是注入简单类型
array,set,list都是类似的
map因为有键值对,所以需要用到entity实体,然后key写键名,value写值名
依赖都是写在<bean id="接口名">
里面的
10.数据源对象管理
第三方数据源配置
数据源要连接哪些????
连接setter(点进去看写的是什么类)
这个除了url可以改,其他都是固定的。才发现之前的是用ioc配置的,本质就是用setter注入简单类型
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring_db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
c3p0
这个时候我们是不知道c3p0要配置哪些的,所以要通过百度什么的来搜索。
这时候可以尝试写bean name不重要,先写class来找c3p0的datatsource
找到后,往里面加依赖,不确定是setter还是构造器,回原本的去看看,(为什么构造方法是setter啊?因为无参吗)
然后把固定的四个往里面加,试试看,因为每个数据源的名字都大差不差
注意:这里面的class要注意不能选错,可以先百度,要不然不知道哪错,debug半天
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="dbc:mysql://localhost:3306/spring_db"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
总的来说分为两点:1.导入pom.xml坐标。 2.配置config.xml的bean
11.加载properties文件
1.加载文件
2.读文件
坑点:可能会有和系统变量重名的情况,这种情况写NEVER
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
可以加载多个文件
<context:property-placeholder location="jdbc.properties,jdbc2.properties" system-properties-mode="NEVER"/>
所有的文件都加载
classpath:*.properties : 设置加载当前工程类路径中的所有properties文件
12.容器
创建容器
第一种好用,不管哪种都能加载多个xml
获取容器
最后一种常用,但是只能有一个,要不然会报错
容器类层次结构
beanFactory
是很早以前用的,也可以用来造容器,但是很久不用了。
区别就是beanfactory延迟加载;
aplicationcontext是立即加载,但也可以延迟加载,要打标记lazy-init
核心容器开发
13.注解开发定义Bean
配置Bean
1.可以指定id名称
2.可以不写(),后面根据类型来getBean就行了
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
BookService bookService = ctx.getBean(BookService.class);
第一个就能写bookDao,要不然getBean找不到
下面一个就不用,因为他是根据类型来写的
注意再xml里面加上扫描语句:
<context:component-scan base-package="com.itheima"/>
Controller,Service,Repository和component效果是一样的,只是可以放在不同的层上,好让我们区分
14.纯注解开发模式
用java来代替配置文件
新建Config类来代替配置文件,在class类上写两个注解就能替换xml了,太方便了叭
没有了xml,那么获取容器ctx的语句自然也要换
总结就三句话:
1.新建个java加上@Configuration 表明他是配置
2.再加上@ComponentScan()里面加上写了component的类的所在包,这样才能扫描到
@Configuration
@ComponentScan({"com.itheima.service","com.itheima.dao"})
public class SpringConfig{
}
3.获取IOC容器的语句
里面加上上面的jave类的类型
ApplicationContext ctx=new AnnotationConfigApplicationContext(SpringConfig.class);
15.注解开发Bean的作用范围和生命管理周期
注意在pom.xml里面添加jsr250-api,否则@PostConstruct和@PreDestroy识别不了
@Scope:singleton;prototype
@PostConstruct
@PreDestroy
注意:非单例的,不会destory,他们的销毁不属于IOC管理
16.注解开发依赖注入
引用类型
1.要往service里注入Dao,就直接在service里面写Autowired标签就好了
2.如果Dao有好几个实现类,就要在每个写上id,然后在autowired下面写@Qualifier里面写上id,注意,必须要和autowired连用
@Autowired
@Qualifier("bookDao")
private BookDao bookDao;
简单类型
在上面写@Value就好了
propoties的加载
1.怎么配置2.怎么加载
1.在springconfig上面配置标签
@PropertySource({"jdbc.properties"})
2.双引号里面写${}里面写上变量名即可
如果要写多个来源,就用{}括起来
标签这里不支持通配符*
每种标签都只能写一次,妙妙妙,真的简洁
17.第三方的Bean怎么导入
主要分为两部分:
1.第三方Bean的书写 DataSource方法的书写:
1.用Druid 取ds
2.设置ds的各项数据
3.return ds
2.将Bean导入SpringConfig中
import括号里面的class不用打双引号
18.注解开发为外部Bean注入数据
注入简单变量
就是用私有成员变量做一个过渡
注入引用变量,把形参注入即可
他会自动装配。但是Dao类上面还是要写标签
注解开发总结
19.Spring整合mybatis
书写Mybati-config文件,在springconfig中import。
就两个地方需要改:"com.itheima.domain"里面是实体类的变量和信息
"com.itheima.dao"里面是数据库的语句
public class MybatisConfig {
//定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
ssfb.setTypeAliasesPackage("com.itheima.domain");
ssfb.setDataSource(dataSource);
return ssfb;
}
//定义bean,返回MapperScannerConfigurer对象
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.itheima.dao");
return msc;
}
}
然后在main函数里面就可以调用IOC容器了
public class App2 {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
AccountService accountService = ctx.getBean(AccountService.class);
Account ac = accountService.findById(1);
System.out.println(ac);
}
}
20.Spring整合Junit
21.AOP
AOP:面向切片编程
OOP:面向对象编程
作用:在不惊动原始设计的基础上(不改源代码),增加功能
连接点:原始方法(自己写的)
切入点:要追加功能的方法
通知:共性的功能
切面:把通知和切入点连接起来,要不然怎么知道哪个方法要哪个通知呢
22.AOP案例
接口执行前输出当前时间
1.导入坐标
2.在SpringConfig上面加上@EnableAspectJAutoProxy
3.创建一个Myadvise类,加上@Component标签
写一个方法,加上@Pointcut("execution(void com.itheima.dao.BookDao.update1())")作为切入点,告诉程序你要对哪些方法用
@Pointcut("execution(void com.itheima.dao.BookDao.update1())")
private void pt(){}
再写一个通知,上面加上@Before标签,告诉大家在扫描前执行还是后执行通知
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
23.AOP工作流程
1.Spring启动
2.加载所有切面配置的切入点(就是读取卸载befor里面的)
3.初始化bean,判定类中对应的方法,是否匹配任意切入点
匹配失败:创建对象
匹配成功:创建原始对象的代理对象
4.获取Bean执行方法
获取Bean,调用方法并执行,完成操作
获取代理对象,拿到原有的方法和额外AOP配置的
但是最后AOP会进行tostring重写,所以看到的有些差距
匹配上:
com.itheima.dao.impl.BookDaoImpl@1e4f4a5c
class jdk.proxy2.$Proxy20
匹配不上:
com.itheima.dao.impl.BookDaoImpl@38467116
class com.itheima.dao.impl.BookDaoImpl
匹配上就调代理对象,匹配不上就原始对象
24.AOP切入点表达式
描述接口或者实现类都可以
* 可以独立存在;也可以写在一个单词后面find*,作为后缀;也可以写在参数里面。但是注意:*是任意,但是要有
.. 任意,有没有都行
+ 子类,接口的实现类就是接口的子类
一般描述接口
访问修饰符因为很多对接口实现,所以一般都是public,可以省略
25.AOP通知类型
before:方法调用前
after:方法调用后
环绕:
开始前和结束后都调用,有对原始要求的调用,得用Object接出来返回值,并且最后return扔回去
AfterReturning
没有抛异常才会调用,在最后调用
AfterThrowing
抛异常后才会调用,在最后调用
26.案例:测量接口案例
写around的时候,注意你切入点的返回值是什么,你要写对应的类型
获取类型和方法名
Signature signature = pjp.getSignature();
String classname = signature.getDeclaringTypeName();
String methodname = signature.getName();
27.AOP获取数据
参数
参数:所有都能调用。用JoinPoint创建对象,getArgs()方法获取Object数组即可
@Before("pt()")
public void be(JoinPoint jp){
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
}
返回值
around和AfterReturning可以用
around的返回值之前学过,直接调用pjp.proceed就好了。而且pjp是jp的子类,继承了所有父类的方法,所以可以getArgs
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
args[0]=666;
Object pr = pjp.proceed(args); //这就是拿到返回值吗
return pr;
}
对于AfterReturning来说有点特殊。所有通知的形参只要有JoinPoint,就必须放在第一位。
首先标签上要定义一个returning 来接下面方法中的object的变量名。然后就好了,注意一定要一一对应。
@AfterReturning(value = "pt()",returning = "ret")
public void afterReturning(JoinPoint jp,Object ret){
System.out.println("*******");
System.out.println(ret);
}
异常
只有Around和AfterTrouwing可以用
Around的贼好拿哈哈哈哈哈,真的方便。直接try catch环绕就能拿到。idea是方便哈
Object pr = null; //这就是拿到返回值吗
try {
pr = pjp.proceed(args);
} catch (Throwable e) {
throw new RuntimeException(e);
}
return pr;
afterThrowing就和上面的afterreturning类似了。
@AfterThrowing(value="pt()",throwing = "t")
public void aft(Throwable t){
System.out.println(t);
}
小技巧:抛异常要是抛不了,可以用以下语句骗一骗
if(true)throw new NullPointerException();
28.百度网盘案例
其实不难,只是之前没写过。那里面的for循环处理非常绝妙!!!!!!!
@Around("servicePt()")
public Object tr(ProceedingJoinPoint pjp) throws Throwable {
Object[] args=pjp.getArgs();
for(int i=0;i< args.length;i++){
if(args[i].getClass().equals(String.class)){
args[i]=args[i].toString().trim();
}
}
Object ret=pjp.proceed(args);
return ret;
}
使用AOP简化共性功能的开发
29.Spring事务
保证业务层对数据库的操作同成功同失败
可以在数据层也可以在业务层开
内部用的是jdbc才能用以下的方法,刚好mybatis就是jdbc
1.业务层接口加上事务管理
接口方法上写一个标签
最好写在接口上,降低耦合
@Transactional
2.设置事务管理器
在jdbcConfig里面定义一个Bean
里面写着PlatformTransactionManager方法
new一个DataSourceTransactionManager
用传参的方式把DataSource传参传进去
然后把创建好的DataSourceTransactionManager.setDataSource(dataSource)
最后返回创建好的DataSourceTransactionManager即可
注意:都是在围绕着DataSourceTransactionManager做操作
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
3.开启注解事务驱动
在SprignConfig上面加上标签
@EnableTransactionManagement
突然发现jdbc里面配置的是mybatis,因为jdbc是用
30.Spring事务角色
mybatis和PlatformTransactionManager里面的dataSource是同一个,要不然没法进行统一管理
31.Spring事务属性
ctrl+h先点进去class。双击右上角的方法。
alt+7再点进去,就能看到每个方法了
、
@Transactional(rollbackFor = {IOException.class})
重新开启一个事务
public interface LogService {
//propagation设置事务属性:传播行为设置为当前操作需要新事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
void log(String out, String in, Double money);
}
所谓事务传播行为本质上就是事务协调员对事务管理员的态度