在项目中,我们会对入参做校验,这些参数的校验逻辑我们会写很多次.能不能将这些参数的校验逻辑提出来呢?答案是可以.Spring 有自己的validate工具方法,我个人感觉不是太好远,想自己定制更加契合自己项目的校验机制.经过哆哆嗦嗦的研究,有点结果,现在贴出来,大家一起看看!
我曾经写过一个工具类,就是会在Service层的方法上加上自定义的注解,利用Spring aop拦截标注注解的方法,如果有缓存就返回缓存,如果没有,则会从数据库取出或者重新计算,
以达到提高吞吐率.实践证明还是挺好用的.
经过以上描述,我们今天的关键词就有了,就是:Spring,Spring MVC,Spring AOP,java反射,java注解
一,Controller的拦截
一般的,我们项目中bean的管理和mvc的配置是分开的,之前轻松的给service层加上了aop切面,但是用之前的方法发现不好使.为什么?因为Controller是归mvc层管理的,按照原来的方法,根本拦截不到controller的方法.
1,将切面配置在spring 的bean.xml中,需要协调Controller注解(以后我再补上原因)
2,将切面配置在spring 的mvc.xml中,这样不用协调Controller注解.
二,Advisor
package com.test.rest.api.web.validators;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.test.common.enums.ErrorCode;
import com.test.framework.common.util.Check;
import com.est.rest.api.util.ResponseUtil;
import com.test.rest.api.web.validators.annos.Must;
import com.test.rest.api.web.validators.annos.Validate;
/**
* 参数验证
* @author michael.bai
* @date 2016年12月20日
*/
public class ParamValidateAdvisor{
private Logger logger = LoggerFactory.getLogger(ParamValidateAdvisor.class);
/**
* 校验入参
* @param point
* @throws Throwable
*/
public Object validate(ProceedingJoinPoint point) throws Throwable{
logger.info("开始拦截接口入参!");
Object[] objs = point.getArgs();
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
//检测
Annotation[][] annos = method.getParameterAnnotations();
boolean flag = validateParameterAnnotation(annos);
//虽然方法加了注解,但是参数么有注解,pass
if(!flag){
return point.proceed(objs);
}
//得到标注@Validate注解的参数
List params = AnnotationHelper.getParms(method,objs);
if(!Check.NuNList(params)){
for(Param param : params){
String validRes = validateDetail(param);
if(!Check.NuNString(validRes)){
logger.info("客户端上报参数错误详细信息:{}",validRes);
return ResponseUtil.message(ErrorCode.CLIENT_ERROR.getCode(), "客户端上报参数错误");
}
}
}
//没有错误就沿着毛主席的路线继续前进!
return point.proceed(objs);
}
/**
* 具体的校验逻辑,返回警告信息
* @param obj
* @return
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
private String validateDetail(Param param) throws IllegalArgumentException, IllegalAccessException{
Validate val = (Validate)param.getAnno();
boolean isVali = val.isValidate();
StringBuilder sb = new StringBuilder();
if(isVali){
if(val.isForm() == true){
String res = validateForm(param);
append(sb,res);
}else{
String res = validateCommon(param);
append(sb,res);
}
}
return sb.toString();
}
private void append(StringBuilder sb,String res){
if(!Check.NuNString(res)){
sb.append("_");
sb.append(res);
}
}
/**
* 验证是否有某个注解
* @param annos
* @param validate
* @return
*/
private boolean validateParameterAnnotation(Annotation[][] annos){
boolean flag = false;
for(Annotation[] at : annos){
for(Annotation a : at){
if(a.annotationType() == Validate.class){
flag = true;
}
}
}
return flag;
}
private String validateCommon(Param param){
String res = null;
if(Check.NuNObject(param.getValue())){
res = param.getName()+"的参数值为空!";
}
return res;
}
private String validateForm(Param param) throws IllegalArgumentException, IllegalAccessException{
Class> clazz = param.getValue().getClass();
Field[] fields = clazz.getDeclaredFields();
StringBuilder sb = new StringBuilder();
for(Field f : fields){
Annotation[] annos = f.getAnnotations();
if(!Check.NuNArray(annos)){
String paramName = param.getName()+"."+f.getName();
Must must = (Must)annos[0];
if(must.isMust()){
f.setAccessible(true);
Object obj = f.get(param.getValue());
Class> type = f.getType();
if(type.isArray()){
Object[] arr = (Object[])obj;
if(Check.NuNArray(arr)){
append(sb, paramName+"不能为空!");
}
}else if(type.isPrimitive()){
if(type == int.class){
int intObj = (int)obj;
if(intObj <= 0){
append(sb, paramName+"不能小于等于0!");
}
}else if(type == long.class){
long longObj = (long)obj;
if(longObj <= 0){
append(sb, paramName+"不能小于等于0!");
}
}
}else if(type == String.class){
if(Check.NuNString((String)obj)){
append(sb, paramName+"不能为空!");
}
}
}
}
}
return sb.toString();
}
}
1,Validate
package com.test.rest.api.web.validators.annos;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 校验注解
* @author michael.bai
* @date 2016年12月20日
*/
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Validate {
public boolean isValidate() default true;
public boolean isForm() default false;
}
2,Must
package com.test.rest.api.web.validators.annos;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 必须的
* @author michael.bai
* @date 2016年12月20日
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Must {
public boolean isMust() default true;
}
package com.test.rest.api.web.validators;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import com.test.rest.api.web.validators.annos.Validate;
/**
* 注解帮助类
* @author michael.bai
* @date 2016年12月20日
*/
public class AnnotationHelper {
/**
* 获取MethodSignature
* @param point
* @return
*/
public static Signature getMethod(ProceedingJoinPoint point){
MethodSignature sign = (MethodSignature) point.getSignature();
return sign;
}
/**
* 获取参数列表
* @param point
* @return
*/
public static Object[] getArgs(ProceedingJoinPoint point){
return point.getArgs();
}
/**
* 获取参数的描述
* @param method
* @param objs
* @return
*/
public static List getParms(Method method,Object[] objs){
Annotation[][] annos = method.getParameterAnnotations();
Class>[] paramTypes = method.getParameterTypes();
List params = new ArrayList();
for(int i=0;i
五,Param
package com.test.rest.api.web.validators;
import java.lang.annotation.Annotation;
/**
* 方法参数类
* @author michael.bai
* @date 2016年12月28日
*/
public class Param {
private String simpleName;//简单名字
private String name;//名字
private Class> type;//类型
private Object value;//值
private Annotation anno;//注解
public Param() {
super();
}
public Param(String simpleName,String name, Class> type, Object value, Annotation anno) {
super();
this.simpleName = simpleName;
this.name = name;
this.type = type;
this.value = value;
this.anno = anno;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Class> getType() {
return type;
}
public void setType(Class> type) {
this.type = type;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public Annotation getAnno() {
return anno;
}
public void setAnno(Annotation anno) {
this.anno = anno;
}
public String getSimpleName() {
return simpleName;
}
public void setSimpleName(String simpleName) {
this.simpleName = simpleName;
}
@Override
public String toString() {
return "Param [simpleName=" + simpleName + ", name=" + name + ", type=" + type + ", value=" + value + ", anno="
+ anno + "]";
}
}
六,TestForm
package com.test.rest.api.web.validators;
import java.io.Serializable;
import com.test.rest.api.web.validators.annos.Must;
public class TestForm implements Serializable{
private static final long serialVersionUID = 1L;
@Must
private int age;
@Must
private String[] pics;
@Must
private long goodsIds;
public String[] getPics() {
return pics;
}
public void setPics(String[] pics) {
this.pics = pics;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public long getGoodsIds() {
return goodsIds;
}
public void setGoodsIds(long goodsIds) {
this.goodsIds = goodsIds;
}
}
七,TestController
package com.test.rest.api.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.test.rest.api.web.validators.TestForm;
import com.test.rest.api.web.validators.annos.Validate;
@Controller
@RequestMapping("/best")
public class TestController {
@RequestMapping("/test")
@ResponseBody
@Validate
public Object test(Integer age,String name,@Validate(isForm=true) TestForm form){
System.out.println("BBBBBBBBBBBBBBBBBBBBBBBBBB");
return age +":您好!";
}
}