2006-12-22
原创 Tapestry的Cache组件
关键字: tapestry
有许多页面的一部分或者这个页面是很少更新的,他们通常是由外部文件来生成这个部分。所以我们可以把这部分内容cache住,当有新的请求时,我们就response cache,这样可以减少服务器的负担,还可以提高性能。其中oscache已经可以实现页面的cache和页面部分cache。oscache使用jsp tags来实现局部cache的。拿到Tapestry中肯定是行不通的。在同事的提醒下,想到写这个Tapestry的cache组件来达到重用的目的。
说干就干,先在头脑中想好要怎样使用cache(页面上的布局)。ok。 我想好了。
这里有2个参数,updateCondition 当为true时,我们就绕过cache, cacheProvider 我把他定义为一个接口,这样用户可以把cache存在任何地方。而且提供这样的一个接口,用户可以更好的操作cache。先看看jwc对cache组件的定义。
下面的是ICacheProvider接口:
ok。 再来看看Cache组件的代码。
在使用中你可以把CacheProvider放到Global或者Visit对象中。注意要使用同一个CacheProvider。
说干就干,先在头脑中想好要怎样使用cache(页面上的布局)。ok。 我想好了。
xml 代码
- <span jwcid="@Cache" cacheProvider="ognl:cacheProvider" updateCondition="ognl:needUpdate">
- //Cache body, which is the content you want to cache.
- span>
这里有2个参数,updateCondition 当为true时,我们就绕过cache, cacheProvider 我把他定义为一个接口,这样用户可以把cache存在任何地方。而且提供这样的一个接口,用户可以更好的操作cache。先看看jwc对cache组件的定义。
xml 代码
- xml version="1.0" encoding="UTF-8"?>
- "-//Apache Software Foundation//Tapestry Specification 4.0//EN"
- "http://jakarta.apache.org/tapestry/dtd/Tapestry_4_0.dtd">
- <component-specification allow-body="yes" allow-informal-parameters="no" class="com.live.spaces.dengyin2000.tapestry.tfancomponents.components.Cache">
- <description>
- Cache component, this component can inclue any content as its body, and cache its body.
- This is useful in rarely updated content.
- description>
- <parameter name="updateCondition" required="no" default-value="false">
- <description>
- The flag that need to refresh cache, it would casue tapestry render not use the cache.
- description>
- parameter>
- <parameter name="cacheProvider" required="yes">
- <description>
- You need to provider an cache provider to store its body content. for some simply use.
- Please see @com.live.spaces.dengyin2000.tapestry.tfancomponents.components.SimpleHtmlSourceCacheProvider
- description>
- parameter>
- component-specification>
下面的是ICacheProvider接口:
java 代码
- package com.live.spaces.dengyin2000.tapestry.tfancomponents.components;
- /**
- * @author Denny - deng.yin@gmail.com
- * @since 2006-12-21
- */
- public interface ICacheProvider {
- /**
- *
- * @param cacheKey
- * @param cacheContent
- */
- public void storeCache(String cacheKey, String cacheContent);
- /**
- *
- * @param cacheKey
- * @return
- */
- public String getCacheContent(String cacheKey);
- /**
- * This method provider to user, so that user can controll cache manaully.
- * @param cacheKey
- */
- public void removeCache(String cacheKey);
- /**
- * This method provider to user, so that user can controll cache manaully.
- * Clear all caches
- *
- */
- public void reset();
- }
ok。 再来看看Cache组件的代码。
java 代码
- package com.live.spaces.dengyin2000.tapestry.tfancomponents.components;
- import java.io.PrintWriter;
- import java.io.StringWriter;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.apache.tapestry.AbstractComponent;
- import org.apache.tapestry.IMarkupWriter;
- import org.apache.tapestry.IRequestCycle;
- import org.apache.tapestry.util.ContentType;
- /**
- * @author Denny deng
- *
- */
- public abstract class Cache extends AbstractComponent {
- protected static final Log logger = LogFactory.getLog(Cache.class);
- public abstract boolean getUpdateCondition();
- public abstract ICacheProvider getCacheProvider();
- @Override
- protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) {
- if (getUpdateCondition()){
- renderComponentWithCache(writer, cycle);
- }else{
- if (getCacheProvider().getCacheContent(this.getIdPath()) != null){
- //response cache html content.
- writer.printRaw(getCacheProvider().getCacheContent(this.getIdPath()));
- }else{
- renderComponentWithCache(writer, cycle);
- }
- }
- }
- private void renderComponentWithCache(IMarkupWriter writer, IRequestCycle cycle) {
- logger.debug("We need to refresh cache now.");
- CacheWriterWrapper cacheWrapper = new CacheWriterWrapper();
- super.renderBody(buildCacheMarkupWriter(cacheWrapper.getPrintWriter(), writer), cycle);
- String cacheContent = cacheWrapper.getCacheContent();
- logger.debug("fetched cache content, ready to put it into cache.");
- getCacheProvider().storeCache(this.getIdPath(), cacheContent);
- // response html content.
- writer.printRaw(cacheContent);
- }
- private IMarkupWriter buildCacheMarkupWriter(PrintWriter writer, IMarkupWriter sourceWriter){
- return this.getPage().getRequestCycle().getInfrastructure().getMarkupWriterSource().newMarkupWriter(writer, new ContentType(sourceWriter.getContentType()));
- }
- class CacheWriterWrapper{
- private StringWriter sw;
- private PrintWriter pw;
- public CacheWriterWrapper() {
- sw = new StringWriter();
- pw = new PrintWriter(sw);
- }
- public String getCacheContent(){
- return sw.getBuffer().toString();
- }
- public PrintWriter getPrintWriter(){
- return pw;
- }
- }
- }
主要是得到cache组件body的内容,然后把body的内容cache住,下次的话就response Cache的内容。 其实也是满简单的。
我自己还写了一个简单CacheProvider。
java 代码
- package com.live.spaces.dengyin2000.tapestry.tfancomponents.components;
- import java.util.HashMap;
- import java.util.Map;
- /**
- * @author Denny - deng.yin@gmail.com
- * @since 2006-12-21
- */
- public class SimpleHtmlSourceCacheProvider implements ICacheProvider {
- private Mapnew HashMap
- public String getCacheContent(String cacheKey) {
- return cache.get(cacheKey);
- }
- public void storeCache(String cacheKey, String cacheContent) {
- cache.put(cacheKey, cacheContent);
- }
- public void removeCache(String cacheKey) {
- cache.remove(cacheKey);
- }
- public void reset() {
- cache.clear();
- }
- }
我在google code host上面建了一个probject地址是http://code.google.com/p/tfancomponents/ 有兴趣的同学可以看看, 这是一个maven项目。
评论
dengyin2000
2007-08-10
koda 写道
不错!我想知道你为什么不直接使用oschache呢?
本来我是打算用oscache来实现了。 其实oscache自己带了filter来处理cache。 我想尽量把他做简单点。 容易理解。 其实你可以看到 我的这个组件需要提供个CacheProvider。 这个是个接口。 所以这样的话, 你想使用任何cache都是可以的。 只要用你现在用的cache实现CacheProvider就行了。 这样岂不是更灵活???
koda
2007-08-10
不错!我想知道你为什么不直接使用oschache呢?
dengyin2000
2006-12-22
tapestry 写道
我的CacheContent意思是封装一个对象,里边有个开关变量,设定是否需要update,当然还有cache的内容(这个才是你的cacheContent的意思),里边有个update方法,因为总感觉update应该是cache自己的事情,但实验了一下概念还是比较乱,先暂时放放。
使用起来应该有两种情况,一种是设置页面的update开关,一种是cacheProvider的removeCache。试想这样一个场景,ShowBlogs页面,你cache住了,然后用户添加了blog,除非他添加完后直接需要到ShowBlogs页面,可以调用ShowBlogs.setNeedUpdate(true),然后显示ShowBlogs页面来刷cache,但一般情况很可能不是跳到ShowBlogs页面,可能是用户自己的ManageBlogs页面,这个时候更新的唯一方法就是调用cacheProvider.removeCache了。
cacheProvider里加个updateCache方法吧,方法里调用removeCache,这样api清楚点。
使用起来应该有两种情况,一种是设置页面的update开关,一种是cacheProvider的removeCache。试想这样一个场景,ShowBlogs页面,你cache住了,然后用户添加了blog,除非他添加完后直接需要到ShowBlogs页面,可以调用ShowBlogs.setNeedUpdate(true),然后显示ShowBlogs页面来刷cache,但一般情况很可能不是跳到ShowBlogs页面,可能是用户自己的ManageBlogs页面,这个时候更新的唯一方法就是调用cacheProvider.removeCache了。
cacheProvider里加个updateCache方法吧,方法里调用removeCache,这样api清楚点。
这里说说我的想法, 这个Cache主要是cache页面的,你想到的比较多,可能你忘了Cache组件中有个updateCondition 属性, 其实我是不太赞成ICacheProvider的removeCache这个方法的,这也是我开始想到使用idpath作为cacheKey的原因,用户不需要知道太多的东西。他仅仅需要知道什么时候我需要让tapestry重新生成一下Body的内容就行。 他也就需要实现updateConditon这个component方法。争对你说的这种场景, 我在updateCondition这个方法里面去判断数据库里面的最新帖子的时间和你生成cache时最新帖子的时间来比较(你可以保存在一个singtlon对象里面),如果时间更新了,return true就ok了。 其实我现在这个项目中也是这种情况,首页内容部分是由管理员upload的一个文件(owl file)生成的内容。这时候我在MiscUtils类里面有个checkOWLFileUpdated()方法去判断owl时候发生了变化, 也是使用file的lastmodfy time来判断的。 我在Cache组件中仅仅是非常方便的委派MiscUtils的那个方法而已。
你在上面也提到了主动的方式,就是在发表玩帖子的时候去设置updateCondition为true。其实你只要把这个值set到一个singlton类属性里面就ok了。 updateCondition方法就去check那个值吧。 我不是很赞成这种做法,因为他把一些非业务相关的逻辑加了进来,可能你会说我可以用aop方便的搞定这个。 但是我还是觉得判断cache是否要更新就让Cache组件来作罢(updateCondition)。
tapestry
2006-12-22
还有几点疑问,一个是tapestry本身就cache住了静态的模版文档,只需要动态内容添加进去然后生成页面,这个cache应该说是想将静态和动态内容cache住,不需要再生成了,既然是动态的必须是可被更新的,所以要给出个key来,好让人更新,老是调用页面去设置开关然后再显示好像不大方便,现在设置key的话就存在key的管理问题了,今天设定为news了,明天改了成newses了,好多地方就会更新不到了,设为enum或者常量都可以,一个数据字典的概念了,比如Home页面有3个cache了,分别是news,tags,links,后台更新数据的时候来看看哪里用了news了,更新下。这个扯远了,不过老是感觉还有改进的可能,但又暂时想不到。
tapestry
2006-12-22
我的CacheContent意思是封装一个对象,里边有个开关变量,设定是否需要update,当然还有cache的内容(这个才是你的cacheContent的意思),里边有个update方法,因为总感觉update应该是cache自己的事情,但实验了一下概念还是比较乱,先暂时放放。
使用起来应该有两种情况,一种是设置页面的update开关,一种是cacheProvider的removeCache。试想这样一个场景,ShowBlogs页面,你cache住了,然后用户添加了blog,除非他添加完后直接需要到ShowBlogs页面,可以调用ShowBlogs.setNeedUpdate(true),然后显示ShowBlogs页面来刷cache,但一般情况很可能不是跳到ShowBlogs页面,可能是用户自己的ManageBlogs页面,这个时候更新的唯一方法就是调用cacheProvider.removeCache了。
cacheProvider里加个updateCache方法吧,方法里调用removeCache,这样api清楚点。
使用起来应该有两种情况,一种是设置页面的update开关,一种是cacheProvider的removeCache。试想这样一个场景,ShowBlogs页面,你cache住了,然后用户添加了blog,除非他添加完后直接需要到ShowBlogs页面,可以调用ShowBlogs.setNeedUpdate(true),然后显示ShowBlogs页面来刷cache,但一般情况很可能不是跳到ShowBlogs页面,可能是用户自己的ManageBlogs页面,这个时候更新的唯一方法就是调用cacheProvider.removeCache了。
cacheProvider里加个updateCache方法吧,方法里调用removeCache,这样api清楚点。
dengyin2000
2006-12-22
tapestry 写道
这个idea不错,还有扩展的余地,保存的key最好不要用idPath,自己可以定义,例如定义一个cache名字为news,key应该为Home:news,这样我可以使用cacheProvider.update("Home:news")来更新,还可以写个annotation,例如我新建了个news,
@InjectCache("Home:news")
public CacheContent getHomeNewsCache();
public void save(){
getNewsService().save(getNews());
getHomeNewsCache().update();
}
这样可用性比较好,有兴趣的话,一起研究一下。
@InjectCache("Home:news")
public CacheContent getHomeNewsCache();
public void save(){
getNewsService().save(getNews());
getHomeNewsCache().update();
}
这样可用性比较好,有兴趣的话,一起研究一下。
开始时我觉得这个cacheKey其实可以对用户不见的,用户不用关心cache的内容。其实这里的cache的内容也就是cache组件的body render之后的html source。但是ICacheProvider提供了removeCache这个方法。假如用idPath的对用户来说确实是不友好的。 指定cacheKey值的话,可读性也很高。这个想法不错。我会加入这个功能,多加个cacheKey属性,假如用户关心的话就指定, 不指定的话默认就是idpath。
<span cacheProvider="ognl:cacheProvider" cacheKey="literal:Home:news" updateCondition="ognl:needUpdate">
//body ....
</span>
@InjectCache("Home:news")
public CacheContent getHomeNewsCache();
你这个annotation的意思是获得cache的内容么? 我这里可能有多个cacheProvider, 而且我想用户可以方便的通过cacheProvider去拿到cache content。 再者我想用户肯定不会想去自己updateCache的内容,因为这个body是tapestry解析后的内容,我想用户关系的只是现在cache中的内容是不是stale的。我现在需不需要更新这个cache。 获得cache的内容交给tapestry来处理。 其实这个组件的目的也是主要是想cache页面的html source而已。
现在想要更新cache中的内容的话, 你只需要让updateCondition为true, 或者让对应的cache 内容为null 即可。
非常谢谢tapestry提供的建议,欢迎一起研究。
tapestry
2006-12-22
这个idea不错,还有扩展的余地,保存的key最好不要用idPath,自己可以定义,例如定义一个cache名字为news,key应该为Home:news,这样我可以使用cacheProvider.update("Home:news")来更新,还可以写个annotation,例如我新建了个news,
@InjectCache("Home:news")
public CacheContent getHomeNewsCache();
public void save(){
getNewsService().save(getNews());
getHomeNewsCache().update();
}
这样可用性比较好,有兴趣的话,一起研究一下。
@InjectCache("Home:news")
public CacheContent getHomeNewsCache();
public void save(){
getNewsService().save(getNews());
getHomeNewsCache().update();
}
这样可用性比较好,有兴趣的话,一起研究一下。
- 浏览: 220476 次
- 性别:

- 来自: 广州

- 详细资料
搜索本博客
我的相册
VB-seamless
共 13 张
共 13 张
最近加入圈子
最新评论
-
使用Terracotta和Tomcat建 ...
renavatior 写道"运行start.bat 9081 这样我们就启动了目 ...
-- by rainsf -
使用Terracotta和Tomcat建 ...
"运行start.bat 9081 这样我们就启动了目录9081中的tomcat ...
-- by renavatior -
广州3年多经验 5500的 ...
fucku 写道广州的软件厂家可比深圳多多了,不过比起北京上海来,还是少了很多, ...
-- by yongfan_420 -
广州3年多经验 5500的 ...
广州的软件厂家可比深圳多多了,不过比起北京上海来,还是少了很多,导致机会也没有这 ...
-- by fucku -
广州3年多经验 5500的 ...
想高工资就去厂家咯,老在集成商里面混能有多大个奔头
-- by fucku






评论排行榜