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