springboot自定義攔截器實現異常的統一捕獲和處理

前言

每個方法中可能會拋出不同的異常,如果都是用try catch去處理,顯得非常冗餘,可以通過spring提供的@ExceptionHandler註解來實現異常的統一封裝和處理


攔截器的實現:

@RestControllerAdvice
public class CommonExceptionHandler {
    /**
     * 攔截Exception類的異常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public Object exceptionHandler(Exception e) {
        e.printStackTrace();
        return Result.result(0, e.getMessage());
    }

    /**
     * 攔截 CommonException 的異常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(CommonException.class)
    public Object exceptionHandler(CommonException ex) {
        ex.printStackTrace();
        return Result.result(ex.getCode(), ex.getMsg());
    }

    @ExceptionHandler(UnauthorizedException.class)
    public Object UnauthorizedExceptionHandler(UnauthorizedException ex) {
        return Result.result(ApiResultEnum.UNAUTHORIZED);
    }

    @ExceptionHandler(AuthorizationException.class)
    public Object AuthorizationExceptionHandler(AuthorizationException ex) {
        return Result.result(0, ex.getMessage());
    }

    @ExceptionHandler(AuthenticationException.class)
    public Object AuthenticationExceptionHandler(AuthenticationException ex) {
        return Result.result(0, ex.getMessage());
    }

    @ExceptionHandler(ParseException.class)
    public Object ParseExceptionHandler(ParseException ex) {
        return Result.result(ApiResultEnum.DATE_FORMAT_ERROR);
    }
}

原理:比如當觸發一個接口後,控制器調用服務類裏面的方法saveUserInfo,saveUserInfo調用test1,test1拋出一個運行時異常。
這時就會被**@RestControllerAdvice註解的異常處理類接收到,這個類會根據@ExceptionHandler**指定的異常,映射到對應的方法中。


這樣的話控制器和service層都不需要處理異常,全部交由攔截器來處理
並且可以在攔截器中自定義接口處理完異常後返回的數據格式和內容

事務回滾也是可以進行全局配置的,springboot配置全局事物管理如下:

import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.*;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;


@Aspect
@Configuration
public class TransactionAdviceConfig {
    /**
     * 定義切點路徑
     */
    private static final String AOP_POINTCUT_EXPRESSION = "execution(* com.jqyd.yundukao..*(..))";

    @Autowired
    private PlatformTransactionManager transactionManager;

    /**
     * @description 事務管理配置
     */
    @Bean
    public TransactionInterceptor TxAdvice() {
        // 事務管理規則,承載需要進行事務管理的方法名(模糊匹配)及設置的事務管理屬性
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();

        // 設置第一個事務管理的模式(適用於“增刪改”)
        RuleBasedTransactionAttribute transactionAttribute1 = new RuleBasedTransactionAttribute();
        // 當拋出設置的對應異常後,進行事務回滾(此處設置爲“Exception”級別)
        transactionAttribute1.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        // 設置隔離級別(存在事務則加入其中,不存在則新建事務)
        transactionAttribute1.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 設置傳播行爲(讀已提交的數據)
        transactionAttribute1.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);

        // 設置第二個事務管理的模式(適用於“查”)
        RuleBasedTransactionAttribute transactionAttribute2 = new RuleBasedTransactionAttribute();
        // 當拋出設置的對應異常後,進行事務回滾(此處設置爲“Exception”級別)
        transactionAttribute2.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        // 設置隔離級別(存在事務則掛起該事務,執行當前邏輯,結束後再恢復上下文事務)
        transactionAttribute2.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
        // 設置傳播行爲(讀已提交的數據)
        transactionAttribute2.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        // 設置事務是否“只讀”(非必需,只是聲明該事務中不會進行修改數據庫的操作,可減輕由事務造成的數據庫壓力,屬於性能優化的推薦配置)
        transactionAttribute2.setReadOnly(true);

        // 建立一個map,用來儲存要需要進行事務管理的方法名(模糊匹配)
        Map<String, TransactionAttribute> txMap = new HashMap<>();
        txMap.put("save*", transactionAttribute1);
        txMap.put("update*", transactionAttribute1);
        txMap.put("delete*", transactionAttribute1);
        txMap.put("agree*", transactionAttribute1);
        txMap.put("reject*", transactionAttribute1);
        txMap.put("approval*", transactionAttribute1);
        txMap.put("query*", transactionAttribute2);

        // 注入設置好的map
        source.setNameMap(txMap);
        // 實例化事務攔截器
        return new TransactionInterceptor(transactionManager, source);
    }

    /**
     * @description 利用AspectJExpressionPointcut設置切面
     */
    @Bean
    public Advisor txAdviceAdvisor() {
        // 聲明切點要切入的面
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        // 設置需要被攔截的路徑
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        // 設置切面和配置好的事務管理
        return new DefaultPointcutAdvisor(pointcut, TxAdvice());
    }
}

然後service層使用Transactional註解即可實現事務回滾
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章