- 浏览: 56891 次
- 性别:
- 来自: 长沙
最新评论
-
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 2042jsonp是json的一种模式,专门用来解决前端跨域请 ... -
javascript实现图片瀑布流
2015-03-26 22:30 554瀑布流主要就是指数据按照瀑布方式自上而下加载,主要由于高度 ... -
数据库持久层框架iBatis、myBatis、Hibernate对比
2015-03-22 19:10 3406在 java 应用的数据库 ... -
页面跳转方式
2015-03-22 18:14 684web前端开发中,经常使用到页面跳转,下面是使用过的几种常用的 ... -
html中的input文本框禁止输入问题
2015-01-04 19:24 1698我们在进行前端页面开发时,经常会遇到输入框禁止输入的问 ... -
httpclient请求方式下的泛型json转换问题
2014-11-26 18:25 3384我们在采用httpclient方式进行服务调用时,如果服务 ... -
modelAndView在sping MVC框架下的使用
2014-11-25 21:26 1549在 spring 框架的控制器中,我们经常在事件处理时 ... -
jquery中的live事件
2014-08-20 19:51 980很久没写了,本来只是学到一点小东西,觉得没必要写的,但是感 ... -
java String类型各种转化
2013-03-07 19:58 1169作为一名底层开发工 ... -
Java 内存输入流问题
2012-12-06 15:36 1111不论Java与C相比有多少优点,至少在输入输出流这点上还是 ... -
http工作原理
2012-12-01 10:47 827温故知新。 一、http协议:通过可靠的TCP连接,让we ... -
http请求响应码分析
2012-11-28 18:14 1159工作中经常遇到不同 ... -
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 1291学习之余,把hibernate的知识点稍作小结。我们知 ... -
MFC 定时器之妙用
2010-12-11 18:27 2967现在有这么一个问题,在VC编程中,如果要每隔一段时间去 ... -
文件流操作之C++版
2010-12-03 01:28 2100昨天简单介绍了一下C ... -
文件流操作之C版
2010-12-02 01:10 2050最近这段时间因为 ... -
java代码的动态编译问题
2010-10-29 23:49 1149上回听了一堂关于Java动态编译和静态编译的课,觉得自 ...
相关推荐
基于Spring boot + maven,以注解+AOP方式实现的java后端项目接口参数校验框架。迄今为止使用最简单、最容易理解的参数校验方案。博客地址:https://blog.csdn.net/weixin_42686388/article/details/104009771
Spring注解AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的一个重要特性,它使得开发者可以在不修改原有代码的情况下,通过添加注解来实现横切关注点,如日志、事务管理等。下面我们将深入探讨...
SpringBoot AOP,即面向切面编程,是Spring框架中的一个重要特性,用于实现代码的横切关注点,如日志记录、事务管理、权限验证等。AOP通过使用代理模式,将这些关注点与核心业务逻辑分离,使得代码更加模块化,更...
在本文中,我们将深入探讨如何在Spring框架中使用注解驱动的Aspect Oriented Programming(AOP),以便实现灵活且模块化的代码结构。AOP是一种编程范式,它允许我们在不改变原有业务逻辑的情况下,对程序进行横向切...
接口签名的主要目的是验证请求的来源合法性,防止数据被篡改,以及确保通信双方的数据一致性。本文将深入探讨如何使用Java和Spring Boot来实现接口签名。 首先,接口签名的基本原理是通过一种约定好的方式,如哈希...
1、按图所示的类图结构,设计接口及其实现类,并完成另外两附加要求:(1)日志功能:在程序执行期间追踪正在发生的活动(打印出调用的方法,以及参数的参数值);(2)验证功能:希望计算器只能处理正数的运算,当...
在Spring Boot框架中,自定义注解和AOP(面向切面编程)是两种强大的工具,可以帮助我们实现灵活且模块化的代码结构。AOP允许我们在不修改原有代码的情况下,通过切面来插入额外的功能,如日志记录、权限检查等。...
- **日志记录**:记录方法的入参、返回值、执行时间等信息。 - **事务管理**:自动处理方法的事务提交和回滚。 - **性能监控**:统计方法的执行时间,用于性能优化。 - **安全控制**:权限检查,防止非法访问。 ...
10. **测试和调试**:指导如何编写测试用例来验证AOP逻辑的正确性,以及如何调试AOP相关的代码问题。 通过阅读博客文章《Java实现aop案例》,读者应该能够了解并掌握如何在实际项目中应用Spring AOP,从而实现代码...
在MVC3中,验证通常是通过数据注解(Data Annotations)或者使用IValidatableObject接口来完成的。数据注解允许你直接在模型属性上添加验证规则,例如`Required`、`StringLength`等。IValidatableObject接口则允许你...
5. 测试:项目可能包含测试类,通过模拟方法调用来验证AOP通知是否正确工作。 通过深入学习和实践`springboot-aop`项目,你可以更深入地理解Spring AOP的工作原理,如何定义和使用切面,以及如何在实际项目中有效地...
2. **基于注解的AOP**:Spring提供了一系列的注解,如`@Aspect`、`@Before`、`@After`、`@Around`、`@AfterReturning`、`@AfterThrowing`,使得AOP配置更加简洁,无需编写XML配置。 **AOP应用场景** 1. **日志记录...
Spring AOP支持运行时织入和编译时织入,但默认采用的是运行时织入。 **Spring AOP的应用场景** 1. **日志记录**:在方法调用前后记录日志,便于追踪程序执行情况。 2. **事务管理**:自动处理数据库事务,确保...
在Spring AOP中,切面通常由一个或多个注解的类组成,这些类定义了切点表达式和通知。 2. **切点(Pointcut)**:切点是程序执行过程中的特定位置,例如方法的调用、异常的抛出等。在Spring AOP中,我们使用切点...
Spring AOP允许开发者定义这些切面,并在运行时自动将它们织入到应用程序中。 2. **Spring AOP的主要组件** - **切面(Aspect)**:一个关注点的模块化,它封装了多个相关的方法(称为通知)。 - **通知(Advice...
通过注解将这个切面应用到目标类,观察日志输出,验证AOP是否正确工作。 在实践中,还可以尝试以下操作: - 使用`@Before`、`@After`、`@AfterReturning`、`@AfterThrowing`和`@Around`注解定义不同类型的通知。 - ...
JDK代理适用于接口实现类,CGLIB代理则适用于无接口或接口未被AOP通知覆盖的情况。 5. **实际应用场景**: - 日志记录:在方法调用前后记录日志信息。 - 事务管理:自动开始和提交事务,异常时回滚。 - 性能监控...
3. **注解驱动的AOP** - `@Aspect`:标记一个类为切面。 - `@Before`:前置通知,方法在目标方法之前执行。 - `@After`:后置通知,无论目标方法是否正常执行,都在其之后执行。 - `@AfterReturning`:返回后...
在这个例子中,`Spring3.1.0-AOP`可能包含Spring AOP的库文件,比如spring-aop.jar,这包含了实现AOP功能所需的类和接口。这些库可能还包含了Spring的核心模块,如spring-context,它支持AOP的配置和装配。 为了...
在这个"SpringAOP注解方式"的示例中,我们将深入探讨如何使用注解来实现Spring AOP的功能。 首先,Spring AOP通过两种主要的方式来定义切面:XML配置和注解。本示例主要关注注解方式,因为它提供了更简洁、更直观的...