Jasper 2 JSP 引擎使用指南
目录
简介
Tomcat 11.0 使用 Jasper 2 JSP 引擎实现 Jakarta Pages 4.0 规范。
Jasper 2 经过重新设计,相对于原始的 Jasper,性能得到了显著提升。除了通用的代码改进外,还进行了以下更改:
- JSP 自定义标签池化 - 用于 JSP 自定义标签实例化的 Java 对象现在可以被池化和重用。这显著提升了使用自定义标签的 JSP 页面的性能。
- 后台 JSP 编译 - 如果您对已编译的 JSP 页面进行了更改,Jasper 2 可以在后台重新编译该页面。先前编译的 JSP 页面仍可用于服务请求。一旦新页面成功编译,它将替换旧页面。这有助于提高生产服务器上 JSP 页面的可用性。
- 包含页面更改时重新编译 JSP - Jasper 2 现在可以检测到 JSP 编译时包含的页面何时发生更改,并随后重新编译父 JSP。
- 使用 JDT 编译 JSP 页面 - Eclipse JDT Java 编译器现在用于执行 JSP Java 源代码编译。该编译器从容器类加载器加载源代码依赖项。Ant 和 javac 仍然可以使用。
Jasper 通过 Servlet 类 org.apache.jasper.servlet.JspServlet
实现。
配置
默认情况下,Jasper 配置用于 Web 应用程序开发。有关在生产 Tomcat 服务器上配置 Jasper 的信息,请参阅生产环境配置部分。
实现 Jasper 的 Servlet 通过全局 $CATALINA_BASE/conf/web.xml
中的初始化参数进行配置。
- checkInterval - 如果 development 为 false 且 checkInterval 大于零,则启用后台编译。checkInterval 是检查 JSP 页面(及其依赖文件)是否需要重新编译的时间间隔(以秒为单位)。默认值
0
秒。 - classdebuginfo - 类文件是否应包含调试信息进行编译?
true
或false
,默认值true
。 - classpath - 定义用于编译生成的 Servlet 的类路径。此参数仅在 ServletContext 属性 org.apache.jasper.Constants.SERVLET_CLASSPATH 未设置时生效。当在 Tomcat 中使用 Jasper 时,此属性始终会设置。默认情况下,类路径是根据当前 Web 应用程序动态创建的。
- compiler - Ant 应该使用哪个编译器来编译 JSP 页面。此参数的有效值与 Ant 的 javac 任务的 compiler 属性相同。如果未设置该值,则将使用默认的 Eclipse JDT Java 编译器而不是 Ant。没有默认值。如果设置了此属性,则应使用
setenv.[sh|bat]
将ant.jar
、ant-launcher.jar
和tools.jar
添加到CLASSPATH
环境变量中。 - compilerSourceVM - 源文件与哪个 JDK 版本兼容?(默认值:
17
) - compilerTargetVM - 生成的文件与哪个 JDK 版本兼容?(默认值:
17
) - development - Jasper 是否在开发模式下使用?如果为 true,则可以通过 modificationTestInterval 参数指定检查 JSP 修改的频率。
true
或false
,默认值true
。 - displaySourceFragment - 异常消息中是否应包含源代码片段?
true
或false
,默认值true
。 - dumpSmap - JSR45 调试的 SMAP 信息是否应转储到文件中?
true
或false
,默认值false
。如果 suppressSmap 为 true,则为false
。 - enablePooling - 确定是否启用标签处理器池化。这是一个编译选项。它不会改变已编译 JSP 的行为。
true
或false
,默认值true
。 - engineOptionsClass - 允许指定用于配置 Jasper 的 Options 类。如果不存在,将使用默认的 EmbeddedServletOptions。
- errorOnUseBeanInvalidClassAttribute - 当 useBean 动作中 class 属性的值不是有效的 Bean 类时,Jasper 是否应该发出错误?
true
或false
,默认值true
。 - fork - 让 Ant 分叉 JSP 页面编译,使其在与 Tomcat 分离的 JVM 中执行?
true
或false
,默认值true
。 - genStringAsCharArray - 为了在某些情况下提高性能,是否应将文本字符串生成为字符数组?默认值
false
。 - javaEncoding - 用于生成 Java 源文件的 Java 文件编码。默认值
UTF8
。 - keepgenerated - 我们是否应该保留每个页面生成的 Java 源代码而不是删除它?
true
或false
,默认值true
。 - mappedfile - 为了方便调试,是否应为每行输入生成一个打印语句的静态内容?
true
或false
,默认值true
。 - maxLoadedJsps - Web 应用程序将加载的最大 JSP 数量。如果加载的 JSP 数量超过此值,则将卸载最近最少使用的 JSP,以便任何时候加载的 JSP 数量不超过此限制。值为零或更小表示没有限制。默认值
-1
。 - jspIdleTimeout - JSP 在卸载前可以空闲的时间(以秒为单位)。值为零或更小表示永不卸载。默认值
-1
。 - modificationTestInterval - 导致 JSP(及其依赖文件)在上次检查修改后的指定时间间隔(以秒为单位)内不被检查修改。值为 0 将导致 JSP 在每次访问时都被检查。仅在开发模式下使用。默认值为
4
秒。 - recompileOnFail - 如果 JSP 编译失败,是否应忽略 modificationTestInterval,并让下次访问触发重新编译尝试?仅在开发模式下使用,默认禁用,因为编译可能耗费资源并导致资源过度使用。
- scratchdir - 编译 JSP 页面时应使用哪个临时目录?默认是当前 Web 应用程序的工作目录。
- suppressSmap - 是否应抑制 JSR45 调试的 SMAP 信息生成?
true
或false
,默认值false
。 - trimSpaces - 是否应从输出中删除完全由空白字符组成的模板文本(
true
),替换为单个空格(single
),或保持不变(false
)?另外,extended
选项将从模板文本中删除前导和尾随空白字符,并将模板文本中的连续空白字符和换行符折叠为单个换行符。请注意,如果 JSP 页面或标签文件指定了trimDirectiveWhitespaces
值为true
,则该设置将优先于此页面/标签的此配置设置。默认值false
。 - xpoweredBy - 确定生成的 Servlet 是否添加 X-Powered-By 响应头。
true
或false
,默认值false
。 - strictQuoteEscaping - 当脚本表达式用于属性值时,是否应严格应用 JSP.1.6 中关于引号字符转义的规则?
true
或false
,默认值true
。 - quoteAttributeEL - 当 EL 用于 JSP 页面上的属性值时,是否应将 JSP.1.6 中描述的属性引用规则应用于表达式?
true
或false
,默认值true
。 - variableForExpressionFactory - 用于表达式语言工厂的变量名称。如果未指定,将使用默认值
_el_expressionfactory
。 - variableForInstanceManager - 用于实例管理器工厂的变量名称。如果未指定,将使用默认值
_jsp_instancemanager
。 - poolTagsWithExtends - 默认情况下,通过页面指令的 extends 属性使用自己基类的 JSP 将禁用标签池化,因为 Jasper 无法保证必要的初始化已完成。这可能对性能产生负面影响。如果替代基类从 Servlet.init() 调用 _jspInit(),将此属性设置为
true
将启用使用替代基类的池化。如果替代基类不调用 _jspInit() 且此属性为true
,则在使用标签时将发生 NPE。true
或false
,默认值false
。 - strictGetProperty - 如果为
true
,则强制要求在jsp:getProperty
动作中引用的对象必须事先“引入”JSP 处理器,如 JSP 2.0 及更高版本规范的 JSP.5.3 章中所述。true
或false
,默认值true
。 - strictWhitespace - 如果为
false
,则将放宽属性名称前的空白字符要求,以便缺少空白字符不会导致错误。true
或false
,默认值true
。 - jspServletBase - 从 JSP 生成的 Servlet 的基类。如果未指定,将使用默认值
org.apache.jasper.runtime.HttpJspBase
。 - serviceMethodName - 基类调用的服务方法的名称。如果未指定,将使用默认值
_jspService
。 - servletClasspathAttribute - 提供 JSP 类路径的 ServletContext 属性名称。如果未指定,将使用默认值
org.apache.catalina.jsp_classpath
。 - jspPrecompilationQueryParameter - 导致 JSP 引擎仅预生成 Servlet 而不调用它的查询参数名称。如果未指定,将使用 JSP 规范 (JSP.11.4.2) 定义的默认值
jsp_precompile
。 - generatedJspPackageName - 编译后的 JSP 的默认包名。如果未指定,将使用默认值
org.apache.jsp
。 - generatedTagFilePackageName - 从标签文件生成的标签处理器的默认包名。如果未指定,将使用默认值
org.apache.jsp.tag
。 - tempVariableNamePrefix - 用于生成临时变量名称的前缀。如果未指定,将使用默认值
_jspx_temp
。 - useInstanceManagerForTags - 如果为
true
,则使用实例管理器获取标签处理器实例。true
或false
,默认值false
。 - limitBodyContentBuffer - 如果为
true
,则任何超出bodyContentTagBufferSize
初始化参数值的标签缓冲区都将被销毁并创建一个新缓冲区。true
或false
,默认值false
。 - bodyContentTagBufferSize - 创建标签缓冲区时使用的缓冲区大小(以字符为单位)。如果未指定,将使用默认值
org.apache.jasper.Constants.DEFAULT_TAG_BUFFER_SIZE
(512)。
Eclipse JDT 中的 Java 编译器作为默认编译器包含在内。它是一个高级 Java 编译器,将从 Tomcat 类加载器加载所有依赖项,这在包含数十个 JAR 包的大型安装中编译时会大有帮助。在快速服务器上,即使对于大型 JSP 页面,这也将允许亚秒级的重新编译周期。
Apache Ant(在之前的 Tomcat 版本中使用)可以通过如上所述配置编译器属性来替代新编译器。
如果您需要更改应用程序的 JSP Servlet 设置,可以通过在 /WEB-INF/web.xml
中重新定义 JSP Servlet 来覆盖默认配置。但是,如果您尝试将应用程序部署到另一个容器,这可能会导致问题,因为 JSP Servlet 类可能无法识别。您可以通过使用 Tomcat 特定的 /WEB-INF/tomcat-web.xml
部署描述符来解决此问题。其格式与 /WEB-INF/web.xml
相同。它将覆盖任何默认设置,但不包括 /WEB-INF/web.xml
中的设置。由于它是 Tomcat 特定的,因此仅当应用程序部署在 Tomcat 上时才会处理它。
已知问题
如bug 39089中所述,一个已知的 JVM 问题bug 6294277,在编译非常大的 JSP 时可能会导致 java.lang.InternalError: name is too long to represent
异常。如果观察到此问题,可以通过以下方法之一解决:
- 减小 JSP 的大小
- 通过将
suppressSmap
设置为true
来禁用 SMAP 生成和 JSR-045 支持。
生产环境配置
可以进行的 JSP 主要优化是 JSP 的预编译。然而,这可能不可行(例如,在使用 jsp-property-group 功能时)或不切实际,在这种情况下,Jasper Servlet 的配置变得至关重要。
在生产 Tomcat 服务器中使用 Jasper 2 时,您应该考虑对默认配置进行以下更改。
- development - 要禁用 JSP 页面编译的访问时检查,请将其设置为
false
。 - genStringAsCharArray - 为了生成稍微更高效的字符数组,请将其设置为
true
。 - modificationTestInterval - 如果由于任何原因(例如动态生成 JSP)必须将 development 设置为
true
,则将其设置为高值将大大提高性能。 - trimSpaces - 为了从响应中删除不必要的字节,请考虑将其设置为
single
或extended
。
Web 应用程序编译
使用 Ant 是使用 JSPC 编译 Web 应用程序的首选方式。请注意,在预编译 JSP 时,只有在 suppressSmap 为 false 且 compile 为 true 时,SMAP 信息才会包含在最终类中。使用下面给出的脚本(“deployer”下载中包含一个类似的脚本)来预编译 Web 应用程序:
<project name="Webapp Precompilation" default="all" basedir=".">
<import file="${tomcat.home}/bin/catalina-tasks.xml"/>
<target name="jspc">
<jasper
validateXml="false"
uriroot="${webapp.path}"
webXmlInclude="${webapp.path}/WEB-INF/generated_web.xml"
outputDir="${webapp.path}/WEB-INF/src" />
</target>
<target name="compile">
<mkdir dir="${webapp.path}/WEB-INF/classes"/>
<mkdir dir="${webapp.path}/WEB-INF/lib"/>
<javac destdir="${webapp.path}/WEB-INF/classes"
debug="on" failonerror="false"
srcdir="${webapp.path}/WEB-INF/src"
excludes="**/*.smap">
<classpath>
<pathelement location="${webapp.path}/WEB-INF/classes"/>
<fileset dir="${webapp.path}/WEB-INF/lib">
<include name="*.jar"/>
</fileset>
<pathelement location="${tomcat.home}/lib"/>
<fileset dir="${tomcat.home}/lib">
<include name="*.jar"/>
</fileset>
<fileset dir="${tomcat.home}/bin">
<include name="*.jar"/>
</fileset>
</classpath>
<include name="**" />
<exclude name="tags/**" />
</javac>
</target>
<target name="all" depends="jspc,compile">
</target>
<target name="cleanup">
<delete>
<fileset dir="${webapp.path}/WEB-INF/src"/>
<fileset dir="${webapp.path}/WEB-INF/classes/org/apache/jsp"/>
</delete>
</target>
</project>
以下命令行可用于运行脚本(将令牌替换为 Tomcat 基础路径和要预编译的 Web 应用程序的路径):
$ANT_HOME/bin/ant -Dtomcat.home=<$TOMCAT_HOME> -Dwebapp.path=<$WEBAPP_PATH>
然后,必须将预编译期间生成的 Servlet 声明和映射添加到 Web 应用程序部署描述符中。将 ${webapp.path}/WEB-INF/generated_web.xml
插入到 ${webapp.path}/WEB-INF/web.xml
文件中的正确位置。重新启动 Web 应用程序(使用管理器)并进行测试,以验证它与预编译的 Servlet 运行正常。放置在 Web 应用程序部署描述符中的适当令牌也可以用于使用 Ant 过滤功能自动插入生成的 Servlet 声明和映射。这实际上是 Tomcat 分发的所有 Web 应用程序作为构建过程的一部分自动编译的方式。
在 jasper 任务中,您可以使用 addWebXmlMappings
选项将 ${webapp.path}/WEB-INF/generated_web.xml
自动合并到当前 Web 应用程序部署描述符 ${webapp.path}/WEB-INF/web.xml
中。
当您希望为 JSP 使用特定版本的 Java 时,请添加具有适当值的 javac 编译器任务属性 source
和 target
。例如,16
用于为 Java 16 编译 JSP。
对于生产环境,您可能希望使用 debug="off"
禁用调试信息。
当您不希望在第一个 JSP 语法错误处停止 JSP 生成时,请使用 failOnError="false"
,并使用 showSuccess="true"
打印所有成功的 JSP 到 Java 生成。有时,当您清理 ${webapp.path}/WEB-INF/src
中的生成 Java 源文件和 ${webapp.path}/WEB-INF/classes/org/apache/jsp
中的编译 JSP Servlet 类时,这会非常有用。
提示
- 当您切换到另一个 Tomcat 版本时,请使用新的 Tomcat 版本重新生成和重新编译您的 JSP。
- 使用 Servlet 上下文参数
org.apache.jasper.runtime.JspFactoryImpl.POOL_SIZE=-1
禁用 PageContext 池化,并使用 JSP Servlet 初始化参数limitBodyContentBuffer=true
限制缓冲区。请注意,更改默认值可能会影响性能,但具体影响会因应用程序而异。
优化
Jasper 中提供了许多扩展点,使用户能够针对其环境优化行为。
第一个扩展点是标签插件机制。这允许为 Web 应用程序提供标签处理器的替代实现。标签插件通过位于 WEB-INF
下的 tagPlugins.xml
文件注册。Jasper 中包含一个 JSTL 的示例插件。
第二个扩展点是表达式语言解释器。可以通过 ServletContext
配置替代解释器。有关如何配置替代 EL 解释器的详细信息,请参阅 ELInterpreterFactory
Javadoc。一个主要针对标签设置的替代解释器在 org.apache.jasper.optimizations.ELInterpreterTagSetters
提供。有关优化及其对规范合规性影响的详细信息,请参阅 Javadoc。
还提供了一个扩展点,用于将 String 值强制转换为 Enum。它在 org.apache.jasper.optimizations.StringInterpreterEnum
提供。有关优化及其对规范合规性影响的详细信息,请参阅 Javadoc。