您的位置: 旅游网 > 明星

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

发布时间:2019-09-13 20:28:49

创建标签库描述符(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>

<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="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容器负责调用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页面被重新编译的时候。当然,在开发完毕的成品环境中缓存必须被启用。

总结

内容缓存是一种非常易用的改善Web应用性能的方法。这篇文章集中讨论了使用JSP表达式语言来为每一个用户或者请求定制缓存内容。贯穿全文的简单介绍的标签库适合小型Web apps并且可以提升中等应用的性能。对于开发大型企业级应用,则该考虑使用支持更好的缓存机制的框架结构,而不仅是使用JSP变量。但是了解基于EL API的定制技术无疑是不无裨益的。查看本文来源

宝宝感冒怎么办
宝宝喝奶粉上火怎么办
孩子流鼻血怎么处理
儿童止咳
猜你会喜欢的
猜你会喜欢的