【问题标题】:JSP in Spring Boot application yields 404 Error no matter what I do无论我做什么,Spring Boot 应用程序中的 JSP 都会产生 404 错误
【发布时间】:2021-02-01 20:05:42
【问题描述】:

首先我会说我已经查看并尝试了我能找到的每个问题的解决方案。最大的问题是这些解决方案中的大多数都非常陈旧,而 Spring Boot 在过去几年中发生了很大变化。需要明确的是,我已经尝试过thisthisthisthis 等等。我还阅读了许多教程。没有任何效果。

我有一个全新的 Spring Boot 应用程序,我正在尝试使用它进行 JSP 渲染。这些是我的依赖项:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>[2.3.4.RELEASE,3)</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
        <version>[2.3.4.RELEASE,3)</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
        <version>[2.3.4.RELEASE,3)</version>
    </dependency>

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>

    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>[2.8.0,3)</version>
    </dependency>

    <dependency>
        <groupId>de.mkammerer</groupId>
        <artifactId>argon2-jvm</artifactId>
        <version>[2.7,3)</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>[8.0.21,9)</version>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>org.glassfish.jaxb</groupId>
        <artifactId>jaxb-runtime</artifactId>
        <version>[2.3.2,)</version>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <version>[2.3.4.RELEASE,3)</version>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <version>[9.0.38,)</version>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <version>[2.3.4.RELEASE,3)</version>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

我的项目布局如下:

- source
  - production
    - java
      - [my source code packages]
    - resources
      - WEB-APP
        - jsp
          - initialization
            - begin.jsp
      - [my resource packages]
  - test
    - java
    - resources

“WEB-APP/jsp”只是我尝试过的最新版本。我试过“WEB-INF/jsp”、“META-INF/jsp”、“webapp/jsp”、没有父级(只是“jsp”)等等,结果都一样。

我知道父目录有点不标准,但它在 Maven 中配置正确,我已经确认它不是我的问题的根源:

<build>
    <sourceDirectory>source/production/java</sourceDirectory>
    <resources>
        <resource>
            <directory>source/production/resources</directory>
        </resource>
    </resources>
    ...
</build>

我的Application类如下:

@SpringBootApplication(scanBasePackages="com.my.project")
@EnableWebMvc
@EnableJpaRepositories("com.my.project.repository")
@EntityScan("com.my.project.model")
public class Application
{
    private static final Logger LOGGER = LogManager.getLogger(Application.class);

    public Application()
    {
    }

    @Bean
    public ViewResolver viewResolver()
    {
        LOGGER.info("Constructing InternalResourceViewResolver[JstlView]");
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-APP/jsp/");
        resolver.setSuffix(".jsp");
        resolver.setViewClass(JstlView.class);
        resolver.setRedirectContextRelative(true);
        resolver.setRedirectHttp10Compatible(false);
        return resolver;
    }

    public static void main(final String[] args)
    {
        SpringApplication.run(Application.class, args);
    }
}

还有我的控制器:

@Controller
public class InitializationController
{
    private static final Logger LOGGER = LogManager.getLogger(InitializationController.class);

    @GetMapping("/initialize_application")
    public String beginInitialization(ModelMap model)
    {
        LOGGER.info("Beginning initialization");
        ...
        LOGGER.info("Returning view");
        return "initialization/begin";
    }
    ...
}

在启动时,我看到“Constructing InternalResourceViewResolver”日志条目(我的视图解析器 bean 已创建)。当我转到/initialize_application 时,出现以下错误:

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Sun Oct 18 21:45:26 CDT 2020
There was an unexpected error (type=Not Found, status=404).

再次查看日志,我看到“开始初始化”和“返回视图”,所以我知道 404 是针对我的 JSP 而不是我的控制器。我的控制器正在工作。

