无论我做什么,Spring Boot应用程序中的JSP都会产生404错误

时间:2020-10-19 03:17:14

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

首先,我会说我已经查看并尝试了有关此问题的每个问题的解决方案。最大的问题是这些解决方案中的大多数都非常老旧,并且在过去几年中,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>

我的应用程序类如下:

@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
  • 在纯命令行上直接运行它
  • 我尝试使用Maven运行配置和命令spring-boot:run(相同的结果)在IntelliJ IDEA中运行它
  • 我已经尝试过<packaging>jar</packaging><packaging>war</packaging>,但是两者都没有什么不同,因为JAR和WAR都没有。 Maven直接在target/classes目录之外运行该应用程序,而不创建工件。
  • 当我尝试使用WEB-INFMETA-INF而不是WEB-APPwebapp或其他方法时,我看到了一条已记录的警告:Path with "WEB-INF" or "META-INF": [WEB-INF/jsp/initialization/begin.jsp] < / li>

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

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

更新1

在阅读this Spring documentation about JSP limitations之后,我现在知道必须使用<packaging>war</packaging>,并且现在正在使用它,但是并没有什么不同。我开始怀疑这里的根本问题是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都无法工作-我仍然遇到404错误,无法解决JSP。

上面链接到的旧的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中删除。

2 个答案:

答案 0 :(得分:1)

您可以尝试将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-tomcat范围的provided。你可以试试这个吗?

    <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,并且出现了您在问题中提到的相同错误。

然后,在进行反复试验时,我从<version>[9.0.38,)</version>中移除了<artifactId>tomcat-embed-jasper</artifactId>,它开始为我服务。

        <!--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

答案 1 :(得分:-1)

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

作为一种解决方法,您可以添加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包中时,才能删除其他注释。然后它将自动检测其他类(例如实体和存储库)。