spring aop攔截Controller做引數校驗
阿新 • • 發佈:2019-02-05
在專案中,我們會對入參做校驗,這些引數的校驗邏輯我們會寫很多次.能不能將這些引數的校驗邏輯提出來呢?答案是可以.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 +":您好!";
}
}
八,測試結果