问题描述
我有一个使用嵌入式tomcat的基于spring-boot的应用程序.通过 mvn spring-boot:run
目标进行部署时,我没有任何问题,但是,当我尝试使用intelliJ spring-boot插件进行部署时,我遇到了问题.重要说明,我们修改了默认pom,将我们的应用变成一场战争,可以部署到完整的tomcat
I have a spring-boot based application, using embedded tomcat. I have no problem when deploying via mvn spring-boot:run
goal, but I have a problems when I try to deploy using intelliJ spring-boot plugins. Important note, we have modified the default pom, turning our app into a war that could be deployed into a full tomcatTraditional Deployment, but this in development mode will be better to just deploy the app using the embedded tomcat. The problem is that basically we ended with this message when trying to run the spring boot app from intelliJ
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
Do you have any ideas of why is this happening ? and how can we solve it ?
EDIT:
I am using IntelliJ IDEA 2017.2 (just in case the build is idea-IU-172.3317.76), and as a SSCCE, lets use the getting-started example of spring-boot, and lets apply the changes proposed for Traditional Deployment (the first link) we will have the following files
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>
Then we will have the 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);
}
}
And I didn' modify the HelloController.java but just for completeness:
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!";
}
}
This example runs without problem using mvn spring-boot:run
but doesn't run when using the spring-boot intelliJ's plug-in
Note - this answer references Spring documentation for v 1.5.6.RELEASE (current release)
Analyses:
Running the main class from IJ is like executing your app from command line. As such, the classpath will include only compile
and runtime
scoped dependencies. Provided
means that it'll be needed during compilation, but you expect the JDK or the container to provide it for your app at runtime:
On the other hand, the spring-boot-maven-plugin
uses a different classpath to execute the run
goal:
Conclusion and solutions:
So it's a classpath configuration issue. As you mentioned, for war packaging or traditional deployment, you should exclude the embedded servlet container. However, to be able to run the app during development, you want the embedded container.
As a solution or work around, you have at least the following 2 options:
1) Remove the <scope>provided</scope>
from the spring-boot-starter-tomcat
dependency declaration. If you wanted to, you could remove that dependency altogether, as Tomcat is the default container used by spring-boot-starter-web
, but perhaps it's best to leave it and add a comment, so you won't forget about it when releasing
2) A neat trick that works with IJ and would make it easier to not forget to uncomment provided
before production release, is to leave the dependency as it is now, and add a maven dev
profile which you can activate from IJ. This way, by default it will be provided
and it can be safely packaged into a war, and only when developing and manually activating the profile it will be included. After adding the profile, reimport your maven project so you can select the profile:
<profiles>
<profile>
<id>dev</id>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
这篇关于使用IntelliJ部署支持嵌入式tomcat的spring-boot应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!