`
dengyin2000
  • 浏览: 1225571 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

原创 Tapestry的Cache组件

阅读更多
有许多页面的一部分或者这个页面是很少更新的,他们通常是由外部文件来生成这个部分。所以我们可以把这部分内容cache住,当有新的请求时,我们就response cache,这样可以减少服务器的负担,还可以提高性能。其中oscache已经可以实现页面的cache和页面部分cache。oscache使用jsp tags来实现局部cache的。拿到Tapestry中肯定是行不通的。在同事的提醒下,想到写这个Tapestry的cache组件来达到重用的目的。

说干就干,先在头脑中想好要怎样使用cache(页面上的布局)。ok。 我想好了。
xml 代码
 
  1. <span jwcid="@Cache" cacheProvider="ognl:cacheProvider" updateCondition="ognl:needUpdate">  
  2.   
  3.     //Cache body, which is the content you want to cache.  
  4.   
  5. <!---->span>  

这里有2个参数,updateCondition 当为true时,我们就绕过cache, cacheProvider 我把他定义为一个接口,这样用户可以把cache存在任何地方。而且提供这样的一个接口,用户可以更好的操作cache。先看看jwc对cache组件的定义。
xml 代码
 
  1. xml version="1.0" encoding="UTF-8"?>  
  2.   "-//Apache Software Foundation//Tapestry Specification 4.0//EN"   
  3.   "http://jakarta.apache.org/tapestry/dtd/Tapestry_4_0.dtd">  
  4.   
  5. <component-specification allow-body="yes" allow-informal-parameters="no" class="com.live.spaces.dengyin2000.tapestry.tfancomponents.components.Cache">  
  6.     <description>  
  7.         Cache component, this component can inclue any content as its body, and cache its body.  
  8.         This is useful in rarely updated content.  
  9.     <!---->description>  
  10.     <parameter name="updateCondition" required="no" default-value="false">  
  11.         <description>  
  12.             The flag that need to refresh cache, it would casue tapestry render not use the cache.  
  13.         <!---->description>  
  14.     <!---->parameter>  
  15.     <parameter name="cacheProvider" required="yes">  
  16.         <description>  
  17.             You need to provider an cache provider to store its body content. for some simply use.  
  18.             Please see @com.live.spaces.dengyin2000.tapestry.tfancomponents.components.SimpleHtmlSourceCacheProvider  
  19.         <!---->description>  
  20.     <!---->parameter>  
  21. <!---->component-specification>  

下面的是ICacheProvider接口:
java 代码
 
  1. package com.live.spaces.dengyin2000.tapestry.tfancomponents.components;  
  2. /** 
  3.  * @author Denny - deng.yin@gmail.com 
  4.  * @since 2006-12-21 
  5.  */  
  6. public interface ICacheProvider {  
  7.       
  8.     /** 
  9.      *  
  10.      * @param cacheKey 
  11.      * @param cacheContent 
  12.      */  
  13.     public void storeCache(String cacheKey, String cacheContent);  
  14.       
  15.     /** 
  16.      *  
  17.      * @param cacheKey 
  18.      * @return 
  19.      */  
  20.     public String getCacheContent(String cacheKey);   
  21.       
  22.     /** 
  23.      * This method provider to user, so that user can controll cache manaully. 
  24.      * @param cacheKey 
  25.      */  
  26.     public void removeCache(String cacheKey);  
  27.       
  28.     /** 
  29.      * This method provider to user, so that user can controll cache manaully. 
  30.      * Clear all caches 
  31.      * 
  32.      */  
  33.     public void reset();  
  34.       
  35. }  

ok。 再来看看Cache组件的代码。
java 代码
 
  1. package com.live.spaces.dengyin2000.tapestry.tfancomponents.components;  
  2.   
  3. import java.io.PrintWriter;  
  4. import java.io.StringWriter;  
  5.   
  6. import org.apache.commons.logging.Log;  
  7. import org.apache.commons.logging.LogFactory;  
  8. import org.apache.tapestry.AbstractComponent;  
  9. import org.apache.tapestry.IMarkupWriter;  
  10. import org.apache.tapestry.IRequestCycle;  
  11. import org.apache.tapestry.util.ContentType;  
  12.   
  13. /** 
  14.  * @author Denny deng 
  15.  * 
  16.  */  
  17. public abstract class Cache extends AbstractComponent {  
  18.       
  19.     protected static final Log logger = LogFactory.getLog(Cache.class);  
  20.       
  21.     public abstract boolean getUpdateCondition();  
  22.       
  23.     public abstract ICacheProvider getCacheProvider();  
  24.       
  25.     @Override  
  26.     protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) {  
  27.           
  28.         if (getUpdateCondition()){  
  29.             renderComponentWithCache(writer, cycle);  
  30.         }else{  
  31.             if (getCacheProvider().getCacheContent(this.getIdPath()) != null){  
  32.                   
  33.                 //response cache html content.  
  34.                 writer.printRaw(getCacheProvider().getCacheContent(this.getIdPath()));  
  35.             }else{  
  36.                 renderComponentWithCache(writer, cycle);  
  37.             }  
  38.         }  
  39.   
  40.     }  
  41.       
  42.     private void renderComponentWithCache(IMarkupWriter writer, IRequestCycle cycle) {  
  43.   
  44.         logger.debug("We need to refresh cache now.");  
  45.         CacheWriterWrapper cacheWrapper = new CacheWriterWrapper();  
  46.         super.renderBody(buildCacheMarkupWriter(cacheWrapper.getPrintWriter(), writer), cycle);  
  47.         String cacheContent = cacheWrapper.getCacheContent();  
  48.         logger.debug("fetched cache content, ready to put it into cache.");  
  49.   
  50.         getCacheProvider().storeCache(this.getIdPath(), cacheContent);  
  51.           
  52.         // response html content.  
  53.         writer.printRaw(cacheContent);  
  54.     }  
  55.       
  56.     private IMarkupWriter buildCacheMarkupWriter(PrintWriter writer, IMarkupWriter sourceWriter){  
  57.         return this.getPage().getRequestCycle().getInfrastructure().getMarkupWriterSource().newMarkupWriter(writer, new ContentType(sourceWriter.getContentType()));  
  58.     }  
  59.       
  60.     class CacheWriterWrapper{  
  61.         private StringWriter sw;  
  62.         private PrintWriter pw;   
  63.         public CacheWriterWrapper() {  
  64.             sw = new StringWriter();  
  65.             pw = new PrintWriter(sw);  
  66.         }  
  67.   
  68.         public String getCacheContent(){  
  69.             return sw.getBuffer().toString();  
  70.         }  
  71.           
  72.         public PrintWriter getPrintWriter(){  
  73.             return pw;  
  74.         }  
  75.     }  
  76. }  

主要是得到cache组件body的内容,然后把body的内容cache住,下次的话就response Cache的内容。 其实也是满简单的。

我自己还写了一个简单CacheProvider。

java 代码
  1. package com.live.spaces.dengyin2000.tapestry.tfancomponents.components;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.Map;  
  5.   
  6. /** 
  7.  * @author Denny - deng.yin@gmail.com 
  8.  * @since 2006-12-21 
  9.  */  
  10. public class SimpleHtmlSourceCacheProvider implements ICacheProvider {  
  11.       
  12.     private Mapnew HashMap
  13.       
  14.     public String getCacheContent(String cacheKey) {  
  15.         return cache.get(cacheKey);  
  16.     }  
  17.   
  18.     public void storeCache(String cacheKey, String cacheContent) {  
  19.         cache.put(cacheKey, cacheContent);  
  20.     }  
  21.   
  22.     public void removeCache(String cacheKey) {  
  23.         cache.remove(cacheKey);  
  24.     }  
  25.   
  26.     public void reset() {  
  27.         cache.clear();  
  28.     }  
  29. }  
在使用中你可以把CacheProvider放到Global或者Visit对象中。注意要使用同一个CacheProvider。

 

我在google code host上面建了一个probject地址是http://code.google.com/p/tfancomponents/ 有兴趣的同学可以看看, 这是一个maven项目。

分享到:
评论
7 楼 dengyin2000 2007-08-10  
koda 写道
不错!我想知道你为什么不直接使用oschache呢?


本来我是打算用oscache来实现了。 其实oscache自己带了filter来处理cache。  我想尽量把他做简单点。 容易理解。  其实你可以看到  我的这个组件需要提供个CacheProvider。 这个是个接口。 所以这样的话, 你想使用任何cache都是可以的。 只要用你现在用的cache实现CacheProvider就行了。  这样岂不是更灵活???
6 楼 koda 2007-08-10  
不错!我想知道你为什么不直接使用oschache呢?
5 楼 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清楚点。


这里说说我的想法, 这个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)。
4 楼 tapestry 2006-12-22  
还有几点疑问,一个是tapestry本身就cache住了静态的模版文档,只需要动态内容添加进去然后生成页面,这个cache应该说是想将静态和动态内容cache住,不需要再生成了,既然是动态的必须是可被更新的,所以要给出个key来,好让人更新,老是调用页面去设置开关然后再显示好像不大方便,现在设置key的话就存在key的管理问题了,今天设定为news了,明天改了成newses了,好多地方就会更新不到了,设为enum或者常量都可以,一个数据字典的概念了,比如Home页面有3个cache了,分别是news,tags,links,后台更新数据的时候来看看哪里用了news了,更新下。这个扯远了,不过老是感觉还有改进的可能,但又暂时想不到。
3 楼 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清楚点。
2 楼 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();
}
这样可用性比较好,有兴趣的话,一起研究一下。


开始时我觉得这个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提供的建议,欢迎一起研究。
1 楼 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();
}
这样可用性比较好,有兴趣的话,一起研究一下。

相关推荐

    tapestry5 自定义组件

    在 Tapestry 5 框架中,自定义组件是扩展其功能的关键方式,它允许开发者根据特定需求创建个性化和可重用的 UI 元素。Tapestry 5 是一个强大的 Java Web 应用程序开发框架,它强调组件化、模块化以及声明式编程模型...

    Tapestry的组件及功能

    介绍Tapestry组件的使用和功能。内容还行,使用初学者入门。

    tapestry页面编辑组件

    在本篇文章中,我们将深入探讨Tapestry的页面编辑组件,以及如何利用这些组件创建交互式的用户界面,包括文本框、单选框、多选框和下拉框。 首先,让我们理解什么是Tapestry页面编辑组件。在Tapestry中,组件是可...

    tapestry组件

    tapestry部分组件绑定参数的列表!

    tapestry4.02中封装ext的GridPanel组件

    本文将深入探讨Tapestry 4.02版本中对ExtJS的GridPanel组件进行封装的相关知识点。 首先,Tapestry是一个基于Java的开源Web应用框架,它提供了组件化开发的方式,使得开发者可以构建出结构清晰、可维护性强的大型...

    Tapestry简单入门.rar_java Tapestry_tapestry

    Tapestry是一款强大的Java Web应用程序框架,由Apache软件基金会维护,它提供了一种基于组件的模型来构建动态、数据驱动的Web应用。本教程将帮助初学者了解Tapestry的基本概念,带你逐步入门并掌握其核心组件的使用...

    tapestry hibernate Spring应用及组件的使用

    Tapestry的组件化设计使得开发人员可以快速构建动态、响应式的用户界面,如在描述中提到的`form`组件和`table`组件。`form`组件用于处理用户输入,创建表单并进行验证,而`table`组件则用于展示数据,支持分页和排序...

    tapestry官方中文文档

    Tapestry是一款强大的Java Web应用程序框架,由Apache软件基金会维护,它强调了组件化、模块化和可重用性,使得开发复杂的Web应用变得更加简单。本文将深入介绍Tapestry 4的相关知识点。 1. **组件化编程**: ...

    Tapestry

    Tapestry是一个基于Java的全栈Web应用程序框架,它的设计目的是为了简化Web应用开发,提供组件化和高度交互性的页面。Tapestry并不是一个应用服务器,而是一个可以在应用服务器中运行的框架,用于构建动态、复杂的...

    tapestry 实例

    2. **组件实例化**:根据页面和组件定义,Tapestry 创建必要的组件实例。 3. **状态管理**:Tapestry 自动处理组件状态的保存和恢复,确保在多个请求间保持一致性。 4. **渲染**:Tapestry 将组件渲染成HTML响应,...

    tapestry-project-4.1.5

    Tapestry 是一个基于 Java 的开源Web 应用程序框架,它强调组件化开发,提供了一种强大的方式来构建动态、高性能的Web 应用。在"tapestry-project-4.1.5"这个压缩包中,我们有机会深入探索 Tapestry 4.1.5 版本的...

    tapestry学习入门资料

    Tapestry 是一个开源的基于 servlet 的应用程序框架,它使用组件对象模型来创建动态的、交互的 web 应用。 Tapestry 使得 Java 代码与 HTML 完全分离,利用这个框架开发大型应用变得轻而易举。并且开发的应用很容易...

    Tapestry笔记

    Tapestry是一个基于Java的Web应用框架,它采用了面向组件的开发方式,这使得它与其他如Structs、WebWork、SpringMVC等采用传统面向元素开发方式的框架有所不同。面向组件的开发模式旨在克服面向元素开发中的局限性,...

    Tapestry4.1.2 入门-介绍

    Tapestry的组件系统非常强大,允许开发者定义自己的可复用组件,每个组件都可以有自己的属性、事件和行为。这些组件可以通过简单的XML标记(如`&lt;span jwcid="@Insert" ...&gt;`)在页面模板中使用。ognl(Object-Graph ...

    tapestry3开发指南,带tapestry3所有jar包

    1. **页面和组件的生命周期**:每个Tapestry 3应用由一系列页面组成,页面又包含多个组件。Tapestry管理这些页面和组件的创建、初始化、渲染和销毁过程。 2. **模板和元数据**:Tapestry使用HTML模板定义页面布局,...

    Tapestry In Action

    - **组件状态管理**:探讨了Tapestry中组件状态的生命周期及其管理方法,包括如何在不同的请求之间保持组件的状态。 #### 第5章:表单输入验证 - **内置验证器**:详细列出了Tapestry提供的内置验证器类型,如长度...

    tapestry的文档

    在学习过程中,开发者会了解到如何使用Tapestry的组件进行页面布局、如何通过tapestry组件实现数据的增删改查操作、如何通过tapestry的LinkSubmit组件实现表单提交、如何进行表单验证以及如何使用tapestry的上传组件...

    tapestry源码 api等

    1. **Tapestry Core**: 这是Tapestry框架的基础部分,包含了核心组件、服务容器(Tapestry IoC)和页面生命周期管理。通过源码分析,我们可以理解其如何实现页面组件的渲染、事件处理和依赖注入。 2. **Tapestry ...

    Tapestry API

    Tapestry API提供了`Component`接口和相关的实现类,例如`Page`和`ComponentTemplate`,用于构建这些组件。 2. **MVC架构** 在Tapestry中,模型、视图和控制器的概念得到了清晰的体现。`Model`代表数据和业务逻辑...

    Tapestry中的Table和Tree的完整教程

    Tapestry是一款强大的Java Web应用程序框架,它提供了一种组件化的开发方式,使得开发者能够创建出高度可维护和可扩展的网页应用。在这个完整的教程中,我们将深入探讨Tapestry中的Table和Tree组件,这两种组件在...

Global site tag (gtag.js) - Google Analytics