英文:
JSP in Spring Boot application yields 404 Error no matter what I do
问题
我首先要说明,我已经查看并尝试了关于这个问题的每一个问题中的解决方案。最大的问题是,大多数这些解决方案都非常陈旧,在过去的几年里,Spring Boot 已经发生了很大变化。明确地说,我已经尝试过 这个,这个,这个,这个,还有更多。我也阅读了许多教程。但都没有奏效。
我有一个全新的 Spring Boot 应用程序,我正试图让 JSP 渲染工作起来。这是我的依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>[2.3.4.RELEASE,3)</version>
</dependency>
<!-- 其他依赖... -->
</dependencies>
我的项目结构如下:
- source
- production
- java
- resources
- WEB-APP
- jsp
- initialization
- begin.jsp
我知道父级目录有些不太标准,但在 Maven 中已经正确配置,我已经确认这不是问题的根源。
我的 Application
类如下:
@SpringBootApplication(scanBasePackages="com.my.project")
public class Application {
// ... 省略部分代码 ...
@Bean
public ViewResolver viewResolver() {
// ... 省略部分代码 ...
}
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
}
我的控制器:
@Controller
public class InitializationController {
// ... 省略部分代码 ...
@GetMapping("/initialize_application")
public String beginInitialization(ModelMap model) {
// ... 省略部分代码 ...
return "initialization/begin";
}
}
但当我访问 /initialize_application
时,出现了 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
(结果相同)。 - 我尝试了
<packaging>jar</packaging>
和<packaging>war</packaging>
,但都没有区别,因为既不会创建 JAR 也不会创建 WAR。Maven 直接从target/classes
目录运行应用程序。 - 当我尝试将
WEB-INF
或META-INF
改为WEB-APP
或webapp
时,会看到一个警告:Path with "WEB-INF" or "META-INF": [WEB-INF/jsp/initialization/begin.jsp]
。
我确认了我的 JSP 存在于 target/classes/WEB-APP/jsp
(或者其他我尝试过的目录,除了 "WEB-APP"),所以它们确实存在。
现在我感到困惑不解。我开始觉得我需要放弃 Spring Boot,改用传统的 Spring Web MVC 应用程序,使用 Servlet 配置和 Tomcat 安装,但我真的很喜欢 Spring Boot 的 "just runs" 特性。感谢任何帮助。
更新 1
在阅读了关于 JSP 限制的这部分 Spring 文档 之后,我现在知道我必须使用 <packaging>war</packaging>
,我正在使用它,但没有任何改变。我开始怀疑根本问题是,maven spring-boot:run
不会创建 WAR 并运行它,它只是将一切构建到 target/classes
并从那里运行。
此外,在找到了这个旧的、官方的 Spring Boot 示例应用程序之后,我对项目结构进行了一些更改:
- source
- production
- java
- resources
- webapp
- META-INF
- WEB-INF
- jsp
- initialization
- begin.jsp
我更新了视图解析器的配置:
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:run
和 mvn package spring-boot:run
都不起作用——我仍然得到 404 错误,无法解析我的 JSP。
以上是您提供的内容的翻译。如果您有其他需要翻译的内容或有任何问题,请随时告诉我。
英文:
I'll start off by saying I've looked at and tried the solutions in every question regarding this that I can find. The biggest problem is that most of these solutions are very old, and Spring Boot has changed a lot in the last several years. To be clear, I've tried this, this, this, this, and more. I've also read numerous tutorials. Nothing works.
I have a brand new Spring Boot application and I'm trying to get JSP rendering working with it. These are my dependencies:
<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>
My project is laid out as follows:
- source
- production
- java
- [my source code packages]
- resources
- WEB-APP
- jsp
- initialization
- begin.jsp
- [my resource packages]
- test
- java
- resources
"WEB-APP/jsp" is just the latest iteration I've tried. I've tried "WEB-INF/jsp", "META-INF/jsp", "webapp/jsp", no parent (just "jsp"), etc., all with the same results.
I know the parent directories are a bit non-standard, but it's configured correctly in Maven and I've confirmed it's not the source of my problems:
<build>
<sourceDirectory>source/production/java</sourceDirectory>
<resources>
<resource>
<directory>source/production/resources</directory>
</resource>
</resources>
...
</build>
My Application class is as follows:
@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);
}
}
And my Controller:
@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";
}
...
}
On startup I see the "Constructing InternalResourceViewResolver" log entry (my view resolver bean is created). When I go to /initialize_application
, I get the following error:
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).
Looking in the log again, I see "Beginning initialization" and "Returning view," so I know that the 404 is for my JSP and not my controller. My controller is working.
Other things I've tried:
- Initially I did not have
@EnableWebMvc
on my application. Without it, the log was empty except my log statements. When I added@EnableWebMvc
, this is now logged with the 404:No mapping for GET /WEB-APP/jsp/initialization/begin.jsp
(or whatever other directory I've tried other than "WEB-APP"). - I've tried running this directly on the pure command line with
mvn spring-boot:run
- I've tried running this in IntelliJ IDEA with a Maven run configuration and command
spring-boot:run
(same result) - I've tried both
<packaging>jar</packaging>
and<packaging>war</packaging>
, but neither make a difference, because neither a JAR nor a WAR are ever made. Maven runs the application directly out of thetarget/classes
directory instead of creating an artifact. - When I've tried
WEB-INF
orMETA-INF
instead ofWEB-APP
orwebapp
or something else, I've seen a logged warning:Path with "WEB-INF" or "META-INF": [WEB-INF/jsp/initialization/begin.jsp]
I have also confirmed that my JSPs are present in target/classes/WEB-APP/jsp
(or whatever other directory I've tried other than "WEB-APP"), so they do exist.
I'm at a loss how to proceed. I'm beginning to think I need to ditch Spring Boot and stick with a traditional boilerplate Spring Web MVC application with a Servlet config and a Tomcat installation, but I was really excited about the "just runs" aspect of Spring Boot. Any help would be appreciated.
UPDATE 1
After reading this Spring documentation about JSP limitations, I now know that I have to use <packaging>war</packaging>
, and I'm using that now, but it hasn't made a difference. I'm starting to suspect that the underlying problem here is that maven spring-boot:run
doesn't create a WAR and run it, it just builds everything to target/classes
and runs it from there.
Also, after finding this old, official Spring boot samples application, I've changed my project structure a little:
- source
- production
- java
- [my source code packages]
- resources
- [my resource packages]
- webapp
- META-INF
- WEB-INF
- jsp
- initialization
- begin.jsp
- test
- java
- resources
Updated my view resolver configuration:
resolver.setPrefix("/jsp/");
resolver.setSuffix(".jsp");
And added this to my 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>
If I run mvn package
, my WAR gets created correctly (classes and JSPs all where they should be), but neither mvn spring-boot:run
nor mvn package spring-boot:run
work—I still get 404 errors resolving my JSPs.
The old Spring Boot sample application linked to above puts the JSPs in WEB-INF/jsp
, but I can't do that, because that results in the warning Path with "WEB-INF" or "META-INF": [WEB-INF/jsp/initialization/begin.jsp]
(and still 404). What's frustrating is that this sample application doesn't exist anymore, nor does any new variation of it. I can't find any updated version that works with the newest version of Spring Boot. The sample application was deleted in 2.2.x.
答案1
得分: 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>
Edit:
我在互联网上寻找了各种 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:
这次我创建了一个新的 Spring Boot 项目,并进行了最少的设置以渲染 JSP。基本上,我遵循了这个 教程,我的项目可以正常运行。
然后,我将 pom.xml 替换为你提供的内容,然后我遇到了你在问题中提到的相同错误。
然后,我尝试了一些试错操作,我将 <artifactId>tomcat-embed-jasper</artifactId>
中的 <version>[9.0.38,)</version>
移除后,问题得到解决。
<!--我在这里移除了版本信息,然后问题得到解决-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<!--<version>[9.0.38,)</version>-->
<scope>provided</scope>
</dependency>
尽管我的目录结构可能有所不同,但正如你所提到的,这不是问题的原因。
我已经将项目上传到了 GitHub。你可以随意拉取并在本地运行。
英文:
Can you try by changing the scope of tomcat-embed-jasper
to provided
as this dependency is needed to compile JSPs.
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>[9.0.38,)</version>
<scope>provided</scope>
</dependency>
Edit:
I looked for various spring-boot + jsp
projects over internet. I noticed that they have they also have spring-boot-starter-tomcat
with provided
scope. Can you try this.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>[2.3.4.RELEASE,3)</version>
<scope>provided</scope>
</dependency>
References :
https://mkyong.com/spring-boot/spring-boot-hello-world-example-jsp/
https://dzone.com/articles/spring-boot-2-with-jsp-view
Edit-2 :
So this time i created a new springboot project. Did bare minimum setup to get jsp rendered. So basically i followed this tutorial and my project was running fine.
Then I replaced the pom.xml with yours and the i got the same error you mentioned in the question.
Then while doing trial and error i removed the <version>[9.0.38,)</version>
from <artifactId>tomcat-embed-jasper</artifactId>
and it started working for me.
<!--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>
Although i have different directory structure. But as you mentioned that is not the cause of issue.
I have uploaded the project to github. Feel free to pull it run it locally.
答案2
得分: -1
假设您的网页内容位于以下位置(这应该在类路径之外),source/production/webapp
。由于在命令行或IDE中运行时DocumentRoot
中的硬编码路径用于检测目录,Spring Boot将忽略此路径(在构建war并运行该war时可以工作)。
作为解决方法,您可以将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
包中时,才能删除其他注解。然后它会自动检测其他类(如实体和存储库)。
英文:
Assuming the following location for your web content (which should be outside the classpath AFAIK) source/production/webapp
. Spring Boot will ignore this due to a hardcoded path in DocumentRoot
for detection of directories when running from the command-line or IDE (it will work when building a war and running that).
As a workaround you can add a TomcatContextCustomizer
as a bean to detect the path and set it as the correct base.
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());
}
}
}
}
}
Now add the following to your application.properties
spring.mvc.view.prefix=/jsp/
spring.mvc.view.suffix=.jsp
NOTE: The removal of the other annotations can only be done if your @SpringBootApplication
annotated class is in the com.my.project
package. It will then automatically detect the other classes (like entities and repositories).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论