springboot,全局异常处理,AOP,对所有地方不单单是controller,捕获后事务回滚,但是保存异常发生记录
背景:
业务场景是对外接口调用失败,或者部分代码块执行失败,在系统中能统一记录下来。
方案
- 自定义异常
- 自定义注解
- 编写切面
在切面中,将异常信息记录到表中。
1.自定义异常
java
public class OutApiException extends RuntimeException{
private String exceptionType;
private String exceptionInfo;
private String exceptionParam;
public OutApiException(String message) {
super(message);
}
public OutApiException(String exceptionType,String exceptionInfo,String exceptionParam,String message) {
super(message);
this.exceptionType = exceptionType;
this.exceptionInfo = exceptionInfo;
this.exceptionParam = exceptionParam;
}
public String getExceptionType() {
return exceptionType;
}
public void setExceptionType(String exceptionType) {
this.exceptionType = exceptionType;
}
public String getExceptionInfo() {
return exceptionInfo;
}
public void setExceptionInfo(String exceptionInfo) {
this.exceptionInfo = exceptionInfo;
}
public String getExceptionParam() {
return exceptionParam;
}
public void setExceptionParam(String exceptionParam) {
this.exceptionParam = exceptionParam;
}
}
2.自定义注解
java
import java.lang.annotation.*;
@Documented
@Target({ElementType.METHOD, ElementType.TYPE}) //可在类或者方法使用
@Retention(RetentionPolicy.RUNTIME)
public @interface OutApiServiceExceptionCatch {
}
3.切面编写
切面中那么多的代码,其实就是捕获对应注解下的,指定异常
其他的代码是我要存储到表中
为什么用jdbc?
因为我的项目用的mybatis,但是mybatis事务对运行时一行会全部回滚。
所有我单开了一个事务,这样其他的事务回滚了,但是异常可以正常存表。
cpp
import com.qygx.framework.exe.OutApiException;
import com.qygx.framework.exe.OutApiServiceExceptionCatch;
import com.qygx.mes.ding.domain.OaTask;
import com.qygx.mes.ding.mapper.OaTaskMapper;
import com.qygx.mes.ding.service.IOaTaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import springfox.documentation.service.ResponseMessage;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@Component
@Aspect
@Slf4j
public class ServiceExceptionHandler {
@Autowired
private IOaTaskService oaTaskService;
@Autowired
private SqlSessionFactory sqlSessionFactory;
@Autowired
private DataSource dataSource;
@Around("@annotation(com.qygx.framework.exe.OutApiServiceExceptionCatch) || @within(com.qygx.framework.exe.OutApiServiceExceptionCatch)")
public ResponseEntity<String> serviceExceptionHandler(ProceedingJoinPoint proceedingJoinPoint) {
ResponseMessage returnMsg;
try {
returnMsg = (ResponseMessage) proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
log.error("ServiceExcepHandler serviceExcepHandler failed", throwable);
//单独处理缺少参数异常
if(throwable instanceof OutApiException) {
Connection conn = null;
try {
// 从数据源获取连接,并设置自动提交为false
conn = dataSource.getConnection();
conn.setAutoCommit(false);
String sql = "INSERT INTO test(oa_type, param) VALUES (?, ?)";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "value1");
pstmt.setString(2, "value2");
pstmt.executeUpdate();
conn.commit();
} catch (Exception e) {
if (conn != null) {
try {
// 如果有异常发生,则回滚事务
conn.rollback();
} catch (SQLException ex) {
log.error("Failed to rollback transaction", ex);
}
}
throw new RuntimeException("Insert operation failed using JDBC", e);
} finally {
if (conn != null) {
try {
// 关闭数据库连接
conn.close();
} catch (SQLException ex) {
log.error("Failed to close database connection", ex);
}
}
}
throw new RuntimeException();
}else{
throw new RuntimeException();
}
}
throw new RuntimeException();
}
}
4测试
添加注解,抛出指定异常
java
@Transactional
@GetMapping("/test")
public void testLogin() throws OutApiException {
OaTask oaTask = new OaTask();
oaTask.setParam("test0314");
oaTaskService.insertOaTask(oaTask);
throw new OutApiException("123");
}
断点进到切面里面了
并且表中只保留了异常数据,;另一个事务回滚了