我有一个基于 spring-boot 的应用程序,使用嵌入式 tomcat。通过 mvn spring-boot:run
目标部署时没有问题,但是当我尝试使用 intelliJ spring-boot 插件进行部署时遇到问题。重要说明,我们修改了默认 pom,将我们的应用程序变成了可以部署到完整 tomcat 中的 war Traditional Deployment ,但是在开发模式下使用嵌入式 tomcat 部署应用程序会更好。问题是基本上我们在尝试从 intelliJ 运行 spring boot 应用程序时以这条消息结束
Caused by: java.lang.IllegalStateException: Failed to introspect annotated methods on class org.springframework.boot.web.support.SpringBootServletInitializer
at org.springframework.core.type.StandardAnnotationMetadata.getAnnotatedMethods(StandardAnnotationMetadata.java:163) ~[spring-core-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.retrieveBeanMethodMetadata(ConfigurationClassParser.java:380) ~[spring-context-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:314) ~[spring-context-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:245) ~[spring-context-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:198) ~[spring-context-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:167) ~[spring-context-4.3.8.RELEASE.jar:4.3.8.RELEASE]
... 17 common frames omitted
Caused by: java.lang.NoClassDefFoundError: javax/servlet/ServletContext
at java.lang.Class.getDeclaredMethods0(Native Method) ~[na:1.8.0_144]
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701) ~[na:1.8.0_144]
at java.lang.Class.getDeclaredMethods(Class.java:1975) ~[na:1.8.0_144]
at org.springframework.core.type.StandardAnnotationMetadata.getAnnotatedMethods(StandardAnnotationMetadata.java:152) ~[spring-core-4.3.8.RELEASE.jar:4.3.8.RELEASE]
... 22 common frames omitted
Caused by: java.lang.ClassNotFoundException: javax.servlet.ServletContext
at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_144]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_144]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335) ~[na:1.8.0_144]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_144]
... 26 common frames omitted
您知道为什么会这样吗?我们该如何解决?
编辑:
我正在使用 IntelliJ IDEA 2017.2(以防构建是 idea-IU-172.3317.76),作为 SSCCE,让我们使用 getting-started example of spring-boot ,然后应用为传统部署(第一个链接)提议的更改,我们将拥有以下文件
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-spring-boot</artifactId>
<version>0.1.0</version>
<packaging>war</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
然后我们将拥有Application.java
package hello;
import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
我没有修改 HelloController.java 只是为了完整性:
package hello;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
public class HelloController {
@RequestMapping("/")
public String index() {
return "Greetings from Spring Boot!";
}
}
此示例使用mvn spring-boot:run
运行没有问题,但使用spring-boot intelliJ 的插件时无法运行
最佳答案
注意 - 此答案引用了 1.5.6.RELEASE(当前版本)的 Spring 文档
分析:
从 IJ 运行主类就像从命令行执行您的应用程序。因此,类路径将仅包含 compile
和 runtime
作用域依赖。 Provided
意味着在编译期间将需要它,但您希望 JDK 或容器在运行时为您的应用提供它:
provided
This is much likecompile
, but indicates you expect the JDK or a container to provide the dependency at runtime. For example, when building a web application for the Java Enterprise Edition, you would set the dependency on the Servlet API and related Java EE APIs to scopeprovided
because the web container provides those classes. This scope is only available on the compilation and test classpath, and is not transitive.
另一方面, spring-boot-maven-plugin
使用不同的类路径来执行 run
目标:
By default, both the
repackage
and therun
goals will include anyprovided
dependencies that are defined in the project. A boot-based project should considerprovided
dependencies as container dependencies that are required to run the application.
结论与解决方案:
所以这是一个类路径配置问题。正如您提到的,对于 war packaging或 traditional deployment ,您应该排除嵌入式 servlet 容器。但是,为了能够在开发期间运行应用程序,您需要嵌入式容器。
作为解决方案或解决方法,您至少有以下 2 个选项:
1) 删除 <scope>provided</scope>
来自 spring-boot-starter-tomcat
依赖声明。如果你愿意,你可以完全删除该依赖项,如 Tomcat is the default container used by spring-boot-starter-web
, 但也许最好留下它并添加评论,这样你就不会在发布时忘记它
2) 一个与 IJ 一起使用的巧妙技巧,可以更容易地忘记取消注释 provided
在产品发布之前,是保留现在的依赖关系,并添加一个 maven dev
您可以从 IJ 激活的配置文件。这样,默认情况下它将是 provided
并且可以安全的打包成war,只有在开发和手动激活profile的时候才会包含。添加配置文件后,重新导入您的 Maven 项目,以便您可以选择配置文件:
<profiles>
<profile>
<id>dev</id>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
关于maven - 使用 IntelliJ 部署启用嵌入式 tomcat 的 spring-boot 应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45515849/