亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JSP > 正文

JSP 2.0下的動態內容緩存分析講解

2024-09-05 00:19:33
字體:
來源:轉載
供稿:網友


在web應用中,內容緩存是最普通的優化技術之一,并且能夠很容易地實現。例如,可以使用一個自定義地jsp標簽——我們將之命名為<jc:cache>——由<jc:cache>和</jc:cache>將每一個需要被緩存的頁面片段封裝起來。任何自定義標簽可以控制它所包含部分 (也即預先封裝的頁面片段)在何時執行,并且動態輸出結果可以被捕獲。<jc:cache>標簽使得jsp容器(例如tomcat)只生成內容一次,作為應用程序范圍內的jsp變量,來存儲每一個緩存片段。每次jsp頁面被執行時,自定義標簽將緩存頁面片段載入而無需再次執行jsp代碼來生成輸出結果。作為jakarta工程的一個部分,標簽庫的開發使用了這項技術。當被緩存內容無需被每一個用戶或者請求所定制的時候,它工作的十分良好。
  
  這篇文章對上面描述的技術做了改進,通過使用jsp 2.0表達式語言(el),允許jsp頁面為每一個請求和用戶定制緩存內容。緩存頁面片段可以包含未被jsp容器賦值的jsp表達式,在每一次頁面被執行時,由自定義標簽來確定這些表達式的值。因此,動態內容的建立被最優化,但是緩存片段可以含有部分由每一個請求使用本機jsp表達式語言產生的內容。通過jsp 2.0 el api的幫助,java開發者可以用表達式語言來使之成為可能。
  
  內容緩存vs數據緩存
  
  內容緩存不是唯一的選擇。例如, 從數據庫中提取的數據同樣可以被緩存。事實上,由于存儲的信息中不包含html markup,以及要求較少的內存,數據緩存可能更加高效率。然而在很多情況下,內存緩存更容易實現。假設在某個案例總,一個應用由大量事務對象,占用重要的cpu資源,產生復雜的數據,并且用jsp頁面來呈現這些數據。工作一切良好,直到某天突然地服務器的負載增加,需要一個緊急解決方案。這時在事務對象和呈現表達層之間建立一個緩存層,時一個非常不錯和有效的方案。但是必須非??焖俸土鲿车匦薷木彺鎰討B內容的jsp頁面。相對于簡單的jsp頁面編輯,應用程序的業務邏輯變化通常要求更多的工作量和測試;另外,如果一個頁面從多個復合源聚合信息時,web層僅有少量的改變。問題在于,當緩存信息變得失去時效時,緩存空間需要被釋放,而事務對象應該知道何時發生這種情況。然而,選擇實現內容緩存還是數據緩存,或者其他的優化技術,有很多不得不考慮的因素,有時是所開發的程序所特殊要求的。
  
  數據緩存和內容緩存沒有必要互相排斥,它們可以一起使用。例如,在數據庫驅動的應用中;從數據庫中提取出來的數據,和呈現該數據的html分別被緩存起來。這與使用jsp實時生成的模板有些相似。這篇文章中討論的基于el api技術說明如何使用jsp el來將數據載入到呈現模板中。
  
  使用jsp變量緩存動態內容
  
  每當實現一個緩存機制是,都需要一個存儲緩存對象的方法,在這篇文章中涉及的是string類型的對象。 一種選擇是使用一個對象——緩存框架結構,或者使用java maps來實現自定義的緩存方案。jsp已經擁有了稱為“scoped attributes”或“jsp variables”來提供id——object映射,這正是緩存機制所需要的。對于使用page或者request scope,這是沒有意義的,而在應用范圍內,這是一個很好的存儲緩存內容的位置, 因為它被所有的用戶和頁面共享。當每一個用戶需要單獨緩存時,session scope也可以被使用,但這不是很有效率。jstl標簽庫可以被是與那個來緩存內容,通過使用jsp變量正如下例所示:
  
  <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><c:if test="${empty cachedfragment}">
 ?。糲:set var="cachedfragment" scope="application">
  ...
  </c:set></c:if>
  
  緩存頁面片段用下列語句輸出結果:
  ${applicationscope.cachedfragment}
  
  當緩存片段需要被每一個請求所定制的時候,到底發生了什么?例如,如果希望包含一個計數器,需要緩存兩個片段:
 ?。?@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><c:if test="${sessionscope.counter == null}">  <c:set var="counter" scope="session" value="0"/></c:if><c:set var="counter" value="${counter+1}" scope="session"/><c:if test="${empty cachedfragment1}">
 ?。糲:set var="cachedfragment1" scope="application">
  ...
  </c:set></c:if><c:if test="${empty cachedfragment2}">
 ?。糲:set var="cachedfragment2" scope="application">
  ...
  </c:set></c:if>
  
  可以使用下面語句輸出緩存內容:
  ${cachedfragment1} ${counter} ${cachedfragment2}
  
  通過專門的標簽庫的幫助,需要定制的頁面片段的緩存變得異常容易了。上面已經提及,緩存內容可以被開始標簽(<jc:cache>)和結尾標簽(</jc:cache>)封裝起來。而每一個定制可以使用另一個標簽(<jc:dynamic expr="..."/>)輸出一個jsp表達式(${...})來表現。動態內容用jsp表達式緩存并在每一次緩存內容被輸出時賦值。在下面的部分可以看到這是如何實現的。counter.jsp緩存了一個包含計數器的頁面片段,當每一次用戶刷新這個頁面的時候計數器會自動+1。
 ?。?@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@ taglib prefix="jc" uri="http://devsphere.com/articles/jspcache" %><c:if test="${sessionscope.counter == null}">
 ?。糲:set var="counter" scope="session" value="0"/></c:if><c:set var="counter" value="${counter+1}" scope="session"/><jc:cache id="cachedfragmentwithcounter">
  ... <jc:dynamic expr="sessionscope.counter"/>
  ...</jc:cache>
  
  jsp 變量易于使用,對于簡單的web apps,這是一個不錯的內容緩存方案。然而,如果應用程序產生大量的動態內容,沒有對緩存大小的控制無疑是一個問題。一種專用的緩存框架結構能夠提供一個更加有力的方案,允許對緩存的監視,限制緩存大小,控制緩存策略,等等……
  
  使用jsp 2.0表達式語言api
  
  jsp容器(例如tomcat)對應用el api的jsp頁面中的表達式予以賦值,并且可以被java代碼所使用。這允許在web頁面外應用jsp el作開發,例如,對xml文件、基于文本的資源以及自定義腳本。當需要控制何時對web頁面中的表達式進行賦值或者書寫與之相關的表達式時,el api同樣是有用的。例如,緩存頁面片段可以包含自定義jsp表達式,并且當每一次緩存內容被輸出時,el api將用來給這些表達式賦值或者重新賦值。
  
  文章提供了一個例子程序(參見文末資源部分),這個應用程序包含了一個java類(jsputils)和類中包含一個方法eval(),這個方法有三個參數: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 class jsputils {
  public static object eval(
  string expr, class type, jspcontext jspcontext)
  throws jspexception {
  try {
  if (expr.indexof("${") == -1)
  return expr;
  expressionevaluator evaluator
  = jspcontext.getexpressionevaluator();
  return evaluator.evaluate(expr, type,
  jspcontext.getvariableresolver(), null);
  } catch (elexception e) {
  throw new jspexception(e);
  }
  }
  ...}
  
  注意:jsputils.eval()主要封裝了標準的expressionevaluator。如果expr不包含${,jsp el api不被調用,因為沒有jsp表達式。

創建標簽庫描述符(tld)文件
  
  jsp標簽庫需要一個標簽庫描述符(tld)文件來自定義標簽的命名,它們的屬性,以及操作該標簽的java類。jspcache.tld描述了兩個自定義標簽,<jc:cache>擁有兩個屬性:緩存頁面片段的id和jsp scope—jsp頁面總需要被儲存的內容范圍。<jc:dynamic>只有一個屬性,即jsp表達式必須在每一次緩存片段被輸出時被賦值。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>
 ?。約hort-name>jc</short-name>
 ?。紆ri>http://devsphere.com/articles/jspcache</uri>
  <tag>
 ?。糿ame>cache</name>
 ?。紅ag-class>com.devsphere.articles.jspcache.cachetag</tag-class>
  <body-content>scriptless</body-content>
 ?。糰ttribute>
  <name>id</name>
 ?。紃equired>true</required>
 ?。紃texprvalue>true</rtexprvalue>
 ?。?attribute>
 ?。糰ttribute>
  <name>scope</name>
 ?。紃equired>false</required>
  <rtexprvalue>false</rtexprvalue>
 ?。?attribute>
  </tag>
 ?。紅ag>
 ?。糿ame>dynamic</name>
  <tag-class>com.devsphere.articles.jspcache.dynamictag</tag-class>
 ?。糱ody-content>empty</body-content>
 ?。糰ttribute>
  <name>expr</name>
 ?。紃equired>true</required>
 ?。紃texprvalue>false</rtexprvalue>
  </attribute>
 ?。?tag></taglib>
  
  tld文件包含在web應用描述符文件(web.xml)中,這五個文件同樣包含一個初始參數指出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">
 ?。糲ontext-param>
  <param-name>com.devsphere.articles.jspcache.enabled</param-name>
 ?。紁aram-value>true</param-value>
  </context-param>
 ?。糺sp-config>
  <taglib>
 ?。紅aglib-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()構造函數用缺省值初始化。
  
  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 class cachetag extends simpletagsupport {
  public static final string cache_enabled
  = "com.devsphere.articles.jspcache.enabled";
  private string id;
  private int scope;
  private boolean cacheenabled;  public cachetag() {
  id = null;    scope = pagecontext.application_scope;
  }  public void setid(string id) {
  this.id = id;
  }  public void setscope(string scope) {
  this.scope = jsputils.checkscope(scope);
  }
  ...}
  
  setscope()方法調用jsputils.checkscope()來校驗已經從string轉換為int類型的scope的屬性值。
  ...public class jsputils {
  ...
  public static int checkscope(string scope) {
  if ("page".equalsignorecase(scope))
  return pagecontext.page_scope;
  else if ("request".equalsignorecase(scope))
  return pagecontext.request_scope;
  else if ("session".equalsignorecase(scope))
  return pagecontext.session_scope;
  else if ("application".equalsignorecase(scope))
  return pagecontext.application_scope;
  else
  throw new illegalargumentexception(
  "invalid scope: " + scope);
  }}
  
  一旦cachetag實例準備對標簽進行操作,jsp容器調用dotag()方法,用getjspcontext()來獲得jsp context。這個對象被造型為pagecontext,從而可以調用getservletcontext()方法。servlet context用來獲取初始化參數的值,這個值標明緩存機制是否被啟用。如果緩存被啟用,dotag()嘗試使用id和scope屬性值來獲得緩存頁面片段。如果頁面片段還沒有被緩存,dotag()使用getjspbody().invoke()來執行由<jc:cache>和</jc:cache>封裝的jsp代碼。由jsp body產生的輸出結果緩沖在stringwriter并且被tostirng()方法獲得。這樣,dotag()調用jsp context的setattribute()方法新建一個jsp變量,這個變量控制可能包含jsp表達式(${…})的緩存內容。這些表達式在用jspcontext.getout().print()輸出內容前,被jsputils.eval()賦值。只有當緩存被啟用的時候,這些行為才發生。否則,dotag()只是通過getjspbody().invoke(null)執行jsp body并且輸出結果不被緩存。

  ...public class 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");
  if (cacheenabled) {
  string cachedoutput
  = (string) jspcontext.getattribute(id, scope);
  if (cachedoutput == null) {
  stringwriter buffer = new stringwriter();
  getjspbody().invoke(buffer);
  cachedoutput = buffer.tostring();
  jspcontext.setattribute(id, cachedoutput, scope);
  }      string evaluatedoutput = (string) jsputils.eval(
  cachedoutput, string.class, jspcontext);
  jspcontext.getout().print(evaluatedoutput);
  } else
  getjspbody().invoke(null);
  }
  ...}
  
  注意一個單獨的jsputils.eval()調用給所有的${…} 表達式賦值。因為一個包含了大量的${…}結構的text也是一個表達式。每一個緩存片段都可以被當作一個復雜的jsp表達式來進行處理。
  
  iscacheenabled()方法返回cacheenabled的值,這個值已經被dotag()初始化。
  ...public class cachetag extends simpletagsupport {
  ...  public boolean iscacheenabled() {
  return cacheenabled;
  }}
  
 ?。糺c:cache>標簽允許頁面開發者自主選擇緩存頁面片段的id。這使得緩存一個頁面片段可以被多個jsp頁面共享,當需要重用jsp代碼時,這是很有用處的。但是仍然需要一些命名協議來避免可能的沖突。通過修改cachetag類來在自動id內部包含url可以避免這種副作用。
  
  理解<jc:dynamic>在做什么
  
  每一個<jc:dynamic>被一個dynamictag類的實例處理,setexpr()方法將expr屬性值存儲到一個私有域。dotag()方法創建jsp表達式,在expr屬性值加上${前綴和}后綴。然后,dotag()使用findancestorwithclass()來查找含有<jc:dynamic>標簽元素的<jc:cache>的cachetag handler。如果沒有查找到或者緩存被禁用,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 class dynamictag extends simpletagsupport {
  private string expr;
  public void setexpr(string expr) {
  this.expr = expr;
  }  public void dotag() throws jspexception, ioexception {
  string output = "${" + expr + "}";
  cachetag ancestor = (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>只是執行其jsp body部分,而讓dynamictag給jsp表達式賦值。禁用緩存有時候是必要的,特別是在開發過程期間出現內容的改變和jsp頁面被重新編譯的時候。當然,在開發完畢的成品環境中緩存必須被啟用。
  
  總結
  
  內容緩存是一種非常易用的改善web應用性能的方法。這篇文章集中討論了使用jsp表達式語言來為每一個用戶或者請求定制緩存內容。貫穿全文的簡單介紹的標簽庫適合小型web apps并且可以提升中等應用的性能。對于開發大型企業級應用,則該考慮使用支持更好的緩存機制的框架結構,而不僅是使用jsp變量。但是了解基于el api的定制技術無疑是不無裨益的。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
色婷婷综合成人| 91亚洲国产成人精品性色| 日本电影亚洲天堂| 成人a级免费视频| 中文字幕久热精品视频在线| 欧美激情视频三区| 色综合色综合网色综合| 亚洲欧美成人在线| 亚洲欧美另类自拍| 国语自产精品视频在免费| 久久精品视频99| 国产精品欧美激情| 九九九热精品免费视频观看网站| 国内精品视频一区| 亚洲精品理论电影| 欧美性精品220| 亚洲人免费视频| 久热精品视频在线观看一区| 欧美成在线视频| 成人激情综合网| 2019中文字幕免费视频| 精品无人区乱码1区2区3区在线| 日韩中文字幕在线免费观看| 久久影院中文字幕| 国产一区二区三区毛片| 在线性视频日韩欧美| 日本免费久久高清视频| 国产一区二区三区在线播放免费观看| 精品亚洲一区二区三区四区五区| 91精品国产乱码久久久久久蜜臀| 狠狠色狠狠色综合日日小说| 欧美成人在线网站| 69影院欧美专区视频| 亚洲精品国产综合久久| 欧美激情va永久在线播放| 国内成人精品视频| 欧美成人黄色小视频| 国产精品一区二区3区| 亚洲高清免费观看高清完整版| 国产在线视频2019最新视频| 国产精品女主播视频| 久久精品小视频| 亚洲电影免费观看高清完整版在线观看| 久久久精品视频成人| 日韩中文字幕在线播放| 国产精品91在线观看| 国产精品无av码在线观看| 亚洲人成在线观| 91精品国产免费久久久久久| 国产成人精品999| 国产精品福利网| 亚洲深夜福利网站| 超碰精品一区二区三区乱码| 久久99久国产精品黄毛片入口| 国产成人在线一区二区| 日韩欧美成人网| 7m第一福利500精品视频| 51精品在线观看| 欧美日韩国产黄| 日韩在线视频一区| 川上优av一区二区线观看| 成人福利视频网| 亚洲精品成人久久电影| 色无极亚洲影院| 在线免费看av不卡| 国产一区二区三区在线免费观看| 久久久精品免费| 国产91精品久久久| 久久理论片午夜琪琪电影网| 日韩电影免费在线观看中文字幕| 色综合伊人色综合网| 岛国av一区二区在线在线观看| 国产亚洲欧美日韩精品| 国产成人免费av| 91国语精品自产拍在线观看性色| 国产精品草莓在线免费观看| 久久精品国产96久久久香蕉| 亚洲人成在线播放| 成人av番号网| 高跟丝袜一区二区三区| 综合欧美国产视频二区| 日韩视频亚洲视频| 中文字幕亚洲综合久久筱田步美| 精品视频中文字幕| 成人写真视频福利网| 欧美视频二区36p| 国产一区二区三区在线观看网站| 欧美激情国产日韩精品一区18| 亚洲最新av在线网站| 亚洲精品国产拍免费91在线| 欧美专区在线视频| 成人精品视频在线| 欧美精品少妇videofree| 中文字幕亚洲欧美日韩2019| 中文字幕国内精品| 国产精品免费视频xxxx| 亚洲国产精品99久久| 亚洲自拍偷拍网址| 国产精品久久久亚洲| 欧美日韩亚洲精品一区二区三区| 亚洲综合在线做性| 欧美极品少妇xxxxⅹ裸体艺术| 国产91网红主播在线观看| 精品日本高清在线播放| 成人伊人精品色xxxx视频| 成人亚洲欧美一区二区三区| 日韩精品在线视频美女| 91av在线播放视频| 久久久久久久久电影| 欧美大片免费看| 尤物yw午夜国产精品视频明星| 亚洲综合在线中文字幕| 午夜伦理精品一区| 亚洲va码欧洲m码| 久久久国产精品免费| 伊人久久大香线蕉av一区二区| 国产精品视频免费在线| 久久亚洲精品视频| 91黄色8090| 91九色视频在线| 97激碰免费视频| 上原亚衣av一区二区三区| 高潮白浆女日韩av免费看| 日韩av免费在线看| 久久久久亚洲精品| yw.139尤物在线精品视频| 色妞久久福利网| 欧美劲爆第一页| 欧美日韩一区二区在线| 国产999视频| 成人激情黄色网| 国产精品综合久久久| 国产精品1234| 久久久噜噜噜久噜久久| 欧美老女人xx| 亚洲电影免费观看高清完整版| 久久琪琪电影院| 亚洲天堂免费视频| 亚洲午夜女主播在线直播| 国内外成人免费激情在线视频| 久久成人亚洲精品| 久久久人成影片一区二区三区观看| 日韩av男人的天堂| 欧美一区视频在线| 97视频在线观看免费高清完整版在线观看| 欧美猛男性生活免费| 97国产suv精品一区二区62| 久久91亚洲精品中文字幕| 一区二区三区高清国产| 欧美黑人性生活视频| 国产成人综合av| 亚洲精品久久久久| 欲色天天网综合久久| 国产精品网红直播| 亚洲激情自拍图| 伊人一区二区三区久久精品| 欧美精品在线免费播放| 日韩久久精品成人| 中文字幕亚洲一区二区三区| 精品久久久久久久久久久| 国产成人精品久久亚洲高清不卡| 亚洲欧美另类在线观看| 国产v综合v亚洲欧美久久| 亚洲精品日韩丝袜精品|