您的位置:首页 > 编程语言 > Java开发

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

2016-10-14 18:27 627 查看
      最近刚刚学习了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.leng
4000
th; 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,所以写得比较粗糙,若有不妥之处,请大牛们批评指正,谢谢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring aop java 参数校验