springboot 统一异常处理 + 日志记录

这篇具有很好参考价值的文章主要介绍了springboot 统一异常处理 + 日志记录。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

      在项目的开发中,在某些情况下,比如非业务的操作,日志记录,权限认证和异常处理等。我们需要对客户端发出的请求进行拦截,常用的API拦截方式有Fliter,Interceptor,ControllerAdvice以及Aspect。先简单介绍一下不同的拦截方式。

一.拦截方式

过滤器:Filter

可以获得Http原始的请求和响应信息,但是拿不到响应方法的信息。

拦截器:Interceptor

可以获得Http原始的请求和响应信息,也拿得到响应方法的信息,但是拿不到方法响应中参数的值。

ControllerAdvice(Controller增强,自spring3.2的时候推出)

主要是用于全局的异常拦截和处理,这里的异常可以使自定义异常也可以是JDK里面的异常,用于处理当数据库事务业务和预期不同的时候抛出封装后的异常,进行数据库事务回滚,并将异常的显示给用户。

切片:Aspect

主要是进行公共方法的,可以拿得到方法响应中参数的值,但是拿不到原始的Http请求和相对应响应的方法。

二.正文

        在开发过程中,有时候一个业务调用链场景,很长,调了各种各样的方法,看日志的时候,各个接口的日志穿插,确实让人头大 。我们需要把同一次的业务调用链上的日志串起来。于是就引出了日志码,在日志打印时输出一个唯一标识(比如UUID)。如下图所示:

springboot 统一异常处理 + 日志记录

我们可以使用 MDC(Mapped Diagnostic Context)诊断上下文映射来报错日志码,MDC是@Slf4j提供的一个支持动态打印日志信息的工具。话不多说,直接上代码。

pom.xml 依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
        <!--lombok配置-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
        </dependency>
    </dependencies>

logback-spring.xml 

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!--日志存储路径-->
    <property name="log" value="logs" />
    <property name="logName" value="business" />
    <!-- 控制台输出 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--输出格式化-->
            <pattern>[%X{TRACE_ID}]  %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 按天生成日志文件 -->
    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件名-->
            <FileNamePattern>${log}/${logName}%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--保留天数-->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>[%X{TRACE_ID}]  %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    <!-- 日志输出级别 -->
    <root level="INFO">
        <appender-ref ref="console" />
        <appender-ref ref="file" />
    </root>
</configuration>

application.yml 

server:
  port: 8080
logging:
  config: classpath:logback-spring.xml

日志切面WebLogAspect

package com.business.aop;

import java.util.Arrays;
import java.util.Enumeration;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import com.business.util.ThreadMdcUtil;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Aspect
@Component
@Slf4j
public class WebLogAspect {
    @Pointcut("execution(public * com.business.controller..*.*(..))")
    public void webLog() {

    }

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) {
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        String tid = UUID.randomUUID().toString().replace("-", "");
        //可以考虑让客户端传入链路ID,但需保证一定的复杂度唯一性;如果没使用默认UUID自动生成
        if (!StringUtils.isEmpty(request.getHeader(ThreadMdcUtil.getTraceId()))){
            tid=request.getHeader("TRACE_ID");
        }
        MDC.put(ThreadMdcUtil.getTraceId(), tid);

        // 记录下请求内容
        log.info("---------------request----------------");
        log.info("请求路径 : " + request.getRequestURL().toString()); //URL : request.getRequestURL().toString()
        log.info("请求方式 : " + request.getMethod()); //HTTP_METHOD : request.getMethod()
        log.info("访问者IP : " + request.getRemoteAddr()); //IP : request.getRemoteAddr()
        log.info("CLASS_METHOD:" + joinPoint.getSignature().getDeclaringTypeName() + "-" + joinPoint.getSignature().getName());
        log.info("ARGS:" + Arrays.toString(joinPoint.getArgs()));

        Enumeration enu = request.getParameterNames();
        while (enu.hasMoreElements()) {
            String name = (String) enu.nextElement();
            log.info("请求参数:" + name + " - 请求值:" + request.getParameter(name));
        }
    }

    @AfterReturning("webLog()")
    public void doAfterReturning() {
        MDC.remove(ThreadMdcUtil.getTraceId());
    }

}

