事务的使用
在spring项目的开发中,通过在方法上添加transactional注解,实现事务的管理,在方法开始开启事务,出现异常进行事务的回滚,方法结束前提交事务。
事务的实现原理
transactional 注解是 spring 框架用来实现声明式事务管理的重要工具。其原理主要基于 aop(面向切面编程),通过动态代理在方法执行前、后以及异常情况下进行事务的处理。
当你在一个方法上使用 @transactional 注解时,spring 会在运行时生成一个代理对象,该对象会拦截对该方法的调用。
在调用之前,代理会开始一个新的事务;在方法执行完成后,代理会提交或回滚事务,具体取决于方法是否抛出了未处理的异常。
具体流程如下:
- spring使用jdk动态代理技术或者cglib代理来创建目标类的代理对象
- 当方法被调用时,实际上调用的是代理类中的增强方法,而不是直接调用目标类中的方法
- 在调用方法之前,代理会根据注解的属性(传播行为和隔离级别)从
platformtransactionmanager中获取一个事务,在调用目标方法前开启事务 - 执行目标方法
- 目标方法执行成功,则提交事务,执行失败,则回滚事务
代理的两种方式:
- jdk 动态代理:当目标类实现至少一个接口时,spring会使用jdk动态代理。这种代理仅适用于基于接口的代理。原理是通过java反射机制,在运行时生成一个实现了目标类接口的代理类。
- cglib 代理:如果目标类没有实现任何接口,或者您强制配置为使用cglib,那么spring会使用cglib库生成一个目标类的子类作为代理。原理是使用cglib库,通过继承目标类并重写其方法来实现代理。
原因
在同一个类中,一个无事务方法直接调用有事务的方法时,是通过this.方法名的方式调用。
this方式:它直接访问的是当前对象的实现,如果当前类被spring aop代理,那么使用this调用的方法将不会触发aop切面。也就是说,切面(如事务管理、日志记录等)不会生效,因为你直接调用了目标对象的方法,而不是代理对象的方法。
通过autowired注解注入的bean进行调用的方式:是通过spring容器管理的代理对象进行调用,这种情况下aop特性可以正常工作,例如事务、日志等会生效。
spring容器管理的代理对象的生成条件和时机
在spring中,代理对象的生成通常与aop(面向切面编程)相关,当类上使用@component、@service、@repository或@controller等注解,并且方法上使用aop相关的注解时,spring会创建代理对象,以便能够在调用这些方法时执行切面逻辑。
常见的aop注解包括:
@transactional@cacheable@async@scheduled
事务失效代码
package com.ruoyi.system.service.impl;
import com.ruoyi.common.core.domain.entity.sysdept;
import com.ruoyi.system.domain.sysconfig;
import com.ruoyi.system.mapper.sysconfigmapper;
import com.ruoyi.system.mapper.sysdeptmapper;
import com.ruoyi.system.service.testtransactionalservice;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.service;
import org.springframework.transaction.annotation.transactional;
/**
* @author linaibo
* @date 2024/8/3 15:36
* @version 1.0
*/
@service
public class testtransactionalserviceimpl implements testtransactionalservice {
@autowired
private sysdeptmapper deptmapper;
@autowired
private sysconfigmapper configmapper;
@override
public int insertdept(sysdept dept) {
dept.setancestors("123123");
deptmapper.insertdept(dept);
insertconfig();
return 1;
}
@override
@transactional(rollbackfor = exception.class)
public int insertconfig() {
sysconfig config;
for (int i = 0; i < 5; i++) {
config = new sysconfig();
config.setconfigname("配置" + i);
configmapper.insertconfig(config);
if (i == 2) {
throw new runtimeexception();
}
}
return 1;
}
}解决方法
①自己autowire自己(也可以将方法放到另外一个service中,然后注入该service进行调用)
package com.ruoyi.system.service.impl;
import com.ruoyi.common.core.domain.entity.sysdept;
import com.ruoyi.system.domain.sysconfig;
import com.ruoyi.system.mapper.sysconfigmapper;
import com.ruoyi.system.mapper.sysdeptmapper;
import com.ruoyi.system.service.testtransactionalservice;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.service;
import org.springframework.transaction.annotation.transactional;
/**
* @author linaibo
* @date 2024/8/3 15:36
* @version 1.0
*/
@service
public class testtransactionalserviceimpl implements testtransactionalservice {
@autowired
private sysdeptmapper deptmapper;
@autowired
private sysconfigmapper configmapper;
@autowired
private testtransactionalservice testtransactionalservice;
@override
public int insertdept(sysdept dept) {
dept.setancestors("123123");
deptmapper.insertdept(dept);
testtransactionalservice.insertconfig();
return 1;
}
@override
@transactional(rollbackfor = exception.class)
public int insertconfig() {
sysconfig config;
for (int i = 0; i < 5; i++) {
config = new sysconfig();
config.setconfigname("配置" + i);
configmapper.insertconfig(config);
if (i == 2) {
throw new runtimeexception();
}
}
return 1;
}
}②通过spring上下文获取到当前代理类
package com.ruoyi.system.service.impl;
import com.ruoyi.common.core.domain.entity.sysdept;
import com.ruoyi.system.domain.sysconfig;
import com.ruoyi.system.mapper.sysconfigmapper;
import com.ruoyi.system.mapper.sysdeptmapper;
import com.ruoyi.system.service.testtransactionalservice;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.context.applicationcontext;
import org.springframework.stereotype.service;
import org.springframework.transaction.annotation.transactional;
/**
* @author linaibo
* @date 2024/8/3 15:36
* @version 1.0
*/
@service
public class testtransactionalserviceimpl implements testtransactionalservice {
@autowired
private sysdeptmapper deptmapper;
@autowired
private sysconfigmapper configmapper;
@autowired
private testtransactionalservice testtransactionalservice;
@autowired
private applicationcontext applicationcontext;
@override
public int insertdept(sysdept dept) {
dept.setancestors("123123");
deptmapper.insertdept(dept);
testtransactionalservice service = applicationcontext.getbean(testtransactionalservice.class);
service.insertconfig();
return 1;
}
@override
@transactional(rollbackfor = exception.class)
public int insertconfig() {
sysconfig config;
for (int i = 0; i < 5; i++) {
config = new sysconfig();
config.setconfigname("配置" + i);
configmapper.insertconfig(config);
if (i == 2) {
throw new runtimeexception();
}
}
return 1;
}
}③使用aopcontext获取到当前代理类,需要在启动类加上enableaspectjautoproxy(exposeproxy = true),exposeproxy = true用于控制aop框架公开代理,公开后才可以通过aopcontext获取到当前代理类。
package com.ruoyi.system.service.impl;
import com.ruoyi.common.core.domain.entity.sysdept;
import com.ruoyi.system.domain.sysconfig;
import com.ruoyi.system.mapper.sysconfigmapper;
import com.ruoyi.system.mapper.sysdeptmapper;
import com.ruoyi.system.service.testtransactionalservice;
import org.springframework.aop.framework.aopcontext;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.context.applicationcontext;
import org.springframework.stereotype.service;
import org.springframework.transaction.annotation.transactional;
import java.util.objects;
/**
* @author linaibo
* @date 2024/8/3 15:36
* @version 1.0
*/
@service
public class testtransactionalserviceimpl implements testtransactionalservice {
@autowired
private sysdeptmapper deptmapper;
@autowired
private sysconfigmapper configmapper;
@override
public int insertdept(sysdept dept) {
dept.setancestors("123123");
deptmapper.insertdept(dept);
testtransactionalservice service = objects.nonnull(aopcontext.currentproxy()) ? (testtransactionalservice)aopcontext.currentproxy() : this;
service.insertconfig();
return 1;
}
@override
@transactional(rollbackfor = exception.class)
public int insertconfig() {
sysconfig config;
for (int i = 0; i < 5; i++) {
config = new sysconfig();
config.setconfigname("配置" + i);
configmapper.insertconfig(config);
if (i == 2) {
throw new runtimeexception();
}
}
return 1;
}
}总结
使用 aop 注解:
- 如果
testtransactionalservice类上使用了 aop 相关的注解(如@transactional,@aspect,@around等) - 通过
applicationcontext.getbean(testtransactionalservice.class)获取的对象将是一个代理对象
代理对象的创建是为了在方法调用前后加入额外的行为,比如事务管理、日志记录等。
未使用 aop 注解:
- 如果该类没有任何 aop 相关的注解
- 获取的对象就是普通的 bean
- 没有经过 aop 的增强
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论