导入依赖
xml
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
使用
项目中使用到了缓存,定义一个切面,拦截类或方法上存在@SysDataCache注解请求,对于这些方法的返回值进行缓存。项目中主要还是使用在缓存常量,一些不易改变的值
定义注解
java
@Documented
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SysDataCache {
}
定义切面和初始化缓存容器并使用缓存
java
@Aspect //定义一个切面
@Configuration
public class SysDataCacheAspect {
private static Logger logger = LogManager.getLogger(SysDataCacheAspect.class);
private static final Map<String,Boolean> cacheFileNames = new ConcurrentHashMap<String, Boolean>();
private static LoadingCache<String,Object> cache = null;
static {
// CacheLoader 初始化
CacheLoader<String, Object> cacheLoader = new CacheLoader<String, Object>() {
@Override
// load方法的作用是在通过get方法从LoadingCache获取不到值时去加载该值并放入缓存。
public Object load(String key) throws Exception {
return null;
}
};
cache = CacheBuilder.newBuilder()
// 设置容量大小
.maximumSize(80000)
//默认一天后过期
.expireAfterWrite(10, TimeUnit.DAYS)
.removalListener(new RemovalListener<String, Object>() {
@Override
public void onRemoval(RemovalNotification<String, Object> notification) {
if(notification.getValue()!=null && notification.getValue() instanceof CacheFile) {
CacheFile cacheFile = (CacheFile)notification.getValue();
removeCacheFile(cacheFile.fileName);
}
}
})
// 加载器配置
.build(cacheLoader);
}
private String normalizedArgsStr(Object[] args){
if(args==null || args.length==0) {
return "";
}
Object[] normalizedArgs = new Object[args.length];
if(args!=null) {
for(int i=0;i<args.length;i++) {
Object arg = args[i];
if(arg instanceof AccessTokenUser) {
AccessTokenUser user = (AccessTokenUser)arg;
normalizedArgs[i]= user.getUserId();
}else {
normalizedArgs[i]=arg;
}
}
}
return JsonConverter.toJsonStr(normalizedArgs);
}
@Around("execution(* (@com.xysd.bizbase.annotation.SysDataCache *).*(..)) || execution(@com.xysd.bizbase.annotation.SysDataCache * *(..))")
public Object process(ProceedingJoinPoint point) throws Throwable {
String className = point.getSignature().getDeclaringTypeName();
String methodName = point.getSignature().getName();
Object[] args = point.getArgs();
String key = className+"_$_"+methodName+"_$_"+(normalizedArgsStr(args));
Object cached = cache.getIfPresent(key);
if(methodName.endsWith("_dontCache")){
return point.proceed(args);
}
if(cached!=null) {
if(cached instanceof CacheFile) {
CacheFile cachedFile = (CacheFile)cached;
Object cachedData = readCachedData(cachedFile);
if(cachedData==null) {
//读取缓存失败
return point.proceed(args);
}else {
return cachedData;
}
}else {
return cached;
}
}else {
cached = point.proceed(args);
if(cached instanceof ApiResultDTO){
if(((ApiResultDTO<?>) cached).getData() == null) return cached;
}
if(cached!=null) {
try {
CacheFile cachedFile = cacheToDiskIfNecessary(cached);
if(cachedFile!=null) {
cache.put(key, cachedFile);
}else {
cache.put(key, cached);
}
}catch(Exception e) {
logger.error("缓存失败,失败信息{}",e.getMessage());
e.printStackTrace();
}
}
return cached;
}
}
private Object readCachedData(CacheFile cachedFile) {
String fileName = cachedFile.getFileName();
String absolutePath = getAbsoluteCacheFilePath(fileName);
File f = new File(absolutePath);
InputStream in = null;
ObjectInputStream oin = null;
try {
in = new FileInputStream(f);
oin = new ObjectInputStream(in);
Object cachedData = oin.readObject();
return cachedData;
}catch(Exception e) {
logger.error("读取缓存序列化文件失败,失败信息:{}",e.getMessage());
e.printStackTrace();
return null;
}
finally {
Utils.clean(in,oin);
}
}
/**
* 当value序列化后占用字节大于50K时写入磁盘进行缓存
* @param value
* @return
*/
private CacheFile cacheToDiskIfNecessary(Object value) {
int cachThreadshold = 50*1024;
ByteArrayOutputStream bos = null ;
ObjectOutputStream oos = null;
try {
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(value);
oos.flush();
byte[] byteArray = bos.toByteArray();
if(byteArray!=null && byteArray.length>cachThreadshold) {
return buildCacheFile(byteArray);
}else {
return null;
}
}catch(Exception e) {
throw new RuntimeException(e);
}finally {
Utils.clean(bos,oos);
}
}
private CacheFile buildCacheFile(byte[] byteArray) {
String fileName = "syscachefile_"+Utils.getUUID("");
String absolutePath = getAbsoluteCacheFilePath(fileName);
File f = new File(absolutePath);
OutputStream out = null;
try {
if(!f.getParentFile().exists()) {
f.getParentFile().mkdirs();
}
out = new FileOutputStream(f);
out.write(byteArray);
out.flush();
cacheFileNames.put(fileName, true);
return new CacheFile(fileName);
}catch(Exception e) {
throw new RuntimeException(e);
}finally {
Utils.clean(out);
}
}
private static String getAbsoluteCacheFilePath(String fileName) {
String sysCacheBaseDir = Utils.getTmpDirRoot()+"/sysDataCache";
return sysCacheBaseDir+"/"+fileName;
}
public static void removeCacheFile(String fileName) {
if(StringUtils.isNoneBlank(fileName)) {
cacheFileNames.remove(fileName);
String absolutePath = getAbsoluteCacheFilePath(fileName);
File f = new File(absolutePath);
try {
if(f.exists() && f.isFile()) {
f.delete();
}
}catch(Exception e) {
//删除失败不做任何处理
e.printStackTrace();
}
}
}
/**
* 清空缓存
*/
public static final void clearCache() {
for(String fileName:cacheFileNames.keySet()) {
removeCacheFile(fileName);
}
cacheFileNames.clear();
cache.invalidateAll();
}
public static class CacheFile implements Serializable {
private static final long serialVersionUID = -6926387004863371705L;
private String fileName;
public CacheFile(String fileName) {
super();
this.fileName = fileName;
}
public String getFileName() {
return fileName;
}
}
}
项目中缓存使用
java
@Service
@Transactional
@SuppressWarnings("unchecked")
public class SysDatasServiceImpl implements _ISysDatasService {
private final static ConstantItem dataKey_dict_politicalStatus = new ConstantItem("politicalStatus", "政治身份");
@Override
@SysDataCache
@Transactional(readOnly = true)
public Map<String, String> getSysDataKeysInfo(AccessTokenUser user) {
result.put((String) dataKey_dict_politicalStatus.getId(), dataKey_dict_politicalStatus.getName());
}
@Override
@SysDataCache
@Transactional(readOnly = true)
public Map<String, Object> getSysDatasByDataKeys(AccessTokenUser user, Map<String, Object> dataKeyAndParams) {
if (dataKey_dict_politicalStatus.getId().equals(entry.getKey())) {//政治身份
Map<String, Object> dictParams = (Map<String, Object>) entry.getValue();
data.put(entry.getKey(), getDictValue(entry.getKey(), dictParams));
continue;
}
}
//从字典中获取数据
private List<SimpTreeNode> getDictValue(String dictCodes, Map<String, Object> params) {
if("all".equals(dictCodes)){
List<SysDataSimpleDTO> rootDicts = systemGatewayService.findAllRootDicts(1);
dictCodes = Optional.ofNullable(rootDicts).orElse(new ArrayList<>()).stream().map(r->r.getId().replace("-","")).collect(Collectors.joining(","));
}
else if (params != null && params.get("dictCodes") != null) {
dictCodes = (String) params.get("dictCodes");
params.remove("dictCodes");
}
List<SimpTreeNode> codeList = systemGatewayService.findDictSimTreeNodeByDictCodes(dictCodes, params);
return codeList;
}
}