我尝试过的其他事情:

  • 最初我的应用程序中没有@EnableWebMvc。没有它,除了我的日志语句之外,日志是空的。当我添加 @EnableWebMvc 时,现在使用 404 记录:No mapping for GET /WEB-APP/jsp/initialization/begin.jsp(或除“WEB-APP”之外我尝试过的任何其他目录)。
  • 我试过用mvn spring-boot:run直接在纯命令行上运行它
  • 我尝试在 IntelliJ IDEA 中使用 Maven 运行配置和命令 spring-boot:run 运行它(结果相同)
  • &lt;packaging&gt;jar&lt;/packaging&gt;&lt;packaging&gt;war&lt;/packaging&gt; 我都试过了,但都没有效果,因为既没有制作过 JAR 也没有制作过 WAR。 Maven 直接在 target/classes 目录之外运行应用程序,而不是创建工件。
  • 当我尝试WEB-INFMETA-INF 而不是WEB-APPwebapp 或其他东西时,我看到了一个记录的警告:Path with "WEB-INF" or "META-INF": [WEB-INF/jsp/initialization/begin.jsp]

我还确认我的 JSP 存在于 target/classes/WEB-APP/jsp(或我尝试过的除“WEB-APP”之外的任何其他目录)中,因此它们确实存在。

我不知道如何继续。我开始认为我需要放弃 Spring Boot 并坚持使用带有 Servlet 配置和 Tomcat 安装的传统样板 Spring Web MVC 应用程序,但我对 Spring Boot 的“只是运行”方面感到非常兴奋。任何帮助将不胜感激。

更新 1

在阅读this Spring documentation about JSP limitations 之后,我现在知道我必须使用&lt;packaging&gt;war&lt;/packaging&gt;,并且我现在正在使用它,但它并没有起到任何作用。我开始怀疑这里的根本问题是 maven spring-boot:run 没有创建 WAR 并运行它,它只是将所有内容构建到 target/classes 并从那里运行它。

另外,在找到this old, official Spring boot samples application之后,我稍微改变了我的项目结构:

- source
  - production
    - java
      - [my source code packages]
    - resources
      - [my resource packages]
    - webapp
      - META-INF
      - WEB-INF
      - jsp
        - initialization
          - begin.jsp
  - test
    - java
    - resources

更新了我的视图解析器配置:

resolver.setPrefix("/jsp/");
resolver.setSuffix(".jsp");

并将其添加到我的 POM 中:

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.3.1</version>
            <configuration>
                <warSourceDirectory>source/production/webapp</warSourceDirectory>
            </configuration>
        </plugin>

如果我运行 mvn package,我的 WAR 会正确创建(类和 JSP 都应该在它们应该在的位置),但 mvn spring-boot:runmvn package spring-boot:run 都不起作用 - 我在解析我的 JSP 时仍然遇到 404 错误。

上面链接的旧 Spring Boot 示例应用程序将 JSP 放在 WEB-INF/jsp 中,但我不能这样做,因为这会导致警告 Path with "WEB-INF" or "META-INF": [WEB-INF/jsp/initialization/begin.jsp](仍然是 404)。令人沮丧的是,这个示例应用程序不再存在,也没有任何新的变体。我找不到任何适用于最新版本 Spring Boot 的更新版本。示例应用程序在 2.2.x 中被删除。

