您当前的位置: AG亚游集团 > 网站编程 > JSP教程 > JSP 2.0下的动态内容缓存

JSP 2.0下的动态内容缓存

作者:guanchaofeng 来源:本站整理 发布时间: 2009-11-12 21:53 点击: 次
在Web应用中,内容缓存是最普通的优化技术之一,并且能够很容易地实现。例如,可以使用一个自定义地JSP标签我们将之命名为jc:cache由jc:cache和/jc:cache将每一个需要被缓存的页面片段封装起来。任何自定义标签可以控制它所包含部分(也即预先封装的页面片段)

AG亚游集团,  参加亲子类,谈及育儿方法,都会提及“与孩子做朋友”,但究竟要怎么变成孩子们的朋友呢?胡可的方式是弱化自己妈妈的身份,让自己变成需要被孩子照顾的一方。她说:“在平常的生活中,我就经常弱化妈妈这个角色。我希望能够在他面前示弱,在他面前做一个小女生,让他去帮助我,克服生活当中的一些困难。我觉得妈妈是这样的,尤其是男孩子的妈妈,一定要懂得示弱,让你的儿子可以成长为一个真正的男子汉。”

  一次训练,扫雷舰编队跑了一个来回,还未能将水雷扫爆。“靠近雷区!”吴光怀下令。这是风险极大的开进,在世界海军扫雷史上,水雷炸伤扫雷舰的事故数不胜数。怎么在家里挣钱  【赛迪网讯】非常高兴今天有机会跟大家分享一下我国香港特区电子政务实施的案例。各位现在看到的是直接连线上去,在香港特区电子政务网上面的,这里我是用网通的CD

