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的思想理念就是将通用逻辑从业务逻辑中分离 ...
随机推荐
- Java-多态经典例子
public class A { public String show(D obj) { return ("A and D"); } public String show(A ob ...
- TCP三次握手那些事
临近5月,春招和实习招聘逐渐进入尾声.本文主要讨论面试中经常提问的TCP连接的机制,附带一些扩展知识. 参加面试的时候,过半的面试官都会问TCP相关问题,而最常见的问题就是:讲一下TCP三次握手(四次 ...
- 天梯赛练习题L2-006. 树的遍历
题目链接 已知一棵树的后序遍历顺序和中序遍历顺序,求层次遍历的顺序: 树的四种遍历: 先序遍历:先访问根节点,再访问左子树,最后访问右子树 中序遍历:先访问左子树,再访问根节点,最后访问右子树 后序遍 ...
- jeecg自定义datagrid查询
为什么要写这篇文章? 我们了解,使用 jeecg 提供的 CriteriaQuery 查询方式,确实能满足绝大数的需求,但是往往有那么个比较复杂的情况,需要我们直接去写 sql,比如多表查询呀等等等等 ...
- [py]python操作zookeeper
参考: https://blog.csdn.net/heizistudio/article/details/79568188 1.安装zookeeper zookeeper-3.4.6.tar.gz ...
- pdf下载速度
- mac 安装robot framework报错:No matching distribution found for Pywin32
运行 pip install robotframework-ride ,出现以下错误 解决办法:修改Python的运行方式,需要32位的python运行 defaults write com.ap ...
- Percona-Toolkit 之 pt-online-schema-change 总结
pt-online-schema-change - ALTER tables without locking them. pt-online-schema-change alters a table' ...
- jOrgChart二叉树效果
引进文件: <link rel="stylesheet" type="text/css" href="Public/com/jQrgChart/ ...
- Python语言——基础01-环境安装、注释、变量
开篇导言: 今天开始进行python学习的笔记更新,以后我都用截图的方式更新,方便不麻烦,界面美观,今天学习更新的python学习内容是环境安装.注释.变量的内容 关注我博客的童鞋从现在开始也可以跟着 ...