spring boot+自定义 AOP 实现全局校验的实例代码
最近公司重构项目,重构为最热的微服务框架springboot,重构的时候遇到几个可以统一处理的问题,也是项目中经常遇到,列如:统一校验参数,统一捕获异常。。。
仅凭代码去控制参数的校验,有时候是冗余的,但通过框架支持的去控制参数的校验,是对于开发者很友好,先看下面的例子
@NotEmpty(message="手机号不能为空") @Size(min=11,max=11,message="手机号码长度不正确") @Pattern(regexp=StringUtils.REGEXP_MOBILE,message="手机号格式不正确") privateStringmobile;
这是springboot支持的校验注解,然后我们在contoller层加上@Valid注解就可以达到校验的目的。这是一种框架自带的
本章就展示一种自定义的AOP校验,首先写一个注解,注解里面可以写上我们需要校验的规则,比如长度,正则。。。
@Documented
@Target({ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public@interfaceValidateParam{
intmin()default0;
intmax()defaultInteger.MAX_VALUE;
Stringmessage()default"paramsisnotnull";
Stringregexp();
Class>[]groups()default{};
Class[]payload()default{};
booleanisNotNull()defaulttrue;
}
然后定义一个AOP类
packagecom.onecard.primecard.common.aop;
importjava.lang.reflect.Field;
importjava.lang.reflect.Method;
importjava.lang.reflect.ParameterizedType;
importjava.util.ArrayList;
importjava.util.Arrays;
importjava.util.regex.Pattern;
importorg.aspectj.lang.JoinPoint;
importorg.aspectj.lang.ProceedingJoinPoint;
importorg.aspectj.lang.annotation.Around;
importorg.aspectj.lang.annotation.Aspect;
importorg.aspectj.lang.annotation.Before;
importorg.aspectj.lang.annotation.Pointcut;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importorg.springframework.context.ApplicationContext;
importorg.springframework.context.support.ClassPathXmlApplicationContext;
importorg.springframework.stereotype.Component;
importcom.jfcf.core.dto.ResultData;
importcom.onecard.core.support.util.StringUtils;
importcom.onecard.primecard.common.annotation.ValidateParam;
importcom.onecard.primecard.common.utils.ResultDataUtil;
/**
*全局切面类(校验参数)
*
*@authorAdministrator
*
*/
@Aspect
@Component
publicclassGobalHandlerAspect{
privatestaticLoggerlogger=LoggerFactory.getLogger(GobalHandlerAspect.class);
@Pointcut("execution(*包名.controller..*.*(..))&&execution(*包名.controller..*.*(..))")
publicvoidcheckAspect(){};
@Before("checkAspect()")
publicvoidbefor(JoinPointjoinPoint)throwsException{
//前置统一输出参数
Object[]args=joinPoint.getArgs();
if(args!=null&&args.length>0){
Objectobj=args[0];
ParameterizedTypept=(ParameterizedType)obj.getClass().getGenericSuperclass();
Class>classzz=(Class>)pt.getActualTypeArguments()[0];
logger.info("【小X卡】-【请求实体入参】:"+classzz.newInstance().toString());
}
}
@Around("checkAspect()")
publicObjectaround(ProceedingJoinPointjoinPoint)throwsThrowable{
//校验参数
Object[]args=joinPoint.getArgs();
Objectobj=null;
if(args!=null&&args.length>0){
obj=args[0];
Classclasszz=obj.getClass();
//没有顺序和秩序的数组
Field[]fieldArray=classzz.getDeclaredFields();
ArrayListfieldList=newArrayList(Arrays.asList(fieldArray));
Stringres=checkParam(fieldList,obj);
if(StringUtils.isNotNull(res)){
returnResultDataUtil.result(ResultData.STATUS_PARAM_ERROR,res);
}
}
returnjoinPoint.proceed();
}
privateStringcheckParam(ArrayListfieldList,Objectobj)throwsException{
for(Fieldfield:fieldList){
ValidateParamvalidateParam=field.getAnnotation(ValidateParam.class);
logger.info("【小X卡】获取注解值:"+validateParam.isNotNull()+"min="+validateParam.min()+"max="+validateParam.max());
Methodmethod=obj.getClass().getMethod("get"+getMethodName(field.getName()));
logger.info("【小X卡】入参实体方法名称:"+method.getName());
if(method!=null){
Objectval=method.invoke(obj);
logger.info("【小x卡】回调方法:"+val);
if(validateParam!=null&&validateParam.isNotNull()==true){
if(null==val||"".equals(val)){
returnfield.getName()+"必填参数为空";
}
}
if(validateParam.min()==11&&validateParam.max()==11){
if(val.toString().length()!=11){
returnfield.getName()+"请输入参数正确的长度";
}
}
if(validateParam.regexp().equals(StringUtils.REGEXP_MOBILE)){
if(!Pattern.matches(StringUtils.REGEXP_MOBILE,val.toString())){
returnfield.getName()+"参数格式错误";
}
}
}
}
returnnull;
}
/**
*方法首字母大写
*@paramfieldName
*@return
*/
privateStringgetMethodName(StringfieldName){
StringBufferbuffer=newStringBuffer();
StringfirstLetter=fieldName.substring(0,1).toUpperCase();
returnbuffer.append(firstLetter).append(fieldName.substring(1,fieldName.length())).toString();
}
}
定义一个切点@Pointcut,用execution表达式,去获取要校验的某个类和某个方法,也就是连接点,然后用定义一个通知,上面代码中有2个通知,一个前置通知@Before,一个环绕通知@Around,我们使用功能最强大的环绕通知。
通过上面的代码可以看出 首先获取参数,然后通过反射机制获取入参对象中的全部字段,再去获取我们在字段中加我们自定义注解的字段,通过反射方法的回调,获取字段值,对值做判断,返回校验结果。
总结
以上所述是小编给大家介绍的springboot+自定义AOP实现全局校验的实例代码,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!