目录
目录
3.1 Maven项目中pox.xml中导入spring依赖
五、控制反转IOC(Inversion of Control)
八、AOP(Aspect Oriented Programming )面向切面编程
8.4.2 spring-context.xml引入AOP命名空间
前言
引用spring框架的目的:
1.解决软件开发中代码的高耦合现象
2.简化代码
3.提高代码的可移植性
一、spring是什么?
1.1 概念
-
Spring是一个项目管理框架,同时也是一套Java EE解决方案。
-
Spring是众多优秀设计模式的组合(工厂、单例、代理、适配器、包装器、观察者、模板、策略)。
-
Spring并未替代现有框架产品,而是将众多框架进行有机整合,简化企业级开发,俗称"胶水框架"。
1.2 spring下载
官方网站:Spring | Home
下载地址:JFrog
二、Spring框架的构成
Spring架构由诸多模块组成,可分类为
-
测试:模拟对象,TestContext框架,Spring MVC测试,WebTestClient。
-
数据访问:事务,DAO支持,JDBC,ORM,封送XML。
-
Spring MVC和 Spring WebFlux Web框架。
-
集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。
-
语言:Kotlin,Groovy,动态语言。
-
GroupId ArtifactId 说明 org.springframework spring-beans Beans 支持,包含 Groovy org.springframework spring-aop 基于代理的AOP支持 org.springframework spring-aspects 基于AspectJ 的切面 org.springframework spring-context 应用上下文运行时,包括调度和远程抽象 org.springframework spring-context-support 支持将常见的第三方类库集成到 Spring 应用上下文 org.springframework spring-core 其他模块所依赖的核心模块 org.springframework spring-expression Spring 表达式语言,SpEL org.springframework spring-instrument JVM 引导的仪表(监测器)代理 org.springframework spring-instrument-tomcat Tomcat 的仪表(监测器)代理 org.springframework spring-jdbc 支持包括数据源设置和 JDBC 访问支持 org.springframework spring-jms 支持包括发送/接收JMS消息的助手类 org.springframework spring-messaging 对消息架构和协议的支持 org.springframework spring-orm 对象/关系映射,包括对 JPA 和 Hibernate 的支持 org.springframework spring-oxm 对象/XML 映射(Object/XML Mapping,OXM) org.springframework spring-test 单元测试和集成测试支持组件 org.springframework spring-tx 事务基础组件,包括对 DAO 的支持及 JCA 的集成 org.springframework spring-web web支持包,包括客户端及web远程调用 org.springframework spring-webmvc REST web 服务及 web 应用的 MVC 实现 org.springframework spring-webmvc-portlet 用于 Portlet 环境的MVC实现 org.springframework spring-websocket WebSocket 和 SockJS 实现,包括对 STOMP 的支持 org.springframework spring-jcl Jakarta Commons Logging 日志系统
三、Spring的使用
3.1 Maven项目中pox.xml中导入spring依赖
<dependencies>
<!-- Spring常用依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
</dependencies>
3.2 创建spring核心配置文件
<?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>
四、Spring创建对象 和使用对象调用对象内的方法
spring创建对象的工作原理(反射)-->通过反射获取类对象-->通过类对象创建对象
4.1底层代码逻辑:
userDAO=com.hrp.dao.UserDAOImpl
userService=com.hrp.service.UserServiceImpl
自定义工厂类
public class MyFactory {
private Properties properties = new Properties();
public MyFactory(){}
public MyFactory(String config) throws IOException {
// 加载配置文件
properties.load(MyFactory.class.getResourceAsStream(config));
}
// 获取对象
public Object getBean(String beanName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
// 获得类路径
String classPath = properties.getProperty(beanName);
if(classPath!=null){
Class claz = null;
// 反射:加载类对象
claz = Class.forName(classPath);
// 反射:获得对象
return claz.newInstance();
}
return null;
}
}
4.2 spring创建对象及使用对象的方式
创建实体类:
public class MyBeanClass {
private Integer id;
private String name;
public void say(){
System.out.println("hello");
}
}
将实体类注册到spring容器(工厂)中:
<!-- 配置实例(id:“唯一标识” class="需要被创建的目标对象全限定名") -->
<bean id="myBean" class="com.hrp.bean.MyBeanClass" />
调用Spring工厂API(ApplicationContext接口 ):
public class TestFactory{
/**
* 程序中的对象都交由Spring的ApplicationContext工厂进行创建。
*/
public static void main(String[] args){
//1. 读取配置文件中所需创建的bean对象,并获得工厂对象
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-context.xml");
//2. 通过id获取bean对象
MyBeanClass myBean= (MyBeanClass) ctx.getBean("myBean");
//3. 使用对象 调用say()方法
myBean.say();
}
}
4.3 spring三种给对象属性赋值的方式
实体类:
public class Person {
private Integer id;
private String name;
private Integer age;
private Date birthday;
private int[]arr;
private List<String>list;
private Set<String>set;
private Map<String,String>map;
private Properties properties;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public int[] getArr() {
return arr;
}
public void setArr(int[] arr) {
this.arr = arr;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Set<String> getSet() {
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
", arr=" + Arrays.toString(arr) +
", list=" + list +
", set=" + set +
", map=" + map +
", properties=" + properties +
'}';
}
}
4.3.1 set/get注入
在<property></property>标签中给对象属性赋值name指的是对象的属性名 ,value则是属性对应的属性值;引用数据类型则需要用ref=引用对象的id。
基本类型+字符串类型+日期类型
<bean id="date" class="java.util.Date"/>
<bean id="person1" class="com.hrp.demotest.Person">
<property name="id" value="1"/>
<property name="name" value="张三"/>
<property name="age" value="22"/>
<property name="birthday" ref="date"/>
</bean>
容器类型(list、set、map、array、properties等)
<property name="arr">
<array>
<value>1</value>
<value>2</value>
<value>3</value>
</array>
</property>
<property name="list">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
<property name="set">
<set>
<value>set1</value>
<value>set2</value>
<value>set3</value>
</set>
</property>
<property name="map">
<map>
<entry key="map1" value="map1"></entry>
<entry key="map2" value="map2"></entry>
<entry key="map3" value="map3"></entry>
</map>
</property>
<property name="properties">
<props>
<prop key="prop1">prop1</prop>
<prop key="prop2">prop2</prop>
<prop key="prop3">prop3</prop>
</props>
</property>
自建类型
<!--次要bean,被作为属性-->
<bean id="addr" class="com.hrp.bean.Address">
<property name="city" value="上海" />
<property name="cityCode" value="100001" />
</bean>
<!--主要bean,操作的主体-->
<bean id="user" class="com.hrp.bean.User">
<property name="address" ref="addr" /><!--address属性引用addr对象-->
</bean>
4.3.2 构造器注入
实体类:
public class Student {
private Integer id;
private String name;
private String sex;
private Integer age;
//构造器
public Student(Integer id , String name , String sex , Integer age){
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
}
}
构造器注入:
<!--构造注入-->
<bean id="u3" class="com.hrp.bean.Student">
<constructor-arg name="id" value="1234" /> <!-- 除标签名称有变化,其他均和Set注入一致 -->
<constructor-arg name="name" value="tom" />
<constructor-arg name="age" value="20" />
<constructor-arg name="sex" value="male" />
</bean>
4.3.3 自动注入(@Autowired注解)
不用在配置中 指定为哪个属性赋值,及赋什么值.
由spring自动根据某个 "原则" ,在工厂中查找一个bean,为属性注入属性值
4.4 bean的作用域
singleton | 单例、容器初始化时创建 |
prototype | 多例、使用时创建 |
request | 每一次HTTP请求都产生一个新的bean,仅在当前HTTP request内生效 |
session | 每一次HTTP请求都产生一个新的bean,仅在当前HTTP session内生效 |
global session | 仅在基于portlet的web应用中才有意义 |
五、控制反转IOC(Inversion of Control)
反转了依赖关系的满足方式,由之前的自己创建依赖对象,变为由工厂推送。(变主动为被动,即反转)
解决了具有依赖关系的组件之间的强耦合,使得项目形态更加稳健
5.1 项目中高耦合的问题
public class UserDAOImpl implements UserDAO{....}
public class UserServiceImpl implements UserService {
// !!!强耦合了UserDAOImpl!!!,使得UserServiceImpl变得不稳健
private UserDAO userDAO= new UserDAOImpl();
@Override
public User queryUser() {
return userDAO.queryUser();
}
....
}
5.2 解决方案
// 不引用任何一个具体的组件(实现类),在需要其他组件的位置预留存取值入口(set/get)
public class UserServiceImpl implements UserService {
// 不再耦合任何DAO实现!!!,消除不稳健因素
private UserDao userDao;
// 为userDAO定义set/get,允许userDAO属性接收spring赋值
//Getters And Setters
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void hello() {
userDao.say();
}
}
<bean id="userDAO" class="com.hrp.dao.impl.UserDaoImpl"></bean>
<!-- UserServiceImpl组件 -->
<bean id="userService" class="com.hrp.dao.impl.UserServiceImpl">
<!-- 由spring为userDAO属性赋值,值为id="userDAO"的bean -->
<property name="userDAO" ref="userDAO"/>
</bean>
六、Spring常用注解
@Repository | 常用于dao层的类中 |
@Service | 常用于service层的类中 |
@Controller | 常用于Controller层的类中 |
@Autowired | 自动注入 常写于属性上方 |
@Autowired+Qualifier (bean类名称) | 等同于@Autowired |
@Autowired + byName 根据属性名称寻找类型 属性名与id相同
@Autowired + byType 根据类型 相同类型>1则报错
PS:使用注解需要在xml中配置
<context:component-scan base-package="扫的包”></context;component-scan>
如:<context:component-scan base-package="com.hrp”></context;component-scan>会扫描com包下hrp目录的所有类中的注解
七、 代理设计模式
将核心功能与辅助功能(事务、日志、性能监控代码)分离,达到核心业务功能更纯粹、辅助业务功能可复用。
7.1 静态代理设计模式
通过代理类的对象,为原始类的对象(目标类的对象)添加辅助功能,更容易更换代理实现类、利于维护。
代理类 = 实现原始类相同接口 + 添加辅助功能 + 调用原始类的业务方法。
静态代理的问题:
代理类数量过多,不利于项目的管理。
多个代理类的辅助功能代码冗余,修改时,维护性差。
7.2 动态设计模式
动态创建代理类的对象,为原始类的对象添加辅助功能。
① JDK动态代理实现(基于接口 )--》有接口的情况
@Test
public void jdkProxyTest() {
//目标类
UserService userService = new UserServiceImpl();
//额外功能
InvocationHandler invocationHandler = new InvocationHandler() {
//设置回调函数(额外功能代码)
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("前置通知");
method.invoke(userService, objects);
return null;
}
};
//创建动态代理类
UserService userService1 =(UserService) Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), userService.getClass().getInterfaces(), invocationHandler);
//调用方法
userService1.add();
}
②CGlib动态代理实现(基于继承)--》无接口的情况
@Test
public void cglibProxyTest() {
//目标类
UserService userService = new UserServiceImpl();
Enhancer enhancer = new Enhancer();//字节码增强对象
enhancer.setSuperclass(userService.getClass());//设置被代理类的类对象
enhancer.setCallback(new InvocationHandler() {//设置代理类重写的方法
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("前置通知");
method.invoke(userService, objects);
return null;
}
});
UserService userService1 = (UserService) enhancer.create();
userService1.add();
}
八、AOP(Aspect Oriented Programming )面向切面编程
8.1概念
AOP(Aspect Oriented Programming),即面向切面编程,利用一种称为"横切"的技术,剖开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说在不修改原代码的情况下用无代码侵入的方式增强原始类的功能 比如 验证功能、日志功能、事务的提交和关闭等都可以通过AOP技术实现
8.2 AOP常见术语
-
连接点(Joinpoint):连接点是程序类中客观存在的方法,可被Spring拦截并切入内容。
-
切入点(Pointcut):被Spring切入连接点。
-
通知、增强(Advice):可以为切入点添加额外功能,分为:前置通知、后置通知、异常通知、环绕通知等。
-
目标对象(Target):代理的目标对象
-
引介(Introduction):一种特殊的增强,可在运行期为类动态添加Field和Method。
-
织入(Weaving):把通知应用到具体的类,进而创建新的代理类的过程。
-
代理(Proxy):被AOP织入通知后,产生的结果类。
-
切面(Aspect):由切点和通知组成,将横切逻辑织入切面所指定的连接点中。
8.3 作用
Spring的AOP编程即是通过动态代理类为原始类的方法添加辅助功能
8.4 AOP的使用
8.4.1 引入AOP相关依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.22</version>
</dependency>
8.4.2 spring-context.xml引入AOP命名空间
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
</beans>
8.4.3 开发流程
①定义原始类
public interface UserService {
void update();
void add();
void getAll();
void delete();
void getOne();
}
//实现接口
public class UserServiceImpl implements UserService{
@Override
public void update() {
System.out.println("执行修改");
}
@Override
public void add() {
System.out.println("执行新增");
}
@Override
public void getAll() {
System.out.println("执行查询所有");
}
@Override
public void delete() {
System.out.println("执行删除");
}
@Override
public void getOne() {
System.out.println("执行查询单个");
int i =2/0;
}
}
②定义通知类(添加辅助功能)下图为前置通知案例
public class BeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(new Date());
}
}
前置通知:MethodBeforeAdvice 在目标方法执行之前执行执行的通知
后置通知:AfterAdvice 在目标方成功执行之后执行的通知。
后置通知:AfterReturningAdvice 返回后通知
异常通知:ThrowsAdvice 在目标方法抛出异常时执行的通知。
环绕通知:MethodInterceptor 目标方法执行之前和之后都可以执行额外代码的通知
③配置spring-Context.xml
定义bean标签
<!--配置原始类对象-->
<bean id="userServiceImpl" class="com.hrp.service.UserServiceImpl"/>
<!--配置通知-->
<bean id="myAdvice" class="com.hrp.advice.BeforeAdvice"/>
<!-- 配置切点-->
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(* com.hrp.service.UserService.update())"/>
<!--配置切面
advice-ref 配置通知
pointcut-ref 配置切点
-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="myPointcut"></aop:advisor>
</aop:config>
九、spring的事务管理
事务的特性(ACID):原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Duration)。
9.1 声明式事务
9.1.1 xml文件配置
创建实体类:
Person实体类:
public class Person {
private Integer id;
private String name;//账户
private Integer balance;//余额
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getBalance() {
return balance;
}
public void setBalance(Integer balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", balance=" + balance +
'}';
}
}
Book实体类:
public class Book {
private String isbn;//书的编号
private String name;//书名
private String author;//作者
private Integer price;//书价
private Integer stock;//库存
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Book{" +
"isbn='" + isbn + '\'' +
", name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
", stock=" + stock +
'}';
}
}
Person类的dao接口及实现类
public interface PersonDao {
Person selectByAccount(String name);//通过账户查询
int updateBalance(String account,Integer bookPrice);//更新余额
}
@Repository
public class PersonDaoImpl implements PersonDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Person selectByAccount(String name) {
return jdbcTemplate.queryForObject("select * from person where name = ?", new RowMapper<Person>() {
@Override
public Person mapRow(ResultSet rs, int rowNum) throws SQLException {
Person person = new Person();
person.setId(rs.getInt(1));
person.setName(rs.getString(2));
person.setBalance(rs.getInt(3));
return person;
}
}, name);
}
@Override
public int updateBalance(String name, Integer bookPrice) {
return jdbcTemplate.update("update person set balance=balance-? where name =?",bookPrice,name);
}
}
Book类的dao接口及实现类:
public interface BookDao {
Book selectByIsbn(String isbn);//根据isbn查询book
int updateStock(String isbn);//更新库存
}
@Repository
public class BookDaoImpl implements BookDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Book selectByIsbn(String isbn) {
Book book = jdbcTemplate.queryForObject("select * from book where isbn=?", new RowMapper<Book>() {
@Override
public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
Book bk = new Book();
bk.setIsbn(rs.getString(1));
bk.setName(rs.getString(2));
bk.setAuthor(rs.getString(3));
bk.setPrice(rs.getInt(4));
bk.setStock(rs.getInt(5));
return bk;
}
}, isbn);
return book;
}
@Override
public int updateStock(String isbn) {
return jdbcTemplate.update("update book set stock=stock-1 where isbn=?",isbn);
}
}
Book类的业务接口及实现类
public interface BookService {
void bookDeal(String name,String ibsn);//书的交易
}
@Service
public class BookServiceImpl implements BookService{
@Autowired
private BookDao bookDao;
@Autowired
private PersonDao personDao;
@Override
public void bookDeal(String name, String ibsn) {
Book book = bookDao.selectByIsbn(ibsn);
Person person = personDao.selectByAccount(name);
System.out.println("交易前book:"+book);
System.out.println("交易前person:"+person);
if (person.getBalance()>=book.getPrice()){//如果用户的账户余额大于书的价格 则账户余额-书价
personDao.updateBalance(name,book.getPrice());
}else {
throw new RuntimeException("嗨呀,余额不足");
}
if (book.getStock()>=1){//如果书的库存大于1 就库存-1
bookDao.updateStock(ibsn);
}else {
throw new RuntimeException("哦吼,库存不足");
}
Book book1 = bookDao.selectByIsbn(ibsn);
Person person1 = personDao.selectByAccount(name);
System.out.println("交易后book:"+book1);
System.out.println("交易后person:"+person1);
}
}
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///studentsys"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--事务传播行为
int PROPAGATION_REQUIRED = 0; //支持当前事务,如果不存在,就新建一个
int PROPAGATION_SUPPORTS = 1; //支持当前事务,如果不存在,就不使用事务
int PROPAGATION_MANDATORY = 2; //支持当前事务,如果不存在,就抛出异常
int PROPAGATION_REQUIRES_NEW = 3;//如果有事务存在,挂起当前事务,创建一个新的事物
int PROPAGATION_NOT_SUPPORTED = 4;//以非事务方式运行,如果有事务存在,挂起当前事务
int PROPAGATION_NEVER = 5;//以非事务方式运行,如果有事务存在,就抛出异常
int PROPAGATION_NESTED = 6;//如果有事务存在,则嵌套事务执行
-->
<!--配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--配置事务属性-->
<tx:attributes>
<tx:method name="bookDeal"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.hrp.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
<!--配置jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置注解扫描-->
<context:component-scan base-package="com.hrp"/>
</beans>
9.1.2 注解方式
????
9.2 编程式事务
9.2.1 execute方法
TransactionTemplate是spring提供出来的能够操作spring中的事务的操作。
使用TransactionTemplate需要配置一个PlatformTransactionManager,但是不需要配置事务定义。因为在TransactionTemplate中,spring已经帮我们配置好了。
直接看下最为核心的方法:execute
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
}
else {
// 获取得到事务状态信息,然后执行对应的方法
TransactionStatus status = this.transactionManager.getTransaction(this);
T result;
try {
result = action.doInTransaction(status);
}
// 执行失败逻辑
catch (RuntimeException | Error ex) {
// Transactional code threw application exception -> rollback
rollbackOnException(status, ex);
throw ex;
}
catch (Throwable ex) {
// Transactional code threw unexpected exception -> rollback
rollbackOnException(status, ex);
throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
}
// 执行成功逻辑
this.transactionManager.commit(status);
return result;
}
}
9.2.2 executeWithoutResult方法
和execute的区别的地方就在于:看返回值是否有对应的结果。如果有返回值,那么就直接使用execute方法;如果没有返回值,就直接使用executeWithoutResult方法即可。那么来看下这里的原理:
default void executeWithoutResult(Consumer<TransactionStatus> action) throws TransactionException {
execute(status -> {
action.accept(status);
return null;
});
}
9.3 事务的属性
9.3.1 隔离级别
名称 | 描述 |
---|---|
default | (默认值)(采用数据库的默认的设置) (建议) |
read-uncommited | 读未提交 (会产生脏读/不可重复读/幻读的风险) |
read-commited | 读提交 (会产生不可重复读/幻读的风险) |
repeatable-read | 可重复读 (会产生幻读)(MySQL数据库默认的隔离级别) |
serialized-read | 序列化读(最安全,但效率最低) |
脏读:读到了其它事务未提交的数据,数据可能回滚,读到了没有存储在数据库中的数据
不可重复读:在一个事务内,多次读取同一条数据,结果不同 (和幻读的区别是 幻读数据会消失 不可重复读只是内容改变)
9.3.2 传播行为
int PROPAGATION_REQUIRED = 0; //支持当前事务,如果不存在,就新建一个 int PROPAGATION_SUPPORTS = 1; //支持当前事务,如果不存在,就不使用事务 int PROPAGATION_MANDATORY = 2; //支持当前事务,如果不存在,就抛出异常 int PROPAGATION_REQUIRES_NEW = 3;//如果有事务存在,挂起当前事务,创建一个新的事物 int PROPAGATION_NOT_SUPPORTED = 4;//以非事务方式运行,如果有事务存在,挂起当前事务 int PROPAGATION_NEVER = 5;//以非事务方式运行,如果有事务存在,就抛出异常 int PROPAGATION_NESTED = 6;//如果有事务存在,则嵌套事务执行
9.3.3 读写性
readonly | |
true | 只读,可提高查询效率。(适合查询) |
flase | 可读可写。 (默认值)(适合增删改) |
9.3.4 事务超时
timeout 事务超时时间 当前事务所需操作的数据被其他事务占用,则等待。 | |
100 | 自定义等待时间100(秒)。 |
-1 | 由数据库指定等待时间,默认值。(建议) |
9.3.5 事务回滚
rollback-for 事务回滚 | |
rollback-for | 如果事务中抛出 RuntimeException,则自动回滚 |
no-rollback-for | 如果事务中抛出异常,事务不回滚 |