工匠解码 2011-09-23
如果我们的项目中采用的是Spring+hibernate来构建的,在缓存方面,我们一定会首先想到Spring自带的EHCache缓存工具,在Spring中集成了目前比较流行的缓存策略EHCache,现在用的比较多的还有像OSCache,MemCached.这些应该是当前用的最多的缓存工具了。
在Spring+hibernate的这样的框架中,EHCache应该属于二级缓存了,我们知道在Hibernate中已经默认的使用了一级缓存,也就是在Session中。二级缓存应该是SessionFactory的范围了。二级缓存默认不会起作用的,这就需要我们简单的配置一下就可以了。
在配置之前,我先说明一点,缓存从理论上来说是可以提高你网站系统的性能,但前提就是你要保证你有一个良好的架构设计。比如用Spring+Hibernate构建的系统,如果用单个服务器,用Spring自带的EHCache来做二级缓存是再好不过了。如果你的系统是分布式的系统,有多台服务器,那么MemCached是最好的选择了,一般来说MemCached在做缓存这一块,要比EHCache和OSCache的性能要好点,但是并不是所有的网站用MemCached都能达到事半功倍的,它虽然是比较好,但它有一个前提,那就是你有多台服务器,是分布式的。这样用MemCached对系统的性能一定OK。因为Memcached是“分布式”的内存对象缓存系统,那么就是说,那些不需要“分布”的,不需要共享的,或者干脆规模小到只有一台服务器的应用, MemCached不会带来任何好处,相反还会拖慢系统效率,因为网络连接同样需要资源 .OSCache这个缓存机制的限制就比较少了。它和EHCache差不多。
在Spring+Hibernate中整合EHCache只需简单的三步。
第一步:配置缓存文件ehcache.xml,默认放到src目录下。下面是简单的配置。
public
class
MethodCacheInterceptor implements
MethodInterceptor, InitializingBean {
private
static
final
Log logger = LogFactory.getLog(MethodCacheInterceptor.class
);
private
Cache cache;
public
void
setCache(Cache cache) {
this
.cache = cache;
}
public
MethodCacheInterceptor() {
super
();
}
public
Object invoke(MethodInvocation invocation) throws
Throwable {
String targetName = invocation.getThis().getClass().getName();
String methodName = invocation.getMethod().getName();
Object[] arguments = invocation.getArguments();
Object result;
logger.debug("Find object from cache is "
+ cache.getName());
String cacheKey = getCacheKey(targetName, methodName, arguments);
Element element = cache.get(cacheKey);
long
startTime = System.currentTimeMillis();
if
(element == null
) {
logger.debug("Hold up method , Get method result and create cache........!"
);
result = invocation.proceed();
element = new
Element(cacheKey, (Serializable) result);
cache.put(element);
long
endTime = System.currentTimeMillis();
logger.info(targetName + "."
+ methodName + " 方法被首次调用并被缓存。耗时"
+ (endTime - startTime) + "毫秒"
+ " cacheKey:"
+ element.getKey());
} else
{
long
endTime = System.currentTimeMillis();
logger.info(targetName + "."
+ methodName + " 结果从缓存中直接调用。耗时"
+ (endTime - startTime) + "毫秒"
+ " cacheKey:"
+ element.getKey());
}
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++) {
sb.append("."
).append(arguments[i]);
}
}
return
sb.toString();
}
public
void
afterPropertiesSet() throws
Exception {
Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it."
);
}
} 这个方法实现了两个接口,一个是MethodInterceptor(方法拦截),它主要是在方法的调用前后都可以执行。另一个InitializingBean (初始化Bean)它主要在方法调用之后做一下简单的检查,主要实现写在afterPropertiesSet()中,就可以了 。
MethodCacheAfterAdvice .java
public
class
MethodCacheAfterAdvice implements
AfterReturningAdvice, InitializingBean {
private
static
final
Log logger = LogFactory.getLog(MethodCacheAfterAdvice.class
);
private
Cache cache;
public
void
setCache(Cache cache) {
this
.cache = cache;
}
public
MethodCacheAfterAdvice() {
super
();
}
public
void
afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws
Throwable {
String className = arg3.getClass().getName();
List list = cache.getKeys();
for
(int
i = 0
; i < list.size(); i++) {
String cacheKey = String.valueOf(list.get(i));
if
(cacheKey.startsWith(className)) {
cache.remove(cacheKey);
logger.debug("remove cache "
+ cacheKey);
}
}
}
public
void
afterPropertiesSet() throws
Exception {
Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it."
);
}
} 这个方法主要是保证缓存的同步,保持与数据库的数据一致性。
第三步:配置Bean了,applicationContext-ehcache.xml文件就是Spring中的Ioc(控制反转容器)的描述了。上面的只是简单的写了两个方法,具体的能起到什么作用,以及何时起作用,以及怎样用声明式的方式(AOP)和Bean结合。
上面我是针对DAO层进行拦截并缓存的,最好是能在业务层进行拦截会更好,你可以根据你的系统具体的设计,如果没有业务层的话,对DAO层拦截也是可以的。拦截采用的是用正规表达式配置的。对find,get的方法只进行缓存,如果 create,update,delete方法进行缓存的同步。对一些频繁的操作最好不要用缓存,缓存的作用就是针对那些不经常变动的操作。
只需这简单的三部就可以完成EHCache了。最好亲自试一试。我并没有针对里面对方法过细的讲解。其实都很简单,多看看就会明白了。不当之处,敬请原谅。