Fork me on GitHub

Map下的精简Java缓存类设计

前言

对于缓存Java这个社区当中存在着很多;之前的文章里面也有讲到分布式的缓存技术Redis。但是有些时候,我们的项目只想简单的优化下。
例如:页面上一些经常不会进行Update操作的数据进行加载到缓存当中,避免刷新时重复加载请求数据库。

最近做的这一个项目里面,首先的类别信息,和公告等信息就需要放在缓存当中。当时提出来使用Redis进行缓存,但是作为内部使用的论坛,并发访问量不是特别大的情况下增加这样一个缓存技术,就有些显得大材小用。那么就需要自己使用Java简单的写一个缓存,适当的优化一下:“减少数据库的访问”。同时在这里面我也会记录一些,关于在完成这个项目的时候,我们小组之间对于代码层面的一些优化,或者是规范吧。

灵感来源

设计想法

上面的前言当中提到了,减少项目当中引入,避免大材小用,就没有使用Redis这样的缓存技术。只是在本项目当中作为一个工具类的形式进行使用。

首先,我就很块想到了使用Map来实现,但是无法判断缓存当中数据是否过期?数据如果不存在缓存当中时,如何重新加载进入缓存,缓存当中数据的key不可重复。
设计时,我就需要考虑到这些问题。

然后,缓存当中不存在数据时,我就需要使用回调函数进行重新加载数据放入缓存当中,同时返回回调函数所得到的数据。

最后,该工具类当中所暴露出来的接口能够满足项目项目当中对缓存数据进行”增删查”的要求。

好了,有了这些想法,我就使用了ConcurrentHashMap这个数据结构来实现,我的这个缓存工具类。

看看代码

先上代码,寥寥几十行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* 使用java当中的ConCurrentHashMap实现简单的带过期时间的缓存
*
* @author : chencong
* @date : 2018/2/12 21:08
* Package : cc.ccoder
*/
public class CacheManager {

/**
* CacheManager当中用来缓存数据的变量,在这里使用的是ConcurrentHashMap的数据结构
*/
private static Map<String, CacheData> CACHE_DATA = new ConcurrentHashMap<String, CacheData>();

public static final Integer ALWAYS_ACTIVE = 0;

/**
* 通过key键从缓存当中获取到数据,如果该key所对应的数据不存在,则调用回调函数并且数据不为空时将其填充进入缓存当中。<br>
* 将数据填充进入缓存当中时,同时设置数据过期时间expire。<br>
* 该方法返回缓存当中key对应的数据,该数据可能从回调函数当中获取,也可能从缓存当中直接获取。取决于数据是否存在。
*
* @param key key
* @param load 回调函数,用来加载数据进入CACHE_DATA当中
* @param expire 设置数据过期时间
* @param <T> 数据类型
* @return 返回CACHE_DATA当中key所对应的数据
*/
public static <T> T getData(String key, Load<T> load, int expire, Map params) {
T data = getData(key);
if (data == null && load != null) {
data = load.load(params);
if (data != null) {
setData(key, data, expire);
}
}
return data;
}

/**
* 从CACHE_DATA缓存当中获取到key所对应的数据。<br>
* 该方法获取时会自动比较expire过期时间和当前时间,如果数据为null或者过期则返回null。
*
* @param key key
* @param <T> 数据类型
* @return 返回key对应的数据,如果data为null或者已过期则返回null。
*/
public static <T> T getData(String key) {
CacheData<T> data = CACHE_DATA.get(key);
if (data != null && (data.getExpire() <= ALWAYS_ACTIVE || data.getSaveTime() >= System.currentTimeMillis())) {
return data.getData();
}
return null;
}

/**
* 数据存储缓存当中,同时设置数据的键key,数据过期时间expire。<br>
* 如果设置为0 ,ALWAYS_ACTIVE则该数据一直保持活跃。
*
* @param key 该数据所对应的key
* @param data 数据value
* @param expire 数据过期时间,<= 0 则代表一直保持活跃
* @param <T> 数据类型
*/
public static <T> void setData(String key, T data, int expire) {
CACHE_DATA.put(key, new CacheData(data, expire));
}

/**
* 通过key清除缓存当中所对应的数据
*
* @param key key
*/
public static void clear(String key) {
CACHE_DATA.remove(key);
}

/**
* 清除缓存当中所有的数据
*/
public static void clearAll() {
CACHE_DATA.clear();
}

/**
* 回调接口,调用getData(key,load,expire)方法时,如果返回数据为null则可以从回到接口load当中进行加载数据<br>
* 同时加载的数据不为空,则将其重新加入缓存 <br>
* 新版本该接口添加了Map params 参数,解决回调函数中调用的函数需要用到参数的问题。
*
* @param <T> 数据类型
*/
public interface Load<T> {
/**
* 新版本该接口添加了Map params 参数,解决回调函数中调用的函数需要用到参数的问题。<br>
* 由于之前使用的无参数的load回调函数,但是在实际的业务操作中发现,当需要使用load回调函数调用service层接口加载数据进入缓存时,无法传递参数。<br>
* 因此又有了这样一个load<Map params>方法,以便满足实际业务需求。
*
* @param params 参数,类型为map
* @return 返回数据,该数据就是getData()获得到的数据,同时会将数据填入缓存
*/
T load(Map params);
}

/**
* 缓存当中存入的数据bean
*
* @param <T> 数据类型
*/
private static class CacheData<T> {
CacheData(T t, int expire) {
this.data = t;
this.expire = expire <= 0 ? 0 : expire * 1000;
this.saveTime = System.currentTimeMillis() + this.expire;
}

/**
* 缓存当中存入的数据
*/
private T data;
/**
* 缓存中数据存活时间
*/
private long saveTime;
/**
* 过期时间,默认可以调用ALWAYS_ACTIVE=0使数据一致保持活跃。<br>
* <= 0标志一直活跃。
*/
private long expire;

public T getData() {
return data;
}

public long getSaveTime() {
return saveTime;
}

public long getExpire() {
return expire;
}
}
}

– 未完待续….

联系

聪聪的独立博客 ,一个喜欢技术,喜欢钻研的95后。如果你看到这篇文章,千里之外,我在等你联系。

坚持原创技术分享,您的支持将鼓励我继续创作!