当前位置: 首页 > news >正文

山东省环保厅官方网站建设项目/百度前三推广

山东省环保厅官方网站建设项目,百度前三推广,做淘客网站 知乎,pc端手机网站 样式没居中本案例包括4个核心类: AopUtils:抽取出来的公共方法 ValidateGroup ValidateField ValidateAspectJHandler功能包括:长度、值范围、正则匹配、非空校验以下是设计思路、最终代码、测试结果后续扩展只需要修改ValidateField 和 ValidateAspect…
  • 本案例包括4个核心类:
    AopUtils:抽取出来的公共方法
    @ValidateGroup
    @ValidateField
    ValidateAspectJHandler
  • 功能包括:长度、值范围、正则匹配、非空校验
  • 以下是设计思路、最终代码、测试结果
  • 后续扩展只需要修改@ValidateField 和 ValidateAspectJHandler

1.演示: 最终使用方法

以注册功能为例

@RestController
@RequestMapping("validater")
public class ValidateController {@ValidateGroup(fields = {@ValidateField(index = 0,notNull = true,maxLen = 10,code = "param1-error",message = "param1校验错误"),@ValidateField(index = 1,notNull = true,fieldName = "passWord",minLen = 6,code = "passWord-erro",message = "密码校验错误"),@ValidateField(index = 1,notNull = true,fieldName = "age",minVal = 0,code = "age-error",message = "年龄不能小于0"),@ValidateField(index = 1,notNull = true,fieldName = "tall",minVal = 0,maxVal = 250.9,code ="tall-error",message = "身高范围出错"),@ValidateField(index = 1,notNull = true,fieldName = "phone",regStr = "^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$",code = "phone-error",message = "手机号错误")})@PostMapping("post")public String postValidater(@RequestParam String param1,  RegisterDto dto){System.out.println("成功通过校验");System.out.println("第一个参数是:" + param1);System.out.println("第二个参数是"+dto.toString());return "succeed";}	
}

其中:请求Dto包含name、passWord、phone等字段;
利用AspectJ对接口方法直接进行代理校验

2.流程分析

  • AspectJ代理注解@ValidateGroup标注的方法,而这个Group注解中的属性就是@ValidateField []
  • 获取到@ValidateField数组后,遍历。通过比对注解的参数 与 dto或者param中对应名字的参数来进行校验
  • 如果校验成功就放行,校验失败就抛异常终止

3.公共方法抽取AopUtils

经过流程分析可知:至少需要以下几个方法(并可以抽取为公共组件)

  • public Method getMethod(ProceedingJoinPoint pjp):获取被AOP拦截的方法Method对象
  • public Annotation getAnnotationByMethod(Method method, Class annoClass):获取目标方法对象的指定注解对象
  • public Object getFieldFromDtoByFieldName(Object dto , String fieldName) 从dto中,获取指定属性名的属性值

如下工具类也可以做成全静态方法