子线程丢失trackId处理

        子线程会丢失trackId,需要进行处理。思路: 将父线程的trackId传递下去给子线程即可。

ThreadPoolConfig线程池

定义线程池,交给spring管理。

package com.business.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import java.util.concurrent.Executor;

/**
 * Author: lgq
 * Date: 2023-4-12 11:07
 * Description: 定义线程池,交给spring管理
 */
@Configuration
@EnableAsync
public class ThreadPoolConfig {
    /**
     * 声明一个线程池
     *
     * @return 执行器
     */
    @Bean("MyExecutor")
    public Executor asyncExecutor() {
        MyThreadPoolTaskExecutor executor = new MyThreadPoolTaskExecutor();
        //核心线程数5:线程池创建时候初始化的线程数
        executor.setCorePoolSize(5);
        //最大线程数5:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setMaxPoolSize(5);
        //缓冲队列500:用来缓冲执行任务的队列
        executor.setQueueCapacity(500);
        //允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
        executor.setKeepAliveSeconds(60);
        //线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
        executor.setThreadNamePrefix("asyncSimple");
        executor.initialize();
        return executor;
    }
}

重写MyThreadPoolTaskExecutor

package com.business.config;

import org.slf4j.MDC;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;

import com.business.util.ThreadMdcUtil;

/**
 * Author: lgq
 * Date: 2023-4-12 11:07
 * Description: 重写ThreadPoolTaskExecutor
 */
public final class MyThreadPoolTaskExecutor  extends ThreadPoolTaskExecutor  {
    public MyThreadPoolTaskExecutor() {
        super();
    }

    @Override
    public void execute(Runnable task) {
        super.execute(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
    }


    @Override
    public <T> Future<T> submit(Callable<T> task) {
        return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
    }

    @Override
    public Future<?> submit(Runnable task) {
        return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
    }
}

ThreadMdcUtil工具类

package com.business.util;

import org.slf4j.MDC;

import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;

/**
 * Author: lgq
 * Date: 2023-4-12 11:07
 * Description: ThreadMDC工具类
 */
public final class ThreadMdcUtil {
    private static final String TRACE_ID = "TRACE_ID";

    public static String getTraceId() {
        return TRACE_ID;
    }

    // 获取唯一性标识
    public static String generateTraceId() {
        return UUID.randomUUID().toString();
    }

    public static void setTraceIdIfAbsent() {
        if (MDC.get(TRACE_ID) == null) {
            MDC.put(TRACE_ID, generateTraceId());
        }
    }

    /**
     * 用于父线程向线程池中提交任务时,将自身MDC中的数据复制给子线程
     *
     * @param callable
     * @param context
     * @param <T>
     * @return
     */
    public static <T> Callable<T> wrap(final Callable<T> callable, final Map<String, String> context) {
        return () -> {
            if (context == null) {
                MDC.clear();
            } else {
                MDC.setContextMap(context);
            }
            setTraceIdIfAbsent();
            try {
                return callable.call();
            } finally {
                MDC.clear();
            }
        };
    }

