Spring AOP完成一个简单的参数统一校验框架

版权声明:此文章转载自_infocool 原文链接:http: www infocool net kb Java 201610 200829 html 如需转载请联系听云College团队成

版权声明:此文章转载自_infocool

原文链接:http://www.infocool.net/kb/Java/201610/200829.html

如需转载请联系听云College团队成员小尹 邮箱:yinhy#tingyun.com

最近刚刚学习了Spring AOP,也是首次使用Spring AOP进行项目开发,尝试写了一个简单的参数校验框架,也许对像我一样新接触spring AOP的童鞋有所参考,故此分享,若有不合理的地方,请大神帮忙指正,非常感谢!

搭建Spring开发环境这里就不详细说明了,除了spring的一些核心包,再引入spring-aop.jar即可。

我想要做的效果是:在所有的Controller方法中,通过注解开关定义是否校验参数,并且可配置参数实体中各个元素的校验规则。

  1. 定义参数校验的开关

在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,所以写得比较粗糙,若有不妥之处,请大牛们批评指正,

想阅读更多技术文章,请访问听云技术博客,访问听云官方网站感受更多应用性能优化魔力。

未登录用户
全部评论0
到底啦