Deploying embedded tomcat spring-boot application using IntelliJ
I have a spring-boot application using embedded tomcat. I have no problem deploying with a mvn spring-boot:run
target , but . I am having problems trying to deploy with intelliJ spring-boot plugins. It is important to note that we changed the default setting, turning our application into war, which can be deployed to full tomcat Traditional deployment , but this in development mode would be better to just deploy the application using embedded tomcat. The problem is that basically we ended up with this post when trying to start spring boot application 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 idea why this is 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 an SSCCE allows the spring-boot example to be used and allows the changes suggested for traditional deployment to be applied (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 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 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 haven't modified 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 works without issue with help mvn spring-boot:run
, but fails to start when using spring-boot intelliJ plug-in
source to share
Note - this answer is referring to Spring documentation for version 1.5.6.RELEASE (current version)
Analyzes:
Running the main class from IJ is similar to running your application from the command line. This way, the classpath will only include dependent scopes compile
and runtime
. Provided
means it will be needed at compile time, but you would expect the JDK or container to provide it for your application at runtime:
Provided
This is very similar tocompile
, but indicates that you expect the JDK or container to provide a runtime dependency . For example, when building a web application for Java Enterprise Edition, you must install a dependency on the servlet API and its associated Java EE scoped APIsProvided
, since the web container exposes these classes. This scope is only available in the compilation and test path and is not transitive.
On the other hand, spring-boot-maven-plugin
uses a different classpath to accomplish the goal run
:
By default, in integers
repackage
and any dependencies that are defined in the projectrun
will be specified . A download based project must account for dependencies as container dependencies required to run the application.Provided
Provided
Conclusion and solutions:
So this is a class configuration issue. As you mentioned, for war packaging or traditional deployment , you should exclude the embedded servlet container. However, to be able to run your application during development, you need an embedded container.
As a solution or work around, you have at least the following 2 options:
1) Remove <scope>provided</scope>
dependencies from declaration spring-boot-starter-tomcat
. You can remove this dependency completely if you want as Tomcat is the default container usedspring-boot-starter-web
, but it might be better to leave it that way and add a comment so you won't forget about it when you drop
2) A clean trick that works with IJ and will make it easy not to forget to uncomment Provided
before releasing the release to leave the dependency as it is now and add a maven dev
that you can activate from within IJ. So it will be by default Provided
and it can be safely packaged in war and only when developing and manually activating the profile will it be enabled. 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>
source to share
If you are using a maven project you need to add the dependency javax.servlet-api
.
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
you can also download the jar from http://central.maven.org/maven2/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar
source to share