controllerAOP管理設計-防重複設計

controllerAOP管理設計-防重複設計

接口結構

在這裏插入圖片描述

代碼結構

在這裏插入圖片描述

功能代碼

import java.lang.annotation.*;

/**
 * @Author feizhou
 * @Description 防止同一個方法被頻繁執行
 * 所有請求的參數都必須重寫toString方法,也就是說凡是帶對象的參數都要重寫toString方法
 * @Date 19:35 2019/4/9
 * @Param
 * @return
 **/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SameMethodFrequentlyRun {
	/**
	 * @Author feizhou
	 * @Description 可以作爲定義key的前綴
	 **/
	String params()  default "";
	/**
	 * @Author feizhou
	 * @Description 描述
	 **/
	String description()  default "";
	/**
	 * @Author feizhou
	 * @Description 單位毫秒,30秒
	 **/
	long milliseconds()  default 30000L;
	/**
	 * @Author feizhou
	 * @Description 根據傳入的參數名稱來定義key.可以和params一起拼接使用
	 **/
	String[] paramsName() default {};
}


	------------------------------

	import org.aspectj.lang.ProceedingJoinPoint;
	import org.aspectj.lang.Signature;
	import org.aspectj.lang.reflect.MethodSignature;
	
	import java.lang.reflect.Method;
	import java.util.ArrayList;
	import java.util.List;
	
	import static com.lolaage.common.aop.controllerAopManager.tools.HandlerMethodUtil.getParamaValue;
	
	
	/**
	 * @Author feizhou
	 * @Description 防止同一個方法被頻繁執行AOP(是否需要頻繁執行看參數params是否不一樣)
	 * @Date 15:13 2019/9/26
	 * @Param null
	 * @return
	 **/
	
	public class SameMethodFrequentlyRunHandler implements HandlerMethod {
	
	
	
	    @Override
	    public Result isRunMethod(ProceedingJoinPoint pjp) {
	
	        Result result = new Result();
	        result.setRunControllerMethod(false);
	
	        // 攔截的放參數類型
	        Signature sig = pjp.getSignature();
	        MethodSignature msig = (MethodSignature) sig;
	        Method method = msig.getMethod();
	
	
	        //獲取方法註解
	        SameMethodFrequentlyRun sameMethodFrequentlyRun = method.getAnnotation(SameMethodFrequentlyRun.class);
	        String description = sameMethodFrequentlyRun.description();
	        String params = sameMethodFrequentlyRun.params();
	        String[] paramsName = sameMethodFrequentlyRun.paramsName();
	
	        // 攔截的實體類,就是當前正在執行的controller
	        Object target = pjp.getTarget();
	        //獲取全類名
	        String className = target.getClass().getName();
	        //方法參數值數組
	        Object[] args = pjp.getArgs();
	        //獲取參數名稱數組
	        String[] parameterNames = msig.getParameterNames();
	        //方法名
	        String methodName = method.getName();
	
	        //構建key
	        int hashCode = buildParamsToHashCode(args, className,methodName, params, paramsName, parameterNames);
	
	        String key = String.valueOf(hashCode);
	
	        long milliseconds = sameMethodFrequentlyRun.milliseconds();
	        Boolean isGetLock = RedisLockTemplate.distributedLock_v2(key, description, milliseconds, false);
	        if (!isGetLock) {
	            result.setErrCode(5004);
	            result.setErrMsg("請不要重複操作");
	            return result;
	        }
	
	
	        result.setRunControllerMethod(true);
	        return result;
	    }
	
	    /**
	     * @return void
	     * @Author feizhou
	     * @Description 構建params, 並轉化爲hashcode
	     * @Date 10:27 2019/8/27
	     * @Param
	     **/
	    private int buildParamsToHashCode(Object[] args, String className,String methodName, String params, String[] paramsName, String[] parameterNames) {
	
	        StringBuilder sb = new StringBuilder();
	        sb.append(className);
	        sb.append(methodName);
	
	        //只要params或者paramsName存在
	        if (!"".equalsIgnoreCase(params)||paramsName.length > 0) {
	            sb.append(params);
	            List<String> allParamValue =getAllParamValue(paramsName, args, parameterNames);
	            //如果paramsName有對應的值,就返回字符串後再返回字符串對應的hashcode
	            if (allParamValue.size() > 0) {
	                sb.append(allParamValue.toString());
	            }
	            return  sb.toString().hashCode();
	        }
	
	        //params=null 且paramsName==null或者paramsName沒有對應的值
	        //返回參數對應的字符串的hashcode
	        for (Object o : args) {
	            if (o == null) {
	                continue;
	            }
	            sb.append(":");
	            sb.append(o.toString());
	        }
	        return sb.toString().hashCode();
	    }
	    /**
	     * @return java.util.List<java.lang.Object>
	     * @Author feizhou
	     * @Description 獲取參數的值
	     * @Date 11:04 2019/8/27
	     * @Param paramsName
	     * @Param args
	     * @Param parameterNames
	     **/
	    private List<String> getAllParamValue(String[] paramsName, Object[] args, String[] parameterNames) {
	
	        List<String> newArgs = new ArrayList<>();
	        for (String paramName : paramsName) {
	
	            String paramaValue = getParamaValue(paramName, parameterNames, args);
	            if(paramaValue!=null){
	                newArgs.add(paramaValue);
	            }
	        }
	        return newArgs;
	    }
	}