JSP 2.0下的动态内容缓存

  在Web应用中,内容缓存是最普通的优化技术之一,并且能够很容易地实现。例如,可以使用一个自定义地JSP标签——我们将之命名为<jc:cache>——由<jc:cache>和</jc:cache>将每一个需要被缓存的页面片段封装起来。任何自定义标签可以控制它所包含部分(也即预先封装的页面片段)在何时执行,并且动态输出结果可以被捕获。<jc:cache>标签使得JSP容器(例如Tomcat)只生成内容一次,作为应用程序范围内的JSP变量,来存储每一个缓存片段。每次JSP页面被执行时,自定义标签将缓存页面片段载入而无需再次执行JSP代码来生成输出结果。作为Jakarta工程的一个部分,标签库的开发使用了这项技术。当被缓存内容无需被每一个用户或者请求所定制的时候,它工作的十分良好。
  
  这篇文章对上面描述的技术做了改进,通过使用JSP2.0表达式语言(EL),允许JSP页面为每一个请求和用户定制缓存内容。缓存页面片段可以包含未被JSP容器赋值的JSP表达式,在每一次页面被执行时,由自定义标签来确定这些表达式的值。因此,动态内容的建立被最优化,但是缓存片段可以含有部分由每一个请求使用本机JSP表达式语言产生的内容。通过JSP2.0ELAPI的帮助,Java开发者可以用表达式语言来使之成为可能。
  
  内容缓存VS数据缓存
  
  内容缓存不是唯一的选择。例如,从数据库中提取的数据同样可以被缓存。事实上,由于存储的信息中不包含HTMLmarkup,以及要求较少的内存,数据缓存可能更加高效率。然而在很多情况下,内存缓存更容易实现。假设在某个案例总,一个应用由大量事务对象,占用重要的CPU资源,产生复杂的数据,并且用JSP页面来呈现这些数据。工作一切良好,直到某天突然地服务器的负载增加,需要一个紧急解决方案。这时在事务对象和呈现表达层之间建立一个缓存层,时一个非常不错和有效的方案。但是必须非常快速和流畅地修改缓存动态内容的JSP页面。相对于简单的JSP页面编辑,应用程序的业务逻辑变化通常要求更多的工作量和测试;另外,如果一个页面从多个复合源聚合信息时,Web层仅有少量的改变。问题在于,当缓存信息变得失去时效时,缓存空间需要被释放,而事务对象应该知道何时发生这种情况。然而,选择实现内容缓存还是数据缓存,或者其他的优化技术,有很多不得不考虑的因素,有时是所开发的程序所特殊要求的。
  
  数据缓存和内容缓存没有必要互相排斥,它们可以一起使用。例如,在数据库驱动的应用中;从数据库中提取出来的数据,和呈现该数据的HTML分别被缓存起来。这与使用JSP实时生成的模板有些相似。这篇文章中讨论的基于ELAPI技术说明如何使用JSPEL来将数据载入到呈现模板中。
  
  使用JSP变量缓存动态内容
  
  每当实现一个缓存机制是,都需要一个存储缓存对象的方法,在这篇文章中涉及的是String类型的对象。一种选择是使用一个对象——缓存框架结构,或者使用Javamaps来实现自定义的缓存方案。JSP已经拥有了称为“scopedattributes”或“JSPvariables”来提供ID——object映射,这正是缓存机制所需要的。对于使用page或者requestscope,这是没有意义的,而在应用范围内,这是一个很好的存储缓存内容的位置,因为它被所有的用户和页面共享。当每一个用户需要单独缓存时,Sessionscope也可以被使用,但这不是很有效率。JSTL标签库可以被是与那个来缓存内容,通过使用JSP变量正如下例所示:
  
  
  <%@taglibprefix="c"uri="/java5b02sun5b02com/jsp/jstl/core"%><c:iftest="${emptycachedFragment}"><c:setvar="cachedFragment"scope="application">...</c:set></c:if>
  
  缓存页面片段用下列语句输出结果:
  ${applicationScope.cachedFragment}
  
  当缓存片段需要被每一个请求所定制的时候,到底发生了什么?例如,如果希望包含一个计数器,需要缓存两个片段:
  
  <%@taglibprefix="c"uri="/java5b02sun5b02com/jsp/jstl/core"%><c:iftest="${sessionScope.counter==null}"><c:setvar="counter"scope="session"value="0"/></c:if><c:setvar="counter"value="${counter+1}"scope="session"/><c:iftest="${emptycachedFragment1}"><c:setvar="cachedFragment1"scope="application">...</c:set></c:if><c:iftest="${emptycachedFragment2}"><c:setvar="cachedFragment2"scope="application">...</c:set></c:if>
  
  可以使用下面语句输出缓存内容:
  ${cachedFragment1}${counter}${cachedFragment2}
  
  通过专门的标签库的帮助,需要定制的页面片段的缓存变得异常容易了。上面已经提及,缓存内容可以被开始标签(<jc:cache>)和结尾标签(</jc:cache>)封装起来。而每一个定制可以使用另一个标签(<jc:dynamicexpr="..."/>)输出一个JSP表达式(${...})来表现。动态内容用JSP表达式缓存并在每一次缓存内容被输出时赋值。在下面的部分可以看到这是如何实现的。Counter.jsp缓存了一个包含计数器的页面片段,当每一次用户刷新这个页面的时候计数器会自动+1。
  
  <%@taglibprefix="c"uri="/java5b02sun5b02com/jsp/jstl/core"%><%@taglibprefix="jc"uri="/devsphere5b02com/articles/jspcache"%><c:iftest="${sessionScope.counter==null}"><c:setvar="counter"scope="session"value="0"/></c:if><c:setvar="counter"value="${counter+1}"scope="session"/><jc:cacheid="cachedFragmentWithCounter">...<jc:dynamicexpr="sessionScope.counter"/>...</jc:cache>
  
  JSP变量易于使用,对于简单的Webapps,这是一个不错的内容缓存方案。然而,如果应用程序产生大量的动态内容,没有对缓存大小的控制无疑是一个问题。一种专用的缓存框架结构能够提供一个更加有力的方案,允许对缓存的监视,限制缓存大小,控制缓存策略,等等……
  
  使用JSP2.0表达式语言API
  
  JSP容器(例如Tomcat)对应用ELAPI的JSP页面中的表达式予以赋值,并且可以被Java代码所使用。这允许在Web页面外应用JSPEL作开发,例如,对XML文件、基于文本的资源以及自定义脚本。当需要控制何时对Web页面中的表达式进行赋值或者书写与之相关的表达式时,ELAPI同样是有用的。例如,缓存页面片段可以包含自定义JSP表达式,并且当每一次缓存内容被输出时,ELAPI将用来给这些表达式赋值或者重新赋值。
  文章提供了一个例子程序(参见文末资源部分),这个应用程序包含了一个Java类(JspUtils)和类中包含一个方法eval(),这个方法有三个参数:JSP表达式、表达式的期望类型和一个JSPcontext对象。Eval()方法从JSPcontext中取得ExpressionEvaluator并且调用evaluate()方法,通过表达式、表达式的期望类型、和一个从JSPcongtext中获得的变量。JspUtils.eval()方法返回表达式的值。
  
  packagecom.devsphere.articles.jspcache;importjavax.servlet.jsp.JspContext;importjavax.servlet.jsp.JspException;importjavax.servlet.jsp.PageContext;importjavax.servlet.jsp.el.ELException;importjavax.servlet.jsp.el.ExpressionEvaluator;importjava.io.IOException;publicclassJspUtils{publicstaticObjecteval(Stringexpr,Classtype,JspContextjspContext)throwsJspException{try{if(expr.indexOf("${")==-1)returnexpr;ExpressionEvaluatorevaluator=jspContext.getExpressionEvaluator();returnevaluator.evaluate(expr,type,jspContext.getVariableResolver(),null);}catch(ELExceptione){thrownewJspException(e);}}...}
  注意:JspUtils.eval()主要封装了标准的ExpressionEvaluator。如果expr不包含${,JSPELAPI不被调用,因为没有JSP表达式。
  
  创建标签库描述符(TLD)文件
  
  JSP标签库需要一个标签库描述符(TLD)文件来自定义标签的命名,它们的属性,以及操作该标签的Java类。jspcache.tld描述了两个自定义标签,<jc:cache>拥有两个属性:缓存页面片段的id和JSPscope—JSP页面总需要被储存的内容范围。<jc:dynamic>只有一个属性,即JSP表达式必须在每一次缓存片段被输出时被赋值。TLD文件将这两个自定义标签映射到CacheTag和DynamicTag类,如下所示:
  
  <?xmlversion="1.0"encoding="UTF-8"?><taglibxmlns="/java5b02sun5b02com/xml/ns/j2ee"xmlns:xsi="/www5b02w35b02org/2001/XMLSchema-instance"xsi:schemaLocation="/java5b02sun5b02com/xml/ns/j2eeweb-jsptaglibrary_2_0.xsd"version="2.0"><tlib-version>1.0</tlib-version><short-name>jc</short-name><uri>/devsphere5b02com/articles/jspcache</uri><tag><name>cache</name><tag-class>com.devsphere.articles.jspcache.CacheTag</tag-class><body-content>scriptless</body-content><attribute><name>id</name><required>true</required><rtexprvalue>true</rtexprvalue></attribute><attribute><name>scope</name><required>false</required><rtexprvalue>false</rtexprvalue></attribute></tag><tag><name>dynamic</name><tag-class>com.devsphere.articles.jspcache.DynamicTag</tag-class><body-content>empty</body-content><attribute><name>expr</name><required>true</required><rtexprvalue>false</rtexprvalue></attribute></tag></taglib>
  
  TLD文件包含在Web应用描述符文件(web.xml)中,这五个文件同样包含一个初始参数指出cache是否可用。
  
  <?xmlversion="1.0"encoding="ISO-8859-1"?><web-appxmlns="/java5b02sun5b02com/xml/ns/j2ee"xmlns:xsi="/www5b02w35b02org/2001/XMLSchema-instance"xsi:schemaLocation="/java5b02sun5b02com/xml/ns/j2eeweb-app_2_4.xsd"version="2.4"><context-param><param-name>com.devsphere.articles.jspcache.enabled</param-name><param-value>true</param-value></context-param><jsp-config><taglib><taglib-uri>/devsphere5b02com/articles/jspcache</taglib-uri><taglib-location>/WEB-INF/jspcache.tld</taglib-location></taglib></jsp-config></web-app>
  
  理解<jc:cache>的工作机理
  
  JSP容器为JSP页面中的每一个<jc:cache>标签创建一个CacheTag实例,来对其处理。JSP容器负责调用setJsp()、setParent()和setJspBody()方法,这是CacheTag类从SimpleTagSupport继承而来。JSP容器同事还为所操作标签的每一个属性调用setter方法。SetId()和setScope()方法存储属性值到私有域,这个值已经用CacheTag()构造函数用缺省值初始化。
  
  
  packagecom.devsphere.articles.jspcache;importjavax.servlet.ServletContext;importjavax.servlet.jsp.JspContext;importjavax.servlet.jsp.JspException;importjavax.servlet.jsp.PageContext;importjavax.servlet.jsp.tagext.SimpleTagSupport;importjava.io.IOException;importjava.io.StringWriter;publicclassCacheTagextendsSimpleTagSupport{publicstaticfinalStringCACHE_ENABLED="com.devsphere.articles.jspcache.enabled";privateStringid;privateintscope;privatebooleancacheEnabled;publicCacheTag(){id=null;scope=PageContext.APPLICATION_SCOPE;}publicvoidsetId(Stringid){this.id=id;}publicvoidsetScope(Stringscope){this.scope=JspUtils.checkScope(scope);}...}
  
  setScope()方法调用JspUtils.checkScope()来校验已经从String转换为int类型的scope的属性值。
  
  ...publicclassJspUtils{...publicstaticintcheckScope(Stringscope){if("page".equalsIgnoreCase(scope))returnPageContext.PAGE_SCOPE;elseif("request".equalsIgnoreCase(scope))returnPageContext.REQUEST_SCOPE;elseif("session".equalsIgnoreCase(scope))returnPageContext.SESSION_SCOPE;elseif("application".equalsIgnoreCase(scope))returnPageContext.APPLICATION_SCOPE;elsethrownewIllegalArgumentException("Invalidscope:"+scope);}}
  
  一旦CacheTag实例准备对标签进行操作,JSP容器调用doTag()方法,用getJspContext()来获得JSPcontext。这个对象被造型为PageContext,从而可以调用getServletContext()方法。servletcontext用来获取初始化参数的值,这个值标明缓存机制是否被启用。如果缓存被启用,doTag()尝试使用id和scope属性值来获得缓存页面片段。如果页面片段还没有被缓存,doTag()使用getJspBody().invoke()来执行由<jc:cache>和</jc:cache>封装的JSP代码。由JSPbody产生的输出结果缓冲在StringWriter并且被toStirng()方法获得。这样,doTag()调用JSPcontext的setAttribute()方法新建一个JSP变量,这个变量控制可能包含JSP表达式(${…})的缓存内容。这些表达式在用jspContext.getOut().print()输出内容前,被JspUtils.eval()赋值。只有当缓存被启用的时候,这些行为才发生。否则,doTag()只是通过getJspBody().invoke(null)执行JSPbody并且输出结果不被缓存。
  
  ...publicclassCacheTagextendsSimpleTagSupport{...publicvoiddoTag()throwsJspException,IOException{JspContextjspContext=getJspContext();ServletContextapplication=((PageContext)jspContext).getServletContext();StringcacheEnabledParam=application.getInitParameter(CACHE_ENABLED);cacheEnabled=cacheEnabledParam!=null&&cacheEnabledParam.equals("true");if(cacheEnabled){StringcachedOutput=(String)jspContext.getAttribute(id,scope);if(cachedOutput==null){StringWriterbuffer=newStringWriter();getJspBody().invoke(buffer);cachedOutput=buffer.toString();jspContext.setAttribute(id,cachedOutput,scope);}StringevaluatedOutput=(String)JspUtils.eval(cachedOutput,String.class,jspContext);jspContext.getOut().print(evaluatedOutput);}elsegetJspBody().invoke(null);}...}
  
  注意一个单独的JspUtils.eval()调用给所有的${…}表达式赋值。因为一个包含了大量的${…}结构的text也是一个表达式。每一个缓存片段都可以被当作一个复杂的JSP表达式来进行处理。
  IsCacheEnabled()方法返回cacheEnabled的值,这个值已经被doTag()初始化。
  
  ...publicclassCacheTagextendsSimpleTagSupport{...publicbooleanisCacheEnabled(){returncacheEnabled;}}
  
  <jc:cache>标签允许页面开发者自主选择缓存页面片段的ID。这使得缓存一个页面片段可以被多个JSP页面共享,当需要重用JSP代码时,这是很有用处的。但是仍然需要一些命名协议来避免可能的冲突。通过修改CacheTag类来在自动ID内部包含URL可以避免这种副作用。
  
  理解<jc:dynamic>在做什么
  
  每一个<jc:dynamic>被一个DynamicTag类的实例处理,setExpr()方法将expr属性值存储到一个私有域。DoTag()方法创建JSP表达式,在expr属性值加上${前缀和}后缀。然后,doTag()使用findAncestorWithClass()来查找含有<jc:dynamic>标签元素的<jc:cache>的CacheTaghandler。如果没有查找到或者缓存被禁用,JSP表达式被JspUtils.eval()赋值并且值被输出。否则,doTag()输出无值表达式。
  
  
  packagecom.devsphere.articles.jspcache;importjavax.servlet.jsp.JspException;importjavax.servlet.jsp.tagext.SimpleTagSupport;importjava.io.IOException;publicclassDynamicTagextendsSimpleTagSupport{privateStringexpr;publicvoidsetExpr(Stringexpr){this.expr=expr;}publicvoiddoTag()throwsJspException,IOException{Stringoutput="${"+expr+"}";CacheTagancestor=(CacheTag)findAncestorWithClass(this,CacheTag.class);if(ancestor==null||!ancestor.isCacheEnabled())output=(String)JspUtils.eval(output,String.class,getJspContext());getJspContext().getOut().print(output);}}
  
  分析以上代码,可以注意到<jc:cache>和<jc:dynamic>合作来实现一个尽可能有效率的方案。如果缓存可用,页面片段和由<jc:dynamic>生成并被CacheTag赋值的JSP表达式一起放入缓冲器。如果缓存被禁用,缓冲变得没有意义,<jc:cache>只是执行其JSPbody部分,而让DynamicTag给JSP表达式赋值。禁用缓存有时候是必要的,特别是在开发过程期间出现内容的改变和JSP页面被重新编译的时候。当然,在开发完毕的成品环境中缓存必须被启用。
  
  总结
  
  内容缓存是一种非常易用的改善Web应用性能的方法。这篇文章集中讨论了使用JSP表达式语言来为每一个用户或者请求定制缓存内容。贯穿全文的简单介绍的标签库适合小型Webapps并且可以提升中等应用的性能。对于开发大型企业级应用,则该考虑使用支持更好的缓存机制的框架结构,而不仅是使用JSP变量。但是了解基于ELAPI的定制技术无疑是不无裨益的。

分享到:
本文"JSP 2.0下的动态内容缓存"由远航站长收集整理而来,仅供大家学习与参考使用。更多AG亚游集团尽在远航站长站。
顶一下
(0)
0%
踩一下
(0)
0%
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 密码: 验证码:
关于本站 - 联系我们 - 广告合作 - 网站声明 - 友情连接- 网站地图 - 站点地图 - 返回顶部
Copyright © 2007-2013 www.yhzhan.com(AG亚游集团). All Rights Reserved .
远航站长:为中小站长提供最佳的学习与交流平台,提供网页制作与网站编程等各类AG亚游集团.
官方QQ:445490277 网站群:26680406 网站备案号:豫ICP备07500620号-4
贵州毕节检方大数据辅助办案 1分钟确定量刑意见 中国一声令下 美国自由女神像要被垃圾“埋”了 电解铝去产能继续推进 铝价短期承压 塔尔德利:很希望给佩莱创造机会 让他也获得进球 俄前双面间谍遭神经毒剂谋杀未遂 英警察同样病倒 皇马有名不输给C罗的核心 有他没他完全两个队 [新浪彩票]足彩18057期大势:曼城主胜可期 一季度全球并购超万亿美元 科技并购如何规避风险? 曝阿森纳切尔西都放弃前巴萨主帅!嫌他傲慢自大 应对美国制裁 伊朗拟激励私人投资者接管国家项目 综述:创业板三连阳上证50续跌 鲍威尔证词偏“鹰” 央视:惠若琪退役张常宁还难扛大旗 曾春蕾回春
雪乡旅游质量如何提升?官方这样说 政协委员:“雾霾”说法不准确 建议改为“灰霾” 美禁令或切断安卓系统授权 操作系统国产化迎来机遇 达拉斯联储主席:支持加息三次 贸易战爆发言之尚早 比尔-盖茨:特朗普分不清HIV和HPV 还想反疫苗 拼多多遭遇“假货”风波 律师:平台有审核不严之过 政协委员:职业女性遇婚姻危机时 社会应予关注 老照片再现周恩来总理一生的120个瞬间(图) 伊朗ATR-72型失事客机黑匣子将被送往法国解码 皇马给C罗下死命令:最多冲刺12米 到禁区前再跑 这一危机还未爆发欧元已连连败退 意大利股债双跌 欧盟天价罚单致谷歌母公司利润下降9%
委内瑞拉通胀将达1000000% 百万富翁仅买一公斤肉 安全工程师:谷歌两步验证保护普及率不足10% 47岁老米成为最年长世锦赛冠军 自信50胜没问题 高盛建议抛售特斯拉 马斯克推特喊话:下注吧 新浪彩票名家双色球第18056期推荐汇总 女子乘飞机过不了安检 只因她这款“网红”手机壳 快手主播被拘:直播中和多名女网民聊天 内容淫秽 男子见出轨妻子与同村男勾肩搭背 杀妻抛尸化粪池 巨头纷纷布局区块链 风口之上谁将领跑? 挣钱最多的行业 白手起家好项目 养牛挣钱为什么没人养 女生最吃香的十大职业 AG亚游集团