【问题讨论】:

  • 您是否使用spring initializr创建项目?
  • 不是我用那个创建的,不是。
  • 不要使用 JSP,而是使用 thymeleaf 之类的东西。话虽这么说,如果你真的想要,正如文档所述,它只在使用战争时才有效,你需要通过 Spring Boot 创建它(所以从你的构建中删除 maven-war-plugin)。您必须将 tomcat 添加为提供的依赖项,否则它将不起作用,并且您还必须包含 jasper(用于编译)。然后您可以部署war 文件或使用java -jar your.war 来运行应用程序。同时删除注释,只留下@SpringBootApplication那些注释会干扰自动配置。
  • 我不想用Thymeleaf,我想用JSP。我将其设置为&lt;packaging&gt;war&lt;/packaging&gt;(见上文)。关于使用 Spring Boot 而不是 maven-war-plugin 创建战争的文档在哪里?我没看到,使用&lt;packaging&gt;war&lt;/packaging&gt; 会自动添加maven-war-plugin。我确实添加了 Tomcat+Jasper 作为提供的依赖项和 JSTL 作为编译依赖项,就像示例应用程序一样。 Spring Boot 文档说您可以使用mvn spring-boot:run 运行。他们并没有说添加 JSP 意味着您必须使用java -jar。没有其他注释我的应用程序错误。
  • 在您的“初始”尝试中,InternalResourceViewResolver 的配置也存在缺陷。由于 JSP 被放置在类路径中(因为 source/production/rsources 被标记为这样,而不是在单独的 Web 目录中。所以你应该在前缀前面加上 classpath。你也不应该使用 @EnableWebMvc 因为这会禁用spring boot 的自动配置。您也不必配置InternalResourceViewResolver,而是可以添加spring.mvc.view.prefix=classpath:/WEB-APP/jsp/spring.mvc.view.suffix=.jsp 来实现相同的效果。

标签: java spring spring-boot jsp spring-boot-maven-plugin


【解决方案1】:

假设您的 Web 内容位于以下位置(应该在类路径 AFAIK 之外)source/production/webapp。 Spring Boot 将忽略这一点,因为 DocumentRoot 中的硬编码路径用于在从命令行或 IDE 运行时检测目录(它会在构建战争并运行时起作用)。

作为一种解决方法,您可以添加 TomcatContextCustomizer 作为 bean 来检测路径并将其设置为正确的基础。

package com.my.project;

@SpringBootApplication
public class Application extends SpringBootServletInitializer 
{
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) 
    {
        return application.sources(DemoApplication.class);
    }

    public static void main(final String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public TomcatContextCustomizer docBaseCustomizer() 
    {
        return new TomcatContextCustomizer() 
        {
            public void customize(Context context) 
            {
                File root = new File("source/production/webapp");
                if (root.exists() && root.isDirectory()) 
                {
                    context.setDocBase(root.getAbsolutePath());
                }        
            }
        }
    } 
}

现在将以下内容添加到您的application.properties

spring.mvc.view.prefix=/jsp/
spring.mvc.view.suffix=.jsp

注意:只有当您的 @SpringBootApplication 注释类位于 com.my.project 包中时,才能删除其他注释。然后它将自动检测其他类(如实体和存储库)。

【讨论】:

    【解决方案2】:

    您可以尝试将tomcat-embed-jasper 的范围更改为provided,因为编译JSP 需要此依赖项。

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>[9.0.38,)</version>
            <scope>provided</scope>
        </dependency>
    

    编辑:

    我在互联网上寻找各种spring-boot + jsp 项目。我注意到他们也有spring-boot-starter-tomcatprovided 范围。你可以试试这个吗?

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <version>[2.3.4.RELEASE,3)</version>
            <scope>provided</scope>
        </dependency>
    

    参考资料:

    https://mkyong.com/spring-boot/spring-boot-hello-world-example-jsp/

    https://dzone.com/articles/spring-boot-2-with-jsp-view

    Edit-2:

    所以这次我创建了一个新的 springboot 项目。做了最低限度的设置来渲染jsp。所以基本上我关注了这个tutorial,我的项目运行良好。

    然后我用你的替换了 pom.xml,我得到了你在问题中提到的同样的错误。

    然后在反复试验时,我从&lt;artifactId&gt;tomcat-embed-jasper&lt;/artifactId&gt; 中删除了&lt;version&gt;[9.0.38,)&lt;/version&gt;,它开始为我工作。

            <!--I have removed version here and it started working for me-->
            <dependency>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-jasper</artifactId>
    <!--            <version>[9.0.38,)</version>-->
                <scope>provided</scope>
            </dependency>
    

    虽然我有不同的目录结构。但正如你提到的,这不是问题的原因。

    我已将项目上传到 github。随意拉它在本地运行它。

    Github

    【讨论】:

    • 对不起,这是我上面的问题中的一个错字。它实际上是我的文件夹结构中的initialization。我正在更正问题的内容。
    • 好的,我已经修复了问题的内容。很棒的收获!我只是希望它真的那么容易。唉,事实并非如此。
    • 如果可能的话,你能把你的项目放在 GitHub 上吗?这样我们就可以在本地运行项目了。
    • 我无法将我的项目放在 GitHub 上。它是专有的。
    • 抱歉,直到刚才我才看到您更改了答案。我将范围更改为“提供”,它没有任何区别。相同的警告和错误,相同的行为。
    猜你喜欢
    • 2018-05-20
    • 2017-09-18
    • 2012-08-11
    • 1970-01-01
    • 2021-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-26
    相关资源
    最近更新 更多