介绍
Tomcat 10.1 使用 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 应用程序开发时使用。 有关配置 Jasper 以在生产 Tomcat 服务器上使用的信息,请参阅生产配置部分。
实现 Jasper 的 servlet 是使用全局 $CATALINA_BASE/conf/web.xml
中的 init 参数配置的。
-
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 参数 .true 或 false(默认为
true
)指定检查 JSP 以进行修改的频率。 -
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 fork JSP 页面,以便在与 Tomcat 不同的 JVM 中执行它们?true 或 false,默认为
true
。 -
genStringAsCharArray - 是否应该将文本字符串生成为 char 数组,以在某些情况下提高性能?默认为
false
。 -
javaEncoding - 用于生成 Java 源文件的 Java 文件编码。默认
UTF8
。 -
keepgenerated - 我们应该保留每个页面生成的 Java 源代码而不是删除它吗?true 或 false,默认为
true
。 -
mappedfile - 我们是否应该为每个输入行生成一个 print 语句的静态内容,以简化调试?true 或 false,默认为
true
。 -
maxLoadedJsps - 将为 Web 应用程序加载的最大 JSP 数。如果加载的 JSP 数超过此数目,则将卸载最近使用最少的 JSP,以便任何时候加载的 JSP 数都不会超过此限制。值为零或更小表示没有限制。默认值
-1
-
jspIdleTimeout - JSP 在卸载之前可以处于空闲状态的时间(以秒为单位)。值 0 或更小表示从不卸载。默认值
-1
-
modificationTestInterval - 导致在上次检查 JSP 是否修改后的指定时间间隔(以秒为单位)内不检查 JSP(及其从属文件)是否修改。值为 0 将导致在每次访问时都检查 JSP。仅在开发模式下使用。默认值为
4
秒。 -
recompileOnFail - 如果 JSP 编译失败,是否应该忽略 modificationTestInterval,并且下一次访问会触发重新编译尝试?仅在开发模式下使用,默认情况下处于禁用状态,因为编译可能很昂贵,并且可能导致资源使用过多。
-
scratchdir - 编译 JSP 页面时应该使用什么暂存目录?Default 是当前 Web 应用程序的工作目录。
-
suppressSmap - 是否应该禁止为 JSR45 调试生成 SMAP 信息?true 或 false,默认 false。
-
trimSpaces - 是否应该从输出中删除完全由空格组成的模板文本 (true),替换为单个空格 (single) 或保持不变 (false)?或者,扩展选项将从模板文本中删除前导和尾随空格,并将模板文本中的空格和换行符序列折叠为单个新行。请注意,如果 JSP 页面或标记文件将 trimDirectiveWhitespaces 值指定为 true,则该值将优先于该页面/标记的此配置设置。默认为
false
。 -
xpoweredBy - 确定 X-Powered-By 响应标头是否由生成的 Servlet 添加。true 或 false,默认
false
。 -
strictQuoteEscaping - 当 scriptlet 表达式用于属性值时,是否应严格应用 JSP.1.6 中用于转义引号字符的规则?true 或 false,默认为
true
。 -
quoteAttributeEL - 当 EL 用于 JSP 页面上的属性值时,是否应将 JSP.1.6 中描述的属性引用规则应用于表达式?true 或 false,默认为
true
。 -
variableForExpressionFactory - 用于表达式语言表达式工厂的变量的名称。如果未指定,将使用默认值
_el_expressionfactory
。 -
variableForInstanceManager - 用于实例管理器工厂的变量的名称。如果未指定,将使用默认值
_jsp_instancemanager
。 -
poolTagsWithExtends - 默认情况下,通过 page 指令的 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 init 参数值的标记缓冲区都将被销毁,并创建一个新缓冲区。true 或 false,默认
false
。 -
bodyContentTagBufferSize - 创建标记缓冲区时使用的大小(以字符为单位)。如果未指定,将使用默认值
org.apache.jasper.Constants.DEFAULT_TAG_BUFFER_SIZE
(512)。
Eclipse JDT 中的 Java 编译器作为默认编译器包含在内。 它是一个高级 Java 编译器,将从 Tomcat 类加载器加载所有依赖项, 这在编译具有数十个 JAR 的大型安装时将有很大帮助。 在快速服务器上,这将允许对大型 JSP 页面进行亚秒级的重新编译周期。
通过如上所述配置 compiler 属性,可以使用在以前的 Tomcat 版本中使用的 Apache Ant 来代替新的编译器。
如果需要更改应用程序的 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 上时才会对其进行处理。
已知问题
如错误 39089 中所述,在编译非常大的 JSP 时,
已知的 JVM 问题 bug 6294277 可能会导致 java.lang.InternalError: name is too long to represent exception
。
如果观察到这种情况,则可以使用以下方法之一来解决:
-
减小 JSP 的大小
-
通过将
suppressSmap
设置为true
来禁用 SMAP 生成和 JSR-045 支持。
生产配置
可以完成的主要 JSP 优化是 JSP 的预编译。 但是,这可能是不可能的(例如,在使用 jsp-property-group 功能时)或不切实际, 在这种情况下,Jasper servlet 的配置变得至关重要。
在生产 Tomcat 服务器中使用 Jasper 2 时,应考虑对默认配置进行以下更改。
-
development - 要禁用 JSP 页面编译的访问检查,请将此项设置为
false
。 -
genStringAsCharArray - 要生成效率稍高的 char 数组,请将此项设置为
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
与
${webapp.path}/WEB-INF/web.xml
中的当前 Web 应用程序部署描述符自动合并。
如果要将特定版本的 Java 用于 JSP,请添加具有适当值的 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 上下文参数禁用 PageContext 池
org.apache.jasper.runtime.JspFactoryImpl.POOL_SIZE=-1
,并使用 JSP Servlet init-paramlimitBodyContentBuffer=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 了解优化的详细信息以及它们对规范合规性的影响。