分布式项目全局异常处理和日志处理

Tips:这里涉及两个模块,一个公共模块,一个需要做日志处理和异常处理的模块;核心是Spring Aop的使用;

  1. 公共模块里面自定义异常类,继承RuntimeException;
  • package com.sh.common.exception;
    
    import lombok.Getter;
    
    /**
     * @author carlos
     */
    @Getter
    public class LyException extends RuntimeException {
        /**
         * 异常状态码信息
         */
        private int status;
    
        public LyException(int status) {
            this.status = status;
        }
    
        public LyException(int status, String message) {
            super(message);
            this.status = status;
        }
    
        public LyException(int status, String message, Throwable cause) {
            super(message, cause);
            this.status = status;
        }
    
        public LyException(int status, Throwable cause) {
            super(cause);
            this.status = status;
        }
    }
    

    2. 对Controller进行全局控制,controller抛出的异常进行处理;

  • 对于@ControllerAdvice,我们比较熟知的用法是结合@ExceptionHandler用于全局异常的处理,但其作用不止于此。ControllerAdvice拆开来就是Controller Advice,关于Advice,在Spring的AOP中,是用来封装一个切面所有属性的,包括切入点和需要织入的切面逻辑。这里ControllerAdvice也可以这么理解,其抽象级别应该是用于对Controller进行切面环绕的,而具体的业务织入方式则是通过结合其他的注解来实现的。@ControllerAdvice是在类上声明的注解,其用法主要有三点:

    1.结合方法型注解@ExceptionHandler,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的。

    @ExceptionHandler的作用主要在于声明一个或多个类型的异常,当符合条件的Controller抛出这些异常之后将会对这些异常进行捕获,然后按照其标注的方法的逻辑进行处理,从而改变返回的视图信息。@ExceptionHandler的作用主要在于声明一个或多个类型的异常,当符合条件的Controller抛出这些异常之后将会对这些异常进行捕获,然后按照其标注的方法的逻辑进行处理,从而改变返回的视图信息。

    package com.sh.common.advice;
    
    @ControllerAdvice
    @Slf4j
    public class ControllerExceptionAdvice {
    
        /**
         * 统一异常处理方法,@ExceptionHandler(RuntimeException.class)声明这个方法处理RuntimeException这样的异常
         * @param e 捕获到的异常
         * @return 返回给页面的状态码和信息
         */
        @ExceptionHandler(LyException.class)
        public ResponseEntity<String> handleLyException(LyException e) {
            return ResponseEntity.status(e.getStatus()).body(e.getMessage());
        }
    }

    3. 统一日志处理,创建切面,构建切面逻辑 

  • package com.sh.common.advice;
    
    import com.sh.common.exceptios.LyException;
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    
    /**
     * @author carlos
     */
    @Slf4j
    @Aspect
    @Component
    public class CommonLogAdvice {
    
        @Around("within(@org.springframework.stereotype.Service *)")
        public Object handleExceptionLog(ProceedingJoinPoint jp) throws Throwable {
            try {
                // 记录方法进入日志
                log.debug("{}方法准备调用,参数: {}", jp.getSignature(), Arrays.toString(jp.getArgs()));
                long a = System.currentTimeMillis();
                // 调用切点方法
                Object result = jp.proceed();
                // 记录方法结束日志
                log.debug("{}方法调用成功,执行耗时{}", jp.getSignature(), System.currentTimeMillis() - a);
                return result;
            } catch (Throwable throwable) {
                log.error("{}方法执行失败,原因:{}", jp.getSignature(), throwable.getMessage(), throwable);
                // 判断异常是否是LyException
                if(throwable instanceof LyException){
                    // 如果是,不处理,直接抛
                    throw throwable;
                }else{
                    // 如果不是,转为LyException
                    throw new LyException(500, throwable);
                }
            }
        }
    }
    

    4. 让切面生效构建注解,其他模块只需要引入公共模块就可以了,同时把注解接入到各个模块的启动类上;

  • package com.sh.common.annotation;
    
    import com.sh.common.advices.CommonExceptionAdvice;
    import com.sh.common.advices.CommonLogAdvice;
    import org.springframework.context.annotation.Import;
    
    import java.lang.annotation.*;
    
    /**
     * @author carlos
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import({CommonExceptionAdvice.class, CommonLogAdvice.class})
    public @interface EnableExceptionAdvice {
    }
    
    @EnableFeignClients(value = {"com.sh.item.client"})
    @MapperScan(basePackages = "com.sh.trade.mapper")
    @EnableExceptionAdvice //这是另外一个模块的启动类
    @SpringBootApplication(scanBasePackages = {"com.sh.trade", "com.sh.common.advice"})
    public class LyTradeApplication {
        public static void main(String[] args) {
            SpringApplication.run(LyTradeApplication.class,args);
        }
    }
    


版权声明:本文为lmh1181243468原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。