Jasper 2 JSP 引擎使用指南

目录

简介

Tomcat 10.1 使用 Jasper 2 JSP 引擎来实现 JavaServer Pages 2.3 规范。

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 - 是否应使用调试信息编译类文件?truefalse,默认值为 true
  • classpath - 定义用于编译生成的 servlet 的类路径。此参数仅在 ServletContext 属性 org.apache.jasper.Constants.SERVLET_CLASSPATH 未设置时有效。当 Jasper 在 Tomcat 中使用时,此属性始终被设置。默认情况下,类路径是根据当前 Web 应用程序动态创建的。
  • compiler - Ant 应使用哪个编译器来编译 JSP 页面。此参数的有效值与 Ant 的 javac 任务的 compiler 属性相同。如果未设置该值,则将使用默认的 Eclipse JDT Java 编译器,而不是使用 Ant。没有默认值。如果设置了此属性,则应使用 setenv.[sh|bat]ant.jarant-launcher.jartools.jar 添加到 CLASSPATH 环境变量中。
  • compilerSourceVM - 源文件与哪个 JDK 版本兼容?(默认值:11
  • compilerTargetVM - 生成的文件与哪个 JDK 版本兼容?(默认值:11
  • development - Jasper 是否在开发模式下使用?如果为 true,则可以通过 modificationTestInterval 参数指定检查 JSP 修改的频率。truefalse,默认值为 true
  • displaySourceFragment - 是否应在异常消息中包含源代码片段?truefalse,默认值为 true
  • dumpSmap - 是否将用于 JSR45 调试的 SMAP 信息转储到文件?truefalse,默认值为 false。如果 suppressSmap 为 true,则为 false
  • enablePooling - 确定是否启用标签处理程序池。这是一个编译选项。它不会改变已经编译的 JSP 的行为。truefalse,默认值为 true
  • engineOptionsClass - 允许指定用于配置 Jasper 的 Options 类。如果不存在,将使用默认的 EmbeddedServletOptions。如果在 SecurityManager 下运行,则忽略此选项。
  • errorOnUseBeanInvalidClassAttribute - 当 useBean 操作中 class 属性的值不是有效的 bean 类时,Jasper 是否应该发出错误?truefalse,默认值为 true
  • fork - 让 Ant 分叉 JSP 页面编译,以便它们在与 Tomcat 分开的 JVM 中执行?truefalse,默认值为 true
  • genStringAsCharArray - 是否应该将文本字符串生成为字符数组,以在某些情况下提高性能?默认值为 false
  • javaEncoding - 用于生成 java 源文件的 Java 文件编码。默认值为 UTF8
  • keepgenerated - 我们是否应该保留每个页面的生成的 Java 源代码,而不是删除它?truefalse,默认值为 true
  • mappedfile - 我们是否应该使用每行输入一个打印语句生成静态内容,以方便调试?truefalse,默认值为 true
  • maxLoadedJsps - 将为 Web 应用程序加载的 JSP 的最大数量。如果加载的 JSP 超过此数量,将卸载最近最少使用的 JSP,以便在任何时候加载的 JSP 数量不超过此限制。值为零或更小表示没有限制。默认值为 -1
  • jspIdleTimeout - JSP 在被卸载之前可以处于空闲状态的时间(以秒为单位)。值为零或更小表示永不卸载。默认值为 -1
  • modificationTestInterval - 导致 JSP(及其依赖文件)在上次检查 JSP 修改后的指定时间间隔(以秒为单位)内不会被检查修改。值为 0 将导致 JSP 在每次访问时都被检查。仅在开发模式下使用。默认值为 4 秒。
  • recompileOnFail - 如果 JSP 编译失败,是否应该忽略 modificationTestInterval 并在下次访问时触发重新编译尝试?仅在开发模式下使用,默认情况下禁用,因为编译可能很昂贵,并且可能导致过度使用资源。
  • scratchdir - 编译 JSP 页面时应该使用哪个 scratch 目录?默认值为当前 Web 应用程序的工作目录。如果在 SecurityManager 下运行,则忽略此选项。
  • suppressSmap - 是否应该抑制为 JSR45 调试生成 SMAP 信息?truefalse,默认值为 false
  • trimSpaces - 模板文本是否应该完全删除空白字符(true)、替换为单个空格(single)或保持不变(false)?或者,extended 选项将删除模板文本中的前导和尾随空白字符,并将模板文本中的空白字符和换行符序列折叠为单个换行符。请注意,如果 JSP 页面或标签文件指定 trimDirectiveWhitespaces 值为 true,则该值将优先于此配置设置,适用于该页面/标签。默认值为 false
  • xpoweredBy - 确定生成的 servlet 是否添加 X-Powered-By 响应头。truefalse,默认值为 false
  • strictQuoteEscaping - 当脚本表达式用于属性值时,是否应严格应用 JSP.1.6 中关于转义引号字符的规则?truefalse,默认值为 true
  • quoteAttributeEL - 当在 JSP 页面上的属性值中使用 EL 时,是否应将 JSP.1.6 中描述的属性引号规则应用于表达式?truefalse,默认值为 true
  • variableForExpressionFactory - 用于表达式语言表达式工厂的变量名称。如果未指定,将使用默认值 _el_expressionfactory
  • variableForInstanceManager - 用于实例管理器工厂的变量名称。如果未指定,将使用默认值 _jsp_instancemanager
  • poolTagsWithExtends - 默认情况下,通过页面指令的 extends 属性使用自己的基类的 JSP 将禁用标签池,因为 Jasper 无法保证必要的初始化已完成。这会对性能产生负面影响。如果提供替代基类从 Servlet.init() 调用 _jspInit(),将此属性设置为 true 将启用使用替代基类的池。如果替代基类未调用 _jspInit() 且此属性为 true,则在尝试使用标签时将发生 NPE。truefalse,默认值为 false
  • strictGetProperty - 如果为 true,则会强制执行 JSP 2.0 及更高版本规范中 JSP.5.3 章中指定的,要求在 jsp:getProperty 操作中引用的对象先前已“引入”到 JSP 处理器。truefalse,默认值为 true
  • strictWhitespace - 如果为 false,则会放宽对属性名称之前的空白字符的要求,因此缺少空白字符不会导致错误。truefalse,默认值为 true
  • jspServletBase - 从 JSP 生成的 Servlet 的基类。如果未指定,将使用 org.apache.jasper.runtime.HttpJspBase 的默认值。
  • serviceMethodName - 基类调用的服务方法的名称。如果未指定,将使用 _jspService 的默认值。
  • servletClasspathAttribute - 提供 JSP 类路径的 ServletContext 属性的名称。如果未指定,将使用 org.apache.catalina.jsp_classpath 的默认值。
  • jspPrecompilationQueryParameter - 导致 JSP 引擎仅预生成 Servlet 而不调用它的查询参数的名称。如果未指定,将使用 jsp_precompile 的默认值,如 JSP 规范 (JSP.11.4.2) 中定义的那样。
  • generatedJspPackageName - 编译后的 JSP 的默认包名。如果未指定,将使用 org.apache.jsp 的默认值。
  • generatedTagFilePackageName - 从标签文件生成的标签处理程序的默认包名。如果未指定,将使用 org.apache.jsp.tag 的默认值。
  • tempVariableNamePrefix - 用于生成的临时变量名的前缀。如果未指定,将使用 _jspx_temp 的默认值。
  • useInstanceManagerForTags - 如果为 true,则使用实例管理器获取标签处理程序实例。truefalse,默认值为 false
  • limitBodyContentBuffer - 如果为 true,则任何扩展超出 bodyContentTagBufferSize 初始化参数值的标签缓冲区都将被销毁并创建一个新的缓冲区。truefalse,默认值为 false
  • bodyContentTagBufferSize - 创建标签缓冲区时使用的尺寸(以字符为单位)。如果未指定,将使用 org.apache.jasper.Constants.DEFAULT_TAG_BUFFER_SIZE (512) 的默认值。

默认编译器为来自 Eclipse JDT 的 Java 编译器。它是一个高级 Java 编译器,它将从 Tomcat 类加载器加载所有依赖项,这在编译包含数十个 JAR 的大型安装时将非常有帮助。在快速服务器上,这将允许即使对于大型 JSP 页面也能实现亚秒级的重新编译周期。

可以使用 Apache Ant 代替新的编译器,方法是按照上面解释的方式配置编译器属性。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 上时才会被处理。

已知问题

错误 39089 中所述,一个已知的 JVM 问题, 错误 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 - 要从响应中删除不必要的字节,请考虑将其设置为 singleextended

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 编译器任务属性sourcetarget。例如,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 初始化参数limitBodyContentBuffer=true限制缓冲。请注意,更改默认值可能会影响性能,但会根据应用程序的不同而有所不同。

优化

Jasper 中提供了一些扩展点,使用户能够优化其环境的行为。

第一个扩展点是标签插件机制。这允许为 Web 应用程序提供标签处理程序的替代实现。标签插件通过位于WEB-INF下的tagPlugins.xml文件注册。Jasper 包含一个针对 JSTL 的示例插件。

第二个扩展点是表达式语言解释器。可以通过ServletContext配置备用解释器。有关如何配置备用 EL 解释器的详细信息,请参阅ELInterpreterFactory javadoc。org.apache.jasper.optimizations.ELInterpreterTagSetters提供了一个主要针对标签设置的备用解释器。有关优化的详细信息及其对规范合规性的影响,请参阅 javadoc。

还提供了一个扩展点用于将字符串值强制转换为枚举。它位于org.apache.jasper.optimizations.StringInterpreterEnum。有关优化的详细信息及其对规范合规性的影响,请参阅 javadoc。