导入依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
使用1
项目中使用到了缓存,定义一个切面,拦截类或方法上存在@SysDataCache注解请求,对于这些方法的返回值进行缓存。项目中主要还是使用在缓存常量,一些不易改变的值文章来源:https://www.toymoban.com/news/detail-823864.html
定义注解
@Documented
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SysDataCache {
}
定义切面和初始化缓存容器并使用缓存
@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;
}
}
}
项目中缓存使用
@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;
}
}
使用2
作为性能缓存工具,这里是作为统计sql查询的耗时,当然这是基于内存的缓存,如果需要保留下来。可以插入到数据库中文章来源地址https://www.toymoban.com/news/detail-823864.html
记录sql执行过程实体模型
public class MonitorTask {
public static final String STATUS_RUUNING = "running";
public static final String STATUS_END = "end";
public static final String STATUS_FAILED = "failed";
private Date beginTime;//开始时间
private String content;//sql
private String taskId;//任务id uuid
private Date endTime;//结束时间
private String status = STATUS_RUUNING;//任务执行状态
public MonitorTask(String content, String taskId) {
super();
this.content = content;
this.taskId = taskId;
//指定任务开始时间
this.beginTime = new Date();
}
public Date getBeginTime() {
return beginTime;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getTaskId() {
return taskId;
}
public void setTaskId(String taskId) {
this.taskId = taskId;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public void setBeginTime(Date beginTime) {
this.beginTime = beginTime;
}
public void finished(boolean success) {
//指定任务结束时间
this.endTime = new Date();
if (success) {
status = STATUS_END;
} else {
status = STATUS_FAILED;
}
}
public String getStatus() {
return status;
}
public boolean isFailed() {
return STATUS_FAILED.equals(this.status);
}
public boolean isSuccess() {
return STATUS_END.equals(this.status);
}
public long getCost() {
if (this.endTime == null) {
return 0;
} else if (this.endTime.getTime() > this.beginTime.getTime()) {
return this.endTime.getTime() - this.beginTime.getTime();
} else {
return 0;
}
}
}
任务性能统计实体模型
public class TaskStat {
private String content;
//执行总数
private volatile int totalCount;
//总耗时
private volatile long totalCost;
//最近一次耗时
private volatile long lastCost;
public int getTotalCount() {
return totalCount;
}
public long getTotalCost() {
return totalCost;
}
public long getLastCost() {
return lastCost;
}
public void finish(MonitorTask task) {
this.totalCount++;
this.totalCost += task.getCost();
this.lastCost = task.getCost();
}
public TaskStat(String content) {
super();
this.content = content;
}
//平均时耗
public double getAvgCost() {
if (this.totalCount == 0) {
return 0;
} else {
return this.totalCost / this.totalCount;
}
}
public String getContent() {
return content;
}
}
任务统计逻辑执行过程
//执行统计逻辑
public class PerformanceStat {
//缓存任务执行的开始时间,结束时间,成功与否,任务状态等等
private static LoadingCache<String, MonitorTask> runningTasks = null;
static {
// CacheLoader 初始化
CacheLoader<String, MonitorTask> cacheLoader1 = new CacheLoader<String, MonitorTask>() {
@Override
// load方法的作用是在通过get方法从LoadingCache获取不到值时去加载该值并放入缓存。
public MonitorTask load(String key) throws Exception {
return null;
}
};
runningTasks = CacheBuilder.newBuilder()
// 设置容量大小
.maximumSize(50000)
//默认一天后过期
//.expireAfterWrite(60*24, TimeUnit.MINUTES)
// 加载器配置
.build(cacheLoader1);
}
//缓存任务总耗时,平均耗时,总执行次数
private static LoadingCache<String, TaskStat> taskStats = null;
static {
// CacheLoader 初始化
CacheLoader<String, TaskStat> cacheLoader = new CacheLoader<String, TaskStat>() {
@Override
// load方法的作用是在通过get方法从LoadingCache获取不到值时去加载该值并放入缓存。
public TaskStat load(String key) throws Exception {
return null;
}
};
taskStats = CacheBuilder.newBuilder()
// 设置容量大小
.maximumSize(200000)
//默认一天后过期
//.expireAfterWrite(60*24, TimeUnit.MINUTES)
// 加载器配置
.build(cacheLoader);
}
/**
* 开始监控任务
*
* @param content 监控任务内容
* @return
*/
public static MonitorTask beginTask(String content) {
MonitorTask task = new MonitorTask(content, UUID.randomUUID().toString());
runningTasks.put(task.getTaskId(), task);
TaskStat stat = taskStats.getIfPresent(task.getContent());
if (stat == null) {
stat = new TaskStat(content);
taskStats.put(task.getContent(), stat);
}
return task;
}
/**
* 任务完成
*
* @param task
* @param success
*/
public static void finishTask(MonitorTask task, boolean success) {
if (task == null) {
return;
}
TaskStat stat = taskStats.getIfPresent(task.getContent());
task.finished(success);
runningTasks.invalidate(task.getTaskId());
if (stat != null) {
stat.finish(task);
}
}
//清空缓存
public static void clear() {
runningTasks.invalidateAll();
taskStats.invalidateAll();
}
//排序
public static List<TaskStat> sort(String sortType) {
List<TaskStat> stats = new ArrayList<TaskStat>(taskStats.asMap().values());
if ("1".equals(sortType)) {
//按照总耗时倒序排序
Collections.sort(stats, new Comparator<TaskStat>() {
@Override
public int compare(TaskStat o1, TaskStat o2) {
return Long.valueOf(o2.getTotalCost()).compareTo(Long.valueOf(o1.getTotalCost()));
}
});
}
if ("2".equals(sortType)) {
//按照平均耗时倒序排序
Collections.sort(stats, new Comparator<TaskStat>() {
@Override
public int compare(TaskStat o1, TaskStat o2) {
return Double.valueOf(o2.getAvgCost()).compareTo(Double.valueOf(o1.getAvgCost()));
}
});
}
return stats;
}
}
定义拦截sql执行的InvocationHandler
public class QueryProxy implements InvocationHandler {
private Object target;
private MonitorTask task;
//指定拦截下面这些方法
private static final List<String> jdbcMethods = Arrays.asList("getSingleResult", "getResultList", "executeUpdate", "getResultList", "uniqueResult", "getSingleResult", "list");
public QueryProxy(String content, Object target) {
super();
this.target = target;
task = PerformanceStat.beginTask(content.toLowerCase());
}
private boolean isJdbcMethod(Method method) {
String n = method.getName();
if (jdbcMethods.contains(n)) {
return true;
} else {
return false;
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Object result = method.invoke(target, args);
if (this.isJdbcMethod(method)) {
PerformanceStat.finishTask(task, true);
}
return result;
} catch (RuntimeException e) {
if (this.isJdbcMethod(method)) {
PerformanceStat.finishTask(task, true);
}
throw e;
}
}
@SuppressWarnings("unchecked")
public static final <T> T newProxyQuery(Class<T> queryClass, String content, Object target) {
QueryProxy proxyHandler = new QueryProxy(content, target);
return (T) Proxy.newProxyInstance(QueryProxy.class.getClassLoader(), new Class[]{queryClass}, proxyHandler);
}
}
查询接口
@RequestMapping(value="/holiday", method=RequestMethod.GET)
public String queryHoliday(HttpServletRequest request){
return myService.getHolidayByYear();
}
public String getHolidayByYear(){
Date year = CalendarUtils.getCurrentYearBeginDate();
year = CalendarUtils.offsetYears(year,-1);
List<DateRec> holidays = commonRepositoryHibernate.getHolidayByYear(year);
if (CollectionUtils.isEmpty(holidays)) return null;
return JsonConverter.toJsonStr(holidays);
}
public List<DateRec> getHolidayByYear(Date year){
String hql = "select d from " + DateRec.class.getName() + " d where d.valid = 1 and dateTime >= :dateTime ";
List<DateRec> holidays = this.createHQLQueryByMapParams(DateRec.class, hql, Utils.buildMap("dateTime", year)).list();
if (holidays == null) holidays = new ArrayList<>();
return holidays;
}
拦截sql
private boolean isMonitorPerformance() {
//这里可以通过配置文件配置
return true;
}
/**
* 创建query
* 通过params设置query查询参数
* @param <T>
* @param hql
* @param params
*/
protected <T> Query<T> createHQLQueryByMapParams(Class<T> resultType,String hql,Map<String,Object> params){
Query<T> query = JpaQueryBuilder.createHQLQueryByMapParams(getSession(), resultType, hql, params);
if(isMonitorPerformance()) {
return QueryProxy.newProxyQuery(Query.class, hql,query);
}
return query;
}
查看sql性能监控
@ApiImplicitParam(name="sort", value="排序类型")
@RequestMapping(value="/stat", method=RequestMethod.GET)
public List<TaskStat> stat(@RequestParam String sort,
HttpServletRequest request){
return PerformanceStat.sort(sort);
}
//排序
public static List<TaskStat> sort(String sortType) {
List<TaskStat> stats = new ArrayList<TaskStat>(taskStats.asMap().values());
if ("1".equals(sortType)) {
//按照总耗时倒序排序
Collections.sort(stats, new Comparator<TaskStat>() {
@Override
public int compare(TaskStat o1, TaskStat o2) {
return Long.valueOf(o2.getTotalCost()).compareTo(Long.valueOf(o1.getTotalCost()));
}
});
}
if ("2".equals(sortType)) {
//按照平均耗时倒序排序
Collections.sort(stats, new Comparator<TaskStat>() {
@Override
public int compare(TaskStat o1, TaskStat o2) {
return Double.valueOf(o2.getAvgCost()).compareTo(Double.valueOf(o1.getAvgCost()));
}
});
}
}
到了这里,关于Google的guava缓存学习使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!