maven - 使用 IntelliJ 部署启用嵌入式 tomcat 的 spring-boot 应用程序

标签 maven tomcat spring-boot intellij-idea

我有一个基于 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 运行主类就像从命令行执行您的应用程序。因此,类路径将仅包含 compileruntime作用域依赖。 Provided 意味着在编译期间将需要它,但您希望 JDK 或容器在运行时为您的应用提供它:

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.

另一方面, spring-boot-maven-plugin 使用不同的类路径来执行 run目标:

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.


结论与解决方案:

所以这是一个类路径配置问题。正如您提到的,对于 war packagingtraditional 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>

reimport project and activate profile

关于maven - 使用 IntelliJ 部署启用嵌入式 tomcat 的 spring-boot 应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45515849/

相关文章:

tomcat - 找不到 spring-boot 可执行 war keystore

maven - 无法在使用 Arquillian 和 WildFly 的 JPA 集成测试中注入(inject) EntityManager

Maven - 完全禁用本地存储库,只转到远程

java - Maven Dropwizard 中的 Hibernate 数据库连接错误

捆绑了依赖项的 JavaFX jar

java - 禁用 SOAP 服务日志跟踪中的打印凭据

tomcat - JMeter - AJP 采样器因非 HTTP 响应消息而失败

java - 无法部署爆炸的 Artifact

Tomcat CORS 过滤器 : Why is the default list of headers in "Access-Control-Allow-Headers" so arbitrarily limited?

spring-boot - Spring 数据 JPA Redis : Cannot write custom method based query