版权声明:此文章转载自_infocool
如需转载请联系听云College团队成员小尹 邮箱:yinhy#tingyun.com
最近刚刚学习了Spring AOP,也是首次使用Spring AOP进行项目开发,尝试写了一个简单的参数校验框架,也许对像我一样新接触spring AOP的童鞋有所参考,故此分享,若有不合理的地方,请大神帮忙指正,非常感谢!
搭建Spring开发环境这里就不详细说明了,除了spring的一些核心包,再引入spring-aop.jar即可。
我想要做的效果是:在所有的Controller方法中,通过注解开关定义是否校验参数,并且可配置参数实体中各个元素的校验规则。
-
定义参数校验的开关
在controller方法中,可能有些参数需要校验,也有些参数不需要校验,所以定义了一个参数校验开关。我使用的是一个注解,Valid注解定义如下:
package com.test.constant.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD,ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface Valid { } Valid只是一个简单的注解,注意它的ElementType我主要用在PARAMETER上,因为这样才可以选择性地去定义哪个参数需要做校验。 @Valid注解在controller中方法使用如下: /** * 功能: 修改问题 * @param form * @param servletRequest * @param servletResponse * @return * @throws Exception */ @ResponseBody @RequestMapping("/work/disProblemController/update") public ResponseInfo<String> updateProblem(@Valid UpdateProblemForm form,HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws Exception {
ResponseInfo<String> responseInfo = new ResponseInfo<String>; try { problemService.updateProblem(form); } catch (Exception e) { throw new Exception("修改问题异常",e); } responseInfo.setRtnCode(AlmRetConstant.code.APP_RET_SUCCESS_CODE); responseInfo.setRtnMsg(AlmRetConstant.msg.APP_RET_SUCCESS_CODE); responseInfo.setSuccess(true); return responseInfo; }
2. 定义参数校验规则
Check注解定义参数校验规则,现在只做了几种简单的校验规则,如后续接入其他更复杂的规则,考虑使用正则表达式校验。代码如下:
package com.test.constant.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Check { /** * 是否非空 */ public boolean notNull default false; /** * 是否为数值 */ public boolean numeric default false; /** * 最大长度 */ public int maxLen default -1; /** * 最小长度 */
public int minLen default -1; /** * 最小数值 */ public long minNum default -999999; } @Check注解在参数实体中的使用方式如下(以UpdateProblemForm实体为例): package com.test.form; import com.pingan.almcenter.constant.annotation.Check; /** * 修改问题表单 * */ public class UpdateProblemForm { @Check(notNull=true) private Integer id; //问题ID @Check(notNull = true, maxLen = 30) private String problemName;// 问题名称 @Check(notNull = true, maxLen = 3) private String problemType;// 问题类型 @Check(notNull = true, maxLen = 3) private String problemStatus;// 问题状态
@Check(maxLen = 320) private String problemMsg;// 问题详细信息 @Check(notNull = true, maxLen = 30) private String bankName;// 所属银行名称 //@Check(notNull=true) private Integer bankId;// 所属银行id public Integer getId { return id; } public void setId(Integer id) { this.id = id; } public String getProblemName { return problemName; } public void setProblemName(String problemName) { this.problemName = problemName; } public String getProblemType { return problemType; } public void setProblemType(String problemType) { this.problemType = problemType; } public String getProblemStatus { return problemStatus; } public void setProblemStatus(String problemStatus) {
this.problemStatus = problemStatus; } public String getProblemMsg { return problemMsg; } public void setProblemMsg(String problemMsg) { this.problemMsg = problemMsg; } public String getBankName { return bankName; } public void setBankName(String bankName) { this.bankName = bankName; } public Integer getBankId { return bankId; } public void setBankId(Integer bankId) { this.bankId = bankId; } }
3. 最后是Aspect的主体内容,定义 Around方法并实现参数校验
Aspect类的代码如下:
定义切点。我只想用它来校验controller包中方法的参数,所以我切点切在controller上,切点的定义方式还有很多种,可以考虑其他方式。
package com.test.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component @Aspect public class ParameterCheckAspect { @Autowired private ParameterCheckOption parameterCheckOption; // 定义切点 @Pointcut("within(com.pingan.almcenter.controller..*)") public void check { } /** * 切面方法,使用统一异常处理 * * @param joinPoint * @return * @throws Throwable */ @Around(value = "check", argNames = "Valid") public Object checkIsValid(ProceedingJoinPoint joinPoint) throws Throwable {
Object object = null; // 参数校验,未抛出异常表示验证OK parameterCheckOption.checkValid(joinPoint); object = ((ProceedingJoinPoint) joinPoint).proceed; return object; } } ParameterCheckOption处理类代码如下: package com.test.aspect; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.math.BigDecimal; import org.aspectj.lang.ProceedingJoinPoint; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import com.pingan.almcenter.constant.BaseComponent; import com.pingan.almcenter.constant.annotation.Check; import com.pingan.almcenter.constant.annotation.Valid; import com.pingan.almcenter.constant.exception.ParameterException; @Component public class ParameterCheckOption extends BaseComponent{
public void checkValid(ProceedingJoinPoint joinPoint) throws Exception{ Object args = null; Method method = null; Object target = null; String methodName = null; String str = ""; try { methodName = joinPoint.getSignature.getName; target = joinPoint.getTarget; method = getMethodByClassAndName(target.getClass, methodName); Annotation annotations = method.getParameterAnnotations; args = joinPoint.getArgs; // 方法的参数 if (annotations != null) { for (int i = 0; i < annotations.length; i++) { Annotation anno = annotations[i]; for (int j = 0; j < anno.length; j++) { if (annotations[i][j].annotationType.equals( Valid.class)) { str = checkParam(args[i]); if (StringUtils.hasText(str)) { throw new ParameterException(str); } } } } }
} catch (Throwable e) { logger.error("参数校验异常" + e); throw new ParameterException(str); } } /** * 校验参数 * * @param args * @return * @throws Exception */ private String checkParam(Object args) throws Exception { String retStr = ""; Field field = args.getClass.getDeclaredFields;// 获取方法参数(实体)的field for (int j = 0; j < field.length; j++) { Check check = field[j].getAnnotation(Check.class);// 获取方法参数(实体)的field上的注解Check if (check != null) { String str = validateFiled(check, field[j], args); if (StringUtils.hasText(str)) { retStr = str; return retStr; } } } return retStr; }
/** * 校验参数规则 * * @param check * @param field * @param name * @return * @throws Exception */ public String validateFiled(Check check, Field field, Object args) throws Exception { field.setAccessible(true); // 获取field长度 int length = 0; if (field.get(args) != null) { length = (String.valueOf(field.get(args))).length; } if (check.notNull) { if (field.get(args) == null || "".equals(String.valueOf(field.get(args)))) { return field.getName + "不能为空"; } } if (check.maxLen > 0 && (length > check.maxLen)) { return field.getName + "长度不能大于" + check.maxLen; } if (check.minLen > 0 && (length < check.minLen)) {
return field.getName + "长度不能小于" + check.minLen; } if (check.numeric && field.get(args) != null) { try { new BigDecimal(String.valueOf(field.get(args))); } catch (Exception e) { return field.getName + "必须为数值型"; } } if (check.minNum != -999999) { try { long fieldValue = Long .parseLong(String.valueOf(field.get(args))); if (fieldValue < check.minNum) { return field.getName + "必须不小于" + check.minNum; } } catch (Exception e) { return field.getName + "必须为数值型,且不小于" + check.minNum; } } return ""; } /** * 根据类和方法名得到方法
*/ @SuppressWarnings("rawtypes") public Method getMethodByClassAndName(Class c, String methodName) throws Exception { Method methods = c.getDeclaredMethods; for (Method method : methods) { if (method.getName.equals(methodName)) { return method; } } return null; } }
由于框架使用了另一个统一异常处理小框架,所以所有的异常都没处理,而是直接抛出,由统一异常处理类去处理。单独用可以自主处理异常。
终上所述,Spring AOP进行参数统一校验的功能就实现了。由于是初接触AOP,所以写得比较粗糙,若有不妥之处,请大牛们批评指正,
想阅读更多技术文章,请访问听云技术博客,访问听云官方网站感受更多应用性能优化魔力。
本文暂时没有评论,来添加一个吧(●'◡'●)