专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »Java教程 » jsp缓存:JSP 2.0下的动态内容缓存Cache分析讲解 »正文

jsp缓存:JSP 2.0下的动态内容缓存Cache分析讲解

来源: 发布时间:星期四, 2008年12月18日 浏览:78次 评论:0
在Web应用中内容缓存Cache是最普通优化技术的并且能够很容易地实现例如可以使用个自定义地JSP标签——我们将的命名为<jc:cache>——由<jc:cache>和</jc:cache>将每个需要被缓存Cache页面片段封装起来任何自定义标签可以控制它所包含部分 (也即预先封装页面片段)在何时执行并且动态输出结果可以被捕获<jc:cache>标签使得JSP容器(例如Tomcat)只生成内容作为应用范围内JSP变量来存储每个缓存Cache片段每次JSP页面被执行时自定义标签将缓存Cache页面片段载入而无需再次执行JSP代码来生成输出结果作为Jakarta工程个部分标签库开发使用了这项技术当被缓存Cache内容无需被每个用户或者请求所定制时候它工作十分良好
  
  这篇文章对上面描述技术做了改进通过使用JSP 2.0表达式语言(EL)允许JSP页面为每个请求和用户定制缓存Cache内容缓存Cache页面片段可以包含未被JSP容器赋值JSP表达式在每次页面被执行时由自定义标签来确定这些表达式因此动态内容建立被最优化但是缓存Cache片段可以含有部分由每个请求使用本机JSP表达式语言产生内容通过JSP 2.0 EL API帮助Java开发者可以用表达式语言来使的成为可能
  
  内容缓存CacheVS数据缓存Cache
  
  内容缓存Cache不是唯选择例如 从数据库中提取数据同样可以被缓存Cache事实上由于存储信息中不包含HTML markup以及要求较少内存数据缓存Cache可能更加高效率然而在很多情况下内存缓存Cache更容易实现假设在某个案例总个应用由大量事务对象占用重要CPU资源产生复杂数据并且用JSP页面来呈现这些数据工作切良好直到某天突然地服务器负载增加需要个紧急解决方案这时在事务对象和呈现表达层的间建立个缓存Cache层个非常不错和有效方案但是必须非常快速和流畅地修改缓存Cache动态内容JSP页面相对于简单JSP页面编辑应用业务逻辑变化通常要求更多工作量和测试;另外如果个页面从多个复合源聚合信息时Web层仅有少量改变问题在于当缓存Cache信息变得失去时效时缓存Cache空间需要被释放而事务对象应该知道何时发生这种情况然而选择实现内容缓存Cache还是数据缓存Cache或者其他优化技术有很多不得不考虑原因有时是所开发所特殊要求
  
  数据缓存Cache和内容缓存Cache没有必要互相排斥它们可以起使用例如在数据库驱动应用中;从数据库中提取出来数据和呈现该数据HTML分别被缓存Cache起来这和使用JSP实时生成模板有些相似这篇文章中讨论基于EL API技术介绍说明如何使用JSP EL来将数据载入到呈现模板中
  
  使用JSP变量缓存Cache动态内容
  
  每当实现个缓存Cache机制是都需要个存储缓存Cache对象思路方法在这篇文章中涉及是String类型对象 种选择是使用个对象——缓存Cache框架结构或者使用Java maps来实现自定义缓存Cache方案JSP已经拥有了称为“scoped attributes”或“JSP variables”来提供ID——object映射这正是缓存Cache机制所需要对于使用page或者request scope这是没有意义而在应用范围内这是个很好存储缓存Cache内容位置 它被所有用户和页面共享当每个用户需要单独缓存Cache时Session scope也可以被使用但这不是很有效率JSTL标签库可以被是和那个来缓存Cache内容通过使用JSP变量正如下例所示:
  
  <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><c: test="${empty cachedFragment}">
  <c: var="cachedFragment" scope="application">
  ...
  </c:></c:
  
  缓存Cache页面片段用下列语句输出结果:
  ${applicationScope.cachedFragment}
  
  当缓存Cache片段需要被每个请求所定制时候到底发生了什么?例如如果希望包含个计数器需要缓存Cache两个片段:
  <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><c: test="${sessionScope.counter null}">  <c: var="counter" scope="session" value="0"/></c:><c: var="counter" value="${counter+1}" scope="session"/><c: test="${empty cachedFragment1}">
  <c: var="cachedFragment1" scope="application">
  ...
  </c:></c:><c: test="${empty cachedFragment2}">
  <c: var="cachedFragment2" scope="application">
  ...
  </c:></c:
  
  可以使用下面语句输出缓存Cache内容:
  ${cachedFragment1} ${counter} ${cachedFragment2}
  
  通过专门标签库帮助需要定制页面片段缓存Cache变得异常容易了上面已经提及缓存Cache内容可以被开始标签(<jc:cache>)和结尾标签(</jc:cache>)封装起来而每个定制可以使用另个标签(<jc:dynamic expr="..."/>)输出个JSP表达式(${...})来表现动态内容用JSP表达式缓存Cache并在每次缓存Cache内容被输出时赋值在下面部分可以看到这是如何实现Counter.jsp缓存Cache了个包含计数器页面片段当每次用户刷新这个页面时候计数器会自动+1
  <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@ taglib prefix="jc" uri="http://devsphere.com/articles/jspcache" %><c: test="${sessionScope.counter null}">
  <c: var="counter" scope="session" value="0"/></c:><c: var="counter" value="${counter+1}" scope="session"/><jc:cache id="cachedFragmentWithCounter">
  ... <jc:dynamic expr="sessionScope.counter"/>
  ...</jc:cache>
  
  JSP 变量易于使用对于简单Web apps这是个不错内容缓存Cache方案然而如果应用产生大量动态内容没有对缓存Cache大小控制无疑是个问题种专用缓存Cache框架结构能够提供个更加有力方案允许对缓存Cache监视限制缓存Cache大小控制缓存Cache策略等等……
  
  使用JSP 2.0表达式语言API
  
  JSP容器(例如Tomcat)对应用EL APIJSP页面中表达式予以赋值并且可以被Java代码所使用这允许在Web页面外应用JSP EL作开发例如对XML文件、基于文本资源以及自定义脚本当需要控制何时对Web页面中表达式进行赋值或者书写和的相关表达式时EL API同样是有用例如缓存Cache页面片段可以包含自定义JSP表达式并且当每次缓存Cache内容被输出时EL API将用来给这些表达式赋值或者重新赋值
  
  文章提供了个例子(参见文末资源部分)这个应用包含了个Java类(JspUtils)和类中包含个思路方法eval这个思路方法有 3个参数:JSP表达式、表达式期望类型和个JSP context对象Eval思路方法从JSP context中取得ExpressionEvaluator并且evaluate思路方法通过表达式、表达式期望类型、和个从JSP congtext中获得变量JspUtils.eval思路方法返回表达式
  package com.devsphere.articles.jspcache;
  import javax.servlet.jsp.JspContext;
  import javax.servlet.jsp.JspException;
  import javax.servlet.jsp.PageContext;
  import javax.servlet.jsp.el.ELException;
  import javax.servlet.jsp.el.ExpressionEvaluator;
  import java.io.IOException;public JspUtils {
  public Object eval(
  String expr, Class type, JspContext jspContext)
  throws JspException {
  try {
   (expr.indexOf("${") -1)
   expr;
  ExpressionEvaluator evaluator
  = jspContext.getExpressionEvaluator;
   evaluator.evaluate(expr, type,
  jspContext.getVariableResolver, null);
  } catch (ELException e) {
  throw JspException(e);
  }
  }
  ...}
  
  注意:JspUtils.eval主要封装了标准ExpressionEvaluator如果expr不包含${JSP EL API不被没有JSP表达式 创建标签库描述符(TLD)文件
  
  JSP标签库需要个标签库描述符(TLD)文件来自定义标签命名它们属性以及操作该标签Java类jspcache.tld描述了两个自定义标签<jc:cache>拥有两个属性:缓存Cache页面片段id和JSP scope—JSP页面总需要被储存内容范围<jc:dynamic>只有个属性即JSP表达式必须在每次缓存Cache片段被输出时被赋值TLD文件将这两个自定义标签映射到CacheTag和DynamicTag类如下所示:
  <?xml version="1.0" encoding="UTF-8" ?><taglib xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd"
  version="2.0">
  <tlib-version>1.0</tlib-version>
  <-name>jc</-name>
  <uri>http://devsphere.com/articles/jspcache</uri>
  <tag>
  <name>cache</name>
  <tag->com.devsphere.articles.jspcache.CacheTag</tag-
  <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->com.devsphere.articles.jspcache.DynamicTag</tag-
  <body-content>empty</body-content>
  <attribute>
  <name>expr</name>
  <required>true</required>
  <rtexprvalue>false</rtexprvalue>
  </attribute>
  </tag></taglib>
  
  TLD文件包含在Web应用描述符文件(web.xml)中这 5个文件同样包含参数指出cache是否可用
  <?xml version="1.0" encoding="ISO-8859-1"?><web-app xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-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容器负责JspParentJspBody思路方法这是CacheTag类从SimpleTagSupport继承而来JSP容器同事还为所操作标签个属性ter思路方法SetIdScope思路方法存储属性值到私有域这个值已经用CacheTag构造用缺省值
  
  package com.devsphere.articles.jspcache;
  import javax.servlet.ServletContext;
  import javax.servlet.jsp.JspContext;
  import javax.servlet.jsp.JspException;
  import javax.servlet.jsp.PageContext;
  import javax.servlet.jsp.tagext.SimpleTagSupport;
  import java.io.IOException;import java.io.StringWriter;
  public CacheTag extends SimpleTagSupport {
  public final String CACHE_ENABLED
  = "com.devsphere.articles.jspcache.enabled";
  private String id;
  private scope;
  private boolean cacheEnabled;  public CacheTag {
  id = null;    scope = PageContext.APPLICATION_SCOPE;
  }  public void Id(String id) {
  this.id = id;
  }  public void Scope(String scope) {
  this.scope = JspUtils.checkScope(scope);
  }
  ...}
  
  Scope思路方法JspUtils.checkScope来校验已经从String转换为类型scope属性值
  ...public JspUtils {
  ...
  public checkScope(String scope) {
   ("page".equalsIgnoreCase(scope))
   PageContext.PAGE_SCOPE;
   ("request".equalsIgnoreCase(scope))
   PageContext.REQUEST_SCOPE;
   ("session".equalsIgnoreCase(scope))
   PageContext.SESSION_SCOPE;
   ("application".equalsIgnoreCase(scope))
   PageContext.APPLICATION_SCOPE;
  
  throw IllegalArgumentException(
  "Invalid scope: " + scope);
  }}
  
  旦CacheTag例子准备对标签进行操作JSP容器doTag思路方法用getJspContext来获得JSP context这个对象被造型为PageContext,从而可以getServletContext思路方法servlet context用来获取化参数这个值标明缓存Cache机制是否被启用如果缓存Cache被启用doTag尝试使用id和scope属性值来获得缓存Cache页面片段如果页面片段还没有被缓存CachedoTag使用getJspBody.invoke来执行由<jc:cache>和</jc:cache>封装JSP代码由JSP body产生输出结果缓冲在StringWriter并且被toStirng思路方法获得这样doTagJSP contextAttribute思路方法新建个JSP变量这个变量控制可能包含JSP表达式(${…})缓存Cache内容这些表达式在用jspContext.getOut.pr输出内容前被JspUtils.eval赋值只有当缓存Cache被启用时候这些行为才发生否则doTag只是通过getJspBody.invoke(null)执行JSP body并且输出结果不被缓存Cache

  ...public CacheTag extends SimpleTagSupport {
  ...
  public void doTag throws JspException, IOException {
  JspContext jspContext = getJspContext;
  ServletContext application
  = ((PageContext) jspContext).getServletContext;
  String cacheEnabledParam
  = application.getInitParameter(CACHE_ENABLED);
  cacheEnabled = cacheEnabledParam != null
  && cacheEnabledParam.equals("true");
   (cacheEnabled) {
  String cachedOutput
  = (String) jspContext.getAttribute(id, scope);
   (cachedOutput null) {
  StringWriter buffer = StringWriter;
  getJspBody.invoke(buffer);
  cachedOutput = buffer.toString;
  jspContext.Attribute(id, cachedOutput, scope);
  }      String evaluatedOutput = (String) JspUtils.eval(
  cachedOutput, String., jspContext);
  jspContext.getOut.pr(evaluatedOutput);
  }
  getJspBody.invoke(null);
  }
  ...}
  
  注意个单独JspUtils.eval给所有${…} 表达式赋值个包含了大量${…}结构text也是个表达式个缓存Cache片段都可以被当作个复杂JSP表达式来进行处理
  
  IsCacheEnabled思路方法返回cacheEnabled这个值已经被doTag
  ...public CacheTag extends SimpleTagSupport {
  ...  public boolean isCacheEnabled {
   cacheEnabled;
  }}
  
  <jc:cache>标签允许页面开发者自主选择缓存Cache页面片段ID这使得缓存Cache个页面片段可以被多个JSP页面共享当需要重用JSP代码时这是很有用处但是仍然需要些命名协议来避免可能冲突通过修改CacheTag类来在自动ID内部包含URL可以避免这种副作用
  
  理解<jc:dynamic>在做什么
  
  每个<jc:dynamic>被个DynamicTag类例子处理Expr思路方法将expr属性值存储到个私有域DoTag思路方法创建JSP表达式在expr属性值加上${前缀和}后缀然后doTag使用findAncestorWithClass来查找含有<jc:dynamic>标签元素<jc:cache>CacheTag handler如果没有查找到或者缓存Cache被禁用JSP表达式被JspUtils.eval赋值并且值被输出否则doTag输出无值表达式
  
  package com.devsphere.articles.jspcache;
  import javax.servlet.jsp.JspException;
  import javax.servlet.jsp.tagext.SimpleTagSupport;
  import java.io.IOException;
  public DynamicTag extends SimpleTagSupport {
  private String expr;
  public void Expr(String expr) {
  this.expr = expr;
  }  public void doTag throws JspException, IOException {
  String output = "${" + expr + "}";
  CacheTag ancestor = (CacheTag) findAncestorWithClass(
  this, CacheTag.);
   (ancestor null || !ancestor.isCacheEnabled)
  output = (String) JspUtils.eval(
  output, String., getJspContext);
  getJspContext.getOut.pr(output);
  }}
  
  分析以上代码可以注意到<jc:cache>和<jc:dynamic>合作来实现个尽可能有效率方案如果缓存Cache可用页面片段和由<jc:dynamic>生成并被CacheTag赋值JSP表达式起放入缓冲器如果缓存Cache被禁用缓冲变得没有意义<jc:cache>只是执行其JSP body部分而让DynamicTag给JSP表达式赋值禁用缓存Cache有时候是必要特别是在开发过程期间出现内容改变和JSP页面被重新编译时候当然在开发完毕成品环境中缓存Cache必须被启用
  
  整理总结
  
  内容缓存Cache是种非常易用改善Web应用性能思路方法这篇文章集中讨论了使用JSP表达式语言来为每个用户或者请求定制缓存Cache内容贯穿全文简单介绍标签库适合小型Web apps并且可以提升中等应用性能对于开发大型企业级应用则该考虑使用支持更好缓存Cache机制框架结构而不仅是使用JSP变量但是了解基于EL API定制技术无疑是不无裨益






TAG: jsp JSP

0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: