spring boot+自定义 AOP 实现全局校验的实例代码

网友投稿 321 2023-01-09


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;

/**

http://* 全局 切面类(校验参数)

*

* @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 fieldList = new ArrayList(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 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卡】入参实体方法名称:"+methodhttp://.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();

returaXaesdGisn buffer.append(firstLetter).append(fieldName.substring(1, fieldName.length())).toString();

}

}

定义一个切点 @Pointcut, 用execution 表达式,去获取要校验的 某个类 和某个方法, 也就是连接点,然后 用定义一个通知,上面代码中有2个通知,一个前置通知@Before,一个环绕通知@Around,我们使用功能最强大的环绕通知。

通过上面的代码可以看出  首先获取参数,然后通过反射机制 获取 入参对象中的全部字段, 再去获取 我们在字段中加 我们自定义注解的字段,通过反射方法的回调,获取字段值,对值做判断, 返回校验结果。

总结

以上所述是给大家介绍的spring boot+自定义 AOP 实现全局校验的实例代码,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,会及时回复大家的!


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:微服务网关压测(微服务常用网关)
下一篇:如何理解Java中基类子对象的构建过程从"基类向外"进行扩散的?
相关文章

 发表评论

暂时没有评论,来抢沙发吧~