【问题标题】:Spring Boot JAR - missing EmbeddedServletContainerFactorySpring Boot JAR - 缺少 EmbeddedServletContainerFactory
【发布时间】:2016-02-14 12:20:00
【问题描述】:

Spring Boot (1.2.7) Web 应用(响应 SSL 请求)在 Intellij IDEA 上运行效果很好。

构建 JAR 工件以从命令行运行,启动时抛出错误

org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception isorg.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.

问题

  1. 如何避免此错误?
  2. 如果我需要声明一个 EmbeddedServletContainerFactory,如何让其 ServletContainer 处理对我的 Controller 的请求?

尝试的解决方案

按错误请求添加 EmbeddedservletContainerFactory。项目现在从 JAR 启动,但控制器没有像从 IntelliJ 运行时那样接收请求。

这是我的 Application.class 添加 EmbeddedServletContainerFactory

后的样子
package org.coursera.diabetes;

import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.support.ConnectionSource;
import diabetes.common.model.*;
import org.apache.catalina.connector.Connector;
import org.apache.coyote.http11.Http11NioProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

@SpringBootApplication
public class Application {
    private static final Logger logger = LoggerFactory.getLogger(Application.class);

    private static final String dbUrl = "jdbc:h2:mem:testdb";

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);

    }

    @Bean
    @Profile("production")
    public EmbeddedServletContainerFactory servletContainer(@Value("${keystore.file}") Resource keystoreFile) {
        TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
        //factory.setPort(8443);
        String absoluteKeystoreFile = "";

        try {
            absoluteKeystoreFile = keystoreFile.getFile().getAbsolutePath();
            logger.info(String.format("******** keystore: %s", absoluteKeystoreFile));
        } catch (IOException e) {
            e.printStackTrace();
        }

        tomcat.addAdditionalTomcatConnectors(createSslConnector(absoluteKeystoreFile));
        return tomcat;
    }

    private Connector createSslConnector(String absKeystoreFile) {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
        try {
            connector.setScheme("https");
            connector.setSecure(true);
            connector.setPort(8443);
            protocol.setSSLEnabled(true);
            protocol.setKeystoreFile(absKeystoreFile);
            protocol.setKeystorePass("changeit");
            protocol.setKeyAlias("tomcat");
            return connector;
        }
        catch (Exception ex) {
            throw new IllegalStateException("can't access keystore: [" + "keystore"
                    + "] or truststore: [" + "keystore" + "]", ex);
        }
    }
}

注意事项

Spring Boot (web) 应用程序在 Intellij IDEA 中运行良好,响应 https 请求,无需在 application.properties 中添加几行代码:

security.require-ssl=true
server.port=8443
server.ssl.key-store=src/main/resources/private/keystore
server.ssl.key-store-password=changeit
server.ssl.key-password=changeit

要从命令行启动 JAR 工件,我使用:

java -Dspring.profiles.active=production -Dkeystore.file=file:///$PWD/keystore -jar diabetes-spring.jar

JAR 启动时不会出错,但 https 网络请求(从 Intellij 工作)只是返回 Apache/tomcat 404 “请求的资源不可用”。

编辑

使用 -Ddebug 运行 JAR 会输出以下信息(我省略了所有 DEBUG 消息,因为找不到配置文件 application.properties / application/application-production.properties 似乎不合适)

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::

2015-11-13 14:16:39.980  INFO 47600 --- [           main] org.coursera.diabetes.Applica
tion        : Starting Application on Bakers-MacBook-Pro.local with PID 47600 (/User
s/baker/coursera-android/MoCCA-15/spring-tests/diabetes_root2/diabetes-spring/out/artifa
cts/diabetes_spring_jar/diabetes-spring.jar started by blam in /Users/blam/coursera-and
roid/MoCCA-15/spring-tests/diabetes_root2/diabetes-spring/out/artifacts/diabetes_spring
_jar)
2015-11-13 14:16:39.983 DEBUG 47600 --- [           main] o.s.boot.SpringApplication
            : Loading source class org.coursera.diabetes.Application
