> 文章列表 > 使用@Aspect和@Before注解以及全局异常拦截

使用@Aspect和@Before注解以及全局异常拦截

使用@Aspect和@Before注解以及全局异常拦截

记录:390

场景:使用aspectjweaver包的@Aspect和@Before注解拦截自定义注解@VerifyCity。自定义注解@VerifyCity作用在Controller的方法上。实现在执行Controller的方法前,先拦截注解校验必要参数。使用@RestControllerAdvice和@ExceptionHandler注解拦截异常,实现优雅返回异常信息。

版本:JDK 1.8,SpringBoot 2.6.3

AOP:Aspect Oriented Programming,面向切面编程。

AOP:将业务逻辑的各个部分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。

AOP:取横向抽取机制,取代了传统纵向继承体系的重复性代码,主要应用在事务处理、日志管理、权限控制、异常处理等方面。

1.自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface VerifyCity {String value() default "";
}

2.使用@Aspect和@Before实现拦截自定义注解

2.1代码

@Aspect
@Order(0)
@Component
@Slf4j
public class VerifyCityAspect {@Before("@annotation(verifyCity)")public void beforeVerify(VerifyCity verifyCity) {RequestAttributes reqAttributes = RequestContextHolder.currentRequestAttributes();HttpServletRequest httpReq = ((ServletRequestAttributes) reqAttributes).getRequest();String httpCityName = httpReq.getHeader("cityName");String annotationCityName = verifyCity.value();log.info("从http请求头中获取的城市名称,cityName=" + httpCityName);log.info("从Controller的方法上的@VerifyCity注解获取的城市名称,cityName=" + annotationCityName);if (!StringUtils.equals(httpCityName, annotationCityName)) {log.info("校验失败,不执行Controller.");throw new VerifyException("校验城市编码失败.");}log.info("校验成功,执行Controller.");}
}

2.2解析

@Aspect,标记实现AOP。

@Before("@annotation(verifyCity)"),标记拦截注解@VerifyCity。

StringUtils.equals(httpCityName, annotationCityName),比对从请求头中获取的cityName的值与从注解@VerifyCity中获取的值,相等则验证通过,否则抛出一个异常,结束程序。

throw new VerifyException("校验城市编码失败."),抛出定义异常,结束本次请求,也就是不会执行@VerifyCity标记的Controller的方法了。

3.自定义异常

自定义VerifyException继承RuntimeException。

public class VerifyException extends RuntimeException {private String errorCode = "";public VerifyException(String message) {super(message);this.errorCode = "500";}public String getErrorCode() {return errorCode;}
}

4.自定义全局拦截器

4.1代码

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(value = VerifyException.class)public String bizExceptionHandler(HttpServletRequest req, VerifyException e) {String errMsg = "异常编码: " + e.getErrorCode() + ";异常信息: " + e.getMessage();log.error(errMsg);return errMsg;}
}

4.2解析

@RestControllerAdvice,标记全局异常。

@ExceptionHandler(value = VerifyException.class),指定拦截的异常类型。

返回值类型String,抛出异常后,会组装成字符串信息返回,而不是一连串报错异常。

5.使用自定义注解

在Controller类使用注解@VerifyCity("Hangzhou")。

@Slf4j
@RestController
@RequestMapping("/hub/example/city")
public class CityController {@VerifyCity("Hangzhou")@PostMapping("/queryCityByCityCode")public Object queryCityByCityCode(String cityCode) {log.info("入参cityCode = "+cityCode);return "执行成功";}
}

6.使用Postman测试

6.1请求地址与入参

地址:http://127.0.0.1:18200/hub-200-base/hub/example/city/queryCityByCityCode

入参:cityCode=310001

请求头:cityName=Hangzhou

返回值:执行成功

6.2优雅拦截异常返回

返回值:异常编码: 500;异常信息: 校验城市编码失败.

6.3不使用全局拦截异常返回

把VerifyCityAspect中抛出异常改为:

throw new RuntimeException("校验城市编码失败.");

不优雅返回值:

{"timestamp": "2023-03-23T23:03:4127+08:00","status": 500,"error": "Internal Server Error","path": "/hub-200-base/hub/example/city/queryCityByCityCode"
}

或者

java.lang.RuntimeException: 校验城市编码失败.at com.hub.example.aspect.VerifyCityAspect.beforeVerify(VerifyCityAspect.java:37) ~[classes/:na]at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_181]at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_181]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]

7.核心依赖包

<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.19</version>
</dependency>

以上,感谢。

2023年3月23日