论坛首页 Java企业应用论坛

基于拦截器的缓存实现参考

浏览 5462 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2005-01-11  
基于拦截器(或者说得好听点,叫AOP)的缓存实现简单算法如下:
// 此处拦截所有business facade调用
IF (读方法);
    key = (根据方法签名计算缓存key);
    value = (根据key取缓存数据);
    IF (value == null);
        value = (实际调用被拦截方法);
        (key:value放入缓存);
    RETURN value
ELSE IF (写方法);
    (清空所有缓存);
    RETURN (实际调用被拦截方法);
ELSE
    RETURN (实际调用被拦截方法);    


请注意,这个缓存的清空策略很简单:一旦调用写方法,便清空所有缓存。因此它适用的范围也很窄:仅仅适用于读方法的调用频率远远高于写方法的情况。我目前在做的是一个网站,大概每天只在早上更新几篇文章,一整天阅读文章则有十万次左右,因此恰好适用这个缓存策略。具体的实现参见附件。

虽然这个拦截器很简单,但是值得一提的是,它封装了一切与缓存相关的逻辑:是否需要做缓存;一个方法是读方法还是写方法,或者是与缓存无关的普通方法;使用什么缓存实现……这些都被封装在拦截器中。需要改变缓存策略时,只需要修改这一个地方,完全不会影响到client代码。并且对client代码也没有任何约束,business facade返回任何值(包括int等原生类型)都可以照单全收。

某天下午抽空一小时的作品。
   发表时间:2005-01-11  
两个问题:
1.缓存机制放在BusinessFacade有什么好处吗?通常我的做法是放在数据访问层。
2.下载代码中是否缺少加锁的机制?
0 请登录后投票
   发表时间:2005-01-11  
partech 写道
两个问题:
1.缓存机制放在BusinessFacade有什么好处吗?通常我的做法是放在数据访问层。
2.下载代码中是否缺少加锁的机制?


1、实现比较简单。数据访问层那边,Hibernate可以配置一个pluggable的cache,所以我也懒得做。

2、是。目前根本没考虑。
0 请登录后投票
   发表时间:2005-01-11  
这种方法有点问题吧?

会在缓存中造成数据的冗余和一致性问题。
0 请登录后投票
   发表时间:2005-01-11  
很多时候需要deep clone
0 请登录后投票
   发表时间:2005-01-12  
前几天看到一篇blog也是写基于Spring AOP的缓存实现。这几天自己也打算写一个,我和gigix的想法基本很像。应该不需要deepclone的,对于方法结果返回的是可序列化对象应该都适用吧?数据冗余和一致性问题也不会造成,因为生成的Key肯定是唯一的,并且在每次写方法时同时都会更新缓存中的数据。
0 请登录后投票
   发表时间:2005-01-14  
这样做法我觉得可能有些问题,
假如系统中有
getUserById()
getUserByName()

而按照你的算法,上面两个方法的cacheKey是不一样的,我觉得要找到一种办法,让user cache只有一个的。

同理,你expire cache的时候也最好能够唯一确定需要expire的Cache。
否则这样的Cache系统应用面实在是太窄了,基本只能是信息发布网站,如果真的如此,那就索性把信息发布成static html好了,更快咯。。。

我自己用annontation的Cache demo code这么做的。

 /**
   * Take it easy guy, just as a proof of concept  :);  
   * @@CacheAttribute(type="demo.User",key="java.lang.Long",action="read");
   * @param id
   * @return
   */
  public User getUser(Long id);{
    try {
      return new UserImpl(userDAO.getUserEntityById(id););;      
    } catch (DAOException e); {
      throw new UserNotFoundException("User with id=" + id + " not found!");;
    }

  }
  
  
  /**
   * @@CacheAttribute(type="demo.User",key="java.lang.String",action="read");
   * @param name
   * @return
   */
  public User getUser(String name);{
    try {
      return new UserImpl(userDAO.getUserEntityByName(name););;  
    } catch (DAOException e); {
      throw new UserNotFoundException("User with name=" + name + " not found!");;
    }
  }


 /**
   * @@CacheAttribute(type="demo.User",value="demo.User",action="invalid");
   * @@TransactionAttribute(type="required");
   * @param user
   */
  public void deleteUser(User user);{
      userDAO.deleteUser((UserEntity);user.getEntity(););;
  }



这里,我在每个需要Cache Interceptor拦截的方法上都加了
@@CacheAttribute(type="demo.User",key="java.lang.String",action="read")
这样的注释,CacheInterceptor会读取这个注释,然后根据里面的Type,来从CacheManager中得到对应的Cache,根据对应的Action进行操作。

这里有2个问题:
(1)一个业务方法可能有多个参数,比如getFoo(Bar bar1,Bar bar2)。那么在Read action的时候,如何确定那个bar?是被Cache的BO的key?
目前我的系统这类需要Cache的read方法只有一个参数,因为一般是通过PK或者来拿对象的。如果真的有多个参数,也可以通过在parameter上添加annontation的方法来确定,但是我担心性能上恐怕有一点点损失,而且也不确定JDK1.5的annontation是否支持。

(2)你在expire cache的时候,同样需要这个Key或者被Cache的Object本身,如何从被拦截的业务方法中获得?
我现在是通过对CacheAttribute的参数增加不同的语义来实现,但是感觉不是最优雅。

权当作抛砖引玉,希望牛人不吝赐教。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics