Java 如何在启动时使 Tomcat 预编译 JSP?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/497830/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I make Tomcat pre-compile JSPs on startup?
提问by Dan Fabulich
We're using both Apache Tomcat 6.0 and Jetty 6 where I work. We mostly use Jetty for testing (it's great for running embedded in JUnit tests) and Tomcat for production.
我们在我工作的地方同时使用 Apache Tomcat 6.0 和 Jetty 6。我们主要使用 Jetty 进行测试(它非常适合在 JUnit 测试中运行嵌入)和 Tomcat 进行生产。
By default, Tomcat compiles JSPs on-the-fly as users request them. But this results in degraded performance for the first hit. It also highlights bizarre bugsin Tomcat's JSP compiler.
默认情况下,Tomcat 会根据用户的请求即时编译 JSP。但这会导致第一次命中的性能下降。它还突出显示了 Tomcat 的 JSP 编译器中的奇怪错误。
The Tomcat documentationgives recommendations for pre-compiling JSPs at build time using Ant (and a Maven plugin is also available)... but the resulting WAR contains Tomcat-specific stuff e.g. PageContextImpl.proprietaryEvaluate, so we can't use it in Jetty.
在Tomcat文档提供了使用Ant进行构建时预编译JSP的建议(和Maven插件也可)......但由此产生的WAR包含Tomcat的具体的东西如PageContextImpl.proprietaryEvaluate,所以我们不能在码头使用.
Is there some flag or setting we can use somewhere to force Tomcat to precompile all JSPs as soon as the WAR is initialized? We're prepared to wait a little longer on startup for this.
是否有一些标志或设置我们可以在某处使用以强制 Tomcat 在 WAR 初始化后立即预编译所有 JSP?我们准备在启动时再等一会儿。
In advance: I know there's a way to pre-compile exactly oneJSP by explicitly identifying a /servlet/load-on-startup tag in web.xml for one JSP. But for dozens or even hundreds of JSPs that becomes unmanageable.
提前:我知道有一种方法预编译正是一个通过明确识别web.xml中/的servlet /时加载的启动标签为一个JSP JSP。但是对于数十个甚至数百个变得难以管理的 JSP。
回答by ariso
http://www.devshed.com/c/a/BrainDump/Tomcat-Capacity-Planning/
http://www.devshed.com/c/a/BrainDump/Tomcat-Capacity-Planning/
project name="pre-compile-jsps" default="compile-jsp-servlets">
<!-- Private properties. -- >
<property name="webapp.dir" value="${basedir}/webapp-dir"/>
<property name="tomcat.home" value="/opt/tomcat"/>
<property name="jspc.pkg.prefix" value="com.mycompany"/>
<property name="jspc.dir.prefix" value="com/mycompany"/>
<!-- Compilation properties. -->
<property name="debug" value="on"/>
<property name="debuglevel" value="lines,vars,source"/>
<property name="deprecation" value="on"/>
<property name="encoding" value="ISO-8859-1"/>
<property name="optimize" value="off"/>
<property name="build.compiler" value="modern"/>
<property name="source.version" value="1.5"/>
<!-- Initialize Paths. -->
<path id="jspc.classpath">
<fileset dir="${tomcat.home}/bin">
<include name="*.jar"/>
</fileset>
<fileset dir="${tomcat.home}/server/lib">
<include name="*.jar"/>
</fileset>
<fileset dir="${tomcat.home}/common/i18n">
<include name="*.jar"/>
</fileset>
<fileset dir="${tomcat.home}/common/lib">
<include name="*.jar"/>
</fileset>
<fileset dir="${webapp.dir}/WEB-INF">
<include name="lib/*.jar"/>
</fileset>
<pathelement location="${webapp.dir}/WEB-INF/classes"/>
<pathelement location="${ant.home}/lib/ant.jar"/>
<pathelement location="${java.home}/../lib/tools.jar"/>
</path>
<property name="jspc.classpath" refid="jspc.classpath"/>
<!--========================================================== -->
<!-- Generates Java source and a web.xml file from JSP files. -->
<!-- ==========================================================
-->
<target name="generate-jsp-java-src">
<mkdir dir="${webapp.dir}/WEB-INF/jspc-src/${jspc.dir.prefix}"/>
<taskdef classname="org.apache.jasper.JspC" name="jasper2">
<classpath>
<path refid="jspc.classpath"/>
</classpath>
</taskdef>
<touch file="${webapp.dir}/WEB-INF/jspc-web.xml"/>
<jasper2 uriroot="${webapp.dir}"
package="${jspc.pkg.prefix}"
webXmlFragment="${webapp.dir}/WEB-INF/jspc-web.xml"
outputDir="${webapp.dir}/WEB-INF/jspc-src/${jspc.dir.prefix}"
verbose="1"/>
</target>
<!-- ========================================================== -->
<!-- Compiles (generates Java class files from) the JSP servlet -->
<!-- source code that was generated by the JspC task. -->
<!-- ========================================================== -->
<target name="compile-jsp-servlets" depends="generate-jsp-java-src">
<mkdir dir="${webapp.dir}/WEB-INF/classes"/>
<javac srcdir="${webapp.dir}/WEB-INF/jspc-src"
destdir="${webapp.dir}/WEB-INF/classes"
includes="**/*.java"
debug="${debug}"
debuglevel="${debuglevel}"
deprecation="${deprecation}"
encoding="${encoding}"
optimize="${optimize}"
source="${source.version}">
<classpath>
<path refid="jspc.classpath"/>
</classpath>
</javac>
</target>
<!-- ========================================================= -->
<!-- Cleans any pre-compiled JSP source, classes, jspc-web.xml -->
<!-- ========================================================= -->
<target name="clean">
<delete dir="${webapp.dir}/WEB-INF/jspc-src"/>
<delete dir="${webapp.dir}/WEB-INF/classes/${jspc.dir.prefix}"/>
<delete file="${webapp.dir}/WEB-INF/jspc-web.xml"/>
</target>
</project
This build file will find all of your webapp's JSP files, compile them into servlet classes, and generate servlet mappings for those JSP servlet classes. The servlet map pings it generates must go into your webapp's WEB-INF/web.xml file, but it would be difficult to write an Ant build file that knows how to insert the servlet mappings into your web.xml file in a repeatable way every time the build file runs. Instead, we used an XML entity include so that the generated servlet mappings go into a new file every time the build file runs and that servlet mappings file can be inserted into your web.xml file via the XML entity include mechanism. To use it, your webapp's WEB- INF/web.xml must have a special entity declaration at the top of the file, plus a reference to the entity in the content of the web.xml file where you want the servlet mappings file to be included. Here is how an empty servlet 2.5 webapp's web.xml file looks with these modifications:
这个构建文件将找到您的所有 webapp 的 JSP 文件,将它们编译成 servlet 类,并为这些 JSP servlet 类生成 servlet 映射。它生成的 servlet 映射 ping 必须进入您的 web 应用程序的 WEB-INF/web.xml 文件,但是很难编写一个 Ant 构建文件,它知道如何以可重复的方式将 servlet 映射插入到您的 web.xml 文件中。构建文件运行的时间。相反,我们使用了一个 XML 实体包含,这样每次构建文件运行时生成的 servlet 映射都会进入一个新文件,并且可以通过 XML 实体包含机制将该 servlet 映射文件插入到您的 web.xml 文件中。要使用它,您的 web 应用程序的 WEB-INF/web.xml 必须在文件顶部有一个特殊的实体声明,以及对 web 内容中的实体的引用。xml 文件,您希望在其中包含 servlet 映射文件。以下是经过这些修改后的空 servlet 2.5 webapp 的 web.xml 文件的外观:
<!DOCTYPE jspc-webxml [
<!ENTITY jspc-webxml SYSTEM "jspc-web.xml">
]>
<web-app xmlns=http://java.sun.com/xml/ns/javaee
xmlns:xsi=http://www.w3.org/2001/ XMLSchema-instance
xsi:schemaLocation="http:// java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/
javaee/web-app_2_5.xsd"
version="2.5">
<!-- We include the JspC-generated mappings here. -->
&jspc-webxml;
<!-- Non-generated web.xml content goes here. -->
</web-app>
Make sure your webapp's web.xml file has the inline DTD (the DOCTYPE tag) all the way at the top of the file and the servlet 2.5 web-app schema declaration below that. Then, wherever you want to insert the generated servlet mappings in your web.xml file, put the entity reference &jspc-webxml; . Remember, the entity reference begins with an ampersand ( & ), then has the name of the entity, and ends with a semicolon ( ; ).
确保您的 webapp 的 web.xml 文件在文件顶部始终具有内联 DTD(DOCTYPE 标记),并在其下方具有 servlet 2.5 web-app 模式声明。然后,无论您想在 web.xml 文件中插入生成的 servlet 映射的任何位置,都将实体引用 &jspc-webxml; . 请记住,实体引用以与号 ( & ) 开头,然后是实体的名称,并以分号 ( ; ) 结尾。
To use the build file, just edit it and set all of the properties at the top to values that match your setup, and then run it like this:
要使用构建文件,只需编辑它并将顶部的所有属性设置为与您的设置匹配的值,然后像这样运行它:
$ ant -f pre-compile-jsps.xml
$ ant -f 预编译-jsps.xml
回答by vphilipnyc
If you go with the solution mentioned by duffymo pointing to Vinny Carpenter's blog, I have a tip. There was one area that caused my container to hang indefinitely while contacting localhost (specifically in the private connect() method). Using the following hack was my workaround:
如果您采用 duffymo 提到的解决方案,并指向 Vinny Carpenter 的博客,我有一个提示。有一个区域导致我的容器在联系本地主机时无限期挂起(特别是在私有 connect() 方法中)。使用以下 hack 是我的解决方法:
private void connect(final String urlString) {
HttpURLConnection conn;
try {
final URL url = new URL(urlString);
conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(5000);
//time it out quickly - otherwise hangs forever
//seems to be an issue hitting localhost
//will still precompile the page
conn.setReadTimeout(100);
conn.setAllowUserInteraction(true);
conn.getInputStream();
conn.disconnect();
}
catch (SocketTimeoutException e) {
log.debug(e);
}
catch (IOException ioe) {
log.error(ioe);
}
}
Setting a timeout and ignoring the SocketTimeoutException worked (though admittedly not the best solution). Also, using this procedure means that you would need to specify the JSPs in web.xml. This was sufficient for my needs.
设置超时并忽略 SocketTimeoutException 有效(尽管公认不是最佳解决方案)。此外,使用此过程意味着您需要在 web.xml 中指定 JSP。这足以满足我的需要。