资源描述
可以实现不等待,线程自动更新缓存。
CacheData.java 存放缓存数据的Bean
01.package com.cari.web.cache;
02./**
03.* @author zsy
04.*
05.*/
06.public class CacheData {
07.private Object data;
08.private long time;
09.private int count;
10.
11.public CacheData() {
12.
13.}
14.
15.public CacheData(Object data, long time, int count) {
16.this.data = data;
17.this.time = time;
18.this.count = count;
19.}
20.
21.public CacheData(Object data) {
22.this.data = data;
23.this.time = System.currentTimeMillis();
24.this.count = 1;
25.}
26.
27.public void addCount() {
28.count++;
29.}
30.
31.public int getCount() {
32.return count;
33.}
34.public void setCount(int count) {
35.this.count = count;
36.}
37.public Object getData() {
38.return data;
39.}
40.public void setData(Object data) {
41.this.data = data;
42.}
43.public long getTime() {
44.return time;
45.}
46.public void setTime(long time) {
47.this.time = time;
48.}
49.}
CacheOperation.java 缓存处理类
001.package com.cari.web.cache;
002.import java.lang.reflect.Method;
003.import java.util.ArrayList;
004.import java.util.Arrays;
005.import java.util.Hashtable;
006.import org.<span href="tag.php?name=apache" onclick="tagshow(event)" class="t_tag">apache</span>.commons.logging.Log;
007.import mons.logging.LogFactory;
008./**
009.* @author zsy
010.*/
011.public class CacheOperation {
012.private static final Log log = LogFactory.getLog(CacheOperation.class);
013.private static CacheOperation singleton = null;
014.
015.private Hashtable cacheMap;//存放缓存数据
016.
017.private ArrayList threadKeys;//处于线程更新中的key值列表
018.
019.public static CacheOperation getInstance() {
020.if (singleton == null) {
021.singleton = new CacheOperation();
022.}
023.return singleton;
024.}
025.
026.private CacheOperation() {
027.cacheMap = new Hashtable();
028.threadKeys = new ArrayList();
029.}
030.
031./**
032.* 添加数据缓存
033.* 与方法getCacheData(String key, long intervalTime, int maxVisitCount)配合使用
034.* @param key
035.* @param data
036.*/
037.public void addCacheData(String key, Object data) {
038.addCacheData(key, data, true);
039.}
040.
041.private void addCacheData(String key, Object data, boolean check) {
042.if (Runtime.getRuntime().freeMemory() < 5L*1024L*1024L) {//虚拟机内存小于10兆,则清除缓存
043.log.warn("WEB缓存:内存不足,开始清空缓存!");
044.removeAllCacheData();
045.return;
046.} else if(check && cacheMap.containsKey(key)) {
047.log.warn("WEB缓存:key值= " + key + " 在缓存中重复, 本次不缓存!");
048.return;
049.}
050.cacheMap.put(key, new CacheData(data));
051.}
052.
053./**
054.* 取得缓存中的数据
055.* 与方法addCacheData(String key, Object data)配合使用
056.* @param key
057.* @param intervalTime 缓存的时间周期,小于等于0时不限制
058.* @param maxVisitCount 访问累积次数,小于等于0时不限制
059.* @return
060.*/
061.public Object getCacheData(String key, long intervalTime, int maxVisitCount) {
062.CacheData cacheData = (CacheData)cacheMap.get(key);
063.if (cacheData == null) {
064.return null;
065.}
066.if (intervalTime > 0 && (System.currentTimeMillis() - cacheData.getTime()) > intervalTime) {
067.removeCacheData(key);
068.return null;
069.}
070.if (maxVisitCount > 0 && (maxVisitCount - cacheData.getCount()) <= 0) {
071.removeCacheData(key);
072.return null;
073.} else {
074.cacheData.addCount();
075.}
076.return cacheData.getData();
077.}
078.
079./**
080.* 当缓存中数据失效时,用不给定的方法线程更新数据
081.* @param o 取得数据的对像(该方法是静态方法是不用<span href="tag.php?name=%CA%B5%C0%FD" onclick="tagshow(event)" class="t_tag">实例</span>,则传Class实列)
082.* @param methodName 该对像中的方法
083.* @param parameters 该方法的参数列表(参数列表中对像都要实现toString方法,若列表中某一参数为空则传它所属类的Class)
084.* @param intervalTime 缓存的时间周期,小于等于0时不限制
085.* @param maxVisitCount 访问累积次数,小于等于0时不限制
086.* @return
087.*/
088.public Object getCacheData(Object o, String methodName,Object[] parameters,
089.long intervalTime, int maxVisitCount) {
090.Class oc = o instanceof Class ? (Class)o : o.getClass();
091.StringBuffer key = new StringBuffer(oc.getName());//生成缓存key值
092.key.append("-").append(methodName);
093.if (parameters != null) {
094.for (int i = 0; i < parameters.length; i++) {
095.if (parameters[i] instanceof Object[]) {
096.key.append("-").append(Arrays.toString((Object[])parameters[i]));
097.} else {
098.key.append("-").append(parameters[i]);
099.}
100.}
101.}
102.
103.CacheData cacheData = (CacheData)cacheMap.get(key.toString());
104.if (cacheData == null) {//等待加载并返回
105.Object returnValue = invoke(o, methodName, parameters, key.toString());
106.return returnValue instanceof Class ? null : returnValue;
107.}
108.if (intervalTime > 0 && (System.currentTimeMillis() - cacheData.getTime()) > intervalTime) {
109.daemonInvoke(o, methodName, parameters, key.toString());//缓存时间超时,启动线程更新数据
110.} else if (maxVisitCount > 0 && (maxVisitCount - cacheData.getCount()) <= 0) {//访问次数超出,启动线程更新数据
111.daemonInvoke(o, methodName, parameters, key.toString());
112.} else {
113.cacheData.addCount();
114.}
115.return cacheData.getData();
116.}
117.
118./**
119.* 递归调用给定方法更新缓存中数据据
120.* @param o
121.* @param methodName
122.* @param parameters
123.* @param key
124.* @return 若反射调用方法返回值为空则返回该值的类型
125.*/
126.private Object invoke(Object o, String methodName,Object[] parameters, String key) {
127.Object returnValue = null;
128.try {
129.Class[] pcs = null;
130.if (parameters != null) {
131.pcs = new Class[parameters.length];
132.for (int i = 0; i < parameters.length; i++) {
133.if (parameters[i] instanceof MethodInfo) {//参数类型是MethodInfo则调用该方法的返回值做这参数
134.MethodInfo pmi = (MethodInfo)parameters[i];
135.Object pre = invoke(pmi.getO(), pmi.getMethodName(), pmi.getParameters(), null);
136.parameters[i] = pre;
137.}
138.if (parameters[i] instanceof Class) {
139.pcs[i] = (Class)parameters[i];
140.parameters[i] = null;
141.} else {
142.pcs[i] = parameters[i].getClass();
143.}
144.}
145.}
146.Class oc = o instanceof Class ? (Class)o : o.getClass();
147.// Method m = oc.getDeclaredMethod(methodName, pcs);
148.Method m = matchMethod(oc, methodName, pcs);
149.returnValue = m.invoke(o, parameters);
150.if (key != null && returnValue != null) {
151.addCacheData(key, returnValue, false);
152.}
153.if (returnValue == null) {
154.returnValue = m.getReturnType();
155.}
156.} catch(Exception e) {
157.log.error("调用方法失败,methodName=" + methodName);
158.if (key != null) {
159.removeCacheData(key);
160.log.error("更新缓存失败,缓存key=" + key);
161.}
162.e.printStackTrace();
163.}
164.return returnValue;
165.}
166.
167./**
168.* 找不到完全匹配的方法时,对参数进行向父类匹配
169.* 因为方法aa(java.util.List) 与 aa(java.util.ArrayList)不能自动匹配到
170.*
171.* @param oc
172.* @param methodName
173.* @param pcs
174.* @return
175.* @throws NoSuchMethodException
176.* @throws NoSuchMethodException
177.*/
178.private Method matchMethod(Class oc, String methodName, Class[] pcs
179.) throws NoSuchMethodException, SecurityException {
180.try {
181.Method method = oc.getDeclaredMethod(methodName, pcs);
182.return method;
183.} catch (NoSuchMethodException e) {
184.Method[] ms = oc.getDeclaredMethods();
185.aa:for (int i = 0; i < ms.length; i++) {
186.if (ms[i].getName().equals(methodName)) {
187.Class[] pts = ms[i].getParameterTypes();
188.if (pts.length == pcs.length) {
189.for (int j = 0; j < pts.length; j++) {
190.if (!pts[j].isAssignableFrom(pcs[j])) {
191.break aa;
192.}
193.}
194.return ms[i];
195.}
196.}
197.}
198.throw new NoSuchMethodException();
199.}
200.}
201.
202./**
203.* 新启线程后台调用给定方法更新缓存中数据据
204.* @param o
205.* @param methodName
206.* @param parameters
207.* @param key
208.*/
209.private void daemonInvoke(Object o, String methodName,Object[] parameters, String key) {
210.if (!threadKeys.contains(key)) {
211.InvokeThread t = new InvokeThread(o, methodName, parameters, key);
212.t.start();
213.}
214.}
215.
216./**
217.* 些类存放方法的主调对像,名称及参数数组
218.* @author zsy
219.*
220.*/
221.public class MethodInfo {
222.private Object o;
223.private String methodName;
224.private Object[] parameters;
225.public MethodInfo(Object o, String methodName,Object[] parameters) {
226.this.o = o;
227.this.methodName = methodName;
228.this.parameters = parameters;
229.}
230.public String getMethodName() {
231.return methodName;
232.}
233.public void setMethodName(String methodName) {
234.this.methodName = methodName;
235.}
236.public Object getO() {
237.return o;
238.}
239.public void setO(Object o) {
240.this.o = o;
241.}
242.public Object[] getParameters() {
243.return parameters;
244.}
245.public void setParameters(Object[] parameters) {
246.this.parameters = parameters;
247.}
248.
249.public String toString() {
250.StringBuffer str = new StringBuffer(methodName);
251.if (parameters != null) {
252.str.append("(");
253.for (int i = 0; i < parameters.length; i++) {
254.if (parameters[i] instanceof Object[]) {
255.str.append(Arrays.toString((Object[])parameters[i])).append(",");
256.} else {
257.str.append(parameters[i]).append(",");
258.}
259.}
260.str.append(")");
261.}
262.return str.toString();
263.}
264.}
265.
266./**
267.* 线程调用方法
268.* @author zsy
269.*
270.*/
271.private class InvokeThread extends Thread {
272.private Object o;
273.private String methodName;
274.private Object[] parameters;
275.private String key;
276.public InvokeThread(Object o, String methodName,Object[] parameters, String key) {
277.this.o = o;
278.this.methodName = methodName;
279.this.parameters = parameters;
280.this.key = key;
281.}
282.
283.public void run() {
284.threadKeys.add(key);
285.invoke(o, methodName, parameters, key);
286.threadKeys.remove(key);
287.}
288.}
289.
290./**
291.* 移除缓存中的数据
292.* @param key
293.*/
294.public void removeCacheData(String key) {
295.cacheMap.remove(key);
296.}
297.
298./**
299.* 移除所有缓存中的数据
300.*
301.*/
302.public void removeAllCacheData() {
303.cacheMap.clear();
304.}
305.
306.public String toString() {
307.StringBuffer sb = new StringBuffer("************************ ");
308.sb.append("正在更新的缓存数据: ");
309.for (int i = 0; i < threadKeys.size(); i++) {
310.sb.append(threadKeys.get(i)).append(" ");
311.}
312.sb.append("当前缓存大小:").append(cacheMap.size()).append(" ");
313.sb.append("************************");
314.return sb.toString();
315.}
316.}
用法:
例1:代码片段如下:
public class Test {
String rulStr=....;
String encoding=....;
public void getData() {
DataCreator c = new DataCreator();
String result = c.initUrlData(urlStr,encoding);
System.out.println(result);
}
}
每次执行上面代码时都要通过调用 initUrlData方法取得数据,假设此方法很耗资源而耗时间,但对数据时实性要求不高,就是可以用以下方式进行缓存处理,保证很快地取得数据,并根据设置的参数自动更新缓存中数据
注意:initUrlData方法参数值一样时才属于同一个缓存,否则会生成一个新的缓存,也就是说从缓存中取数据与initUrlData方法参数值有关
......
public void getData() {
DataCreator data = new DataCreator();
CacheOperation co = CacheOperation.getInstance();
String str = (String)co.getCacheData(data, "initUrlData",new Object[]{urlStr, encoding}, 120000, 100);
System.out.println(result);
}
......
getCacheData方法返回值与initUrlData方法返回类型一样,参数说明:
data:调用initUrlData方法的实列,如果该方法是静态的,则传类的类型,如(DataCreator .class);
"initUrlData":方法名称;
new Object[]{urlStr, encoding}:initUrlData方法的参数数组,如果某一参数为空则传该参数的类型,若encoding 为空,则为new Object[]{urlStr, String.class}或new Object[]{urlStr, ""};
120000:缓存时间,单位:豪秒,即过两分钟更新一次缓存;值为0时为不限,即不更新缓存;
100:访问次数,当缓存中数据被访问100次时更新一次缓存;值为0时为不限,即不更新缓存;
例2:代码片段如下:
......
String province = request.getParameter("province");
String city= request.getParameter("city");
String county= request.getParameter("county");
Document doc = XMLBuilder.buildLatelyKeyword(kwm.latelyKeyword(province, city, county));
out.write(doc);
......
做缓存并两分钟更新一次,如下:
......
String province = request.getParameter("province");
String city= request.getParameter("city");
String county= request.getParameter("county");
CacheOperation co = CacheOperation.getInstance();
MethodInfo mi = co.new MethodInfo(kwm, "latelyKeyword", new Object[]{province, city, county});
Document doc = (Document )co.getCacheData(XMLBuilder.class,"buildLatelyKeyword",new Object[]{mi}, 120000, 0);
out.write(doc);
......
以上方法是嵌套调用, 要先定义内部方法说明即MethodInfo,此类是CacheOperation 的一个内部类。
说明的不是很清楚,具体实现过程看源程序。。
展开阅读全文