1. 程式人生 > >Springboot統一異常處理並儲存到資料庫

Springboot統一異常處理並儲存到資料庫

一.統一異常處理

之前我們平時在專案中遇到異常,用try-catch這種標準的捕獲方式處理就可以解決問題,但是在每個介面中都這麼寫,這種重複造輪子的事情對於我們程式設計師當然是不太願意的。

然而我們可以在springboot專案中利用@ControllerAdvice 這個註解可以更好的實現異常的捕獲處理。

@ControllerAdvice,是spring3.2提供的新註解,從名字上就可以看出大體的意思是控制器增強。

@Target(ElementType.TYPE)  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Component  
public @interface ControllerAdvice {  
  
} 

沒有什麼特別之處,該註解使用@Component註解,這樣的話當我們使用<context:component-scan>掃面時也能掃描到。

@ControllerAdvice註解內部可以使用@ExceptionHandler、@InitBinder、@ModelAttribute的方法應用到所有的@RequestMapping註解的方法。不過我們這裡只使用@ExceptionHandler。

.建立ExceptionController類

建立 ExceptionController,並新增 @ControllerAdvice註解。 

package com.left.config;

import com.left.druid.utils.Exception.ErrorLogsUtil;
import com.left.enums.CodeEnums;
import com.left.pub.LpayException;
import com.left.pub.ResponseData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

/**
 * Created by pillar on 2017/3/27.
 * qq 347637454
 */
@ControllerAdvice
public class ExceptionHandlers {
    Logger logger = LoggerFactory.getLogger(ExceptionHandlers.class);
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseData handle(Exception e) {
        ErrorLogsUtil.error(e); //將異常寫入資料庫
        if (e instanceof LpayException) {
            LpayException pillarException = (LpayException) e;
            return new ResponseData(pillarException.getStatus());
        }else if (e instanceof MethodArgumentTypeMismatchException){
            return new ResponseData(CodeEnums.PARAM_TYPE_ERROR,(Object)((MethodArgumentTypeMismatchException) e).getName());
        }else {
            logger.info(e.toString());
            e.printStackTrace();
            return new ResponseData(CodeEnums.ERROR);
        }
    }
}

其中ResponseData類為封裝的統一返回物件。

ResponseData:

package com.left.pub;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.left.enums.CodeEnums;

import java.io.Serializable;

/**
 *
 * @date 2017/3/27
 * qq 347637454
 * 統一返回物件
 */
public class ResponseData implements Serializable{

    /**
     * 返回資料
     */
    private Object data;
    /**
     * 錯誤描述
     */
    private String msg;
    private int code;
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private String sign;

    public String getSign() {
        return sign;
    }

    public void setSign(String sign) {
        this.sign = sign;
    }

    public ResponseData(CodeEnums status) {
        this.msg = status.getMessage();
        this.code = status.getCode();
    }

    public ResponseData() {
    }

    public ResponseData(Object data,String api_key){
        this.data = data;
        this.msg = CodeEnums.SUCCESS.getMessage();
        this.code = CodeEnums.SUCCESS.getCode();
        this.sign = api_key;
    }
    public ResponseData(CodeEnums enums,Object data) {
        this.data = data;
        this.msg = enums.getMessage();
        this.code = enums.getCode();
    }
    public ResponseData(int code,String msg){
        this.code =code;
        this.msg = msg;
    }



    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    @Override
    public String toString() {
        return "ResponseData{" +
                "data=" + data +
                ", msg='" + msg + '\'' +
                ", code=" + code +
                ", sign='" + sign + '\'' +
                '}';
    }
}

其中還有句程式碼為將異常寫入資料庫,需要ErrorLogsUtil.java工具類

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * @Date 14:29 2018/05/29
 * @Description  錯誤日誌工具
 */
public class ErrorLogsUtil {

    /**
     * @param: [e]
     * @return  void
     * @Description  列印錯誤日誌並儲存到資料庫
     */
    public static void error(Exception e) {
        StackTraceElement stackTraceElement= e.getStackTrace()[0];
        Connection con = null;
        try {
            Class.forName(MySqlConfig.driver);
            con = (Connection) DriverManager.getConnection(MySqlConfig.url, MySqlConfig.username, MySqlConfig.password);
        } catch (SQLException e1) {
            e1.printStackTrace();
        } catch (ClassNotFoundException e1) {
            e1.printStackTrace();
        }
        PreparedStatement ps = null;
        String sql = "INSERT INTO itc_error_logs VALUES (UUID(), NOW(), ?, ?)";
        try {
            ps = con.prepareStatement(sql);
            //列印日誌,錯在第幾行
            String errorInfo = e.toString()+",errorMassage:"+stackTraceElement+","+"errorLine:"+stackTraceElement.getLineNumber();
            ps.setString(1, errorInfo);
            ps.execute();
        } catch (SQLException e1) {
            e1.printStackTrace();
        } finally {
            try {
                if(ps != null) {
                    ps.close();
                }
                if(con != null) {
                    con.close();
                }
            } catch (SQLException e1) {
                e1.printStackTrace();
            }

        }
    }
}

MySqlConfig類:

import com.alibaba.druid.pool.DruidDataSource;
import com.left.druid.utils.DataSourceEnum;
import com.left.druid.utils.LocalThreadDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**********************************************
 * @author pillar
 * @date ${DATE}
 **********************************************/
@Configuration
@MapperScan(basePackages = MySqlConfig.PACKAGE)
public class MySqlConfig{

    static final String PACKAGE = "com.left";

    @Value("${spring.datasource.url}")
    public static String url;
 
    @Value("${spring.datasource.username}")
    public static String user;
 
    @Value("${spring.datasource.password}")
    public static String password;

    @Value("${spring.datasource.driverClassName}")
    public static String driverClass;

    @Value("${spring.datasource.initialSize}")
    private int initialSize;

    @Value("${spring.datasource.minIdle}")
    private int minIdle;

    @Value("${spring.datasource.maxActive}")
    private int maxActive;

    @Value("${spring.datasource.filters}")
    private String filters;

    @Value("${spring.datasource.publicKey}")
    private String publicKey;}

這樣後臺的異常資訊就可以統一儲存到資料庫了。

最後如果你們公司的前端夠閒的話,最好是做個前端頁面展示,這樣就不用連資料庫檢視,直接在前端展示,更為直觀方便。