问题
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
回答1:
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:
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.
On the other hand, the spring-boot-maven-plugin uses a different classpath to execute the run
goal:
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.
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>
回答2:
if you are using maven project then you need to add javax.servlet-api
dependency.
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
you can also download jar from http://central.maven.org/maven2/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar
来源:https://stackoverflow.com/questions/45515849/deploying-embedded-tomcat-enabled-spring-boot-app-using-intellij