- 浏览: 55725 次
- 性别:
- 来自: 长沙
最新评论
-
tian_ya_nan:
真不错 !
文件流操作之C版 -
aogeback:
看到楼主来自湖南大学了
MFC 定时器之妙用 -
seedjyh:
什么ACM,是ICPC吧……
算法分析最小重量机器设计问题 -
zk_sima:
楼主标题党,这也算妙用吗,这叫基础知识普及吧,没看到你用定时器 ...
MFC 定时器之妙用 -
glasslion:
苍山洱海 写道0 1背包?
对啊,明明是经典的01背包问题的 ...
算法分析最小重量机器设计问题
为什么要入参验证
系统之间在进行接口调用时,往往是有入参传递的,入参是接口业务逻辑实现的先决条件,有时入参的缺失或错误会导致业务逻辑的异常,大量的异常捕获无疑增加了接口实现的复杂度,也让代码显得雍肿冗长,因此提前对入参进行验证是有必要的,可以提前处理入参数据的异常,并封装好异常转化成结果对象返回给调用方,也让业务逻辑解耦变得独立。
为什么要使用aop方式
入参验证的方式有多种,传统的方式是在接口实现中代码注入,即在接口实现的业务逻辑处理之前,通过硬编码的方式对入参进行有效性验证,简单粗暴,也直接有效。但代码注入带来的问题是代码的重用,不同的接口不同的入参,都需要编写不同的入参验证逻辑,造成了代码的重复使用,而这些验证逻辑大部分是可以复用的,并且代码注入方式虽然从一定程度上对业务逻辑进行了解耦,但依然需要在接口实现中注入代码,从一定程度上不够独立。因此,从代码重用和业务完全解耦上看,aop注解方式验参更加有效。
怎么实现aop方式
spring框架的aop是一种面向切面编程,说的简单点就是将非核心业务的公共逻辑从业务层面抽离开来封装成可重用的模块,实现对业务逻辑的高度解耦,减少系统的重复代码。
aop方式需要首先在spring配置中定义aop映射,使得服务能够依赖注解有效切入。然后在服务调用前先定义好注解接口类及注解验证方法,通过在业务接口实现方法上增加注解来实现aop。服务调用时会根据接口方法的注解在切面通知方法中进行参数验证,验证失败则抛出异常,服务中止,业务逻辑完全独立,如下:
一行注解搞定入参验证,代码瞬间简单大气,注解的实现接下来分析。使用注解,必然是需要先定义注解,注解可以根据系统的需要定义多种验证方法,比如像是否需要验证token,是否有调用次数限制。
因为注解是采用aop方式实现,就一定会有aop通知方法来实现对参数的验证。在通知方法中,就需要根据注解接口的方法来验证参数了。
怎么验证参数
参数验证有多种方式,可以验证参数非空、参数类型、参数格式、赋值范围等,最直接的方法就是在注解通知方法中依次验证,但验证逻辑就会很长,并且不同的参数需要验证的类型不尽相同,在同一个方法中显然很难做到灵活验证,因此,就需要将参数验证类型进行配置化管理。
在spring配置文件中,定义spring入参验证配置,对需要验证的入参配置验证类型(非空、数值、金额、范围等),并自定义spring标签,配置多种验证类型,如下所示:
这样就能实现对入参不同参数的验证类型灵活配置,也能对同一入参对象配置多个验证模块,以满足同一入参对象在不同接口中的多种验证类型需求(比如同一入参的同一属性,在A接口必传,却在B接口可空)。当然,所有这些验证标签都是在spring中自定义的,我们可以根据业务的需要增加各种类型验证。
标签定义好后,就是实现标签的验证逻辑了。首先我们需要定义一个统一的参数验证接口,然后根据自定义的标签一一实现接口逻辑,再根据spring的反射机制,将自定义标签绑定到对应的接口实现类上,接口实现类如下:
当需要增加验证类型时,无需修改代码,只需要自定义一套标签并增加一个对应的验证实现类即可,非常方便灵活。
定义好整套验证实现类后,就可以在上面的注解通知方法中来统一调用了。在通知方法中,获取入参对象,根据spring配置文件的定义,匹配到自定义标签集合。根据标签集合的验证类型调用不同的验证实现类,对入参的每个属性进行验证。直接上代码:
验证完成后,如果验证失败集合不为空,则使用公共返回对象封装实例化,返回调用方相关的错误代码与提示信息。
到此,接口入参的注解aop验证方法介绍完毕,当然,上面所贴的代码并非完整的验证代码,诸如spring标签定义、接口反射等逻辑就不在这里展示了,这里主要是想表达下自己的见解和原理。从上面的分析可以看出,注解aop验证入参,最大的好处就是让参数验证与业务逻辑高度解耦,用专业的方法干专业的事,然后就是实现了验证方式的灵活配置,这个能让我们对代码的伤害降到最低。
系统之间在进行接口调用时,往往是有入参传递的,入参是接口业务逻辑实现的先决条件,有时入参的缺失或错误会导致业务逻辑的异常,大量的异常捕获无疑增加了接口实现的复杂度,也让代码显得雍肿冗长,因此提前对入参进行验证是有必要的,可以提前处理入参数据的异常,并封装好异常转化成结果对象返回给调用方,也让业务逻辑解耦变得独立。
为什么要使用aop方式
入参验证的方式有多种,传统的方式是在接口实现中代码注入,即在接口实现的业务逻辑处理之前,通过硬编码的方式对入参进行有效性验证,简单粗暴,也直接有效。但代码注入带来的问题是代码的重用,不同的接口不同的入参,都需要编写不同的入参验证逻辑,造成了代码的重复使用,而这些验证逻辑大部分是可以复用的,并且代码注入方式虽然从一定程度上对业务逻辑进行了解耦,但依然需要在接口实现中注入代码,从一定程度上不够独立。因此,从代码重用和业务完全解耦上看,aop注解方式验参更加有效。
怎么实现aop方式
spring框架的aop是一种面向切面编程,说的简单点就是将非核心业务的公共逻辑从业务层面抽离开来封装成可重用的模块,实现对业务逻辑的高度解耦,减少系统的重复代码。
aop方式需要首先在spring配置中定义aop映射,使得服务能够依赖注解有效切入。然后在服务调用前先定义好注解接口类及注解验证方法,通过在业务接口实现方法上增加注解来实现aop。服务调用时会根据接口方法的注解在切面通知方法中进行参数验证,验证失败则抛出异常,服务中止,业务逻辑完全独立,如下:
@Override @ParamValid(className = "orderRequestVoValidator") public GenericResult<OrderResponseVo> orderRequest(OrderRequestVo vo) { log.info("质押收单请求开始,vo:{}", GsonUtils.toJson(vo)); GenericResult<OrderResponseVo> result = tradeOrderService.orderRequest(vo); log.info("质押收单请求完成,result:{}", GsonUtils.toJson(result)); return result; }
一行注解搞定入参验证,代码瞬间简单大气,注解的实现接下来分析。使用注解,必然是需要先定义注解,注解可以根据系统的需要定义多种验证方法,比如像是否需要验证token,是否有调用次数限制。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface ParamValid { String className(); /* 是否限制调用次数 */ boolean isLimit() default false; /* 方法访问次数 */ long limitNum() default 10l; /* 方法访问限制时效 */ int limitTime() default 300; }
因为注解是采用aop方式实现,就一定会有aop通知方法来实现对参数的验证。在通知方法中,就需要根据注解接口的方法来验证参数了。
怎么验证参数
参数验证有多种方式,可以验证参数非空、参数类型、参数格式、赋值范围等,最直接的方法就是在注解通知方法中依次验证,但验证逻辑就会很长,并且不同的参数需要验证的类型不尽相同,在同一个方法中显然很难做到灵活验证,因此,就需要将参数验证类型进行配置化管理。
在spring配置文件中,定义spring入参验证配置,对需要验证的入参配置验证类型(非空、数值、金额、范围等),并自定义spring标签,配置多种验证类型,如下所示:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:validator="http://com.jd.assetPledge/schema/validator" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://com.jd.assetPledge/schema/validator http://com.jd.assetPledge/schema/validator/validator.xsd" default-lazy-init="false" default-autowire="byName"> <validator:validator id="orderRequestVoValidator" class="com.jd.assetPledge.trade.export.vo.OrderRequestVo"> <validator:property name="pin"> <validator:NotNull message="请填写pin"/> </validator:property> <validator:property name="assetNo"> <validator:NotNull message="请填写资产编号"/> </validator:property> <validator:property name="assetType"> <validator:NotNull message="请填写资产类型"/> </validator:property> <validator:property name="channelType"> <validator:NotNull message="请填写渠道号"/> </validator:property> <validator:property name="sourceType"> <validator:NotNull message="请填写移动来源"/> </validator:property> <validator:property name="token"> <validator:Token message="系统token为空或错误"/> </validator:property> </validator:validator>
这样就能实现对入参不同参数的验证类型灵活配置,也能对同一入参对象配置多个验证模块,以满足同一入参对象在不同接口中的多种验证类型需求(比如同一入参的同一属性,在A接口必传,却在B接口可空)。当然,所有这些验证标签都是在spring中自定义的,我们可以根据业务的需要增加各种类型验证。
标签定义好后,就是实现标签的验证逻辑了。首先我们需要定义一个统一的参数验证接口,然后根据自定义的标签一一实现接口逻辑,再根据spring的反射机制,将自定义标签绑定到对应的接口实现类上,接口实现类如下:
public class NotNullValidator extends ValidatorImpl implements Validator { public NotNullValidator() { } public boolean isValid(Object object) { if(object instanceof String) { int length = ((String)object).length(); return length > 0; } else { return object != null; } } }
当需要增加验证类型时,无需修改代码,只需要自定义一套标签并增加一个对应的验证实现类即可,非常方便灵活。
定义好整套验证实现类后,就可以在上面的注解通知方法中来统一调用了。在通知方法中,获取入参对象,根据spring配置文件的定义,匹配到自定义标签集合。根据标签集合的验证类型调用不同的验证实现类,对入参的每个属性进行验证。直接上代码:
@Component @Aspect public class ParamValidAspect { private static Logger log = LoggerFactory.getLogger(ParamValidAspect.class); @Autowired private CacheRpc cacheRpc; @Resource(name = "checkService") private CheckService checkService; @Value("${paramValid.token}") private String token; @Value("${paramValid.isLimit}") private boolean isLimit; @Value("${paramValid.limitNum}") private long limitNum; @Value("${paramValid.limitTime}") private int limitTime; @Pointcut("execution(* *(..)) && @annotation(com.jd.assetPledge.trade.web.validator.ParamValid)") public void paramValidPointcut() { log.info("paramValid aspect pointcut initialize successful..."); } @Around("paramValidPointcut()") public Object aroundParamValidReturn(ProceedingJoinPoint pjp) throws Throwable { Method method = getMethod(pjp); Class[] parameterTypes = method.getParameterTypes(); Object[] args = pjp.getArgs(); if (parameterTypes.length < 1 || args.length < 1) { return pjp.proceed(); } try { ParamValid paramValid = method.getAnnotation(ParamValid.class); String objName = paramValid.className(); WebApplicationContext applicationContext = ContextLoader.getCurrentWebApplicationContext(); Object obj = args[0]; /* 判断入参是否正确 */ String checkMsgTip = checkService.check(obj, (ValidatorBean) applicationContext.getBean(objName)); if(StringUtils.isNotBlank(checkMsgTip)) { return getResObj(method,ResultInfoEnum.REQUEST_PARAMS_ERROR, checkMsgTip); } /* 判断调用次数限制 */ if(paramValid.isLimit() || isLimit) { if(paramValid.isLimit()) { limitNum = paramValid.limitNum(); limitTime = paramValid.limitTime(); } BaseRequestVo base = (BaseRequestVo)args[0]; long count = cacheRpc.countKey(base.getPin() + method.getName(), limitTime); if(count > limitNum) { return getResObj(method,ResultInfoEnum.REQUEST_LIMIT_ERROR, null); } } } catch (Exception e) { return getResObj(method,ResultInfoEnum.UNKNOW_ERROR, null); } return pjp.proceed(); } private Object getResObj(Method method,ResultInfoEnum enumType, String checkMsgTip) { Class<?> returnType = method.getReturnType(); Class[] classArgs = new Class[2]; classArgs[0] = String.class; classArgs[1] = String.class; try { Constructor constructor = returnType.getConstructor(classArgs); String code = enumType.getErrorCode(); String message = enumType.getErrorMsg(checkMsgTip); return constructor.newInstance(code, message); } catch (Exception e) { log.error("exception", e); } return null; } private Method getMethod(ProceedingJoinPoint pjp) throws NoSuchMethodException { Signature sig = pjp.getSignature(); MethodSignature msig = (MethodSignature) sig; return getClass(pjp).getMethod(msig.getName(), msig.getParameterTypes()); } private Class<? extends Object> getClass(ProceedingJoinPoint pjp) throws NoSuchMethodException { return pjp.getTarget().getClass(); } }
public class CheckService { private static final Logger logger = LoggerFactory.getLogger(CheckService.class); public CheckService() { } public <T> String check(T t, ValidatorBean nameValidator) throws IllegalAccessException { long t1 = System.currentTimeMillis(); logger.info("参数验证开始-----------------------"); Map filedValidatorListMap = nameValidator.getFiledValidatorListMap(); ArrayList fieldList = new ArrayList(); DynamicUtil.getClassAllField(t.getClass(), fieldList); StringBuffer checkMsgTip = new StringBuffer(""); Iterator i$ = fieldList.iterator(); while(true) { Field field; List validatorList; do { if(!i$.hasNext()) { logger.info("参数验证结束---------------------------时间{}", Long.valueOf(System.currentTimeMillis() - t1)); return checkMsgTip.toString(); } field = (Field)i$.next(); String fieldName = field.getName(); field.setAccessible(true); validatorList = (List)filedValidatorListMap.get(fieldName); } while(validatorList == null); Iterator i$1 = validatorList.iterator(); while(i$1.hasNext()) { Validator validator = (Validator)i$1.next(); ValidatorImpl validator1 = (ValidatorImpl)validator; boolean checkResult = validator.isValid(field.get(t)); if(!checkResult) { logger.info(nameValidator.getName() + ":" + "field=" + validator1.getValidatorField() + ",value=" + field.get(t) + ",validatorType=" + validator1.getValidatorType() + "结果:" + checkResult + ",消息" + ((ValidatorImpl)validator).getMessage()); checkMsgTip.append(((ValidatorImpl)validator).getMessage() + "\n"); } } } } }
验证完成后,如果验证失败集合不为空,则使用公共返回对象封装实例化,返回调用方相关的错误代码与提示信息。
到此,接口入参的注解aop验证方法介绍完毕,当然,上面所贴的代码并非完整的验证代码,诸如spring标签定义、接口反射等逻辑就不在这里展示了,这里主要是想表达下自己的见解和原理。从上面的分析可以看出,注解aop验证入参,最大的好处就是让参数验证与业务逻辑高度解耦,用专业的方法干专业的事,然后就是实现了验证方式的灵活配置,这个能让我们对代码的伤害降到最低。
发表评论
-
jsonp跨域及rest接口实现
2017-02-04 18:01 2005jsonp是json的一种模式,专门用来解决前端跨域请 ... -
javascript实现图片瀑布流
2015-03-26 22:30 512瀑布流主要就是指数据按照瀑布方式自上而下加载,主要由于高度 ... -
数据库持久层框架iBatis、myBatis、Hibernate对比
2015-03-22 19:10 3372在 java 应用的数据库 ... -
页面跳转方式
2015-03-22 18:14 643web前端开发中,经常使用到页面跳转,下面是使用过的几种常用的 ... -
html中的input文本框禁止输入问题
2015-01-04 19:24 1656我们在进行前端页面开发时,经常会遇到输入框禁止输入的问 ... -
httpclient请求方式下的泛型json转换问题
2014-11-26 18:25 3343我们在采用httpclient方式进行服务调用时,如果服务 ... -
modelAndView在sping MVC框架下的使用
2014-11-25 21:26 1517在 spring 框架的控制器中,我们经常在事件处理时 ... -
jquery中的live事件
2014-08-20 19:51 945很久没写了,本来只是学到一点小东西,觉得没必要写的,但是感 ... -
java String类型各种转化
2013-03-07 19:58 1137作为一名底层开发工 ... -
Java 内存输入流问题
2012-12-06 15:36 1052不论Java与C相比有多少优点,至少在输入输出流这点上还是 ... -
http工作原理
2012-12-01 10:47 793温故知新。 一、http协议:通过可靠的TCP连接,让we ... -
http请求响应码分析
2012-11-28 18:14 1123工作中经常遇到不同 ... -
JSP学习——JSP页面重用
2012-09-19 00:07 721.何为页面重用: Jsp页面在一个web应用中,主要 ... -
JSP学习——JSP隐含对象详解
2012-09-19 00:06 311.jsp隐式对象简介 JSP 架 ... -
JSP学习——JSP基础入门
2012-09-19 00:05 301.jsp简化入门 1.jsp基础 ... -
hibernate学习小结
2010-12-16 22:34 1253学习之余,把hibernate的知识点稍作小结。我们知 ... -
MFC 定时器之妙用
2010-12-11 18:27 2932现在有这么一个问题,在VC编程中,如果要每隔一段时间去 ... -
文件流操作之C++版
2010-12-03 01:28 2068昨天简单介绍了一下C ... -
文件流操作之C版
2010-12-02 01:10 2011最近这段时间因为 ... -
java代码的动态编译问题
2010-10-29 23:49 1119上回听了一堂关于Java动态编译和静态编译的课,觉得自 ...
相关推荐
基于Spring boot + maven,以注解+AOP方式实现的java后端项目接口参数校验框架。迄今为止使用最简单、最容易理解的参数校验方案。博客地址:https://blog.csdn.net/weixin_42686388/article/details/104009771
1、按图所示的类图结构,设计接口及其实现类,并完成另外两附加要求:(1)日志功能:在程序执行期间追踪正在发生的活动(打印出调用的方法,以及参数的参数值);(2)验证功能:希望计算器只能处理正数的运算,当...
为了保证数据传输的安全性,跟其他系统进行数据交互时,双方应该约定好密钥,把数据进行加密,接口签名,这样双方调用接口时,验证接口签名一致时就表明数据传输过程中没有被修改。
它提供了很多方面的功能,比如依赖注入、面向方面编程(AOP)、数据访问抽象及ASP.NET扩展等等。Spring.NET以Java版的Spring框架为基础,将Spring.Java的核心概念与思想移植到了.NET平台上。 第一章 序言 第二章 ...
使用自定义注解+aop 替代shiro的功能,简化了配置,增强了可拓展性 设计思路 核心 每个登录用户拥有各自的N条权限,比如 文章:查看/编辑/发布/删除 后端 基于 RBAC新解 . 通常我们的权限设计都是 用户--角色--权限 ,...
1、按图所示的类图结构,设计接口及其实现类,并完成另外两附加要求:(1)日志功能:在程序执行期间追踪正在发生的活动(打印出调用的方法,以及参数的参数值);(2)验证功能:希望计算器只能处理正数的运算,当...
spring-dubbo-service微服务一,服务模块1,服务器配置:servlet,侦听器,拦截器,过滤器,aop,定时任务mybatis配置集成,多数据源;级联查询一对一对一对多注解配置及xml配置方式;分页处理:传递Page参数或继承...
使用自定义注解+aop 替代shiro的功能,简化了配置,增强了可拓展性 设计思路 核心 每个登录用户拥有各自的N条权限,比如 文章:查看/编辑/发布/删除 后端 基于 . 通常我们的权限设计都是 用户--角色--权限 ,其中角色是...
* 登录状态保持:后端实现token值是否有效,因为大部分接口都需要验证登陆,所以进行方法抽取,用aop注解切面来返回用户信息(需要自定义注解:1.@interface + 注解名,2. 元注解:修饰注解的注解,@Target:注解用...
微服务-> springMVC rest定制属性-> json 客户监听器模式hystrix工作方式kafka kafka api 流简单实现验证别名注释注解派生aop切面,过滤器,拦截器安全resttemplate自定义restTemplate 拼写作用域resttemplate自定义...
7.4.2 在函数入参中使用通配符 7.4.3 逻辑运算符 7.4.4 不同增强类型 7.4.5 引介增强用法 7.5 切点函数详解 7.5.1 @annotation() 7.5.2 execution() 7.5.3 args()和@args() 7.5.4 within() 7.5.5 @within...
7.4.2 在函数入参中使用通配符 7.4.3 逻辑运算符 7.4.4 不同增强类型 7.4.5 引介增强用法 7.5 切点函数详解 7.5.1 @annotation() 7.5.2 execution() 7.5.3 args()和@args() 7.5.4 within() 7.5.5 @within...
- 新增aop注解实现操作日志功能。 - 支持动态权限修改,采用RBAC模型,前端菜单和后台权限实时更新。 - 后台管理支持修改背景图片,博客配置等信息,操作简单,支持上传相册。 - 代码支持多种搜索模式(Elastic...
128、在JSP中如何使用注释? 32 129、在JSP中如何执行浏览重定向? 32 130、如何防止在JSP或SERVLET中的输出不被BROWSER保存在CACHE中? 32 131、在JSP中如何设置COOKIE? 32 132、在JSP中如何删除一个COOKIE? 32 133、...
###简介: 面向移动端(手机App,公众号,小程序.....)提供的API服务脚手架开发框架,项目代码: 可用于实施基于Dubbo的分布式...结合AOP,实现简单的安全验证,如接口验收,接口速率限制,黑白列表(结合具体业务场景
java8 源码 最初的梦想 三步走: version1.0为高可用(物理库,内存库)ssm web应用; version2.0为web服务性能提升:tomcat集群...采用AOP的方式判断验证结果 Druid网络统计与监控 捕获全局错误 实现GlobalExceptionR
10.4. Remember-Me接口和实现 10.4.1. TokenBasedRememberMeServices 10.4.2. PersistentTokenBasedRememberMeServices 11. 会话管理 11.1. SessionManagementFilter 11.2. SessionAuthenticationStrategy...
泛型技巧系列:避免基类及接口约束 New Article 不该用Generics实现Abstract Factory的理由 C#2.0-泛型 C#2.0-extern C#2.0-可空类型 C#2.0-分部类 C#2.0-迭代器 C#2.0 的新增功能学习 泛型的序列化问题 .NET 2.0 ...
10.4. Remember-Me接口和实现 10.4.1. TokenBasedRememberMeServices 10.4.2. PersistentTokenBasedRememberMeServices 11. 会话管理 11.1. SessionManagementFilter 11.2. SessionAuthenticationStrategy ...
10.4. Remember-Me 接口和实现 10.4.1. TokenBasedRememberMeServices 10.4.2. PersistentTokenBasedRememberMeServices 11. 会话管理 11.1. SessionManagementFilter 11.2. ...