有好久没写java相关的博文了。最近又忙于java项目,有了一个新的需求。
具体需求如下:
以一种通用的方法,不修改原来代码的情况下,符合开闭原则,对某一特定方法进行请求参数校验,比如判空。
这样一来就涉及到面向切面编程了,会用到的东西就是 spring aop。
下面说下面对这一场景,如何完成的代码实现。
框架选型: springboot、validation-api
看下 maven 的依赖图,validation-api 用于验证参数注解,进行校验。它的父包来自 hibernate-validator,而hibernate-validator的父包来自 spring-boot-starter-web
所以pom.xml引入以下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
文件目录结构:
|--service
|-impl
|-xxxxServiceImpl.java
|-xxxService.java
xxxServiceImpl.java 源码 :
@Override
public PersonReportResponseParam personReport(PersonReportRequestParam requestParam) {
}
PersonReportRequestParam.java 源码:
@Data
public class PersonReportRequestParam {
/**
* 姓名
*/
@NotNull(message = "姓名不能为空!")
private String name;
/**
* 性别
*/
@NotNull(message = "性别不能为空!")
private String gendar;
}
Apsect前置校验:
@Aspect
@Component
public class ValidationAspect {
@Autowired
private Validator validator;
/**
* 前置通知
* @param joinPoint
*/
@Before("execution( * com.xxx.service.imp.xxxxServiceImpl.*(..))")
public void permissionCheck(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
if (args == null) {
return;
}
for (Object o : args) {
if (o == null) {
continue;
}
Set<ConstraintViolation<Object>> result = validator.validate(o);
if (result.size() > 0) {
ConstraintViolation<Object> v = result.iterator().next();
String message = v.getPropertyPath() + " " + v.getMessage();
throw new ConstraintViolationException(message, result);
}
}
}
}
xxxService.java源码:
在这里插入代码片
Apsect前置校验:
@Aspect
@Component
public class ValidationAspect {
@Autowired
private Validator validator;
@Before(value = "@annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void start(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
if (args == null) {
return;
}
for (Object o : args) {
if (o == null) {
continue;
}
Set<ConstraintViolation<Object>> result = validator.<Object>validate(o);
if (result.size() > 0) {
ConstraintViolation<Object> v = result.iterator().next();
String message = v.getPropertyPath() + " " + v.getMessage();
throw new ConstraintViolationException(message, result);
}
}
}
}
注意:这里抛出去的异常,会到你通知作用的范围内,比如@Before中的是扫描的是@RequestMapping 注解,则会抛到异常参数的 Controller 层。
切入点:在类里边可以有很多方法被增强,比如实际操作中,只是增强了个别方法,则定义实际被增强的某个方法为切入点。
通知/增强:增强的逻辑,称为增强,比如扩展日志功能,这个日志功能称为增强。包括:
切面:把增强应用到具体方法上面的过程称为切面。
execution( * com.xxx.service.imp.xxxxServiceImpl.*(..))
例如上述表达式:
1、 execution():用于匹配方法执行的连接点;
2、第一个*号:表示返回类型, *号表示所有的类型。
3、包名:表示需要拦截的包名,以及具体的类名
4、*(…):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数
以上,aop使用的一些实战小技巧。果然,实战才能使人记忆深刻。