    /**
     * 用于父线程向线程池中提交任务时,将自身MDC中的数据复制给子线程
     *
     * @param runnable
     * @param context
     * @return
     */
    public static Runnable wrap(final Runnable runnable, final Map<String, String> context) {
        return () -> {
            if (context == null) {
                MDC.clear();
            } else {
                MDC.setContextMap(context);
            }
            setTraceIdIfAbsent();
            try {
                runnable.run();
            } finally {
                MDC.clear();
            }
        };
    }
}

统一异常处理类

需要注意是,AfterThrowing 优先于 ExceptionHandler,因此在ExceptionHandler中移除traceId。

package com.business.common;


import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import com.business.exception.BusinessException;
import com.business.exception.ParamException;
import com.business.pojo.response.ResponseResult;
import com.business.util.ThreadMdcUtil;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ResponseBody;


@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    /**
     * 系统未知错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = Error.class)
    @ResponseBody
    public ResponseResult errorHandler(Error ex) {
        String logCode = MDC.get(ThreadMdcUtil.getTraceId());
        if (ObjectUtils.isEmpty(logCode)) {
            ThreadMdcUtil.setTraceIdIfAbsent();
            logCode = MDC.get(ThreadMdcUtil.getTraceId());
        }
        log.error("错误码:{}, 未知错误", logCode, ex);
        MDC.remove(ThreadMdcUtil.getTraceId());
        return ResponseResult.fail("系统异常,请联系管理员", logCode);
    }

    /**
     * 系统未知异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseResult exceptionHandler(Exception ex) {
        String logCode = MDC.get(ThreadMdcUtil.getTraceId());
        if (ObjectUtils.isEmpty(logCode)) {
            ThreadMdcUtil.setTraceIdIfAbsent();
            logCode = MDC.get(ThreadMdcUtil.getTraceId());
        }
        log.error("错误码:{}, 未知异常", logCode, ex);
        MDC.remove(ThreadMdcUtil.getTraceId());
        return ResponseResult.fail("系统异常,请联系管理员", logCode);
    }


    /**
     * 业务异常
     */
    @ExceptionHandler(value = BusinessException.class)
    @ResponseBody
    public ResponseResult handleBusinessException(BusinessException e) {
        String logCode = ThreadMdcUtil.generateTraceId();
        log.error("错误码:{}, 业务处理异常:{}", logCode, e.getMessage(), e);
        MDC.remove(ThreadMdcUtil.getTraceId());
        return ResponseResult.fail(logCode, CommonStatusEnum.BUSINESS_ERROR.getCode(),
                CommonStatusEnum.BUSINESS_ERROR.getValue());
    }

    /**
     * 业务参数异常
     */
    @ExceptionHandler(value = ParamException.class)
    @ResponseBody
    public ResponseResult handleParamException(ParamException e) {
        String logCode = ThreadMdcUtil.generateTraceId();
        log.error("错误码:{}, 业务参数处理异常:{}", logCode, e.getMessage(), e);
        MDC.remove(ThreadMdcUtil.getTraceId());
        return ResponseResult.fail(logCode, CommonStatusEnum.PARAM_ERROR.getCode(),
                CommonStatusEnum.BUSINESS_ERROR.getValue());
    }

    /**
     * 参数校验(Valid)异常
     */
    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    @ResponseBody
    public ResponseResult handleValidException(MethodArgumentNotValidException e) {
        String logCode = ThreadMdcUtil.generateTraceId();
        if (ObjectUtils.isEmpty(logCode)) {
            logCode = ThreadMdcUtil.generateTraceId();
        }
        log.error("错误码:{},数据校验异常:{},异常类型:{}", logCode, e.getMessage(), e.getClass(), e);
        MDC.remove(ThreadMdcUtil.getTraceId());
        BindingResult bindingResult = e.getBindingResult();
        Map<String, String> errorMap = getErrorMap(bindingResult);
        return ResponseResult.fail(logCode, CommonStatusEnum.PARAM_ERROR.getCode(), CommonStatusEnum.PARAM_ERROR.getValue(), errorMap);
    }

    /**
     * 参数绑定异常
     */
    @ExceptionHandler(value = {BindException.class})
    @ResponseBody
    public ResponseResult handleValidException(BindException e) {
        String logCode = ThreadMdcUtil.generateTraceId();
        log.error("错误码:{}, 数据校验异常:{},异常类型:{}", logCode, e.getMessage(), e.getClass(), e);
        MDC.remove(ThreadMdcUtil.getTraceId());
        BindingResult bindingResult = e.getBindingResult();
        Map<String, String> errorMap = getErrorMap(bindingResult);
        return ResponseResult.fail(logCode, CommonStatusEnum.PARAM_ERROR.getCode(), CommonStatusEnum.PARAM_ERROR.getValue(), errorMap);
    }

