浏览 3253 次
|
锁定老贴子 主题:基于拦截器的缓存实现参考
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
|---|---|
| 作者 | 正文 |
|
最后更新时间:2005-01-11
基于拦截器(或者说得好听点,叫AOP)的缓存实现简单算法如下:
[code:1] // 此处拦截所有business facade调用 IF (读方法) key = (根据方法签名计算缓存key) value = (根据key取缓存数据) IF (value == null) value = (实际调用被拦截方法) (key:value放入缓存) RETURN value ELSE IF (写方法) (清空所有缓存) RETURN (实际调用被拦截方法) ELSE RETURN (实际调用被拦截方法) [/code:1] 请注意,这个缓存的清空策略很简单:一旦调用写方法,便清空所有缓存。因此它适用的范围也很窄:仅仅适用于读方法的调用频率远远高于写方法的情况。我目前在做的是一个网站,大概每天只在早上更新几篇文章,一整天阅读文章则有十万次左右,因此恰好适用这个缓存策略。具体的实现参见附件。 虽然这个拦截器很简单,但是值得一提的是,它封装了一切与缓存相关的逻辑:是否需要做缓存;一个方法是读方法还是写方法,或者是与缓存无关的普通方法;使用什么缓存实现……这些都被封装在拦截器中。需要改变缓存策略时,只需要修改这一个地方,完全不会影响到client代码。并且对client代码也没有任何约束,business facade返回任何值(包括int等原生类型)都可以照单全收。 某天下午抽空一小时的作品。 声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
|
|
| 返回顶楼 | |
|
最后更新时间:2005-01-11
两个问题:
1.缓存机制放在BusinessFacade有什么好处吗?通常我的做法是放在数据访问层。 2.下载代码中是否缺少加锁的机制? |
|
| 返回顶楼 | |
|
最后更新时间:2005-01-11
partech 写道 两个问题:
1.缓存机制放在BusinessFacade有什么好处吗?通常我的做法是放在数据访问层。 2.下载代码中是否缺少加锁的机制? 1、实现比较简单。数据访问层那边,Hibernate可以配置一个pluggable的cache,所以我也懒得做。 2、是。目前根本没考虑。 |
|
| 返回顶楼 | |
|
最后更新时间:2005-01-11
这种方法有点问题吧?
会在缓存中造成数据的冗余和一致性问题。 |
|
| 返回顶楼 | |
|
最后更新时间:2005-01-11
很多时候需要deep clone
|
|
| 返回顶楼 | |
|
最后更新时间:2008-08-13
前几天看到一篇blog也是写基于Spring AOP的缓存实现。这几天自己也打算写一个,我和gigix的想法基本很像。应该不需要deepclone的,对于方法结果返回的是可序列化对象应该都适用吧?数据冗余和一致性问题也不会造成,因为生成的Key肯定是唯一的,并且在每次写方法时同时都会更新缓存中的数据。
|
|
| 返回顶楼 | |
|
最后更新时间:2008-08-13
这样做法我觉得可能有些问题,
假如系统中有 getUserById() getUserByName() 而按照你的算法,上面两个方法的cacheKey是不一样的,我觉得要找到一种办法,让user cache只有一个的。 同理,你expire cache的时候也最好能够唯一确定需要expire的Cache。 否则这样的Cache系统应用面实在是太窄了,基本只能是信息发布网站,如果真的如此,那就索性把信息发布成static html好了,更快咯。。。 我自己用annontation的Cache demo code这么做的。 [code:1] /** * 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()); } [/code:1] 这里,我在每个需要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的参数增加不同的语义来实现,但是感觉不是最优雅。 权当作抛砖引玉,希望牛人不吝赐教。 |
|
| 返回顶楼 | |











