使用IntelliJ部署支持嵌入式tomcat的spring-boot应用程序 [英] Deploying embedded tomcat enabled spring-boot app using IntelliJ

查看:64
本文介绍了使用IntelliJ部署支持嵌入式tomcat的spring-boot应用程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用嵌入式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:

provided This is much like compile, 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 scope provided 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 the run goals will include any provided dependencies that are defined in the project. A boot-based project should consider provided 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>

这篇关于使用IntelliJ部署支持嵌入式tomcat的spring-boot应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