注解实现校验前端参数
环境
jdk1.6 + SSM
原理
切面拦截controller方法,然后捕获带@CheckParam注解方法参数实例,最后反射实例校验。突破口在于第二点,这里参考了SpringMVC是如何解析@RequestParam,底层实现有兴趣可以跟进去瞅瞅。Spring源码例子如下:
类名org.springframework.web.bind.annotation.support.HandlerMethodInvoker#resolveHandlerArguments
MethodParameter methodParam = new MethodParameter(handlerMethod, i);
methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
例子
校验model的fillInstructions字段
controller
@RequestMapping(value = "update" )
@ResponseBody
public ResultBean update(@CheckParam ProgramMaterialModel materialModel, HttpServletRequest request){
return ResultBean.ok();
}
model
public class ProgramMaterialModel implements Serializable{
private static final long serialVersionUID = 4081985131881304723L;
@CheckParam(notNull = true)
private String fillInstructions;
}
annotation
@Target(value={ElementType.PARAMETER,ElementType.FIELD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckParam {
boolean notNull() default false;
}
aspect
package com.minstone.apprprograminterface.common.annotation;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author lianxp
* @Title: ${file_name}
* @Package ${package_name}
* @Description: ${todo}
* @date 2018/3/29 9:21
*/
@Component
@Aspect
public class CheckParamAspect {
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void methodPointCut() {
}
/**
* 环绕切入方法
* @author lianxp
* @date 2018/3/29 9:33
* @param
**/
@Around("methodPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature msig = (MethodSignature) point.getSignature();
Method method = msig.getMethod();
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
Object[] args = point.getArgs();
for (int i = 0; i < args.length; i++) {
Object obj = args[i];
MethodParameter mp = new MethodParameter(method,i);
mp.initParameterNameDiscovery(u);
GenericTypeResolver.resolveParameterType(mp, method.getClass());//Spring处理参数
//String paramName = mp.getParameterName();//参数名
CheckParam anno = mp.getParameterAnnotation(CheckParam.class);//参数注解
if(anno != null){
check(obj);
}
}
return point.proceed();
}
/**
* 校验成员变量
* @author lianxp
* @date 2018/3/29 9:56
* @param
**/
private void check(Object obj) throws IllegalAccessException {
Class clazz = obj.getClass();
for(Field field : clazz.getDeclaredFields()){
CheckParam cp = field.getAnnotation(CheckParam.class);
if(cp != null){
check(obj,clazz, field,cp);
}
}
}
/**
* 取出注解,校验变量
* @author lianxp
* @date 2018/3/29 10:01
* @param
**/
private void check(Object obj, Class clazz, Field field, CheckParam cp) throws IllegalAccessException {
if(cp.notNull()){
field.setAccessible(true);
Object f = field.get(obj);
if(StringUtils.isEmpty(f)){
throw new IllegalArgumentException("类" + clazz.getName() + "成员" + field.getName() + "检测到非法参数");
}
}
}
}
测试
浏览器直接访问调controller