一,背景描述
对于方法A,做一个切面,前后记录日志,如果这个方法A事务包裹,此时会发生什么,且看代码
二,上代码
1,定义一个切面
less
@Component
@Aspect
@Slf4j
public class logAspect {
@Resource
LogService logService;
@Pointcut("execution(public * com.service.ShopResultService.syn*(..))")
public void logPointCut() {
}
@Around("logPointCut()")
public void process(ProceedingJoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
LogRecord record = logService.start(name);
try {
joinPoint.proceed();
logService.success(record);
} catch (Throwable throwable) {
logService.fail(record);
}
}
}
定义一个日志切面,里面有logPointCut切点,对于切点,在方法前后打日志,LogRecord对应mysql表
2,日志服务
csharp
@Service
public class LogService {
@Autowired
LogRecordMapper logRecordhMapper;
/**
* 新增
*/
public LogRecord start(String taskDef) {
//新增日志
LogRecord record = new LogRecord();
record.setProjectNo(taskDef);
record.setStatus("doing");
logRecordhMapper.insert(record);
return record;
}
/**
* 成功
*/
public void success(LogRecord record) {
//更新日志
auth.setStatus("success");
logRecordhMapper.updateById(record);
}
/**
* 失败
*/
public void fail(LogRecord record) {
//失败日志
auth.setStatus("fail");
logRecordhMapper.updateById(record);
}
}
如果任务执行成功,日志更新为success,否则是fail。
3,被切的服务
csharp
@Transactional
public void synData() {
UserData userData = new UserData();
userData.setShopName("test");
UserDataMapper.insert(userData);
System.out.println("over");
}
4,问题
arduino
public LogRecord start(String taskDef)
发现日志方法虽然没有加事务,但其实是在事务里的,也就是说日志方法记录完成后,表暂态是没有记录的,此时日志是被切面事务包裹在一起,也就是说,如果synData失败,切面也会抛异常,因为update一条数据库不存在的记录。
三,结论
spring事务和切面都是通过代理完成,代理链的设计导致这两个逻辑绑定在一起
解决方案:1,使用注解,切面在事务之前执行
java
@Order(Ordered.LOWEST_PRECEDENCE - 1)
less
@Component
@Aspect
@Slf4j
@Order(Ordered.LOWEST_PRECEDENCE - 1)
public class logAspect {
@Resource
LogService logService;
@Pointcut("execution(public * com.service.ShopResultService.syn*(..))")
public void logPointCut() {
}
@Around("logPointCut()")
public void process(ProceedingJoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
LogRecord record = logService.start(name);
try {
joinPoint.proceed();
logService.success(record);
} catch (Throwable throwable) {
logService.fail(record);
}
}
}
解决方案2,使用spring事务隔离级别,将两个事务隔离开
csharp
@Service
public class LogService {
@Autowired
LogRecordMapper logRecordhMapper;
/**
* 新增
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public LogRecord start(String taskDef) {
//新增日志
LogRecord record = new LogRecord();
record.setProjectNo(taskDef);
record.setStatus("doing");
logRecordhMapper.insert(record);
return record;
}
/**
* 成功
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void success(LogRecord record) {
//更新日志
auth.setStatus("success");
logRecordhMapper.updateById(record);
}
/**
* 失败
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void fail(LogRecord record) {
//失败日志
auth.setStatus("fail");
logRecordhMapper.updateById(record);
}
}