华易动力天津网站制作公司 免费咨询热线:400-0022-823 电话:022-28261501 28335110
地址:天津市河西区围堤道146号华盛广场B座22楼
点击这里给我发消息  点击这里给我发消息  点击这里给我发消息
  首页 关于我们 建网站:从这里开始 域名空间服务 案例展示 新闻中心 网站制作知识 网站设计技巧 人才招聘 联系我们  
  网站设计技巧
设计技巧
天津网站制作方案 更多..
如何获得了大量的好的外链资源
常见的长尾关键词组合方法
如何利用百度平台获取流量
营销优化型网站的简介
新顶级域名的形式
网站建设分为如下八步
网站专题设计
关于门户网站设计方案
华易动力售后服务规范
服务器机房介绍
优秀客户展示(排名无先后) 更多..
环化(天津)燃气设备安装有限公司
天津市国信投资担保有限公司
天津城市职业学院
天津大学EMBA
南开大学CID
北京同仁堂天津药店有限公司
金权道(天津)餐饮管理有限公司
天津市友好合作城市企业促进会
天津天大求实电力新技术股份有限公司
天津红旗农贸投资控股有限公司
天津二人锅餐饮管理有限公司
天津市滨湖剧院
渤海产业投资基金管理有限公司
天津天富软管工业有限公司
新闻中心-行业资讯 更多..
· 企业为什么要做APP   2016-11-24
· 如何搭配网站   2016-11-16
· css表单布局的五个小技巧   2016-11-16
· 服务器维护的重点   2016-11-14
· 如何建设你想要的网站类型   2016-11-14
· 阿里设计师用这2方法提升设计创新力   2016-11-3
· 我们看过的各种设计理论法则   2016-11-3
· Windows 8设计时的安全性   2013-12-27
· 未来网站技术的发展   2013-10-12
· 企业网站建设规划   2013-7-30
全部信息 当前位置:首页网站设计技巧 → 详细内容
JSP 2.0下的动态内容缓存方法介绍
更新时间:2012/8/9 点击:1018次
在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="http://java.sun.com/jsp/jstl/core"%><c:iftest="${emptycachedFragment}"><c:setvar="cachedFragment"scope="application">...</c:set></c:if>
  
  缓存页面片段用下列语句输出结果:
  ${applicationScope.cachedFragment}
  
  当缓存片段需要被每一个请求所定制的时候,到底发生了什么?例如,如果希望包含一个计数器,需要缓存两个片段:
  
  <%@taglibprefix="c"uri="http://java.sun.com/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="http://java.sun.com/jsp/jstl/core"%><%@taglibprefix="jc"uri="http://devsphere.com/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="http://java.sun.com/xml/ns/j2ee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/j2eeweb-jsptaglibrary_2_0.xsd"version="2.0"><tlib-version>1.0</tlib-version><short-name>jc</short-name><uri>http://devsphere.com/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="http://java.sun.com/xml/ns/j2ee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/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>http://devsphere.com/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的定制技术无疑是不无裨益的。
 
  公司总机:022-28261501 28335110
客户服务邮箱:service@nfree.cn,手机直线:15822335163
CopyRight 2006~2024 All Rights Reserved 天津市华易动力信息科技有限公司
地址:天津市河西区围堤道146号华盛广场B座22楼 邮编:300202

津公网安备 12010302001042号


在线QQ(售前咨询)
点击这里给我发消息  点击这里给我发消息  点击这里给我发消息