    /**
     * 约束校验异常
     */
    @ExceptionHandler(value = {ConstraintViolationException.class})
    public ResponseResult handleValidException(ConstraintViolationException e) {
        String logCode = ThreadMdcUtil.generateTraceId();
        log.error("错误码:{}, 数据校验异常,{},异常类型:{}", logCode, e.getMessage(), e.getClass(), e);
        MDC.remove(ThreadMdcUtil.getTraceId());
        List<String> violations = e.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage).collect(Collectors.toList());
        String error = violations.get(0);
        return ResponseResult.fail(logCode, CommonStatusEnum.CONSTRAINT_ERROR.getCode(), CommonStatusEnum.CONSTRAINT_ERROR.getValue(), error);
    }

    /**
     * 获取校验失败的结果
     */
    private Map<String, String> getErrorMap(BindingResult result) {
        return result.getFieldErrors().stream().collect(
                Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage, (k1, k2) -> k1)
        );
    }

    /**
     * DataBinder 数据绑定访问器,集合参数校验时需要这个数据绑定
     */
    @InitBinder
    private void activateDirectFieldAccess(DataBinder dataBinder) {
        dataBinder.initDirectFieldAccess();
    }

}

其他异常处理类

package com.business.exception;

public class BusinessException extends RuntimeException {

    public BusinessException() {
        super();
    }

    public BusinessException(String message) {
        super(message);
    }

    public BusinessException(String message, Throwable cause) {
        super(message, cause);
    }

    public BusinessException(Throwable cause) {
        super(cause);
    }

    protected BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

public class ParamException extends RuntimeException {
    public ParamException() {
        super();
    }

    public ParamException(String message) {
        super(message);
    }

    public ParamException(String message, Throwable cause) {
        super(message, cause);
    }

    public ParamException(Throwable cause) {
        super(cause);
    }

    protected ParamException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

其他类

package com.business.common;

import lombok.Getter;

public enum CommonStatusEnum {

    /**
     * 未知异常
     */
    SERVER_UNKNOW_ERROR(1001,"服务器未知异常,请联系管理员!"),

    /**
     * 业务异常
     */
    BUSINESS_ERROR(1002,"业务逻辑异常!"),

    /**
     * 网络请求异常
     */
    NETWORK_ERROR(1003,"网络请求异常"),

    /**
     * 参数校验(Valid)异常
     */
    PARAM_ERROR(1004,"参数异常"),

    /**
     * 约束校验异常
     */
    CONSTRAINT_ERROR(1005,"约束异常"),





    /**
     * 成功
     */
    SUCCESS(200,"success"),
    /**
     * 失败
     */
    FAIL(500,"fail")

    ;
    @Getter
    private int code;
    @Getter
    private String value;

    CommonStatusEnum(int code, String value) {
        this.code = code;
        this.value = value;
    }
}


package com.bussiness.pojo.response;

import com.bussiness.common.CommonStatusEnum;

import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
public class ResponseResult<T> {
    private int code;
    private String message;
    private T data;
    private String logCode;

    /**
     * 成功响应的方法
     *
     * @param <T>
     * @return
     */
    public static <T> ResponseResult<T> success() {
        return new ResponseResult().setCode(CommonStatusEnum.SUCCESS.getCode()).setMessage(CommonStatusEnum.SUCCESS.getValue());
    }

    /**
     * 成功响应的方法
     *
     * @param data
     * @param <T>
     * @return
     */
    public static <T> ResponseResult<T> success(T data) {
        return new ResponseResult().setCode(CommonStatusEnum.SUCCESS.getCode()).setMessage(CommonStatusEnum.SUCCESS.getValue()).setData(data);
    }

    /**
     * 失败:统一的失败
     *
     * @param data
     * @param <T>
     * @return
     */
    public static <T> ResponseResult<T> fail(T data) {
        return new ResponseResult().setCode(CommonStatusEnum.FAIL.getCode()).setData(data);
    }

