Tips:这里涉及两个模块,一个公共模块,一个需要做日志处理和异常处理的模块;核心是Spring Aop的使用;
- 公共模块里面自定义异常类,继承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版权协议,转载请附上原文出处链接和本声明。