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

首頁 > 編程 > JSP > 正文

詳解JSP 2.0下的動態內容緩存

2019-11-18 16:39:12
字體:
來源:轉載
供稿:網友
在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    <c:set var="cachedFragment" scope="application">
   ...
   </c:set></c:if>
  
   緩存頁面片段用下列語句輸出結果:
   ${applicationScope.cachedFragment}
  
   當緩存片段需要被每一個請求所定制的時候,到底發生了什么?例如,如果希望包含一個計數器,需要緩存兩個片段:
   <%@ taglib prefix="c" uri="   <c:set var="cachedFragment1" scope="application">
   ...
   </c:set></c:if><c:if test="${empty cachedFragment2}">
   <c: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="
   <c: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="   xmlns:xsi="   xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-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是否可用。
   <?xml version="1.0" encoding="ISO-8859-1"?><web-app xmlns="   xmlns:xsi="   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容器負責調用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;
   }}
  
   <jc: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頁面被重新編譯的時 候。當然,在開發完畢的成品環境中緩存必須被啟用。
  
   總結
  
    對于開發大型企業級應用,則該考慮使用支持更好的緩存機制的框架結構,而不僅是使用JSP變量。但是了解基于EL API的定制技術無疑是不無裨益的。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
成人a免费视频| 国产午夜精品久久久| 国产精品欧美日韩| 日韩视频免费观看| 在线免费观看羞羞视频一区二区| 亚洲男人天堂网| 国产精品久久久久福利| 国产视频精品一区二区三区| 一区二区三区四区在线观看视频| 国产小视频国产精品| 国产成人亚洲综合青青| 欧美激情在线视频二区| 亚洲性xxxx| 国产成人福利视频| 青青a在线精品免费观看| 2020久久国产精品| 欧美寡妇偷汉性猛交| 国产最新精品视频| 91久久国产精品91久久性色| 精品国产乱码久久久久久天美| 91免费看片在线| 欧美日韩国产成人| 成人精品一区二区三区电影免费| 国产精品久久久久久久久久久久| 7777精品久久久久久| 欧美国产日产韩国视频| 国产综合久久久久久| 中国china体内裑精亚洲片| 亚洲精品美女久久| 国产精品69精品一区二区三区| 久久五月天色综合| 亚洲情综合五月天| 国产亚洲精品91在线| 国产精品美女www| 国产99在线|中文| 久热精品视频在线观看| 亚洲人成77777在线观看网| 亚洲黄色在线看| 亚洲天堂男人天堂| 日韩在线观看免费全| 亚洲高清一二三区| 精品日本美女福利在线观看| 日韩美女福利视频| 亚洲欧美国产精品va在线观看| 最近2019中文免费高清视频观看www99| 日韩av免费在线| 91影视免费在线观看| 久久精品99无色码中文字幕| 亚洲免费人成在线视频观看| 97久久精品在线| 日韩av在线网| 亚洲护士老师的毛茸茸最新章节| 国产精品一二区| 欧美成人免费全部观看天天性色| 国产午夜精品视频免费不卡69堂| 久久久久久久亚洲精品| 中文字幕亚洲欧美日韩高清| 精品性高朝久久久久久久| 精品女同一区二区三区在线播放| 亚洲午夜激情免费视频| 欧美成人精品xxx| 亚洲国产成人久久综合| 日韩欧美国产视频| 91精品国产色综合久久不卡98| 久久精品国产一区二区电影| 在线性视频日韩欧美| 国产免费一区二区三区在线能观看| 国产精品吴梦梦| 亚洲免费影视第一页| 国产一区二区三区四区福利| 另类少妇人与禽zozz0性伦| 精品成人乱色一区二区| 国产在线播放不卡| 国产精品稀缺呦系列在线| 国产精品久久久久久久久久久久久| 欧美精品少妇videofree| 亚洲精品在线91| 日韩电影中文字幕在线观看| 亚洲性av网站| 久久艳片www.17c.com| 久久久免费高清电视剧观看| 亚洲美女性生活视频| 国产精品爽黄69天堂a| 色婷婷av一区二区三区在线观看| 国产亚洲欧美一区| 欧美激情小视频| 久久久久久国产免费| 亚洲欧美日韩网| 成人免费观看a| 国内偷自视频区视频综合| 日韩精品视频免费专区在线播放| 最近2019中文免费高清视频观看www99| 国产成人精品免高潮在线观看| 精品高清美女精品国产区| 久久久久久久久久久免费| 久久九九有精品国产23| 亚洲欧洲国产精品| 久久久精品网站| 欧美成人h版在线观看| 亚洲无av在线中文字幕| 欧美国产极速在线| 亚洲福利视频久久| 国产美女精彩久久| 日韩av在线高清| 亚洲精品小视频在线观看| 国产精品一区二区久久久久| 成人中心免费视频| 97av在线视频| 久久久久www| 国产91热爆ts人妖在线| 国产一区二区三区网站| 日韩国产高清污视频在线观看| 久久久国产精彩视频美女艺术照福利| 91高清在线免费观看| 91精品国产91久久久久久最新| 亚洲春色另类小说| 国产精品中文久久久久久久| 亚洲成人中文字幕| 久久久中精品2020中文| 91亚洲精品一区二区| 欧美孕妇与黑人孕交| 久久久久久12| 国产精品男人爽免费视频1| 亚洲国产精品va在线看黑人动漫| 亚洲国产精品小视频| 26uuu日韩精品一区二区| 91网站免费观看| 亚洲欧美一区二区三区久久| 日韩精品电影网| 国产精品扒开腿做爽爽爽视频| 欧美另类第一页| 色噜噜亚洲精品中文字幕| 国产精品入口免费视频一| 国产成一区二区| 久久久噜噜噜久久| 日韩大片在线观看视频| 中文字幕视频在线免费欧美日韩综合在线看| 日韩av快播网址| 欧美小视频在线| 亚洲qvod图片区电影| 中文字幕日韩欧美| 97高清免费视频| 亚洲一区二区三区视频播放| 欧美一级成年大片在线观看| 欧美午夜精品久久久久久久| 日韩精品黄色网| 最近2019中文字幕在线高清| 亚洲精品久久久久久久久久久| 欧美日韩黄色大片| 欧美成人在线影院| 亚洲专区在线视频| 国产精品一区二区3区| 91手机视频在线观看| 久久久久久久久91| 国产欧美精品一区二区三区介绍| 亚洲精品国精品久久99热一| 欧美黄色片在线观看| 国产成人自拍视频在线观看| 日韩小视频在线| 人体精品一二三区| 日本乱人伦a精品| 亚洲japanese制服美女| 精品久久久999| 国产精品久久久久久久久影视|