1. 程式人生 > >spring aop攔截Controller做引數校驗

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

八,測試結果