public class AopUtils {private volatile static AopUtils aopUtils;private AopUtils() {
}public static AopUtils getInstance() {if (aopUtils == null) {synchronized (AopUtils.class) {if (aopUtils == null) {aopUtils = new AopUtils();}}}return aopUtils;
}/*** 获取目标类的指定方法*/
public Method getMethodByClassAndName(Class c, String methodName) {Method[] methods = c.getDeclaredMethods();for (Method method : methods) {if (method.getName().equals(methodName)) {return method;}}return null;
}/*** 获取目标方法的指定注解* 相当于 method.getAnnotation(xxxx.class);*/
public Annotation getAnnotationByMethod(Method method, Class annoClass) {Annotation all[] = method.getAnnotations();for (Annotation annotation : all) {if (annotation.annotationType() == annoClass) {return annotation;}}return null;
}/*** 获取被拦截方法的对象* 配合使用,最终用于在Aspectj中获取被拦截方法上的注解* 例如:AopUtils.getMethod(pjp).getDeclaredAnnotation(被aop拦截的注解.class)*/
public Method getMethod(ProceedingJoinPoint pjp) {//获取参数的签名MethodSignature msig = (MethodSignature) pjp.getSignature();// MethodSignature.getMethod() 获取的是顶层接口或者父类的方法对象 如果在实现类的方法上,应该使用反射获取当前对象的方法对象Object target = pjp.getTarget();//获取连接点所在的目标对象(被代理的对象)而不是父类or接口//方法名 + 方法形参 ————》获取指定的方法对象(重载)String methodName = msig.getName();Class[] parameterTypes = msig.getParameterTypes();Method method = null;try {method = target.getClass().getMethod(methodName, parameterTypes);} catch (NoSuchMethodException e) {//log.error(...);}return method;
}/*** 从dto中,获取指定属性名的属性值;*/
public Object getFieldFromDtoByFieldName(Object dto , String fieldName) throws NoSuchFieldException, IllegalAccessException {Class<?> dtoClazz = dto.getClass();Field field = dtoClazz.getDeclaredField(fieldName);field.setAccessible(true);return field.get(dto);
}//  这个其实还有另一种写法
//  private Method getMethod(ProceedingJoinPoint joinPoint) {
//    try {
//      Class[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
//      return joinPoint.getTarget().getClass().getMethod(joinPoint.getSignature().getName(), parameterTypes);
//    } catch (NoSuchMethodException e) {
//      e.printStackTrace();
//    }
//    return null;
//  }
}

4.注解及设计原理

4.1ValidateGroup

这个注解用于被AspectJ拦截,其属性是一个数组,用于参数校验

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface ValidateGroup {ValidateFiled[] fields();}

4.2ValidateField

  • 当且仅当只有一个参数的时候可以不用指定index
  • index默认为0,例如public void register(@RequestParam String param1 , @RequestBody Dto dto){}中应该设置index = 1 ,这是由于joinPoint.getArgs()获取的形参是一个数组,需要用index指定位置
  • 所有参数都有默认值(不进行校验)

如下:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface ValidateField {/*** 参数索引位置:接口中有多个参数时用index指定需要校验的参数* 默认:0号索引,当且仅当接口方法只有一个参数*/int index() default 0;/*** 默认:如果参数是基本数据类型或String,就不用指定该参数* 如果参数是对象,要验证对象里面某个属性,就用该参数指定属性名*/String fieldName() default "";/*** 错误码,用于日志记录*/String code() default "";/*** 错误提示语,用于日志记录*/String message() default "";/*** 正则验证*/String regStr() default "";/*** 非空校验,为true表示不能为空,false表示能够为空*/boolean notNull() default false;/*** 字符串最大长度*/int maxLen() default 0x7fffffff;/*** 字符串最小长度*/int minLen() default 0;/*** 最大值,用于验证数值类型数据*/double maxVal() default 0x1.fffffffffffffP+1023;/*** 最小值,用于验证数值类型数据*/double minVal() default 0x0.0000000000001P-1022;}

5.ValidateAspectJHandler

5.1大体结构

