二,动态缓存
方案1:使用自定义annotation接口进行aspectj动态缓存
由于系统需求需要对各个接口进行key-value缓存(以参数为key,返回的对象为value),当然对于这种情况首先考虑到的是使用aop,前段时间看过aspectj的一些介绍,借此机会正好加以应用和体会一下,aspectj是AOP最早成熟的java实现,它稍微扩展了一下java语言,增加了一些keyword等,具体的aspectj的基本语法见这里,进行缓存的框架使用较成熟的ehcache.
下面开始进行配置
首先是ehcache的配置文件
- <?xml version="1.0" encoding="UTF-8"?>
- <ehcache>
- <diskStore path="/home/workspace/gzshine/trunk/ehcache"/>
- <cache name="DEFAULT_CACHE"
- maxElementsInMemory="10000"
- eternal="false"
- timeToIdleSeconds="3600"
- timeToLiveSeconds="3600"
- overflowToDisk="true"
- />
- </ehcache>
这个的DEFAULT_CACHE是默认配置,最大的缓存数为10000,时间为一个小时
接下来的是spring下的配置
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="
- http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-2.5.xsd">
-
-
-
- <aop:aspectj-autoproxy proxy-target-class="true"/>
- <bean id = "methodCacheAspectJ" class="com.***.shine.aspectj.MethodCacheAspectJ" >
- <property name="cache">
- <ref local="methodCache" />
- </property>
- </bean>
-
- <bean id="cacheManager"
- class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
- <property name="configLocation">
- <value>classpath:ehcache.xml</value>
- </property>
- </bean>
-
-
-
- <bean id="methodCache"
- class="org.springframework.cache.ehcache.EhCacheFactoryBean">
- <property name="cacheManager">
- <ref local="cacheManager" />
- </property>
- <property name="cacheName">
- <value>DEFAULT_CACHE</value>
- </property>
- </bean>
<aop:aspectj-autoproxy proxy-target-class="true"/>
是为aspectj在所有class下开启自动动态代理
<bean >指定刚刚的ehcache配置文件
接下来编写一个自定义的annotation
- package com.***.shine.cache;
-
- import java.lang.annotation.Documented;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
-
- @Target({ElementType.METHOD,ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface MethodCache {
- int second() default 0;
- }
<bean id = "methodCacheAspectJ">是一个aspectj进行Pointcuts和Advice的类需注入methodCache
- package com.***.shine.aspectj;
-
- @Aspect
- public class MethodCacheAspectJ {
- Log logger = LogFactory.getLog(MethodCacheAspectJ.class);
-
- private Cache cache;
-
-
-
-
- public void setCache(Cache cache) {
- this.cache = cache;
- }
-
- @Pointcut("@annotation(com.***.shine.cache.MethodCache)")
- public void methodCachePointcut(){
- }
-
- @Around("methodCachePointcut()")
- public Object methodCacheHold(ProceedingJoinPoint joinPoint) throws Throwable{
- String targetName = joinPoint.getTarget().getClass().getName();
- String methodName = joinPoint.getSignature().getName();
- Object[] arguments = joinPoint.getArgs();
- Object result = null;
- String cacheKey = getCacheKey(targetName, methodName, arguments);
- Element element = cache.get(cacheKey);
- if (element == null) {
- try{
- result = joinPoint.proceed();
- }catch(Exception e){
- logger.info(e);
- }
- if(result!=null){
- try{
- element = new Element(cacheKey, (Serializable) result);
- Class targetClass = Class.forName(targetName);
- Method[] method = targetClass.getMethods();
- int second = 0;
- for(Method m:method){
- if (m.getName().equals(methodName)) {
- Class[] tmpCs = m.getParameterTypes();
- if(tmpCs.length==arguments.length){
- MethodCache methodCache = m.getAnnotation(MethodCache.class);
- second = methodCache.second();
- break;
- }
- }
- }
- if(second>0){
- element.setTimeToIdle(second);
- element.setTimeToLive(second);
- }
- cache.put(element);
- }catch(Exception e){
- logger.info("!!!!!!!!!"+cacheKey+"!!!!!!!!!未能执行方法缓存"+e);
- }
- }
- }
- return element.getValue();
- }
-
- private String getCacheKey(String targetName, String methodName,
- Object[] arguments) {
- StringBuffer sb = new StringBuffer();
- sb.append(targetName).append(".").append(methodName);
- if ((arguments != null) && (arguments.length != 0)) {
- for (int i = 0; i < arguments.length; i++) {
- if (arguments[i] instanceof Date) {
- sb.append(".").append(
- DateUtil.datetoString((Date) arguments[i]));
- } else {
- sb.append(".").append(arguments[i]);
- }
- }
- }
- return sb.toString();
- }
- }
@Pointcut("@annotation(com.netease.shine.cache.MethodCache)")
对有应用com.netease.shine.cache.MethodCache进行注解的方法进行横切面拦截
@Around("methodCachePointcut()")
并在Advice中处理这个Pointcut,这里的的Advice使用的是Around(环绕通知)
String cacheKey = getCacheKey(targetName, methodName, arguments);
接下来使用类型,方法名,参数为key进入缓存处理
Element element = cache.get(cacheKey);
当然如果在cache队列中取得非null对象则直接返回该对象
MethodCache methodCache = m.getAnnotation(MethodCache.class);
second = methodCache.second();
取得second的值(缓存的时间,如在@annotation中无重写只为int second() default 0)
element.setTimeToIdle(second);
element.setTimeToLive(second);
如果非零则重新设置缓存时间
- @MethodCache(second=300)
- public List<Sort> getSort(int type,int parentid){
- System.out.println("!!!!!!!!!!!!!没缓存到");
- Row row = new Row();
- row.put("type", type);
- row.put("parentid", parentid);
- return (List<Sort>)gz_Template.queryForList("sort.getSort", row);
- }
最后需要将@MethodCache要缓存方法的实现类
方案2 web应用的java动态缓存
可以实现不等待,线程自动更新缓存
java动态缓存jar包请下载。
源代码:
001 |
CacheData.java 存放缓存数据的Bean |
006 |
package com.cari.web.cache;
|
012 |
public class CacheData {
|
021 |
public CacheData(Object data, long time, int count) {
|
027 |
public CacheData(Object data) {
|
029 |
this.time = System.currentTimeMillis();
|
033 |
public void addCount() {
|
037 |
public int getCount() {
|
040 |
public void setCount(int count) {
|
043 |
public Object getData() {
|
046 |
public void setData(Object data) {
|
049 |
public long getTime() {
|
052 |
public void setTime(long time) {
|
059 |
CacheOperation.java 缓存处理类 |
061 |
package com.cari.web.cache;
|
063 |
import java.lang.reflect.Method;
|
064 |
import java.util.ArrayList;
|
065 |
import java.util.Arrays;
|
066 |
import java.util.Hashtable;
|
068 |
import org.apache.commons.logging.Log;
|
069 |
import org.apache.commons.logging.LogFactory;
|
074 |
public class CacheOperation {
|
075 |
private static final Log log = LogFactory.getLog(CacheOperation.class);
|
076 |
private static CacheOperation singleton = null;
|
078 |
private Hashtable cacheMap;
|
080 |
private ArrayList threadKeys;
|
082 |
public static CacheOperation getInstance() {
|
083 |
if (singleton == null) {
|
084 |
singleton = new CacheOperation();
|
089 |
private CacheOperation() {
|
090 |
cacheMap = new Hashtable();
|
091 |
threadKeys = new ArrayList();
|
096 |
* 与方法getCacheData(String key, long intervalTime, int maxVisitCount)配合使用
|
100 |
public void addCacheData(String key, Object data) {
|
101 |
addCacheData(key, data, true);
|
104 |
private void addCacheData(String key, Object data, boolean check) {
|
105 |
if (Runtime.getRuntime().freeMemory() < 5L*1024L*1024L) {
|
106 |
log.warn("WEB缓存:内存不足,开始清空缓存!");
|
107 |
removeAllCacheData();
|
109 |
} else if(check && cacheMap.containsKey(key)) {
|
110 |
log.warn("WEB缓存:key值= " + key + " 在缓存中重复, 本次不缓存!");
|
113 |
cacheMap.put(key, new CacheData(data));
|
118 |
* 与方法addCacheData(String key, Object data)配合使用
|
120 |
* @param intervalTime 缓存的时间周期,小于等于0时不限制
|
121 |
* @param maxVisitCount 访问累积次数,小于等于0时不限制
|
124 |
public Object getCacheData(String key, long intervalTime, int maxVisitCount) {
|
125 |
CacheData cacheData = (CacheData)cacheMap.get(key);
|
126 |
if (cacheData == null) {
|
129 |
if (intervalTime > 0 && (System.currentTimeMillis() - cacheData.getTime()) > intervalTime) {
|
130 |
removeCacheData(key);
|
133 |
if (maxVisitCount > 0 && (maxVisitCount - cacheData.getCount()) <= 0) {
|
134 |
removeCacheData(key);
|
137 |
cacheData.addCount();
|
139 |
return cacheData.getData();
|
143 |
* 当缓存中数据失效时,用不给定的方法线程更新数据
|
144 |
* @param o 取得数据的对像(该方法是静态方法是不用实例,则传Class实列)
|
145 |
* @param methodName 该对像中的方法
|
146 |
* @param parameters 该方法的参数列表(参数列表中对像都要实现toString方法,若列表中某一参数为空则传它所属类的Class)
|
147 |
* @param intervalTime 缓存的时间周期,小于等于0时不限制
|
148 |
* @param maxVisitCount 访问累积次数,小于等于0时不限制
|
151 |
public Object getCacheData(Object o, String methodName,Object[] parameters,
|
152 |
long intervalTime, int maxVisitCount) {
|
153 |
Class oc = o instanceof Class ? (Class)o : o.getClass();
|
154 |
StringBuffer key = new StringBuffer(oc.getName());
|
155 |
key.append("-").append(methodName);
|
156 |
if (parameters != null) {
|
157 |
for (int i = 0; i < parameters.length; i++) {
|
158 |
if (parameters[i] instanceof Object[]) {
|
159 |
key.append("-").append(Arrays.toString((Object[])parameters[i]));
|
161 |
key.append("-").append(parameters[i]);
|
166 |
CacheData cacheData = (CacheData)cacheMap.get(key.toString());
|
167 |
if (cacheData == null) {
|
168 |
Object returnValue = invoke(o, methodName, parameters, key.toString());
|
169 |
return returnValue instanceof Class ? null : returnValue;
|
171 |
if (intervalTime > 0 && (System.currentTimeMillis() - cacheData.getTime()) > intervalTime) {
|
172 |
daemonInvoke(o, methodName, parameters, key.toString());
|
173 |
} else if (maxVisitCount > 0 && (maxVisitCount - cacheData.getCount()) <= 0) {
|
174 |
daemonInvoke(o, methodName, parameters, key.toString());
|
176 |
cacheData.addCount();
|
178 |
return cacheData.getData();
|
186 |
* @return 若反射调用方法返回值为空则返回该值的类型
|
188 |
private Object invoke(Object o, String methodName,Object[] parameters, String key) {
|
189 |
Object returnValue = null;
|
192 |
if (parameters != null) {
|
193 |
pcs = new Class[parameters.length];
|
194 |
for (int i = 0; i < parameters.length; i++) {
|
195 |
if (parameters[i] instanceof MethodInfo) {
|
196 |
MethodInfo pmi = (MethodInfo)parameters[i];
|
197 |
Object pre = invoke(pmi.getO(), pmi.getMethodName(), pmi.getParameters(), null);
|
200 |
if (parameters[i] instanceof Class) {
|
201 |
pcs[i] = (Class)parameters[i];
|
202 |
parameters[i] = null;
|
204 |
pcs[i] = parameters[i].getClass();
|
208 |
Class oc = o instanceof Class ? (Class)o : o.getClass();
|
210 |
Method m = matchMethod(oc, methodName, pcs);
|
211 |
returnValue = m.invoke(o, parameters);
|
212 |
if (key != null && returnValue != null) {
|
213 |
addCacheData(key, returnValue, false);
|
215 |
if (returnValue == null) {
|
216 |
returnValue = m.getReturnType();
|
218 |
} catch(Exception e) {
|
219 |
log.error("调用方法失败,methodName=" + methodName);
|
221 |
removeCacheData(key);
|
222 |
log.error("更新缓存失败,缓存key=" + key);
|
230 |
* 找不到完全匹配的方法时,对参数进行向父类匹配
|
231 |
* 因为方法aa(java.util.List) 与 aa(java.util.ArrayList)不能自动匹配到
|
237 |
* @throws NoSuchMethodException
|
238 |
* @throws NoSuchMethodException
|
240 |
private Method matchMethod(Class oc, String methodName, Class[] pcs
|
241 |
) throws NoSuchMethodException, SecurityException {
|
243 |
Method method = oc.getDeclaredMethod(methodName, pcs);
|
245 |
} catch (NoSuchMethodException e) {
|
246 |
Method[] ms = oc.getDeclaredMethods();
|
247 |
aa:for (int i = 0; i < ms.length; i++) {
|
248 |
if (ms[i].getName().equals(methodName)) {
|
249 |
Class[] pts = ms[i].getParameterTypes();
|
250 |
if (pts.length == pcs.length) {
|
251 |
for (int j = 0; j < pts.length; j++) {
|
252 |
if (!pts[j].isAssignableFrom(pcs[j])) {
|
260 |
throw new NoSuchMethodException();
|
265 |
* 新启线程后台调用给定方法更新缓存中数据据
|
271 |
private void daemonInvoke(Object o, String methodName,Object[] parameters, String key) {
|
272 |
if (!threadKeys.contains(key)) {
|
273 |
InvokeThread t = new InvokeThread(o, methodName, parameters, key);
|
279 |
* 些类存放方法的主调对像,名称及参数数组
|
283 |
public class MethodInfo {
|
285 |
private String methodName;
|
286 |
private Object[] parameters;
|
287 |
public MethodInfo(Object o, String methodName,Object[] parameters) {
|
289 |
this.methodName = methodName;
|
290 |
this.parameters = parameters;
|
292 |
public String getMethodName() {
|
295 |
public void setMethodName(String methodName) {
|
296 |
this.methodName = methodName;
|
298 |
public Object getO() {
|
301 |
public void setO(Object o) {
|
304 |
public Object[] getParameters() {
|
307 |
public void setParameters(Object[] parameters) {
|
308 |
this.parameters = parameters;
|
311 |
public String toString() {
|
312 |
StringBuffer str = new StringBuffer(methodName);
|
313 |
if (parameters != null) {
|
315 |
for (int i = 0; i < parameters.length; i++) {
|
316 |
if (parameters[i] instanceof Object[]) {
|
317 |
str.append(Arrays.toString((Object[])parameters[i])).append(",");
|
319 |
str.append(parameters[i]).append(",");
|
324 |
return str.toString();
|
333 |
private class InvokeThread extends Thread {
|
335 |
private String methodName;
|
336 |
private Object[] parameters;
|
338 |
public InvokeThread(Object o, String methodName,Object[] parameters, String key) {
|
340 |
this.methodName = methodName;
|
341 |
this.parameters = parameters;
|
347 |
invoke(o, methodName, parameters, key);
|
348 |
threadKeys.remove(key);
|
356 |
public void removeCacheData(String key) {
|
357 |
cacheMap.remove(key);
|
364 |
public void removeAllCacheData() {
|
368 |
public String toString() {
|
369 |
StringBuffer sb = new StringBuffer("************************ ");
|
370 |
sb.append("正在更新的缓存数据: ");
|
371 |
for (int i = 0; i < threadKeys.size(); i++) {
|
372 |
sb.append(threadKeys.get(i)).append(" ");
|
374 |
sb.append("当前缓存大小:").append(cacheMap.size()).append(" ");
|
375 |
sb.append("************************");
|
376 |
return sb.toString();
|
378 |
}
用法:
例1:代码片段如下:
07 |
public void getData() {
|
09 |
DataCreator c = new DataCreator();
|
11 |
String result = c.initUrlData(urlStr,encoding);
|
13 |
System.out.println(result);
|
每次执行上面代码时都要通过调用 initUrlData方法取得数据,假设此方法很耗资源而耗时间,但对数据时实性要求不高,就是可以用以下方式进行缓存处理,保证很快地取得数据,并根据设置的参数自动更新缓存中数据
注意:initUrlData方法参数值一样时才属于同一个缓存,否则会生成一个新的缓存,也就是说从缓存中取数据与initUrlData方法参数值有关
01 |
public void getData() {
|
03 |
DataCreator data = new DataCreator();
|
05 |
CacheOperation co = CacheOperation.getInstance();
|
07 |
String str = (String)co.getCacheData(data, "initUrlData",new Object[]{urlStr, encoding}, 120000, 100);
|
09 |
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:代码片段如下:
1 |
String province = request.getParameter("province");
|
3 |
String city= request.getParameter("city");
|
5 |
String county= request.getParameter("county");
|
7 |
Document doc = XMLBuilder.buildLatelyKeyword(kwm.latelyKeyword(province, city, county)); |
做缓存并两分钟更新一次,如下:
01 |
String province = request.getParameter("province");
|
03 |
String city= request.getParameter("city");
|
05 |
String county= request.getParameter("county");
|
07 |
CacheOperation co = CacheOperation.getInstance(); |
09 |
MethodInfo mi = co.new MethodInfo(kwm, "latelyKeyword", new Object[]{province, city, county});
|
11 |
Document doc = (Document )co.getCacheData(XMLBuilder.class,"buildLatelyKeyword",new Object[],120000, 0);
|
以上方法是嵌套调用, 要先定义内部方法说明即MethodInfo,此类是CacheOperation 的一个内部类。
|