spring boot+自定义 AOP 实现全局校验
最近公司重构项目,重构为最热的微服务框架 spring boot, 重构的时候遇到几个可以统一处理的问题,也是项目中经常遇到,列如:统一校验参数,统一捕获异常。。。
仅凭代码 去控制参数的校验,有时候是冗余的,但通过框架支持的 去控制参数的校验,是对于开发者很友好,先看下面的例子
@NotEmpty(message="手机号不能为空")
@Size(min=11,max=11,message="手机号码长度不正确")
@Pattern(regexp=StringUtils.REGEXP_MOBILE,message="手机号格式不正确")
private String mobile;
这是spring boot支持的 校验注解,然后我们在 contoller层 加上@Valid 注解 就可以达到校验的目的。这是一种框架自带的
本章 就展示一种 自定义的 AOP 校验,首先 写一个注解,注解里面可以写上 我们需要校验的规则, 比如长度,正则。。。
@Documented
@Target({ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidateParam { int min() default 0; int max() default Integer.MAX_VALUE; String message() default "params is not null"; String regexp(); Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; boolean isNotNull() default true; }
然后定义一个AOP类
package com.onecard.primecard.common.aop; import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.regex.Pattern; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component; import com.jfcf.core.dto.ResultData;
import com.onecard.core.support.util.StringUtils;
import com.onecard.primecard.common.annotation.ValidateParam;
import com.onecard.primecard.common.utils.ResultDataUtil; /**
* 全局 切面类(校验参数)
*
* @author Administrator
*
*/
@Aspect
@Component
public class GobalHandlerAspect { private static Logger logger = LoggerFactory.getLogger(GobalHandlerAspect.class); @Pointcut("execution(* 包名.controller..*.*(..)) && execution(* 包名.controller..*.*(..))")
public void checkAspect(){}; @Before("checkAspect()")
public void befor(JoinPoint joinPoint) throws Exception{
//前置统一输出参数
Object[] args = joinPoint.getArgs();
if(args != null && args.length>0){
Object obj = args[0];
ParameterizedType pt = (ParameterizedType)obj.getClass().getGenericSuperclass();
Class<?> classzz = (Class<?>) pt.getActualTypeArguments()[0];
logger.info("【小X卡】-【请求实体入参】:"+classzz.newInstance().toString());
} } @Around("checkAspect()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
//校验参数
Object[] args = joinPoint.getArgs();
Object obj = null;
if(args != null && args.length > 0){
obj = args[0];
Class classzz = obj.getClass();
//没有顺序和秩序的数组
Field[] fieldArray = classzz.getDeclaredFields();
ArrayList<Field> fieldList = new ArrayList<Field>(Arrays.asList(fieldArray));
String res = checkParam(fieldList,obj);
if(StringUtils.isNotNull(res)){
return ResultDataUtil.result(ResultData.STATUS_PARAM_ERROR, res);
}
} return joinPoint.proceed();
} private String checkParam(ArrayList<Field> fieldList, Object obj) throws Exception { for(Field field : fieldList){
ValidateParam validateParam = field.getAnnotation(ValidateParam.class);
logger.info("【小X卡】获取注解值:"+validateParam.isNotNull()+"min="+validateParam.min()+"max="+validateParam.max());
Method method = obj.getClass().getMethod("get"+getMethodName(field.getName()));
logger.info("【小X卡】入参实体方法名称:"+method.getName());
if(method != null){
Object val = method.invoke(obj);
logger.info("【小x卡】回调方法:"+val);
if(validateParam != null && validateParam.isNotNull() == true){
if(null == val || "".equals(val) ){
return field.getName()+"必填参数为空";
}
}
if(validateParam.min()==11 && validateParam.max() == 11){
if(val.toString().length() != 11){
return field.getName()+"请输入参数正确的长度";
}
}
if(validateParam.regexp().equals(StringUtils.REGEXP_MOBILE)){
if(!Pattern.matches(StringUtils.REGEXP_MOBILE, val.toString())){
return field.getName()+"参数格式错误";
}
}
}
}
return null;
} /**
* 方法首字母大写
* @param fieldName
* @return
*/
private String getMethodName(String fieldName) {
StringBuffer buffer = new StringBuffer();
String firstLetter = fieldName.substring(0, 1).toUpperCase();
return buffer.append(firstLetter).append(fieldName.substring(1, fieldName.length())).toString(); }
}
定义一个切点 @Pointcut, 用execution 表达式,去获取要校验的 某个类 和某个方法, 也就是连接点,然后 用定义一个通知,上面代码中有2个通知,一个前置通知@Before,一个环绕通知@Around,我们使用功能最强大的环绕通知。
通过上面的代码可以看出 首先获取参数,然后通过反射机制 获取 入参对象中的全部字段, 再去获取 我们在字段中加 我们自定义注解的字段,通过反射方法的回调,获取字段值,对值做判断, 返回校验结果。
spring boot+自定义 AOP 实现全局校验的更多相关文章
- Spring Boot 使用 Aop 实现日志全局拦截
前面的章节我们学习到 Spring Boot Log 日志使用教程 和 Spring Boot 异常处理与全局异常处理,本章我们结合 Aop 面向切面编程来实现全局拦截异常并记录日志. 在 Sprin ...
- 玩转Spring Boot 自定义配置、导入XML配置与外部化配置
玩转Spring Boot 自定义配置.导入XML配置与外部化配置 在这里我会全面介绍在Spring Boot里面如何自定义配置,更改Spring Boot默认的配置,以及介绍各配置的优先 ...
- Spring Boot 之:接口参数校验
Spring Boot 之:接口参数校验,学习资料 网址 SpringBoot(八) JSR-303 数据验证(写的比较好) https://qq343509740.gitee.io/2018/07/ ...
- Spring Boot 2 Webflux的全局异常处理
https://www.jianshu.com/p/6f631f3e00b9 本文首先将会回顾Spring 5之前的SpringMVC异常处理机制,然后主要讲解Spring Boot 2 Webflu ...
- 峰哥说技术:06-手撸Spring Boot自定义启动器,解密Spring Boot自动化配置原理
Spring Boot深度课程系列 峰哥说技术—2020庚子年重磅推出.战胜病毒.我们在行动 06 峰哥说技术:手撸Spring Boot自定义启动器,解密Spring Boot自动化配置原理 Sp ...
- Spring Boot使用AOP的正确姿势
一.为什么需要面向切面编程? 面向对象编程(OOP)的好处是显而易见的,缺点也同样明显.当需要为多个不具有继承关系的对象添加一个公共的方法的时候,例如日志记录.性能监控等,如果采用面向对象编程的方法, ...
- spring boot自定义线程池以及异步处理
spring boot自定义线程池以及异步处理@Async:什么是线程池?线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使 ...
- Spring Boot自定义配置与加载
Spring Boot自定义配置与加载 application.properties主要用来配置数据库连接.日志相关配置等.除了这些配置内容之外,还可以自定义一些配置项,如: my.config.ms ...
- Spring Boot学习——AOP编程的简单实现
首先应该明白一点,AOP是一种编程范式,是一种程序设计思想,与具体的计算机编程语言无关,所以不止是Java,像.Net等其他编程语言也有AOP的实现方式.AOP的思想理念就是将通用逻辑从业务逻辑中分离 ...
随机推荐
- Linux基础命令1
文件的操作: Tab键:自动补齐 反斜杠‘\’:强制换行 Ctrl+u:清空至行首 Ctrl+k:清空至行尾 Ctrl+l:清屏 --clear 清屏 Ctrl+c:取消本次命令编辑,中断当前操作 ...
- pymysql连接数据库报错:'NoneType' object has no attribute 'encoding'
直接写 utf8 即可.
- oracle储存过程学习笔记
转载至: https://www.2cto.com/database/201610/559389.htm 1.什么是oracle存储过程 存储过程和函数也是一种PL/SQL块,是存入数据库的PL/SQ ...
- SpringMVC中映射路径的用法之请求限制、命名空间
SpringMVC中映射路径的请求限制 什么是SpringMVC请求限制? 在SpringMVC中,支持对请求的设置.如果不满足限制条件的话,就不让请求访问执行方法,这样可以大大提高执行方法 的安全性 ...
- 快速排序 之添加复合插入排序和原始序列取中值左pivot
quicksort中,当n小于一定值时,排序效率就比直接插入排序底了,所以,此时就不要再递归下去了,直接插入排序好了:快速的原理就是因为折半递归,所以初始pivot应该有个好一点的选择,这里在原序列左 ...
- vue中上传文件之multipart/form-data
首先在项目里用了拦截器的,由于拦截器会将传递的参数转成对象,所以你i提交的时候会发现multipart/form-data或转变成application/json 其次关于input的文件上传是需要一 ...
- linux----------linux下安装rar和unrar命令
1.wget http://www.rarlab.com/rar/rarlinux-x64-4.2.0.tar.gz 2.tar xf rarlinux-x64-4.2.0.tar.gz 解压下 ...
- #WEB安全基础 : HTTP协议 | 0x12 MIME多用途邮件扩展以及多部分对象集合
我们是怎么让邮件里又有图片又有文字的? 文字和图片是两个不同的类型,而邮件又是一个类型. C语言的结构体允许用户定义一个含有多类型的自定义类型 像这样,看不懂没关系,你只要知道邮件里有多个类型就可以了 ...
- UBUNTU安装 SSH 服务
输入命令"sudo apt -y install openssh-server" 输入当前用户密码,等待完成openssh-server安装. 安装完毕,运行命令"sud ...
- 《高性能Mysql》解读---Mysql的事务和多版本并发
1.base:ACID属性,并发控制 2.MySql事务的隔离级别有哪些,含义是什么? 3.锁知多少,读锁,写锁,排他锁,共享锁,间隙锁,乐观锁,悲观锁. 4.Mysql的事务与锁有什么关联?MySq ...