@Component
@Aspect
public class ValidateAspectHandler {private AopUtils aopUtils = AopUtils.getInstance();/*** 使用AOP对使用了ValidateGroup的方法进行代理校验*/@Around("@annotation(ValidateGroup)")public Object validateAround(ProceedingJoinPoint joinPoint) throws Throwable {
//1.获取当前方法对象method
//2.根据method对象获取对应的ValidateGroup对象
//3.调用封装方法,将validateGroup.fields() 与 joinPoint.getArgs()形参数组传入,进行校验
//4.如果为true,则执行下一步return joinPoint.proceed();}/*** 封装方法:验证参数是否合法*/private boolean validateField(ValidateFiled[] validateFields, Object[] args) {for (ValidateFiled validateFiled : validateFields) {//1.每次循环都是一个校验逻辑//2.index仍然是指定对第几号元素进行校验//3.fieldName如果不指定,那就是对基本数据类型即@RequestParam进行校验;如果指定,则对Dto即@RequestBody进行校验}

5.2具体实现

@Component
@Aspect
public class ValidateAspctJHandler {//    private static org.slf4j.Logger logger = LoggerFactory.getLogger("ValidateAspctJHandler");private AopUtils aopUtils = AopUtils.getInstance();/*** 使用AOP对使用了ValidateGroup的方法进行代理校验*///相当于用@Around + return joinPoint.proceed(); //使用@Before的时候不能用ProceedingJoinPoint 只能用JoinPoint@Before("@annotation(ValidateGroup)")public void validateAround(JoinPoint joinPoint) throws Throwable {//获取被代理的方法对象Method method = aopUtils.getMethod(joinPoint);//获取被代理的方法对象对应的@ValidateGroup对象ValidateGroup validateGroup = (ValidateGroup)aopUtils.getAnnotationByMethod(method, ValidateGroup.class);//获取被代理方法的参数数组(这是参数值,而不是 method.getParameterTypes()返回的是Class[]  )Object[] args = joinPoint.getArgs();/** args和validateGroup中包含了全部需要校验的信息,因此可以封装为一个方法* 在这个方法中,如果校验失败则用throws抛异常的方式终止*/validateAllFields(validateGroup.fields(), args);}/*** 验证参数是否合法* ValidateField[]中每一条都是一个校验规则,每一条都对应一个属性* Object[] args中是所有的请求参数值,需要从args[validateFiled.index()中确定是对谁进行校验*/private void validateAllFields(ValidateField[] validateFields, Object[] args) throws NoSuchFieldException, IllegalAccessException {//遍历:对每个@ValidateField进行校验for (ValidateField validateFiled : validateFields) {Object arg;//1.当fieldName为默认值""的时候,此时是对@RequestParam即基本数据类型orString进行校验if ("".equals(validateFiled.fieldName())) {//arg是基本数据类型orStringarg = args[validateFiled.index()];//2.如果fieldName设置了,那就是对dto中的某个属性进行校验} else {//获取第index号参数dto指定的属性值arg = aopUtils.getFieldFromDtoByFieldName(args[validateFiled.index()], validateFiled.fieldName());}//3.以下是校验流程,需要同时考虑是对dto属性or基本数据类型orString//3.1判断参数是否为空if (validateFiled.notNull()) {if (arg == null || arg.equals("")) {
//            logger.error(validateFiled.code() + ":" + validateFiled.message());throw new RuntimeException(validateFiled.code()  + ":" + validateFiled.message());//如果该参数能够为空,并且当参数为空时,就不用判断后面的了 ,直接返回} }else {if (arg == null || arg.equals("")) {return;}}//3.2判断字符串最大长度  如果设置为一个负数则不校验  默认为最大int值if (validateFiled.maxLen() >= 0) {if (arg.toString().length() > validateFiled.maxLen()) {
//          logger.error(validateFiled.code() + ":" + validateFiled.message());throw new RuntimeException(validateFiled.code()  + ":" + validateFiled.message());}}//3.3判断字符串最小长度  如果设置为一个负数则不校验  默认为0if (validateFiled.minLen() >= 0) {if (arg.toString().length() < validateFiled.minLen()) {
//          logger.error(validateFiled.code() + ":" + validateFiled.message());throw new RuntimeException(validateFiled.code()  + ":" + validateFiled.message());}}//3.4判断数值最大值  当不是默认值0x1.fffffffffffffP+1023的时候进行判断if (validateFiled.maxVal() != 0x1.fffffffffffffP+1023) {if (Double.parseDouble(arg.toString()) > validateFiled.maxVal()) {
//          logger.error(validateFiled.code() + ":" + validateFiled.message());throw new RuntimeException(validateFiled.code()  + ":" + validateFiled.message());}}//3.5判断数值最小值   当不是默认值0x0.0000000000001P-1022的时候进行判断if (validateFiled.minVal() != 0x0.0000000000001P-1022) {if (Double.parseDouble(arg.toString()) < validateFiled.minVal()) {
//          logger.error(validateFiled.code() + ":" + validateFiled.message());throw new RuntimeException(validateFiled.code()  + ":" + validateFiled.message());}}//3.6判断正则 若未设置正则校验则跳过if (!"".equals(validateFiled.regStr())) {if (arg instanceof String || arg instanceof Integer || arg instanceof BigDecimal || arg instanceof Double) {if (!(arg.toString()).matches(validateFiled.regStr())) {
//            logger.error(validateFiled.code() + ":" + validateFiled.message());throw new RuntimeException(validateFiled.code()  + ":" + validateFiled.message());}}}}return;}
}

5.3接口测试

@RestController
@RequestMapping("validater")
public class ValidateController {@ValidateGroup(fields = {//如果是index=0也可以省略不写@ValidateField(index = 0,notNull = true,maxLen = 10,code = "param1-error",message = "param1校验错误"),@ValidateField(index = 1,notNull = true,fieldName = "passWord",minLen = 6,code = "passWord-erro",message = "密码校验错误"),@ValidateField(index = 1,notNull = true,fieldName = "age",minVal = 0,code = "age-error",message = "年龄不能小于0"),@ValidateField(index = 1,notNull = true,fieldName = "tall",minVal = 0,maxVal = 250.9,code ="tall-error",message = "身高范围出错"),@ValidateField(index = 1,notNull = true,fieldName = "phone",regStr = "^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$",code = "phone-error",message = "手机号错误")})@PostMapping("post")public String postValidater(@RequestParam String param1,  RegisterDto dto){System.out.println("成功通过校验");System.out.println("第一个参数是:" + param1);System.out.println("第二个参数是"+dto.toString());return "succeed";}
}


如果参数错误,则会直接抛异常终止请求

如果参数都正确就可以通过校验,如下:

http://www.lbrq.cn/news/796807.html

相关文章:

  • 时时彩 网站开发/郑州网站制作公司哪家好
  • 网站建设的设备/杭州企业seo
  • 万网主机网站建设数据库怎么弄/seo排名优化教程
  • 数据库做网站/广东短视频seo搜索哪家好
  • 局网站建设进入前十名/有免费推广平台
  • c 网站开发 调试/seo排名的影响因素有哪些
  • 电子商务他们的代表网站/济南网站建设方案
  • 域名访问网站应该怎么做/seo专业技术培训
  • 做拍卖的网站有哪些/品牌宣传方式
  • 鲜花网站建设项目概述/衡阳有实力seo优化
  • 网页网站导读怎么做/太原seo公司
  • 常见c2c网站有哪些/谷歌sem服务商
  • 昆明做网站建设技巧公司/网络宣传渠道有哪些
  • wordpress的配置文件/好搜自然seo
  • 上海徽与章网站建设宗旨/网站如何快速被百度收录
  • 杭州本地网站有哪些/下载百度软件
  • 自学python的网站/代做seo排名
  • 做网站具体步骤/天津百度seo代理
  • 代做电大网站ui作业/店铺推广怎么做
  • 上市公司做家具网站/百度官网认证免费
  • 郑州做网站报价/上海广告公司排名
  • 江西省做网站/seo技术分享
  • 商标设计大全/seo黑帽培训骗局
  • 行业网站域名选择/seo交流论坛seo顾问
  • vps 同时做ssh和做网站/北京seo关键词优化收费
  • 镇江住房建设网站/网站页面关键词优化
  • 做网站的说3年3年包括什么/汕头seo全网营销
  • 网站开发兼职合同/网络推广网站排名
  • 小程序商城制作平台/厦门seo培训学校
  • 二级建造师官网查询系统/优化大师最新版下载
  • Compose笔记(四十一)--ExtendedFloatingActionButton
  • Redis+Lua的分布式限流器
  • 专网内网IP攻击防御:从应急响应到架构加固
  • Rust:开发 DLL 动态链接库时如何处理 C 字符串
  • Rustdesk中继服务器搭建(windows 服务器)
  • 【领域热点】【Vue】Vue 与 WebAssembly:前端性能优化的黄金搭档