2015-11-13 14:16:40.011 DEBUG 47600 --- [           main] o.s.b.c.c.ConfigFileApplicati
onListener  : Activated profiles production
2015-11-13 14:16:40.011 DEBUG 47600 --- [           main] o.s.b.c.c.ConfigFileApplicati
onListener  : Loaded config file 'file:./config/application.properties'
2015-11-13 14:16:40.020  INFO 47600 --- [           main] ationConfigEmbeddedWebApplica
tionContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbe
ddedWebApplicationContext@23faf8f2: startup date [Fri Nov 13 14:16:40 WET 2015]; root o
f context hierarchy
2015-11-13 14:16:40.022 DEBUG 47600 --- [           main] ationConfigEmbeddedWebApplica
tionContext : Bean factory for org.springframework.boot.context.embedded.AnnotationConf
igEmbeddedWebApplicationContext@23faf8f2: org.springframework.beans.factory.support.Def
aultListableBeanFactory@6cc7b4de: defining beans [org.springframework.context.annotatio
n.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.inter
nalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequired
AnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProc
essor,application]; root of factory hierarchy
2015-11-13 14:16:40.220 DEBUG 47600 --- [           main] ationConfigEmbeddedWebApplica
tionContext : Unable to locate MessageSource with name 'messageSource': using default [
org.springframework.context.support.DelegatingMessageSource@20ce78ec]
2015-11-13 14:16:40.221 DEBUG 47600 --- [           main] ationConfigEmbeddedWebApplica
tionContext : Using ApplicationEventMulticaster [org.springframework.context.event.Simp
leApplicationEventMulticaster@393671df]
2015-11-13 14:16:40.226  WARN 47600 --- [           main] ationConfigEmbeddedWebApplica
tionContext : Exception encountered during context initialization - cancelling refresh
attempt

org.springframework.context.ApplicationContextException: Unable to start embedded conta
iner; nested exception is org.springframework.context.ApplicationContextException: Unab
le to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactor
y bean.
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRe
fresh(EmbeddedWebApplicationContext.java:132)
        at org.springframework.context.support.AbstractApplicationContext.refresh(Abstr
actApplicationContext.java:474)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refr
esh(EmbeddedWebApplicationContext.java:117)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:68
9)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:321)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:969)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:958)
        at org.coursera.diabetes.Application.main(Application.java:40)
Caused by: org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.getEmbeddedServletContainerFactory(EmbeddedWebApplicationContext.java:182)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.createEmbeddedServletContainer(EmbeddedWebApplicationContext.java:155)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:129)
        ... 7 common frames omitted

2015-11-13 14:16:40.227  INFO 47600 --- [           main] .b.l.ClasspathLoggingApplicationListener : Application failed to start with classpath: [file:/Users/baker/coursera-android/MoCCA-15/spring-tests/diabetes_root2/diabetes-spring/out/artifacts/diabetes_spring_jar/diabetes-spring.jar]
2015-11-13 14:16:40.228 ERROR 47600 --- [           main] o.s.boot.SpringApplication               : Application startup failed

org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:132)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:474)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:117)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:689)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:321)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:969)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:958)
        at org.coursera.diabetes.Application.main(Application.java:40)
Caused by: org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.getEmbeddedServletContainerFactory(EmbeddedWebApplicationContext.java:182)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.createEmbeddedServletContainer(EmbeddedWebApplicationContext.java:155)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:129)
        ... 7 common frames omitted

【问题讨论】:

  • 尝试恢复原状并查看自动配置报告(使用 --debug 运行)以了解嵌入式 servlet 容器未自动配置的原因
  • @AndyWilkinson 谢谢。尝试了 -Ddebug 选项。奇怪的是,在运行 JAR 时没有生成自动配置报告。 (上面复制的输出。)。在 IDEA 中运行 --debug,报告按预期生成。我想知道在构建 JAR 工件时是否以某种方式关闭了自动配置。
  • 你如何构建jar?
  • 在 IDEA -> Project Structure (Cmd + ;) -> Artifacts -> + button, -> JAR -> from Modules with Dependencies -> 选择主模块 -> 选择主类 -> JAR 文件从库中:解压缩到目标 JAR -> META-INF/MANIFEST.MF 的目录:/src/main/resources/ -> 构建(在菜单中)-> 构建工件 -> [选择 jar 名称] - > 构建

标签: java spring spring-boot


【解决方案1】:

在 IntelliJ IDEA - 项目结构 - 工件 - 可用元素中,所有 Gradle 依赖库都需要提取到输出根目录,以便 Spring Boot 应用程序的 JAR 从 java 命令行执行中正常运行.

在将 JAR 工件更改为此之后,我可以构建 JAR 并从命令行 JVM 运行它。

要让默认的 TomcatEmbeddedServletContainerFactory 在 HTTPS/SSL 端口 8443 上运行,请指定与 application.properties 中所需的等效的 java 命令行参数。

即

java -jar your-spring.jar --security.require-ssl=true --server.port=8443 --server.ssl.key-store=keystore --server.ssl.key-store-password=changeit --server.ssl.key-password=changeit

【讨论】:

    猜你喜欢
    • 2014-08-30
    • 1970-01-01
    • 1970-01-01
    • 2016-04-27
    • 1970-01-01
    • 2016-03-05
    • 2014-07-28
    • 1970-01-01
    • 2018-09-19
    相关资源
    最近更新 更多