缓存AOP编程的文摘(java例子),数据缓存切入编程的文章。【截取】

来源:互联网 发布:大连市软件行业协会 编辑:程序博客网 时间:2024/05/01 20:22
另一个关于横切关注点的典型例子是缓存数据。增加缓存的主要目的是改进性能,特别是当获取数据是高代价操作的时候。大多数在最后一章讨论的框架都有他们自己的缓存解决方案,然而缓存是很实际的横切关注点。
 
       由于你引入缓存的动机是提高性能,可以增加一个测试类UserManagerTest测试UserManagerImpl的性能。把下面的代码加到test/org/appfuse/service/UserManagerTest.java中。
 注意
测试中的StopWatch是一个记录任务时间的实用类。
 
public void testGetUserPerformance() {
user = new User();
user.setFirstName("Easter");
user.setLastName("Bunny");
 
user = mgr.saveUser(user);
 
String name = "getUser";
StopWatch sw = new StopWatch(name);
sw.start(name);
log.debug("Begin timing of method '" + name + "'");
 
for (int i=0; i < 200; i++) {
mgr.getUser(user.getId().toString());
}
 
sw.stop();
log.info(sw.shortSummary());
log.debug("End timing of method '" + name + "'");
}
 
       执行ant test -Dtestcase=UserManagerTest(applicationContext.xml中没有配置拦截器)会得出类似图9.2的结果。在上面的测试中,获取相同的用户花费大概10秒钟。
 
 
警告
如果使用-Dtestcase=UserManager(没有“Test”后缀),会执行上一章创建的模拟测试。这些测试从Spring分离了UserManager,他们不能证明应用了在applicationContext.xml中定义的拦截器。
 
图9.2 执行ant test -Dtestcase=UserManagerTest的结果
 
       为了提升性能,在UserManagerImpl中添加一个简单的缓存(cache)。下面是缓存的基本需求:
1、当需要从数据库取得对象时,把这些对象放入缓存(在本例中用简单的HashMap)。
2、当save()delete()方法调用时,从缓存中剔除对象。
 
       在MyUsers这个应用中,你可以通过添加BaseManager (在org.appfuse.service.impl 包中)实现基本的(非AOP)缓存解决方案,所有的管理方法都从中扩展。
 
package org.appfuse.service.impl;
 
// use your IDE to organize imports
 
public class BaseManager {
// Adding this log variable will allow children to re-use it
protected final Log log = LogFactory.getLog(getClass());
protected Map cache;
 
protected void putIntoCache(String key, Object value) {
if (cache == null) {
cache = new HashMap();
}
cache.put(key, value);
}
 
protected void removeFromCache(String key) {
if (cache != null) {
cache.remove(key);
}
}
}
 
 
修改UserManagerImpl使它继承BaseManager,以使用缓存,然后在每个方法中增加对缓存的调用。下面是添加缓存调用之前UserManagerImpl的简单版本:
 
public User getUser(String userId) {
return dao.getUser(Long.valueOf(userId));
}
public User saveUser(User user) {
dao.saveUser(user);
return user;
}
public void removeUser(String userId) {
dao.removeUser(Long.valueOf(userId));
}
 
添加这些方法(注:缓存调用)之后,他们显得更详细:
 
public User getUser(String userId) {
// check cache for user
User user = (User) cache.get(userId);
if (user == null) {
// user not in cache, fetch from database
user = dao.getUser(Long.valueOf(userId));
super.putIntoCache(userId, user);
}
return user;
}
 
public User saveUser(User user) {
dao.saveUser(user);
// update cache with saved user
super.putIntoCache(String.valueOf(user.getId()), user);
return user;
}
 
public void removeUser(String userId) {
dao.removeUser(Long.valueOf(userId));
// remove user from cache
super.removeFromCache(userId);
}
 
执行ant test -Dtestcase=UserManagerTest,在图9.3中可以看见,运行时间是9秒(快了1秒)。
图9.3 执行ant test -Dtestcase=UserManagerTest的结果
 
 
虽然在像MyUsers这样的应用中添加对来自缓存的add/remove调用很简单,但是在大一些的应用中它马上就变得困难起来。不得不记住在每个管理类中添加这些方法。你不应该过分关注缓存,因为它不是应用的核心关注点。
 
       使用AOP,可以去掉这些方法调用,检查应该那些需要使用缓存的方法。另外,从代码中提出缓存,可以让你更关注业务逻辑以实现工程。
To add a caching interceptor, perform the following steps:
 
按下面的步骤增加缓存拦截器:
 
1、用下面的代码在org.appfuse.aop这个包里创建一个CacheInterceptor类。
 
package org.appfuse.aop;
 
// use your IDE to organize imports
 
public class CacheInterceptor implements MethodInterceptor {
private final Log log = LogFactory.getLog(CacheInterceptor.class);
 
public Object invoke(MethodInvocation invocation) throws Throwable {
String name = invocation.getMethod().getName();
Object returnValue;
 
// check cache before executing method
if (name.indexOf("get") > -1 && !name.endsWith("s")) {
String id = (String) invocation.getArguments()[0];
returnValue = cache.get(id);
if (returnValue == null) {
// user not in cache, proceed
returnValue = invocation.proceed();
putIntoCache(id, returnValue);
return returnValue;
} else {
//log.debug("retrieved object id '" + id + "' from cache");
}
} else {
returnValue = invocation.proceed();
 
// update cache after executing method
if (name.indexOf("save") > -1) {
Method getId = returnValue.getClass().getMethod("getId", new Class[]{});
Long id = (Long) getId.invoke(returnValue, new Object[]{});
putIntoCache(String.valueOf(id), returnValue);
} else if (name.indexOf("remove") > -1) {
String id = (String) invocation.getArguments()[0];
removeFromCache(String.valueOf(id));
}
}
return returnValue;
}
 
protected Map cache;
 
protected void putIntoCache(String key, Object value) {
if (cache == null) {
cache = new HashMap();
}
cache.put(key, value);
}
 
protected void removeFromCache(String key) {
if (cache != null) {
cache.remove(key);
}
}
}
 
2、添加cacheInterceptor这个bean到applicationContext.xml (在web/WEB-INF目录下)。
 
 
3、在userManager这个bean的配置中,用cacheInterceptor代替loggingInterceptor
 
 
 
执行ant test -Dtestcase=UserManagerTest,会发现在性能提升上颇为夸张的结果。
图9.4 执行ant test -Dtestcase=UserManagerTest的测试结果。
 
使用缓存切面(caching aspect),性能损耗减到0!性能上夸张提升的原因是,CacheInterceptor几乎总在UserManagerImpl的方法调用之前返回数据。
 
       这里的缓存例子很简单,更健壮的缓存实现可以参考Pieter Coucke的Spring AOP Cache或者using EHCache with Spring。
 
注:
Spring AOP Cache:http://www.onthoo.com/blog/programming/2004/09/spring-aop-cache.html
using EHCache with Spring:http://opensource.atlassian.com/confluence/spring/display/DISC/Caching+the+result+of+methods+using+Spring+and+EHCache
  
原创粉丝点击