    /**
     * 失败:统一的失败
     *
     * @param data
     * @param <T>
     * @return
     */
    public static <T> ResponseResult<T> fail(T data, String logCode) {
        return new ResponseResult().setCode(CommonStatusEnum.FAIL.getCode())
                .setLogCode(logCode).setData(data);
    }

    /**
     * 失败:统一的失败
     *
     * @param message
     * @return
     */
    public static ResponseResult fail(String message) {
        return new ResponseResult().setCode(CommonStatusEnum.FAIL.getCode()).setMessage(message);
    }

    /**
     * 失败:统一的失败
     *
     * @param message
     * @return
     */
    public static ResponseResult fail(String message, String logCode) {
        return new ResponseResult().setCode(CommonStatusEnum.FAIL.getCode())
                .setLogCode(logCode).setMessage(message);
    }

    /**
     * 失败:自定义失败 错误码和提示信息
     *
     * @param code
     * @param message
     * @return
     */
    public static ResponseResult fail(int code, String message) {
        return new ResponseResult().setCode(code).setMessage(message);
    }

    /**
     * 失败:自定义失败 错误码和提示信息
     *
     * @param code
     * @param message
     * @return
     */
    public static ResponseResult fail(String logCode, int code, String message) {
        return new ResponseResult().setCode(code).setLogCode(logCode).setMessage(message);
    }

    /**
     * 失败:自定义失败 错误码、提示信息、具体错误
     *
     * @param code
     * @param message
     * @param data
     * @return
     */
    public static <T> ResponseResult<T> fail(int code, String message, T data) {
        return new ResponseResult().setCode(code).setMessage(message).setData(data);
    }

    /**
     * 失败:自定义失败 错误码、提示信息、具体错误
     *
     * @param code
     * @param message
     * @param data
     * @return
     */
    public static <T> ResponseResult<T> fail(String logCode, int code, String message, T data) {
        return new ResponseResult().setCode(code).setMessage(message).setData(data).setLogCode(logCode);
    }


}

package com.business.pojo.request;

import javax.validation.constraints.Min;

import lombok.Getter;
import lombok.Setter;

public class PageQuery {

    @Getter
    @Setter
    @Min(value = 1, message = "当前页码不合法")
    private int pageNo = 1;

    @Getter
    @Setter
    @Min(value = 1, message = "每页展示数量不合法")
    private int pageSize = 10;

    @Setter
    private int offset;

    public int getOffset() {
        return (pageNo - 1) * pageSize;
    }
}

package com.business.pojo.request;

import lombok.Data;
import org.hibernate.validator.constraints.NotBlank;
import java.util.List;

@Data
public class DemoReq {
    /**
     * 领域编码
     */
    @NotBlank(message = "领域编码不能为空")
    private String domainKey;

    /**
     * 模型编码
     */
    @NotBlank(message = "模型编码不能为空")
    private String modelKey;

    /**
     * 时间
     */
    private List<String> startTime;

    /**
     * 开始时间
     */
    private String beginTime;

    /**
     * 结束时间
     */
    private String endTime;
}


参考文章:

拦截机制中Aspect、ControllerAdvice、Interceptor、Fliter之间的区别详解 - 简书

Springboot 同一次调用日志怎么用ID串起来,方便最终查找_日志id开线程后还能用吗_小目标青年的博客-CSDN博客
hibernate-validator校验参数(统一异常处理)_无法访问org.hibernate.validator.constraints.range_鱼找水需要时间的博客-CSDN博客文章来源地址https://www.toymoban.com/news/detail-415944.html

到了这里,关于springboot 统一异常处理 + 日志记录的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • springboot优雅的统一返回格式 + 全局异常处理(包括404等异常)

    目录 1.自定义枚举类 2.自定义统一返回格式类 3.统一返回格式的高级实现 4.全局异常处理 5.更优雅的全局异常处理 6.处理404错误 该枚举类为我们和前端约定好的返回状态码和描述信息,可根据自己的需求修改状态码和描述 @Data 注解为Lombok工具类库中的注解,提供类的get、s

