Spring 是一个轻量级的开发框架,以Ioc和AOP为内核,提供了展现层Spring MVC和业务层事务管理等众多企业级应用技术。还能整合众多的第三方类库,是目前java非常流行的框架。Spring 的核心思想就是IOC和AOP。
Ioc与DI都是描述的同一件事,但是出发点不同。Ioc的英文全称是Inversion of Contronl,翻译过来是反转控制。DI 英文全称为Depedency Inection ,翻译过来是依赖注入。IOC和DI是一种思想,不是一种技术!!
IOC和DI描述的都是同一件事情,只是从不同角度出发。IOC是针对对象而言,把对象的控制权交给容器,DI是从容器的角度出发,从容器中把对象的依赖注入给对象。
传统的开发中,我们有两个对象A和B,A关联B,我们一般会定义如下:
public class A {
private B b = new B();
}
而IOC和DI思想则不需要在关联或者依赖中让我们手动去new想对象。而是使用一个容器把对象保存起来,在需要使用对象的地方自动注入该对象。
public class A {
@Autowire
private B b; <----------从容器中获取唯一对象注入给b----一对象---容器 《-----创建后保存到容器---- A对象、B对象
}
AOP英文全称为Aspect oriented Programming ,面向切面的编程方式。AOP是相对于OOP面向对象编程来比较的,面向对象的三个特点就是继承、封装和多态,是一种垂直架构。
OOP面向切面编程就是对方法的增强,在不修改方法的前提下,在方法运行过程中添加通用的逻辑操作,将代码解耦。
模拟一个银行转账的案例,通过案例我们来深入理解IOC和AOP的思想,以理解Spring框架的核心思想。
我们首先定义一个传统的MVC工程,模拟银行转账案例。
1)创建一个JAVE EE项目,编写前端转账功能
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
转账案例
2)创建数据库和pojo实体类
CREATE TABLE USER_ACCOUNT(
accound int,
money int
)
public class Account {
private int money;
private String account;
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
}
3)编写Servlet接收转账请求
@WebServlet("/transfer")
public class TransferServlet extends HttpServlet {
TransferService transferService = new TransferServiceImpl();
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
doPost(request, response);
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response){
String transferAccount = request.getParameter("transferAccount");
String accessAccount = request.getParameter("accessAccount");
int money = Integer.parseInt(request.getParameter("money"));
try {
int result = transferService.transfer(transferAccount, accessAccount, money);
if (result == 2) {
PrintWriter writer = response.getWriter();
writer.println("成功");
} else {
PrintWriter writer = response.getWriter();
writer.println("失败");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
4)编写Service类处理转账请求业务
public interface TransferService {
int transfer(String transferAccount, String accessAccount, int money) throws Exception;
}
public class TransferServiceImpl implements TransferService {
TransferDao transferDao = new TransferDao();
@Override
public int transfer(String transferAccount, String accessAccount, int money) throws Exception {
Account transferAccountMoney = transferDao.getMoneyByTransferAccount(transferAccount);
Account accessAccountMoney = transferDao.getMoneyByTransferAccount(accessAccount);
int result = transferDao.updateMoney(transferAccount, transferAccountMoney.getMoney() - money);
result += transferDao.updateMoney(accessAccount, transferAccountMoney.getMoney() + money);
return result;
}
}
5) 编写转账数据库DAO层
public class TransferDao {
private final static String url = "";
private final static String user = "";
private final static String password = "";
public Account getMoneyByTransferAccount(String transferAccount) throws ClassNotFoundException, SQLException {
Account account = new Account();
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection(url, user, password);
PreparedStatement preparedStatement = connection.prepareStatement("SELECT id, money, account FROM account_money WHERE account = ?");
preparedStatement.setString(1, transferAccount);
ResultSet rs = preparedStatement.executeQuery();
while(rs.next()) {
int money = rs.getInt("money");
account.setAccount(transferAccount);
account.setMoney(money);
}
return account;
}
public int updateMoney(String transferAccount, int money) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection(url, user, password);
PreparedStatement preparedStatement = connection.prepareStatement("UPDATE account_money SET money = ? WHERE account = ?");
int i = preparedStatement.executeUpdate();
return i;
}
}
这个案例使用了传统的MVC模式开发的项目,这样子编写存在什么问题?
一、代码耦合,每一层对上一层的调用都是用了new关键字,但是如果上层技术架构改变我们还要需要改变其他层的代码,不符合开闭原则。
二、DAO层中使用的数据库连接对象Connetion不是同一个对象,意味着这里开启了两个事务,而我们希望转账的操作是在同一个事务中,这样才能保证数据的一致性。
对以上的问题我们如何去解决?
问题一:代码耦合,各层级之间存在耦合
方案一:我们可以使用设计模式的简单工厂模式与反射相结合,通过简单的参数传入反射相应对象,不直接关联该对象。因为Servlet 是单例多线程的,我们频繁的从工厂创建对象会造成性能问题,我们把创建的对象放入容器中,整个应用只存在一个实例,再需要的时候向容器获取,这样可以避免性能问题。
问题二:没有在service开启事务,并且在DAO层中开了两个事务
方案一:使用JDK代理service层对象,对代码进行拦截增强。使用代理模式和工厂模式的结合,通过工厂获取到service代理对象,对service方法进行增强,在代理对象中开启事务和提交及回滚。
改进代码:
1、编写一个XML,维护我们需要从工程拿到的对象bean
2、编写一个FactoryBean,将配置文件中维护的对象放入一个Map集合中管理
public class FactoryBean {
private static Map beanMap = new HashMap<>();
static {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
InputStream resourceAsStream = FactoryBean.class.getClassLoader().getResourceAsStream("applicationContent.xml");
Document xml = builder.parse(resourceAsStream);
NodeList beans = xml.getElementsByTagName("bean");
for (int i = 0; i < beans.getLength(); i++) {
Node item = beans.item(i);
NamedNodeMap attributes = item.getAttributes();
String id = attributes.getNamedItem("id").getNodeValue();
String classPath = attributes.getNamedItem("class").getNodeValue();
// 处理bean标签
Class> aClass = Class.forName(classPath);
Object o = aClass.newInstance();
beanMap.put(id , o);
}
NodeList property = xml.getElementsByTagName("property");
for (int i = 0; i < property.getLength(); i++) {
Node item = property.item(i);
NamedNodeMap attributes = item.getAttributes();
String name = attributes.getNamedItem("name").getNodeValue();
String ref = attributes.getNamedItem("ref").getNodeValue();
// 要注入的节点
Object diObj = beanMap.get(ref);
// 找到父类节点
Node parentNode = item.getParentNode();
NamedNodeMap parentAttributes = parentNode.getAttributes();
String id = parentAttributes.getNamedItem("id").getNodeValue();
Object o = beanMap.get(id);
Method method = o.getClass().getMethod("set" + name, diObj.getClass());
method.invoke(o, diObj);
beanMap.put(id , o);
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/**
* 根据id获取bean对象
* @param name
* @return: java.lang.Object
* @author: 壮乡码农
* @date: 2021/12/21 14:43
*/
public static Object getBean(String name) {
return beanMap.get(name);
}
}
3、修改TransactionService代码,不直接new TransferDao, 而是仅仅声明该变量,提供一个set方法,让FactoryBean启动时注入对象
public class TransferServiceImpl implements TransferService {
TransferDao transferDao;
public void setTransferDao(TransferDao transferDao) {
this.transferDao = transferDao;
}
@Override
public int transfer(String transferAccount, String accessAccount, int money) throws Exception {
Account transferAccountMoney = transferDao.getMoneyByTransferAccount(transferAccount);
Account accessAccountMoney = transferDao.getMoneyByTransferAccount(accessAccount);
int result = transferDao.updateMoney(transferAccount, transferAccountMoney.getMoney() - money);
result += transferDao.updateMoney(accessAccount, transferAccountMoney.getMoney() + money);
return result;
}
}
4、编写一个ConnectionUtil对象,让DAO层获取到的数据库连接为同一个连接
public class ConnectionUtil {
private String user = "root";
private String password = "123456";
private String driver = "com.mysql.Driver";
private String url = "jdbc.mysql://127.0.0.1:3306/test";
ThreadLocal local = new ThreadLocal();
public Connection getLocalConnect() throws ClassNotFoundException, SQLException {
Connection connection = local.get();
if (connection == null) {
Class.forName(driver);
connection = DriverManager.getConnection(url, user, password);
}
return connection;
}
}
5、将ConnectionUitl放到容器中管理
6、修改DAO层TransfernDao , 使用ConnectionUtil工具获取数据库连接
public class TransferDao {
// private final static String url = "";
// private final static String user = "";
// private final static String password = "";
ConnectionUtil connectionUtil;
public void setConnectionUtil(ConnectionUtil connectionUtil) {
this.connectionUtil = connectionUtil;
}
public Account getMoneyByTransferAccount(String transferAccount) throws ClassNotFoundException, SQLException {
Account account = new Account();
// Class.forName("com.mysql.jdbc.Driver");
// Connection connection = DriverManager.getConnection(url, user, password);
Connection connection = connectionUtil.getLocalConnect();
PreparedStatement preparedStatement = connection.prepareStatement("SELECT id, money, account FROM account_money WHERE account = ?");
preparedStatement.setString(1, transferAccount);
ResultSet rs = preparedStatement.executeQuery();
while(rs.next()) {
int money = rs.getInt("money");
account.setAccount(transferAccount);
account.setMoney(money);
}
return account;
}
public int updateMoney(String transferAccount, int money) throws ClassNotFoundException, SQLException {
// Class.forName("com.mysql.jdbc.Driver");
// Connection connection = DriverManager.getConnection(url, user, password);
Connection connection = connectionUtil.getLocalConnect();
PreparedStatement preparedStatement = connection.prepareStatement("UPDATE account_money SET money = ? WHERE account = ?");
int i = preparedStatement.executeUpdate();
return i;
}
}
7 、编写一个TrancationManger 对象,定义开启事务、提交事务、回滚事务等行为
public class TransactionManager {
private ConnectionUtil connectionUtil;
public void setConnectionUtil(ConnectionUtil connectionUtil) {
this.connectionUtil = connectionUtil;
}
public void beginTransaction() throws SQLException, ClassNotFoundException {
Connection localConnect = connectionUtil.getLocalConnect();
localConnect.setAutoCommit(false);
}
public void commit() throws SQLException, ClassNotFoundException {
Connection localConnect = connectionUtil.getLocalConnect();
localConnect.commit();
}
public void rollback() throws SQLException, ClassNotFoundException {
Connection localConnect = connectionUtil.getLocalConnect();
localConnect.rollback();
}
}
8、编写一个代理类工厂,在代理工程中开启事务,提交事务和回滚
public class ProxyFactory {
private TransactionManager transactionManager;
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public Object getProxyObject(String beanName) {
Object target = FactoryBean.getBean(beanName);
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 开启事务
transactionManager.beginTransaction();
Object o = null;
try {
o = invoke(proxy, method, args);
} catch (Exception e) {
e.printStackTrace();
// 回滚事务
transactionManager.rollback();
throw e.getCause();
} finally {
// 提交事务
transactionManager.commit();
}
return o;
}
});
}
}
通过改造之后,我们每一层的实例化对象定义在bean中,解耦了代码,通过ProxyFactory 在service开启事务。good!
1、创建一个工程,引入Spring 相关jar包jar包
jar包下载地址:JFrog
2、Spring 管理bean的几种方式(xml,xml+注解,注解)
在web.xml中配置Spring启动类和启动方式,
contentConfigLocation
classpath:application.xml
org.springframework.web.context.ContextLoaderListener
3、xml方式配置bean
引入Spring DTD约束头
使用bean标签配置Spring 管理的bean对象。
bean标签可使用的标签属性如下:
- id: bean的唯一标识
- class:bean的全限定类名
- scope:singleton、prototype、request、session、application、websocket(六个可选值)
- init-method: bean被装在后执行的初始化方法,要求是无参的
- lazy-init: 延迟加载,当getBean之后再加载,这个配置只对singleton有效
- factory-bean: bean产生的工厂
- factory-method: 工厂生产bean的方法,可以配合class属性或则factory使用,使用class属 性的工厂对象啊必须是静态方法。
- destory-method: bean对象销毁前执行,scope为singleton有效
DI-依赖注入相关的配置:
DI依赖注入的方式看,有两种方式:
从注入的类型来看:
相关标签:
1)set方法注入可以使用propety
property: bean对象中的依赖(关联)的属性,
name: 关联的属性名称
value: 属性值,这列的值一般为Java 9种变量类型
ref: 属性为一个对象,使用ref引用Spring bean 中的对象
2)构造器注入使用constructor-arg标签
constructor-arg:
name : 构造器参数名称
index:参数位置value: 构造器参数值
ref: 属性为一个对象,使用ref引用Spring bean 中的对象
复杂对象的注入,再property中使用ref 引用其他对象,再property内使用map、set、list、props标签
3)注入一个Map集合
4)注入一个Set集合
张三
18
5)注入一个List对象
张三
李四
6)注入一个Properties对象
张三
李四
上面几种集合类型中,properties、set、list类型都是使用value标签,value标签除了注入基本的数据类型之外还可以使用ref属性引用一个Spring bean管理的对象,map的类型使用entry标签向map中注入数据,也可以使用value-ref 向map中注入Spring bean管理的对象。
7)、注解方式使用bean
xml标签 | 注解 |
@Conpoment 也可以使用 @Service @Controller @Repository 这几个标签和@Conpoment 效果一致,只是在程序上更好读 |
五. Spring框架的三个高级使用
1、延迟加载Lazy_init
延迟加载的使用,只需要在
2、FactoryBean 工厂bean
FactoryBean定义了bean定义了bean的生产细节。
public interface FactoryBean {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
/**
* 获取FactoryBean对象产生的对象,如果 isSingleton() 返回true,则bean是单例的,放入容器Map中维护
* @return: T
* @author: 壮乡码农
* @date: 2021/12/21 15:20
*/
@Nullable
T getObject() throws Exception;
/**
* 获取FactoryBean创建的bena类型
* @return: Class>
* @author: 壮乡码农
* @date: 2021/12/21 15:21
*/
@Nullable
Class> getObjectType();
/**
* 是否是单例的
* @return: boolean
* @author: 壮乡码农
* @date: 2021/12/21 15:20
*/
default boolean isSingleton() {
return true;
}
}
使用案例:
1)定义一个类,这个类有FacoryBean工厂对象生产
/**
* TODO
*
* @author 壮乡码农
* @version 1.0
* @date 2021/12/21 15:30
*/
public class Product {
private String name;
private String describle;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescrible() {
return describle;
}
public void setDescrible(String describle) {
this.describle = describle;
}
}
2)定义一个工厂方法实现FactoryBean
public class ProductFactoryBean implements FactoryBean {
String productName;
@Override
public Product getObject() throws Exception {
Product product = new Product();
product.setDescrible("我是产品,通通过ProductFactoryBean生产");
product.setName(productName);
return product;
}
@Override
public Class> getObjectType() {
return Product.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
3) 修改配置文件
3)测试使用
public class FactoryBeanTest {
public static void main(String[] arg) {
ConfigurableApplicationContext configurableApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 获取实例对象
Product product = (Product) configurableApplicationContext.getBean("product");
System.out.println(product.getName());
// 获取对象类型
Object roduct = configurableApplicationContext.getBean("&product");
System.out.println(roduct.getClass().getTypeName());
}
}
3、后置处理器 BeanPostProcessor 和 BeanFactoryPostProcessor
1)BeanPostProcessor
BeanPostProcessor 针对bean进行处理,可以在两个方法中判断beanName对单个bean做处理。
public interface BeanPostProcessor {
/**
* bean容器加载了bean,在bean执行init-method之前
* @param bean
* @param beanName
* @return: Object
* @author: 壮乡码农
* @date: 2021/12/21 16:10
*/
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
/**
* bean容器加载了bean,执行了init-method方法之后执行
* @param bean
* @param beanName
* @return: Object
* @author: 壮乡码农
* @date: 2021/12/21 16:11
*/
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
2)BeanFactoryPostProcessor
BeanFactoryPostProcessor针对整个bean工厂进行处理。
public interface BeanFactoryPostProcessor {
/**
* bean实例化之后调用,这里可以修改bean的属性也可以初始化bean
* @param beanFactory
* @return: void
* @author: 壮乡码农
* @date: 2021/12/21 16:23
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
六.Spring 循环依赖的问题及处理
Spring 循环依赖就是有两个bean对象相互依赖(关联)对方。BeanA 中依赖BeanB,BeanB中依赖BeanC,BeanC中依赖BeanA最终形成了闭环。
为什么会产生循环依赖?我们有两个对象A和B,A中依赖了B,B中也依赖了A。这时候我们new A对象的时候,A依赖了B也把B实例化了,B实例化后B里面又依赖了A,又把A实例化了,如此反复,直到内存溢出。
public class A{
public B = new B();
}
public class B{
public B = new A();
}
先不分析了,头疼,忘记了,以后再分析一次。
七.Spring AOP
1、面向切面变成的基本概
概念 | 白话解释 |
连接点 | 能够增强的方法都可以是连接点 |
切入点 | 已经接入的增强方法的连接点就成为了切入点 |
切面 | 定义增强行为的类,就是切面 |
通知/增强 | 增强的方法执行的时机,如前置通知、后置通知、环绕通知、异常通知 |
目标对象 | 被代理的对象,只有被代理了才能够进行横切逻辑 |
代理 | 代理的对象 |
织入 | 把增强代码和目标对象连接起来的过程,就是织入 |
2、Spring 面向切面编程开启方式
1)引入jar包,Spring 是模块化开发,用什么就引入什么
org.springframework
spring-aop
5.1.12.RELEASE
org.aspectj
aspectjweaver
1.9.4
2) 在Spring 配置文件中添加约束头
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
3)将切面对象交给Spring管理
标签 | 说明 |
定义切面
id: 唯一的全局切面标识
ref: 引用的bean对象
|
|
前置通知 正常执⾏时通知,执行完毕后执行 后置通知 异常通知 环绕通知 |
配置通通知 methods: 增强的方法 pointcut: 切入点表达式,定义哪些类的方法可以被拦截 切入点表达式说明: public * ....(..) 从左到右,分别为 访问权限 返回值 包.类名.方法名(参数) 1)访问权限可以忽略 2)返回值可以用*表示,*代表任意返回值都可以 3)包名,包名可能是多级的如com.sql这样的形式,可以用.表示任意包 4)类名,类型可以使用.来代替,表示任意类 5)方法名,方法名可以使用.来代替,表示任意方法 6)参数,参数可以用..来代替,..代表任意参数都可以,也可以指定参数的全限定名称 |
|
开启Spring 对AOP注解的支持 |
4)面向切面使用案例
@Component // 交给Spring 为互这个类
@Aspect // 定义切面
public class LogAspect {
// 拦截切入点,拦截访问修饰符为public 任意返回值 service.Transfer
@Pointcut("execution(public * service.TransferService..(..))")
public void pointcut(){}
@Before("pointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("前置通知");
}
@After("pointcut()")
public void after() {
System.out.println("后置通知");
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) {
System.out.println("环绕通知");
//定义返回值
Object rtValue = null;
try{
//前置通知
System.out.println("前置通知");
System.out.println("环绕通知");
//1.获取参数
Object[] args = pjp.getArgs();
//2.执⾏切⼊点⽅法
rtValue = pjp.proceed(args);
//后置通知
System.out.println("后置通知");
System.out.println("环绕通知");
}catch (Throwable t){
//异常通知
System.out.println("异常通知");
t.printStackTrace();
}finally {
//最终通知
System.out.println("最终通知");
}
return rtValue;
}
@AfterThrowing
public void exception(Throwable e) {
System.out.println("异常通知");
}
@AfterReturning
public void afterReturning(Object rtValue) {
System.out.println("执行完毕后执行");
}
}
八、Spring 声明式事务
1、什么是声明事事务?
在业务代码中编写事务的代码,这样的事务是编程式事务。通过配置文件或则注解达到事务控制的目的,这个就是声明式事务
2、事务的概念
事务的四大特征
特征 | 说明 |
原子性 | 一个事务里面的操作,要么全部完成,要么都不做 |
一致性 | 一个状态改变到另外一个状态,前后的总量应该是一致的。如转账1000元,转账后两个账户的金额总额是一致的,不会凭空多出也不会平凭空减少 |
隔离性 | 一个事务不会被另外一个事务所干扰 |
持久性 | 数据库中的数据改变是永久性的 |
事务的隔离级别:
数据库事务在没有隔离级别的情况下可能发生的情况!!
脏读 | 一个事务读取了另外一个事务未提交的数据,当另外一个事务回滚后,这个事务读到的就是脏数据 |
不可重复读 | 一个事务读取了数据,数据未结束,此时另外一个事务修改了数据,这个事务在此读取该数据,得到了前后不一致的数据 |
幻读 | 一个数据读取了数据库中id<10的数据,此时事务还没有结束,另外一个数据又插入了或则删除了id<10的数据,这个事务再去读取,得到的结果集数量不一致 |
针对这几种情况,数据库中提供了集中事务的隔离级别:
读未提交 | 脏读、不可重复读、幻读都有可能发送 |
读已提交 | 可以避免脏读。但是在读事务的时候,还允许另外一个事务的进行写操作和更新操作,所以不能避免不可重复读和幻读。大部分的数据默认的事务隔离级别。 |
可重复读 | 可以避免脏读、不可可重复读的问题,一个事务中没有提交,会对update操作进行锁定,不能进行更新操作,但是不能避免幻读。MySQL默认的事务级别。 |
序列化 | 事务一个一个的执行,不能并行运行,可避免脏读、不可重复读与幻读 |
3、事务的传播
事务的隔离级别是数据库级别的,我们在编写的代码的时候,往往会存在着事务的传播问题。比如我们在service开启了事务,serviceA 调用了serviceB ,这时候就存在了事务的传播。
我们站在serviceB的角度,来看事务的传播行为:
PROPATATION_REQUIRE | 如果当前没有事务,就创建一个事务,如果当前有事务,那就加入该事务 |
PROPATATION_SUPPORTS | 如果当前有事务,就加入该事务,没有就以非事务的方式执行 |
PROPATATION_MANDRTOR | 如果有事务就加入事务,没有就抛出异常 |
PROPATATION_NEW_REQURIRE | 新建一个事务,如果当前有事务,就挂起当前事务 |
PROPATATION_NOT_SUPPORTS | 以非事务的方式执行,如果当前存在事务,就挂起当前事务 |
PROPATATION_NEVER | 以非事务的方式执行,如果存在当前事务,就抛出异常 |
PROPATATION_NESTED | 如果当前存在事务,就嵌套在当前事务中执行,没有就新建一个事务 |
4、Spring 事务的API
Spring本身不支持事务的实现,但是定义了事务的标准。
public interface PlatformTransactionManager extends TransactionManager {
/**
* 获取当前事务装填信息
* @param definition
* @return: TransactionStatus
* @author: 壮乡码农
* @date: 2021/12/22 14:40
*/
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
/**
* 提交事务
* @param status
* @return: void
* @author: 壮乡码农
* @date: 2021/12/22 14:40
*/
void commit(TransactionStatus status) throws TransactionException;
/**
* 回滚事务
* @param status
* @return: void
* @author: 壮乡码农
* @date: 2021/12/22 14:40
*/
void rollback(TransactionStatus status) throws TransactionException;
}
需要特别说明的是,这个类是一个策略类,Spring使用了策略模式,不用的框架有着不同的实现。
5、使用XML+注解的方式开启事务
1)导入jar包
org.springframework
spring-context
5.1.12.RELEASE
org.aspectj
aspectjweaver
1.9.4
org.springframework
spring-jdbc
5.1.12.RELEASE
org.springframework
spring-tx
5.1.12.RELEASE
2)将事务管理器交由Spring管理
3)开启Spring 对事务注解的支持
4)再需要启动事务的service添加@Transactional注解
@Transactional(readonly=Boolean.True, propagation= Propagation.REQUIRE)
readonly : 只读事务
propagation: 事务的传播行为