本篇文章給大家分享的是有關如何通過spring-aop的方式自定義注解來實現spring-cache的功能,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
設計的過程中參考一下幾個原則:
代碼無侵入
按需加載
配置多樣化
首先自定義注解:只能作用于方法上,運行期有效,key支持spel表達式,其中FunctionEnum是根據業務自定義的一個枚舉
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GlobalCache {
/**
* SPEL表達式,緩存key
* @return
*/
String key() default "";
String value() default "";
/**
* 當前具體的操作
* eg:信息新增,刪除等
*
* @return
*/
FunctionEnum functionEnum() default FunctionEnum.DEFAULT;
}通過定義aop的切面來解析當前這個注解,核心實現如下
@Around("@annotation(com.xxx.xxxx.core.foreign.annotation.GlobalCache)")
public Object globalCacheAround(ProceedingJoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
ServiceData result;
GlobalCache globalCache = method.getAnnotation(GlobalCache.class);
String cacheKey = globalCache.functionEnum().name() + ":" + ForeignUtils.combineParam(methodSignature.getParameterNames(),joinPoint.getArgs(),globalCache.key(),String.class,"test-spel-123");
if ("GET".equals(request.getMethod())) {
result = defaultRedisTemplate.opsForValue().get(cacheKey);
if (result != null) {
log.info("命中緩存,緩存的key:{}", cacheKey);
return result;
}
}
Map<String,String> httpParams = ForeignUtils.builtParams(methodSignature.getParameterNames(), request);
result = CompletableFuture.supplyAsync(() -> {
//重定向相關操作
return redirectUrl(cacheKey,request,httpParams,ForeignUtils.getRequestBody(method,joinPoint));
}, LocalThreadPool.FOREIGN_EXEC_CACHE).whenComplete((serviceData, ex) -> {
if (ex == null) {
//本地緩存的業務處理
notice.onSuccess(globalCache, httpParams, serviceData);
} else {
notice.onError(ex, serviceData);
throw new ForeignInvokeException("current request was deny...");
}
}).join();
return result;
}在構造的過程中遇到很多小的零碎的問題,其中涉及到如何解析PUT請求中的body等等,下面附上ForeignUtils工具類的代碼
/**
* 組裝緩存key
* 格式: 方法名:參數值1:參數值2
* 自動解析格式
*
* @param functionName 當前操作對應的名稱
* @param args 所有變量對應的參數
* @return
*/
public static String combineParameter(String functionName, Method method, Object[] args) {
Class[] classArr = method.getParameterTypes();
for (int index = 0; index < classArr.length; index++) {
if (classArr[index] == HttpServletRequest.class) {
//是否存在其他待忽略的?
continue;
}
functionName += ":" + args[index];
}
return functionName;
}
/**
* 請求參數的參數名稱和參數對應的值 key參數:value參數變量
* title:test-123,subtitle:test-subtitle-123
*
* @param params
* @param request
* @return
*/
public static Map<String, String> builtParams(String[] params, HttpServletRequest request) {
Map<String, String> keyMap = Maps.newHashMap();
for (int i = 0; i < params.length; i++) {
String value = request.getParameter(params[i]);
if (StringUtils.isNotBlank(value)) {
keyMap.put(params[i], value);
}
}
return keyMap;
}
/**
* 拼裝http后請求參數,占位符的方式
* title={title}&subtitle={subtitle}
* 可以使用queryString()替代
*
* @param httpParams
* @return
*/
public static String builtHttpParams(Map<String, String> httpParams) {
String result = "";
for (Map.Entry entry : httpParams.entrySet()) {
result += entry.getKey() + "= {" + entry.getKey() + "}&";
}
if (result.endsWith("&")) {
return result.substring(0, result.length() - 1);
}
return result;
}
/**
* 獲取當前請求中的body值
*
* @param method
* @param joinPoint
* @return
*/
public static Object getRequestBody(Method method, ProceedingJoinPoint joinPoint) {
Annotation[][] currentAnnotionArr = method.getParameterAnnotations();
Object body = null;
for (int index = 0; index < currentAnnotionArr.length; index++) {
try {
if (currentAnnotionArr[index][0].annotationType() == RequestBody.class) {
body = joinPoint.getArgs()[index];
break;
}
} catch (Exception e) {
}
}
return body;
}
/**
* 獲取請求中path的參數對
* @param method
* @param joinPoint
* @return
*/
public static String getPathArgs(Method method, ProceedingJoinPoint joinPoint) {
Annotation[][] currentAnnotionArr = method.getParameterAnnotations();
String pathValue = null;
for (int index = 0; index < currentAnnotionArr.length; index++) {
try {
if (currentAnnotionArr[index][0].annotationType() == PathVariable.class) {
pathValue = String.valueOf(joinPoint.getArgs()[index]);
break;
}
} catch (Exception e) {
}
}
return pathValue;
}
private static ExpressionParser parser = new SpelExpressionParser();
/**
* 解析SPEL表達式 緩存對應key信息
*
* @param params
* @param args
* @param spel
* @param clazz
* @param defaultResult
* @return
*/
public static <T> T combineParam(String[] params, Object[] args, String spel, Class<T> clazz, T defaultResult) {
EvaluationContext context = new StandardEvaluationContext();
for (int index = 0; index < params.length; index++) {
context.setVariable(params[index], args[index]);
}
try {
Expression expression = parser.parseExpression(spel);
return expression.getValue(context, clazz);
} catch (Exception e) {
return defaultResult;
}
}上面的工具類主要涉及到參數的組裝,解析等;
以上就是如何通過spring-aop的方式自定義注解來實現spring-cache的功能,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。