    2024年02月11日
    浏览(33)
  • SpringBoot -05 SpringBoot web相关配置(静态资源访问、统一异常处理、文件上传、拦截器、统一跨域请求处理)

    小总结 SpringBoot是一个基于Spring的工具集,去帮我们完成了大量的配置。在SpringBoot中有一个约定大于配置的概念,就是他把我们很多第三方框架帮我们写好了,而且把我们整个第三方框架所需要的依赖全都通过起步依赖加进去了。开发中只需要加入起步依赖就可以实现某个场

    2024年02月01日
    浏览(38)
  • 使用SpringBoot AOP记录操作日志和异常日志

    平时我们在做项目时经常需要对一些重要功能操作记录日志,方便以后跟踪是谁在操作此功能;我们在操作某些功 能时也有可能会发生异常,但是每次发生异常要定位原因我们都要到服务器去查询日志才能找到,而且也不能对发 生的异常进行统计,从而改进我们的项目,要

    2024年02月04日
    浏览(38)
  • 【规范】SpringBoot接口返回结果及异常统一处理,这样封装才优雅

    博友的需求就是我最大的动力 博友一说话,本狗笑哈哈。 博友要我写啥,我就写啥 。 特来一篇关于 SpringBoot接口返回结果及异常统一处理 ,虽说封不封装都能用,但咱后端也得给前端小姐姐留个好印象不是。项目前后端分离, 规范的数据传输格式,让REST风格的API具有简单

    2024年02月08日
    浏览(36)
  • SpringBoot初级开发--整体应用的统一性异常管理(7)

      在整个系统中,通常会要求有统一性的异常抛出,统一的异常格式,统一的异常界面,而不是把整个堆栈错误信息抛出,这样对整个系统的安全性以及错误定位都非常不好,接下来我们紧接上一章的源码,加上统一异常。 注解@ControllerAdvice表示这是一个控制器增强类,当

    2024年02月10日
    浏览(29)
  • uniapp微信小程序投票系统实战 (SpringBoot2+vue3.2+element plus ) -全局异常统一处理实现

    锋哥原创的uniapp微信小程序投票系统实战: uniapp微信小程序投票系统实战课程 (SpringBoot2+vue3.2+element plus ) ( 火爆连载更新中... )_哔哩哔哩_bilibili uniapp微信小程序投票系统实战课程 (SpringBoot2+vue3.2+element plus ) ( 火爆连载更新中... )共计21条视频,包括:uniapp微信小程序投票系统实

    2024年02月03日
    浏览(32)
  • 【踩坑日志】SpringBoot读取nacos配置信息并提取信息中的IP地址(配置属性解析异常+排错记录)

    缘起 :项目需读取nacos中动态的TDengine数据库连接信息并提取IP,一个并不复杂的操作,但作为一个nacos知识浅薄的菜鸡,我愣是捯饬了几个小时……惭愧惭愧…… 异常代码 报错信息 报错核心: Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Coul

    2024年01月19日
    浏览(50)
  • 【Spring Boot】拦截器与统一功能处理:统一登录验证、统一异常处理与统一数据返回格式

     Spring AOP是一个基于面向切面编程的框架,用于将横切性关注点(如日志记录、事务管理)与业务逻辑分离,通过代理对象将这些关注点织入到目标对象的方法执行前后、抛出异常或返回结果时等特定位置执行,从而提高程序的可复用性、可维护性和灵活性。但使用原生Sp

    2024年02月16日
    浏览(38)
  • spring的异常统一处理

    前情回顾:         首先明确,当你的代码遇到bug时会报错,那报错后不处理就会程序停止运行。 那就需要异常捕获了,两种方法:         一、try...catch...直接自己处理         二、throws 将异常抛给调用者         在spring的框架中,要求就是简洁,在大量的代码中有t

    2024年02月04日
    浏览(32)
  • 统一异常处理

    Result为封装传递给前端的包装类 全局异常处理 特定异常处理 自定义异常处理 创建异常类,继承RuntimeException 在异常的地方,手动添加异常 添加执行方法

    2024年02月04日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包