引言: 相信在我们的系统开发过程中,一定会有一些运算量大的报表或者耗时的JSP页面(如使用了RMI或SOAP),这些页面往往都是整个系统中最耗时的部分。而时常我们又会发现,其实这些报表并不需要时时更新,尤其是当报表中的数据依赖于其他系统的同步时,如一个日产量的经营分析报表,它需要的数据只需要一天天的更新,而并不需要每次页面访问时都从数据库中提取所有的数据进行处理。此时,如果我们能够在页面显示层和业务逻辑层中建立一个缓存机制的话,将会使我们的系统处理能力得到很大的提高。本文就在JSP2.0的环境下实现动态缓存进行了介绍。
我们首先需要搭建JSP2.0的平台,具体的步骤请参看http://www.javayou.com/showlog.jspe?log_id=539。
一、直接定义JSP变量的实现方式
<%@ taglib prefix="cache" uri="http://java.sun.com/jsp/jstl/core" %>
<!-判断是否已经进行缓存-->
<cache:if test="${empty cachedContext}">
<!-设置一个application变量保存需要进行缓存的内容-->
<cache:set var="cachedContext" scope="application">
这段内容被缓存!
</cache:set>
</cache:if> |
在上面的代码中我们通过定义一个application的变量来保存需要缓存的内容。这样在以后需要取得缓存内容的时候,就可以使用${applicationScope.cachedContext}取出。这种方式比较适用于每个用户需要Cache的内容是相同的情况,那么如果我们需要根据不同的用户Cache不同的内容的时候,我们就需要把根据User变化的内容定义为Session变量了。
<%@ taglib prefix="cache" uri="http://java.sun.com/jsp/jstl/core" %>
<!-把变化的内容设置到session变量中-->
<cache:if test="${sessionScope.enterNum == null}">
<cache:set var="enterNum" scope="session" value="0"/>
</cache:if>
<cache:set var="enterNum" value="${enterNum +1}" scope="session"/>
<cache:if test="${empty cachedStartContext1}">
<cache:set var="cachedStartContext1" scope="application">
访问了本页面
</cache:set>
</cache:if>
<cache:if test="${empty cachedEndContext2}">
<cache:set var="cachedEndContext2" scope="application">
次数。
</cache:set> |
这样,我们就可以通过${applicationScope.cachedStartContext1}${ sessionScope. enterNum } ${applicationScope.cachedStartContext2}
得到被缓存的信息。
二、使用自定义JSP标签达到动态Cache页面内容
可以发现上面实现动态缓存的方法,使用起来并不是很方便,下面我们来看看如何使用自定义JSP标签达到动态Cache页面内容。
这里我先说明下实现的大致步骤,首先定义两个标签CacheTag和DynamicTag,其中CacheTag是用来设置cache到JSP变量中,而DynamicTag是用来生成动态的数据。
为此我们需要一个JSP的工具类JspUtil。这个类主要是计算Jsp表达式的值。
public class JspUtils {
public static Object eval(
String expr, Class type, JspContext jspContext)
throws JspException {
try {
if (expr.indexOf("${"} == -1)
{return expr; } //没有含有JSP表达式则不做处理。
ExpressionEvaluator evaluator
= jspContext.getExpressionEvaluator();
//获取 ExpressionEvaluator以便运算JSP表达式
return evaluator.evaluate(expr, type,
jspContext.getVariableResolver(), null);//求出JSP表达式的值
} catch (ELException e) {
throw new JspException(e);
}
}
//转换字符串为SCOPE的值
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(
"错误的SCOPE: " + scope);
}
} |
其中eval方法是求出带有动态数据的值的方法,checkScope是根据字符串的值得出相应的scope的值.
CacheTag标签代码如下:
public class CacheTag extends SimpleTagSupport {
public static final String CACHE_ENABLED
= " com.javayou.jspcache.enabled";//WEB的初始变量,控制是否起用缓存功能
private String id;
private int scope; //存储缓存的范围
private boolean cacheEnabled;//是否进行缓存
public void doTag() throws JspException, IOException {
JspContext jspContext = getJspContext();
ServletContext scontext = ((PageContext) jspContext).getServletContext();
//取得初始变量,判断是否使用缓存功能
String needCache
= scontext.getInitParameter(CACHE_ENABLED);
if(needCache != null && needCache.equals("true"))
{
String cachedOut
= (String) jspContext.getAttribute(id, scope);
//判断是否已经Cache过。
if (cachedOut == null) {
StringWriter buffer = new StringWriter();
getJspBody().invoke(buffer); //取得标签的BODY处理内容。
cachedOut = buffer.toString();
jspContext.setAttribute(id, cachedOut, scope);//设置到缓存中
}
//计算JSP表达式,得到最终输出。
String evaluatedOutput = (String) JspUtils.eval(
cachedOutput, String.class, jspContext);
jspContext.getOut().print(evaluatedOutput);
jspContext.getOut().print(evaluatedOutput);
} else
getJspBody().invoke(null);
} |
注意:在此标签中首先我们是通过取得WEB应用的初始变量CACHE_ENABLED 的值来判断是否需要Cache,如果需要,则调用getJspBody的invoke方法取得被标签包围的内容,然后放在指定的JSP变量中,最后调用ExpressionEvaluator 的evaluate方法取得含有动态内容的数据,再打印到页面。
我们再来看另一个标签DynamicTag
public class DynamicTag extends SimpleTagSupport {
private String expr;//设置JSP表达式
public void setExpr(String expr) {
this.expr = expr;
}
public void doTag() throws JspException, IOException {
String output = "${" + expr + "}";
CacheTag ancestor = (CacheTag) findAncestorWithClass(
this, CacheTag.class);//得到父标签 CacheTag
if (ancestor == null || !ancestor.isCacheEnabled())
output = (String) JspUtils.eval(
output, String.class, getJspContext());
//求出JSP表达式的值
getJspContext().getOut().print(output);
}
} |
我们再给这两个标签加上TLD,
<tag>
<name>cache</name>
<tag-class>com.javayou.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.javayou.jspcache.DynamicTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>expr</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag> |
在WEB.XML中设置控制是否Cache的变量
<context-param>
<param-name>com.javayou.jspcache.enabled</param-name>
<param-value>true</param-value>
</context-param>
<jsp-config>
<taglib>
<taglib-uri>http://javayou.com/jspcache<;/taglib-uri>
<taglib-location>/WEB-INF/jspcache.tld</taglib-location>
</taglib>
</jsp-config> |
我们就可以在JSP页面使用这两个标签了,
于是,我们就可以实现了当需要对页面内容进行Cache时,可以根据需要对动态的数据进行解析,实现动态内容的缓存。
注意:以上主要通过一个简单的例子说明了怎样对页面的内容进行缓存的方法。在实际的过程中,数据的来源常常不会是这么简单,比如是从数据库中获取,或是通过业务逻辑类经过复杂的运算而得。
另外,在实际中,我们可以对Cache加入一个时间的控制,当cache保存的时间超作这个值之后,变重新获取新的数据。这样,对于那些不是时时更新的运算量大的数据报表在访问速度上可以得到有效的提高。
最后,因为随着Cache变量设置的增加,从而增大了JSP变量的空间。所以在实际的过程中,还需要对Cache的最大值进行限制
from: http://www.javayou.com/diary/597 |