如何在Spring启动应用程序中预编译jsp?

Hug*_*ège 10 spring jsp spring-mvc maven spring-boot

我正在使用Spring启动,之前我们使用Spring和Tomcat.两年前我们使用Spring和Tomcat时,我们使用maven插件来预编译jsp.在部署之后避免每次首次访问都要进行编译是非常有用的.

但是,我们知道的所有maven插件都会转储一个web.xml文件,该文件列出了所有jsp和相关的生成的servlet.使用Spring启动时,它不再使用web.xml,因此忽略此文件.

我们仍然有编辑,这是一个安全带,但每页第一次访问都会受到惩罚.

有人知道是否可以在Spring启动应用程序中预编译jsp?

pau*_*lcm 18

我预编译在服务器启动时(不必使用JspC,如此简单的构建文件)和构建时(更快的服务器启动时间)工作.我动态注册生成的servlet,因此如果添加/删除JSP,则不必手动更改任何文件.

在服务器启动时

使用ServletRegistration.Dynamic注册一个JSP_SERVLET_CLASS为每个JSP的servlet.使用设置JSP文件名(ref)initParameter jspFile

例如,对于ServletContextInitializer(ref)中的SpringBoot :

@Bean
public ServletContextInitializer preCompileJspsAtStartup() {
    return servletContext -> {
        getDeepResourcePaths(servletContext, "/WEB-INF/jsp/").forEach(jspPath -> {
            log.info("Registering JSP: {}", jspPath);
            ServletRegistration.Dynamic reg = servletContext.addServlet(jspPath, Constants.JSP_SERVLET_CLASS);
            reg.setInitParameter("jspFile", jspPath);
            reg.setLoadOnStartup(99);
            reg.addMapping(jspPath);
        });
    };
}

private static Stream<String> getDeepResourcePaths(ServletContext servletContext, String path) {
    return (path.endsWith("/")) ? servletContext.getResourcePaths(path).stream().flatMap(p -> getDeepResourcePaths(servletContext, p))
            : Stream.of(path);
}
Run Code Online (Sandbox Code Playgroud)

在构建时

使用JspC(ref)为每个JSP和web.xml生成Java源文件及其servlet映射.

然后使用ServletContext(通过web.xml使用Tomcat 解析WebXmlParser,例如SpringBoot来注册这些:

@Value("classpath:precompiled-jsp-web.xml")
private Resource precompiledJspWebXml;

@Bean
public ServletContextInitializer registerPreCompiledJsps() {
    return servletContext -> {
        // Use Tomcat's web.xml parser (assume complete XML file and validate).
        WebXmlParser parser = new WebXmlParser(false, true, true);
        try (InputStream is = precompiledJspWebXml.getInputStream()) {
            WebXml webXml = new WebXml();
            boolean success = parser.parseWebXml(new InputSource(is), webXml, false);
            if (!success) {
                throw new RuntimeException("Error parsing Web XML " + precompiledJspWebXml);
            }
            for (ServletDef def :  webXml.getServlets().values()) {
                log.info("Registering precompiled JSP: {} = {} -> {}", def.getServletName(), def.getServletClass());
                ServletRegistration.Dynamic reg = servletContext.addServlet(def.getServletName(), def.getServletClass());
                reg.setLoadOnStartup(99);
            }

            for (Map.Entry<String, String> mapping : webXml.getServletMappings().entrySet()) {
                log.info("Mapping servlet: {} -> {}", mapping.getValue(), mapping.getKey());
                servletContext.getServletRegistration(mapping.getValue()).addMapping(mapping.getKey());
            }
        } catch (IOException e) {
            throw new RuntimeException("Error registering precompiled JSPs", e);
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

示例Maven配置生成并编译JSP类,并生成precompiled-jsp-web.xml:

<!-- Needed to get the jasper Ant task to work (putting it in the plugin's dependencies didn't work) -->
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-catalina-ant</artifactId>
    <version>8.0.32</version>
    <scope>provided</scope>
</dependency>

<!-- ... -->

<plugin>
    <artifactId>maven-antrun-plugin</artifactId>
    <version>1.8</version>
    <executions>
        <execution>
            <id>precompile-jsp-generate-java</id>
            <!-- Can't be generate-sources because we need the compiled Henry taglib classes already! -->
            <phase>compile</phase>
            <goals>
                <goal>run</goal>
            </goals>
            <configuration>
                <tasks>
                    <echo message="Precompiling JSPs"/>
                    <property name="compile_classpath" refid="maven.compile.classpath"/>
                    <property name="target_dir" value="${project.basedir}/generated-sources/jspc" />
                    <path id="jspc_classpath">
                        <path path="${compile_classpath}"/>
                    </path>

                    <typedef resource="org/apache/catalina/ant/catalina.tasks" classpathref="jspc_classpath"/>

                    <mkdir dir="${target_dir}/java"/>
                    <mkdir dir="${target_dir}/resources"/>
                    <jasper
                            validateXml="false"
                            uriroot="${project.basedir}/src/main/webapp"
                            compilertargetvm="1.8"
                            compilersourcevm="1.8"
                            failonerror="true"
                            javaencoding="UTF-8"
                            webXml="${target_dir}/resources/precompiled-jsp-web.xml"
                            outputDir="${target_dir}/java/" >
                    </jasper>
                    <!-- Can't use Maven to compile the JSP classes because it has already compiled the app's classes
                         (needed to do that becuase JspC needs compiled app classes) -->
                    <javac srcdir="${target_dir}/java" destdir="${project.build.outputDirectory}" classpathref="jspc_classpath" fork="true"/>
                    <!-- Have to copy the web.xml because process-resources phase has already finished (before compile) -->
                    <copy todir="${project.build.outputDirectory}">
                        <fileset dir="${target_dir}/resources"/>
                    </copy>
                </tasks>
            </configuration>
        </execution>
    </executions>
</plugin>
<!-- Not strictly necessary, because Ant does the compilation, but at least attempts to keep it in sync with Maven -->
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>build-helper-maven-plugin</artifactId>
    <executions>
        <execution>
            <id>add-precompiled-jsp-java-sources</id>
            <phase>generate-sources</phase>
            <goals><goal>add-source</goal></goals>
            <configuration>
                <sources>
                    <source>${project.basedir}/generated-sources/jspc/java</source>
                </sources>
            </configuration>
        </execution>
        <execution>
            <id>add-precompiled-jsp-resources</id>
            <phase>generate-resources</phase>
            <goals><goal>add-resource</goal></goals>
            <configuration>
                <resources>
                    <resource>
                        <directory>${project.basedir}/generated-sources/jspc/resources</directory>
                    </resource>
                </resources>
            </configuration>
        </execution>
    </executions>
</plugin>
Run Code Online (Sandbox Code Playgroud)

  • 你是一个天才,我希望我可以将这个帖子提升10倍 (4认同)