时间:2021-05-19
最近公司重构项目,重构为最热的微服务框架 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@Componentpublic 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 实现全局校验的实例代码,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
了解过spring-Boot这个技术的,应该知道Spring-Boot的核心配置文件application.properties,当然也可以通过注解自定义配置文
自定义Starter命名规则注意artifactId的命名规则,Spring官方Starter通常命名为spring-boot-starter-{name}如s
自定义Starter命名规则注意artifactId的命名规则,Spring官方Starter通常命名为spring-boot-starter-{name}如s
本文介绍基于springboot和jdk8编写一个aop,结合自定义注解实现通用的接口参数校验。缘由目前参数校验常用的方法是在实体类上添加注解,但对于不同的方法
简介实现功能自定义文本自定义类型(默认,消息,成功,警告,危险)自定义过渡时间使用vue-cli3.0生成项目toast全局组件编写/src/toast/toa