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的思想理念就是将通用逻辑从业务逻辑中分离 ...
随机推荐
- js canvas游戏初级demo-躲避障碍物
在线演示地址 http://200ok.fun:3100/html/game_demo.html 继上次js canvas游戏初级demo-上下左右移动(https://www.cnblogs.com ...
- 平安银行在开源技术选型上的思考和实践 RocketMQ
小结: 1. https://mp.weixin.qq.com/s/z_c5D8fvHaYvHSczm0nYFA 平安银行在开源技术选型上的思考和实践 平安银行·吴建峰 阿里巴巴中间件 3月7日 随着 ...
- 海思编译链编译出现__aeabi_unwind_cpp_pr1重定义怎么回事
1.用arm-hisiv100nptl-linux-gcc编译代码,结果发现报错,__aeabi_unwind_cpp_pr1重定义,在librt.a先定义,使用的海思芯片是hi3520d. 2.本来 ...
- shell 脚本示例
#!/bin/sh #while true #do cd /mnt/vfw/third_meteor for sub in `ls` do cd /mnt/vfw/third_meteor/${sub ...
- Ubuntu18.04 快速返回桌面 【快捷键】
设置->设备->键盘->窗口->隐藏窗口 :双击重置快捷键
- vue-cli 创建项目失败
vue-cli 创建一个vue项目报错 npm code 404. 尝试解决方法都有,我是第3种方法生效 更新npm 重新安装nodejs vue-cli@3.0.1 在创建项目的时候,不能开启其他v ...
- Log4j配置和解释
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台.文件.GUI组件,甚至是套接口服务器.NT的事件记录器.UNIX Syslog守护进程等:我们也可 ...
- 彻底清空SharePoint回收站(仅限IE)
1.导航到回收站页面2.F12,在控制台输入javascript:emptyItems()3.回车 4.点击确定即可 注意:这种方法可能只适用于Internet Explorer
- DateTimeFormat
中文:星期一,星期二 System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.GetDayName(DateTime.Now.Da ...
- struts 2.5配置
1.jar包的变动 必需jar包,旧版本: 必需jar包,新版本: 在struts-2.5.16版本的lib目录下没有xwork-core的jar包,原因是被合并到struts-core这个jar里了 ...