|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
没有那个大公司会傻了吧唧用.net开发大型项目,开发了,那等于自己一半的生命线被微软握着呢。而.net不行,限制在window系统,又是捆绑,鄙视微软之!在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;
else
thrownewIllegalArgumentException(
"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);
}else
getJspBody().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的定制手艺无疑是不无裨益的。
手机用到的是用j2me所编出来的小程序。 |
|