In Web applications, content caching is one of the most common optimization techniques and can be easily implemented. For example, you can use a custom JSP tag--we'll name it <jc:cache>--by <jc:cache> and </jc:cache> encapsulate each page fragment that needs to be cached. Any custom label can control when the part (or the pre-packaged page fragment) it contains is executed, and the dynamic output can be captured. The <jc:cache> label makes the JSP container (for example, Tomcat) produce only the content once, and serves as the application-scoped JSP variable to store each cached fragment. Each time a JSP page is executed, the custom label loads the page fragment without having to execute the JSP code again to produce the output. As part of the Jakarta Project, the development of tag libraries uses this technique. When cached content does not need to be customized by every user or request, it works very well.
This article improves on the techniques described above, allowing JSP pages to customize cached content for each request and user by using the JSP 2.0 Expression language (EL). A cached page fragment can contain a JSP expression that is not assigned to a JSP container, and the value of those expressions is determined by the custom label each time the page is executed. As a result, the creation of dynamic content is optimized, but the cached fragment can contain part of the content produced by each request using native JSP expression language. With the help of the JSP 2.0 EL API, Java Developers can make it possible with an expression language.
content caching vs Data caching
Content caching is not the only option. For example, data extracted from a database can also be cached. In fact, because the stored information contains no HTML markup and requires less memory, the data cache may be more efficient. In many cases, however, the memory cache is easier to implement. Suppose in a case total, an application is a large number of transaction objects, occupy important CPU resources, produce complex data, and use JSP pages to render the data. Work everything well until one day suddenly the load on the server increases and requires an emergency solution. A very good and effective solution is to create a cache layer between the transaction object and the rendering expression layer. However, JSP pages that cache dynamic content must be modified very quickly and smoothly. As opposed to simple JSP page edits, the application's business logic changes often require more effort and testing, and if a page aggregates information from multiple composite sources, the Web layer has only a small amount of change. The problem is that cache space needs to be freed when cached information becomes lost, and the transaction object should know when this happens. However, choosing whether to implement content caching or data caching, or other optimization techniques, has many factors to consider, sometimes the particular requirements of the program being developed.
There is no need for data caching and content caching to be mutually exclusive, and they can be used together. For example, in a database-driven application, the data extracted from the database and the HTML that renders the data are cached separately. This is somewhat similar to the template that is generated in real time using JSP. The El API technology discussed in this article demonstrates how to use JSP el to load data into the rendering template.
caching dynamic content with JSP variables
Whenever you implement a caching mechanism, you need a way to store the cached object, and in this article is a string type of object. One option is to use an object-cache frame structure, or use Java maps to implement a custom caching scheme. JSP already has the name "scoped attributes" or "JSP variables" to provide id--object mapping, which is what the caching mechanism needs. This is meaningless for using page or request scope, and it is a good place to store cached content within the scope of the application, because it is shared by all users and pages. Session scope can also be used when each user needs to be cached separately, but this is not very efficient. The JSTL tag library can be cached with that content by using a JSP variable as shown in the following example:
<%@ taglib prefix= "C" uri= "Http://java.sun.com/jsp/jstl/core"%><c:if test= "${empty cachedfragment}"
<c:set var= "cachedfragment" scope= "Application"
...
</c:set> </c:if>
The cached page fragment outputs the result with the following statement:
${applicationscope.cachedfragment}
What happens when a cached fragment needs to be customized by each request? For example, if you want to include a counter, you need to cache two fragments:
<%@ 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}" >
<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>
You can output the cached content using the following statement:
${CACHEDFRAGMENT1} ${counter} ${cachedfragment2}
With the help of a dedicated tag library, it is much easier to customize the caching of page fragments. As mentioned above, the cached content can be encapsulated by the start tag (<jc:cache>) and the end tag (</jc:cache>). Each customization can use another tag (<jc:dynamic expr= "...") to output a JSP expression (${...} ) to perform. Dynamic content is cached with a JSP expression and assigned when each cached content is output. You can see how this is achieved in the following sections. counter.jsp caches a page fragment containing a counter that automatically +1 when each user refreshes the page.
<%@ 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} "
<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 variables are easy to use, which is a good content caching scheme for simple web apps. However, if the application produces a large amount of dynamic content, no control of the cache size is a problem. A dedicated caching framework structure can provide a more robust scenario, allowing for caching monitoring, limiting cache size, controlling caching policies, and so on.
using the JSP 2.0 Expression language API
A JSP container, such as Tomcat, is assigned to an expression in a JSP page that applies the El API and can be used by Java code. This allows the application of JSP El for development outside of a Web page, for example, for XML files, text-based resources, and custom scripts. The EL API is also useful when you need to control when to assign a value to an expression in a Web page or write an expression associated with it. For example, cached page fragments can contain custom JSP expressions, and when each cached content is output, the EL API is used to assign or reassign these expressions.
The article provides an example program (see the Resource section at the end of this article), which contains a Java class (Jsputils) and contains a method eval () in a class that has three parameters: the JSP expression, the expected type of the expression, and a JSP context object. The Eval () method obtains expressionevaluator from the JSP context and invokes the evaluate () method, through an expression, the expected type of the expression, and a variable obtained from the JSP Congtext. The Jsputils.eval () method returns the value of an expression.
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);
}
}
...}
Note: the Jsputils.eval () mainly encapsulates the standard ExpressionEvaluator. If expr does not contain the ${,jsp EL API, it is not invoked because there is no JSP expression.
Create a tag library descriptor (TLD) file
The JSP tag library requires a tag library descriptor (TLD) file to customize the naming of tags, their properties, and the Java classes that manipulate the label. Jspcache.tld describes two custom labels, and <jc:cache> has two properties: The ID of the cached page fragment and the content range that the JSP scope-jsp page always needs to be stored in. The <jc:dynamic> has only one property, that is, the JSP expression must be assigned every time the cache fragment is output. The TLD file maps the two custom tags to the Cachetag and Dynamictag classes, as follows:
<?xml version= "1.0" encoding= "UTF-8" ><taglib "xmlns="
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>
The TLD file is contained in the Web application descriptor file (Web.xml), which also contains an initial parameter indicating whether the cache is available.
<?xml version= "1.0" encoding= "iso-8859-1" ><web-app "xmlns="
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>
Understand the working mechanism of
The JSP container creates a Cachetag instance of each <jc:cache> tag in the JSP page to process it. The JSP container is responsible for invoking the setjsp (), SetParent (), and Setjspbody () methods, which are inherited from Simpletagsupport by the Cachetag class. The JSP container colleague also invokes the setter method for each property of the label being manipulated. The SetId () and Setscope () methods store the value of the property to the private domain, which has been initialized with the Cachetag () constructor with the default value.
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);
}
...}
The Setscope () method calls Jsputils.checkscope () to verify the property value of the scope that has been converted from string to type int.
... 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);
}}
Once the Cachetag instance is ready to operate on the label, the JSP container invokes the Dotag () method, using Getjspcontext () to obtain the JSP context. This object is styled as PageContext so that the Getservletcontext () method can be invoked. The servlet context is used to get the value of the initialization parameter, which indicates whether the caching mechanism is enabled. If the cache is enabled, Dotag () attempts to use the ID and scope property values to obtain cached page fragments. If the page fragment has not yet been cached, Dotag () uses Getjspbody (). Invoke () to execute the JSP code encapsulated by <jc:cache> and </jc:cache>. The output results generated by the JSP body are buffered in StringWriter and obtained by the TOSTIRNG () method. In this way, Dotag () invokes the SetAttribute () method of the JSP context to create a new JSP variable that controls the possible inclusion of a JSP expression (${...}) The cached content. These expressions are assigned by Jsputils.eval () before the content is output with Jspcontext.getout (). Print (). These behaviors occur only when the cache is enabled. Otherwise, Dotag () executes the JSP body only through Getjspbody (). Invoke (null) and the output is not cached.
... 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);
}
...}
Note that a separate jsputils.eval () call is assigned to all ${.} expressions. Because one contains a lot of ${...} The text of the struct is also an expression. Each cached fragment can be treated as a complex JSP expression.
The iscacheenabled () method returns the value of Cacheenabled, which has been initialized by Dotag ().
... public class Cachetag extends Simpletagsupport {
... public boolean iscacheenabled () {
return cacheenabled;
}}
The <jc:cache> label allows the page developer to choose the ID of the cached page fragment. This allows caching of a page fragment to be shared by multiple JSP pages, which is useful when JSP code needs to be reused. However, some naming protocols are still needed to avoid possible conflicts. This side effect can be avoided by modifying the Cachetag class to include URLs within the automatic ID.
Understand what is doing
Each <jc:dynamic> is handled by an instance of a Dynamictag class, and the Setexpr () method stores the expr property value in a private domain. The Dotag () method creates a JSP expression that adds the ${prefix and the suffix to the Expr property value. Dotag () then uses Findancestorwithclass () to find the Cachetag handler that contains <jc:cache> the <jc:dynamic> label element. If no search is found or the cache is disabled, the JSP expression is Jsputils.eval () and the value is output. Otherwise, Dotag () outputs an expression of no value.
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);
}}
By analyzing the above code, you can note that <jc:cache> and <jc:dynamic> collaborate to achieve a solution that is as efficient as possible. If the cache is available, the page fragment is placed in the buffer with the JSP expression that is generated by the <jc:dynamic> and assigned by Cachetag. If the cache is disabled, the buffering becomes meaningless, and the <jc:cache> simply executes its JSP body part and lets Dynamictag assign a value to the JSP expression. Disabling caching is sometimes necessary, especially when content changes occur during the development process and when JSP pages are recompiled. Of course, caching must be enabled in the finished production environment.
Summary
Content caching is a very easy-to-use way to improve Web application performance. This article focuses on using the JSP expression language to customize cached content for each user or request. A simple introduction to the full text of the tag library for small web apps and can improve the performance of medium applications. For developing large enterprise-level applications, consider using a framework structure that supports a better caching mechanism, rather than just using JSP variables. But it is no doubt helpful to understand the custom technology based on the El API.