公共功能代碼

	public interface HandlerMethod {
	
	
	
	
	    /**
	     * @Author feizhou
	     * @Description 處理controller方法是否執行,
	     * @Date 10:21 2019/9/27
	     * @Param pjp
	     * @return com.lolaage.common.aop.controllerAopManager.Result
	     **/
	    Result isRunMethod(ProceedingJoinPoint pjp);
	}

	---------------------------------
	public interface BeforeHandlerMethod {


    /**
     * @Author feizhou
     * @Description 處理controller方法執行前需要發生的一些動作,比如說緩存
     * @Date 10:20 2019/9/27
     * @Param pjp
     * @return com.lolaage.common.aop.controllerAopManager.Result
     **/
    Result toBeforeHandlerMethod(ProceedingJoinPoint pjp);


	}

	---------------------------------
	 
	


	
	 

	/**
	 * @Author feizhou
	 * @Description 提供一些共有的方法
	 * @Date 15:13 2019/9/26
	 * @Param null
	 * @return
	 **/

	public class HandlerMethodUtil {


		//簽名key
		private static final String SECRET_KEY = "xxxx";

		//簽名的參數名稱
		private static String psignName = "xxxx";
		//時間戳參數名稱
		private static String timeName = "xxx";

		/**
		 * @return java.lang.String
		 * @Author feizhou
		 * @Description 獲取參數下標對應的值
		 * @Date 10:48 2019/8/27
		 * @Param paramName
		 * @Param args
		 **/
		public static String getParamaValue(int paramaIndex, Object[] args) {
			Object obj = args[paramaIndex];
			if (obj == null) {
				return null;
			}
			//判斷數組情況
			Class clazz = obj.getClass();
			if (clazz.isArray()) { //這個對象是數組,轉list
				int length = Array.getLength(obj);  //獲取數組長度
				StringBuffer sb=new StringBuffer();
				//返回已逗號分隔的字符串
				for (int i = 0; i < length; i++) {
					Object o = Array.get(obj, i);//取出數組中每個值
					if(i==length-1){
						sb.append(o.toString());
					}else {
						sb.append(o.toString()).append(",");
					}
				}
				return sb.toString();
			}
			return obj.toString();
		}


		/**
		 * @return java.lang.String
		 * @Author feizhou
		 * @Description 獲取參數名稱對應的數組下標
		 * @Date 10:48 2019/8/27
		 * @Param paramName
		 * @Param args
		 **/
		public static int getParamaIndex(String paramName, String[] parameterNames) {
			return ArrayUtils.indexOf(parameterNames, paramName);
		}

		/**
		 * @return java.lang.Object
		 * @Author feizhou
		 * @Description 獲取參數名稱對應的值
		 * @Date 9:58 2019/9/27
		 * @Param paramName
		 * @Param parameterNames
		 * @Param args
		 **/
		public static String getParamaValue(String paramName, String[] parameterNames, Object[] args) {
			int paramaIndex = getParamaIndex(paramName, parameterNames);
			if(paramaIndex==-1){
				return null;
			}
			String paramaValue = getParamaValue(paramaIndex, args);
			return paramaValue;
		}

		/**
		 * @return boolean
		 * @Author feizhou
		 * @Description 檢查簽名
		 * @Date 14:04 2019/9/27
		 * @Param sign
		 * @Param args
		 **/
		public static boolean checkSign(String psign,String[] parameterNames, Object[] args) {

			if (args == null || args.length == 0) {
				return false;
			}

			//生成簽名
			String md5Sign = generatePsign(  parameterNames,   args);

			if (StringUtils.isEmpty(md5Sign)) {
				return false;
			}

			// 校驗md5
			return psign.equals(md5Sign);

		}

		/**
		 * @return void
		 * @Author feizhou
		 * @Description 生成簽名
		 * @Date 14:06 2019/9/27
		 * @Param
		 **/
		private static String generatePsign(String[] parameterNames, Object[] args) {

			//參數數組排序
			Map map = sortParam(parameterNames, args);

			//去掉簽名
			map.remove(psignName);

			//生成url
			String url = mapToUrl(map);
			url=url+"&key="+SECRET_KEY;

			String md5Sign=null;
			try {
				md5Sign=MD5Util.string2MD5(url);
			} catch (UnsupportedEncodingException e) {
				return null;
			}

			map=null;
			url=null;
			return md5Sign;
		}

		/**
		 * @return java.lang.String
		 * @Author feizhou
		 * @Description 對請求的參數按照參數名Unicode碼從小到大排序(字典序),並返回Map
		 * @Date 14:15 2019/9/27
		 * @Param paraMap
		 * @Param urlEncode
		 * @Param keyToLower
		 **/
		public static Map sortParam(String[] parameterNames, Object[] args) {
			//默認升序
			Map<String, String> treeMap = new TreeMap<String, String>();

			for (String paramName : parameterNames) {
				String paramaValue = getParamaValue(paramName, parameterNames, args);
				if(paramaValue!=null){
					treeMap.put(paramName,paramaValue);
				}
			}
			return treeMap;
		}
		/**
		 * @Author feizhou
		 * @Description Map轉URL
		 * @Date 14:46 2019/9/27
		 * @Param map
		 * @return java.util.Map
		 **/
		public static String mapToUrl( Map<String, String>  map) {
			StringBuffer sb=new StringBuffer();
			for (String key : map.keySet()) {
				String value = map.get(key);
				sb.append("&").append(key).append("=").append(value);
			}
			return sb.substring(1,sb.length());
		}

		/**
		 * @return java.lang.String
		 * @Author feizhou
		 * @Description 獲取加簽參數
		 * @Date 9:47 2019/9/27
		 * @Param
		 **/
		public static  String getPsign(String[] parameterNames, Object[] args) {
			String paramaValue = HandlerMethodUtil.getParamaValue(psignName, parameterNames, args);
			if (paramaValue != null) {
				return paramaValue;
			}
			return null;
		}

		/**
		 * @return java.lang.String
		 * @Author feizhou
		 * @Description 獲取時間戳參數
		 * @Date 9:47 2019/9/27
		 * @Param
		 **/
		public static  Long getTime(String[] parameterNames, Object[] args) {
			String paramaValue = HandlerMethodUtil.getParamaValue(timeName, parameterNames, args);
			if (paramaValue != null) {
				return Long.valueOf(paramaValue);
			}
			return null;
		}


	}




	--------------------
	 
	import org.aspectj.lang.ProceedingJoinPoint;

	public class ControllerAopManager {

		private HandlerMethod handlerMethod;
		private ProceedingJoinPoint pjp;


		public  Result toHandlerMethod(){

			return  handlerMethod.isRunMethod(pjp);

		}

		public void setHandlerMethod(HandlerMethod handlerMethod) {
			this.handlerMethod = handlerMethod;
		}

		public void setPjp(ProceedingJoinPoint pjp) {
			this.pjp = pjp;
		}
	}
	-------------------------
	 

	/**
	 * @Author feizhou
	 * @Description 使用範圍
	 * 1. 針對所有controller上的註解
	 * <p>
	 * 功能範圍
	 * 1.處理controller方法是否執行
	 * 2.處理controller方法執行前需要發生的一些動作,比如說緩存,當前還沒有使用
	 * <p>
	 * 注意:
	 * 1.對外提供一個總接口
	 * 2.所有子類一一對應相應的註解
	 * 3 後期擴展按註解擴展
	 * @Date 16:50 2019/9/27
	 * @Param null
	 * @return
	 **/

	@Aspect
	@Component
	public class ControllerMethodHandlerAop {
		private static Logger logger = Logger.getLogger(ControllerMethodHandlerAop.class);


		// 配置接入點,即爲所要記錄的action操作目錄
		@Pointcut("execution(* com.lolaage.helper.web.controller..*.*(..))")
		private void controllerAspect() {

		}

		@Around("controllerAspect()")
		public Object around(ProceedingJoinPoint pjp) {
			Object returnObj = null;


			// 攔截的放參數類型
			Signature sig = pjp.getSignature();
			MethodSignature msig = (MethodSignature) sig;
			Method method = msig.getMethod();

			ControllerAopManager controllerAopManager = new ControllerAopManager();
			controllerAopManager.setPjp(pjp);

			//獲取所有註解
			Annotation[] annotations = method.getAnnotations();

			Boolean isRunControllerMethod = true;
			Result result = null;

			for (Annotation annotation : annotations) {
				HandlerMethod handlerMethod = HandlerMethodFactory.createHandlerMethod(annotation);
				controllerAopManager.setHandlerMethod(handlerMethod);

				result = controllerAopManager.toHandlerMethod();
				if (!result.isRunControllerMethod()) {
					isRunControllerMethod = false;
					break;
				}
			}

			//不執行controller方法
			if (!isRunControllerMethod) {
				//提示不要重複操作
				JsonModel jsonModel = new JsonModel();
				jsonModel = jsonModel.resultToJsonModel(result);
				return jsonModel;
			}

			//執行controller方法
			try {
				returnObj = pjp.proceed();
			} catch (Throwable e) {
				logger.error("請求執行異常:" + e.getMessage());
				e.printStackTrace();
			}
			return returnObj;
		}
	}
	--------------------------
	 

	public class HandlerMethodFactory {


		public static HandlerMethod createHandlerMethod(Annotation annotation) {

			if(annotation instanceof Encryption){
				return  new EncryptionHandler();
			}
			if(annotation instanceof SameMethodFrequentlyRun){
				return  new SameMethodFrequentlyRunHandler();
			}


			//返回默認處理類型
			return new DefaultHandler();
		}
	}
	-------------------------
	 

	@Data
	public class Result {

		//是否運行controller的方法
		private boolean  isRunControllerMethod;
		//需要返回的數據,比如走緩存的時候,直接返回給客戶端的數據
		private Object  returnData;
		//錯誤碼
		private int  errCode;
		//錯誤提示
		private String  errMsg;


	}


	--------------------------
	 
	 

	/**
	 * @Author feizhou
	 * @Description 對需要加密的controller方法的處理
	 * @Date 15:13 2019/9/26
	 * @Param null
	 * @return 
	 **/

	public class DefaultHandler implements HandlerMethod {




		@Override
		public Result isRunMethod(ProceedingJoinPoint pjp) {
			Result result=new Result();
			result.setRunControllerMethod(true);
			return result;
		}
	}

這編文章是對利用分佈式共享鎖實現防止方法重複調用的升級

https://blog.csdn.net/zhou920786312/article/details/89187080

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章