This is a follow-up to the 'overhead of jsp include' question below:
JSP Performance using jsp:include
In our application, developers have 'modularized' jsp fragments by heavy use of "jsp:includes" for "common" jsp code repeated throughout the application.
Pros
The pro's are as follows:
it's DRY-- we define the jsp fragment once. This is a big help when you need to change some html and don't need to find/replace/search/destroy.
it's fairly easy to follow: you clearly pass in the parameters. When you edit the 'included' page, you 'know what you're getting', i.e. vs. some 'global variables' declared in the 'including/calling' page.
Cons
- performance overhead of the additional request
Questions
So as a follow-on:
- how much performance overhead does a 'jsp:include' incur? It's not obvious from the tomcat code (though you see it does a whole lot more than would an inline call). Also in profiling the app I've never requestDispatcher.include() or invoke() methods appears as hotspots.
- could someone point as to where precisely lies the bulk of the overhead开发者_运维百科? (i.e. method X in class Y) Or whether it's just all the "little stuff" (e.g. setting attributes or object creation and subsequent GC) that happens with each request?
- what are the alternatives? (AFAIK @include and jsp:include. anything else?)
- (Stupid bonus question) why couldn't the servlet engine 'include' the jsp at compile time, i.e like an 'inline macro with parameters' so that we developers could get the clarity of 'jsp:include' and the peformance of '@include'.
I've wondered about this last question for a while. I've used code generation tools in a past life and never quite understood the lack of options for including jsp fragments.
For the benefit of the reader I've included the tomcat 'applicationDispatcher.invoke()' method (tomcat 5.5. sorry if it's dated). For clarity, I've pruned the exception handling.
thanks in advance
will
private void invoke(ServletRequest request, ServletResponse response,
State state) throws IOException, ServletException {
// Checking to see if the context classloader is the current context
// classloader. If it's not, we're saving it, and setting the context
// classloader to the Context classloader
ClassLoader oldCCL = Thread.currentThread().getContextClassLoader();
ClassLoader contextClassLoader = context.getLoader().getClassLoader();
if (oldCCL != contextClassLoader) {
Thread.currentThread().setContextClassLoader(contextClassLoader);
} else {
oldCCL = null;
}
// Initialize local variables we may need
HttpServletResponse hresponse = (HttpServletResponse) response;
Servlet servlet = null;
IOException ioException = null;
ServletException servletException = null;
RuntimeException runtimeException = null;
boolean unavailable = false;
// Check for the servlet being marked unavailable
if (wrapper.isUnavailable()) {
wrapper.getLogger().warn(
sm.getString("applicationDispatcher.isUnavailable",
wrapper.getName()));
long available = wrapper.getAvailable();
if ((available > 0L) && (available < Long.MAX_VALUE))
hresponse.setDateHeader("Retry-After", available);
hresponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm
.getString("applicationDispatcher.isUnavailable", wrapper
.getName()));
unavailable = true;
}
// Allocate a servlet instance to process this request
try {
if (!unavailable) {
servlet = wrapper.allocate();
}
}
...exception handling here....
// Get the FilterChain Here
ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();
ApplicationFilterChain filterChain = factory.createFilterChain(request,
wrapper,servlet);
// Call the service() method for the allocated servlet instance
try {
String jspFile = wrapper.getJspFile();
if (jspFile != null)
request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
else
request.removeAttribute(Globals.JSP_FILE_ATTR);
support.fireInstanceEvent(InstanceEvent.BEFORE_DISPATCH_EVENT,
servlet, request, response);
// for includes/forwards
if ((servlet != null) && (filterChain != null)) {
filterChain.doFilter(request, response);
}
// Servlet Service Method is called by the FilterChain
request.removeAttribute(Globals.JSP_FILE_ATTR);
support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
servlet, request, response);
}
...exception handling here....
// Release the filter chain (if any) for this request
try {
if (filterChain != null)
filterChain.release();
}
...exception handling here....
// Deallocate the allocated servlet instance
try {
if (servlet != null) {
wrapper.deallocate(servlet);
}
}
...exception handling here....
// Reset the old context class loader
if (oldCCL != null)
Thread.currentThread().setContextClassLoader(oldCCL);
// Unwrap request/response if needed
// See Bugzilla 30949
unwrapRequest(state);
unwrapResponse(state);
// Rethrow an exception if one was thrown by the invoked servlet
if (ioException != null)
throw ioException;
if (servletException != null)
throw servletException;
if (runtimeException != null)
throw runtimeException;
}
If you've profiled the app, then you've really answered your own question - if there's no measurable performance hit for using <jsp:include>
, then it's not worth worrying about. Internally, Tomcat will construct a new HttpServletRequest
and associated gubbins, but it's probably smart enough to do keep it lightweight. The lesson is to not assume there's a performance problem with Feature X until you've actually observed it.
A good alternative to <jsp:include>
is JSP 2.0 tag files. These allow you to encapsulate reusable content, like with a <jsp:include>
, but with a clearly defined interface for the fragment, and without incurring the overhead (however small that may be) of <jsp:include>
. I much prefer them, I think it's a more elegant approach.
(Bonus Answer) There is an include mechanism that inlines: <%@ include file="x.jsp" %>
. This performs a compile-time inlining of the included content. You have to be careful with that one, though, since if you change the content of x.jsp
at runtime, the "host" page won't be recompiled.
精彩评论