spring aop拦截Controller做参数校验

       在项目中,我们会对入参做校验,这些参数的校验逻辑我们会写很多次.能不能将这些参数的校验逻辑提出来呢?答案是可以.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注解.

     

<bean id="validateAdvitor" class="com.test.rest.api.web.validators.ParamValidateAdvisor"/>
	<aop:config proxy-target-class="true">
		<aop:aspect ref="validateAdvitor">
			<aop:pointcut expression="@annotation(com..rest.api.web.validators.annos.Validate)" id="validateCut"/>
			<aop:around method="validate" pointcut-ref="validateCut"/>
		</aop:aspect>
	</aop:config>

二,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<Param> 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;
}

四,AnnotationHelper

   

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<Param> getParms(Method method,Object[] objs){
		Annotation[][] annos = method.getParameterAnnotations();
		Class<?>[] paramTypes = method.getParameterTypes();
		List<Param> params = new ArrayList<Param>();
		for(int i=0;i<annos.length;i++){
			for(int j=0;j<annos[i].length;j++){
				//如果出现指定的注解类型
				if(annos[i][j].annotationType() == Validate.class){
					Param param = new Param(paramTypes[i].getSimpleName(),
							        paramTypes[i].getName(),//名称
			                                        paramTypes[i],//参数类型
	                                                        objs[i],//参数值
			                                        annos[i][j]);//筛选出的注解
					params.add(param);
				}
			}
		}
		return params;
	}
}

五,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 +":您好!";
	}
}

八,测试结果

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页