基于SpringAOP+Caffeine實現(xiàn)本地緩存的實例代碼
基于SpringAOP+Caffeine實現(xiàn)本地緩存的實例,文中有詳細的代碼供大家參考,需要的朋友可以參考下
一、背景
公司想對一些不經(jīng)常變動的數(shù)據(jù)做一些本地緩存,我們使用AOP+Caffeine來實現(xiàn)
二、實現(xiàn)
1、定義注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 本地緩存
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LocalCacheable {
// 過期時間 默認(rèn)10分鐘
long expired() default 600;
// key創(chuàng)建器
String keyGenerator() default "org.springframework.cache.interceptor.KeyGenerator";
}
2、切面
import com.google.gson.internal.LinkedTreeMap;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 本地緩存
*/
@Aspect
@Component
public class LocalCacheAspect {
private static final String separator = ":";
@Around("@annotation(com.framework.localcache.LocalCacheable)")
public Object around(ProceedingJoinPoint point) throws Throwable {
if (AopUtils.isAopProxy(point.getTarget())) {
return point.proceed();
}
Method method = getMethodSignature(point).getMethod();
if (method == null) {
return point.proceed();
}
LocalCacheable annotation = method.getAnnotation(LocalCacheable.class);
if (annotation == null) {
return point.proceed();
}
// 生成key
String key = generateKey(point);
// System.out.println("生成的key:" + key);
long expired = annotation.expired();
Throwable[] throwable = new Throwable[1];
Object proceed = LocalCache.cacheData(key, () -> {
try {
return point.proceed();
} catch (Throwable e) {
throwable[0] = e;
}
return null;
}, expired);
if (throwable[0] != null) {
throw throwable[0];
}
return proceed;
}
/**
* 獲取方法
*/
private MethodSignature getMethodSignature(ProceedingJoinPoint point) {
Signature signature = point.getSignature();
if (signature instanceof MethodSignature) {
return ((MethodSignature) signature);
}
return null;
}
/**
* 獲取key
*/
private String generateKey(ProceedingJoinPoint point) {
// 目標(biāo)類、方法、參數(shù)等
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(point.getTarget());
Method method = getMethodSignature(point).getMethod();
String[] parameterNames = getMethodSignature(point).getParameterNames();
Object[] args = point.getArgs();
// 解析參數(shù),生成key
LinkedTreeMap<String, Object> paramResolveResult = new LinkedTreeMap<>();
if (ArrayUtils.isNotEmpty(args)) {
for (int i = 0; i < args.length; i++) {
resolveParam(args[i], paramResolveResult, parameterNames[i]);
}
}
StringBuilder key = new StringBuilder(targetClass.getName() + separator + method.getName() + separator);
paramResolveResult.forEach((k, v) -> {
if (v != null) {
key.append(k + "," + v + separator);
}
});
// 根據(jù)方法名和參數(shù)生成唯一標(biāo)識
return key.toString();
}
private void resolveParam(Object param, Map<String, Object> paramResolveResult, String prefix) {
if (param == null) {
return;
}
Class<?> type = param.getClass();
if (type == List.class) {
List<Object> param0 = (List) param;
for (int i = 0; i < param0.size(); i++) {
resolveParam(param0.get(i), paramResolveResult, prefix + "[" + i + "]");
}
} else if (type == Map.class) {
Map<Object, Object> param0 = (Map) param;
param0.forEach((k, v) -> {
resolveParam(v, paramResolveResult, prefix + "." + k);
});
} else if (type.isArray()) {
Object[] param0 = (Object[]) param;
for (int i = 0; i < param0.length; i++) {
resolveParam(param0[i], paramResolveResult, prefix + "[" + i + "]");
}
} else if (type == Byte.class
|| type == Short.class
|| type == Integer.class
|| type == Long.class
|| type == Float.class
|| type == Double.class
|| type == Boolean.class
|| type == Character.class
|| type == String.class) {
paramResolveResult.put(prefix, param);
} else {
// 復(fù)雜類型
Map<String, Object> fieldMap = new HashMap<>();
// CGLIB代理
if (Enhancer.isEnhanced(type)) {
getAllFieldsAndValue(param, type.getSuperclass(), fieldMap);
} else {
getAllFieldsAndValue(param, type, fieldMap);
}
fieldMap.forEach((k, v) -> {
if (v == null) {
return;
}
resolveParam(v, paramResolveResult, prefix + "." + k);
});
}
}
/**
* 獲取所有字段和值
*/
private void getAllFieldsAndValue(Object o, Class type, Map<String, Object> fieldMap) {
for (Field field : type.getDeclaredFields()) {
if (field.trySetAccessible()) {
try {
fieldMap.put(field.getName(), field.get(o));
} catch (IllegalAccessException e) {}
}
}
if (type.getSuperclass() != Object.class) {
getAllFieldsAndValue(o, type.getSuperclass(), fieldMap);
}
}
}
3、緩存工具類
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
* 本地緩存
*/
public class LocalCache {
private static final Map<Long, Cache<Object, Object>> cacheMap = new ConcurrentHashMap<>();
/**
* 創(chuàng)建本地緩存
* @param seconds 過期時間:秒
*/
private static Cache<Object, Object> createCache(long seconds) {
return Caffeine.newBuilder()
.expireAfterWrite(seconds, TimeUnit.SECONDS)
.build();
}
/**
* 創(chuàng)建本地緩存
* @param seconds 過期時間:秒
* @param loader 緩存方法
*/
private Cache<Object, Object> createLoadingCache(long seconds, CacheLoader<Object, Object> loader) {
return Caffeine.newBuilder()
.expireAfterWrite(seconds, TimeUnit.SECONDS)
.build(loader);
}
/**
* 獲取一個緩存組
* @param seconds 緩存過期時間
*/
private static Cache<Object, Object> getAndLoad(long seconds) {
if (cacheMap.containsKey(seconds)) {
return cacheMap.get(seconds);
}
Cache<Object, Object> cache = createCache(seconds);
cacheMap.put(seconds, cache);
return cache;
}
/**
* 緩存數(shù)據(jù),過期時間默認(rèn)10分鐘
* @param key key
* @param supplier 數(shù)據(jù)來源的方法
*/
public static Object cacheData(Object key, Supplier<Object> supplier) {
return cacheData(key, supplier, 600);
}
/**
* 緩存數(shù)據(jù)
* @param key key
* @param supplier 數(shù)據(jù)來源的方法
* @param seconds 過期時間:秒
*/
public static Object cacheData(Object key, Supplier<Object> supplier, long seconds) {
Assert.state(seconds > 0, "過期時間必須大于0秒");
Cache<Object, Object> cache = getAndLoad(seconds);
return cache.get(key, k -> supplier.get());
}
}
三、測試
@LocalCacheable
@GetMapping("test1")
public String test1() {
System.out.println("執(zhí)行了");
return "success";
}
@LocalCacheable
@GetMapping("test2")
public String test2(String a) {
System.out.println("執(zhí)行了" + a);
return "success";
}
@LocalCacheable
@GetMapping("test3")
public String test3(String a, int b, String c) {
System.out.println("執(zhí)行了" + a + b + c);
return "success";
}
@LocalCacheable
@GetMapping("test4")
public String test4(UserInfo user) {
System.out.println("執(zhí)行了" + user);
return "success";
}
@LocalCacheable
@GetMapping("test5")
public String test5(UserInfo[] users) {
System.out.println("執(zhí)行了" + users);
return "success";
}
@LocalCacheable
@GetMapping("test6")
public String test6(List<UserInfo> users) {
System.out.println("執(zhí)行了" + users);
return "success";
}
@LocalCacheable
@GetMapping("test7")
public String test7(UserInfo user) {
System.out.println("執(zhí)行了" + user.getMap());
return "success";
}
到此這篇關(guān)于基于SpringAOP+Caffeine實現(xiàn)本地緩存的文章就介紹到這了,更多相關(guān)SpringAOP affeine本地緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入探究Bean生命周期的擴展點Bean Post Processor
在Spring框架中,Bean生命周期的管理是非常重要的一部分,在Bean的創(chuàng)建、初始化和銷毀過程中,Spring提供了一系列的擴展點,其中,Bean Post Processor(后處理器)是一個重要的擴展點,它能夠在Bean的初始化前后做一些額外的處理,本文就和大家一起深入探究2023-07-07
IDEA啟動Springboot報錯:無效的目標(biāo)發(fā)行版:17 的解決辦法
這篇文章主要給大家介紹了IDEA啟動Springboot報錯:無效的目標(biāo)發(fā)行版:17 的解決辦法,文中通過代碼示例和圖文講解的非常詳細,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-02-02
SpringBoot中攔截器和動態(tài)代理的區(qū)別詳解
在?Spring?Boot?中,攔截器和動態(tài)代理都是用來實現(xiàn)功能增強的,所以在很多時候,有人會認(rèn)為攔截器的底層是通過動態(tài)代理實現(xiàn)的,所以本文就來盤點一下他們兩的區(qū)別,以及攔截器的底層實現(xiàn)吧2023-09-09
java 漢諾塔Hanoi遞歸、非遞歸(仿系統(tǒng)遞歸)和非遞歸規(guī)律 實現(xiàn)代碼
漢諾塔(Hanoi) 算法Java實現(xiàn)。通過三個函數(shù),分別對Hanoi進行遞歸、非遞歸和非遞歸規(guī)律實現(xiàn)。2013-05-05

