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的思想理念就是将通用逻辑从业务逻辑中分离 ...
随机推荐
- kettle获取系统时间
Date.prototype.Format = function (fmt) { //author: meizz var o = { "M+": this.getMonth() + ...
- [daily] emacs显示file name buffer的全路径
在用emacs看代码的时候,在文件间跳来跳去,如果工程太大的话,很可能会有重名文件,这个时候, 你就不知道自己在什么的地方了.需要一个查看当前正在编辑区的文件的名字的方法. emacs在这个地方,存储 ...
- 第一次C语言实验
[实验感受]第一次实验感觉什么都还没有记住就要写小程序了,有些小着急.现在才发现c语言就是要在实践中掌握的,多敲点代码,多犯错误,就会明白其中的规则. 实验一:输入一个数判断奇数还是偶数,现在想想其实 ...
- flex总结一下
display:flex:规定元素是flex布局,里面的元素自然会像浮动一样横向排列: flex-direction:row | row-reverse | column | column-rever ...
- defaultdict(list)
- BeautifulSoup库的使用
1.简介 BeautifulSoup库也是一个HTML/XML的解析器,其使用起来很简单,但是其实解析网站用xpath和re已经足矣,这个库其实很少用到.因为其占用内存资源还是比xpath更高. '' ...
- Python类__call__()方法
在python中,创建类型的时候定义了__call__()方法,那这个类型创建出来的实例就是可调用的.例def如: class A(object): def __init__(self,name,ag ...
- MongoDB系列----查
开启查询: db.getMongo().setSlaveOk() 查版本: db.servion(); db.serverBuildInfo(); db.serverStatus().storageE ...
- Cocos Creator 监听安卓屏幕下方返回键
addEscEvent = function(node){ cc.eventManager.addListener({ event: cc.EventListener.KEYBOARD, onKeyP ...
- 基于FPGA视频时序生成中的库文件
上一篇分享了一个视频时序生成代码,下面我根据之前项目中用到的时序,对各个参数做了库文件,方便调用. -- -- Package File Template -- -- Purpose: This pa ...