Spring-boot Unable to start EmbeddedWebApplicationContext 分析与解决方法

 2019-10-17 22:00  阅读(2459)
文章分类:Spring boot

异常触发背景

项目中依赖了第三方的dubbo调用依赖。

<dependency>
          <groupId>xxx.xxx.com</groupId>
          <artifactId>dubbo-test-client</artifactId>
          <version>1.0-SNAPSHOT</version>
        </dependency>

随后启动spring-boot application后产生异常

org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.

    Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.

异常产生原因

在启动时Spring-boot会检查当前环境,org.springframework.boot.SpringApplication内部维护 webEnvironment成员,记录当前是否是web环境。

private boolean webEnvironment;

在启动时会沿如下的调用路径通过deduceWebEnvironment方法判断当前环境,并给webEnvironment变量赋值。

this.webEnvironment = deduceWebEnvironment();

at org.springframework.boot.SpringApplication.deduceWebEnvironment(SpringApplication.java:256)
          at org.springframework.boot.SpringApplication.initialize(SpringApplication.java:248)
          at org.springframework.boot.SpringApplication.<init>(SpringApplication.java:225)
          at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)
          at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)
          at springboot.commandline.StartupRunner.main(StartupRunner.java:36)

deduceWebEnvironment,通过尝试加载WEB_ENVIRONMENT_CLASSES数组中的下面两个类,如果全部加载成功那么推断当前属于web环境。

  • javax.servlet.Servlet(servelet-api jar)

  • org.springframework.web.context.ConfigurableWebApplicationContext (spring-web jar)

    private boolean deduceWebEnvironment() { for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return false; } } return true; }

    private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" };

由于在maven中添加的依赖同时引入WEB_ENVIRONMENT_CLASSES中的类,导致spring-boot判断当前为web环境。

当判断为web环境,会在springboot.run方法中通过createApplicationContext()方法创建EmbeddedWebApplicationContext,最终会沿如下的调用链调用createEmbeddedServletContainer()方法去创建servlet容器。

at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.createEmbeddedServletContainer(EmbeddedWebApplicationContext.java:159)
          at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:134)
          at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:537)
          - locked <0xbc0> (a java.lang.Object)
          at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
          at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
          at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
          at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
          at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)
          at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)
          at springboot.commandline.StartupRunner.main(StartupRunner.java:36)

createEmbeddedServletContainer中会通过getEmbeddedServletContainerFactory()方法来获取容器工厂对象。

private void createEmbeddedServletContainer() {
            EmbeddedServletContainer localContainer = this.embeddedServletContainer;
            ServletContext localServletContext = getServletContext();
            if (localContainer == null && localServletContext == null) {
                EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
                this.embeddedServletContainer = containerFactory
                        .getEmbeddedServletContainer(getSelfInitializer());
            }
            else if (localServletContext != null) {
                try {
                    getSelfInitializer().onStartup(localServletContext);
                }
                catch (ServletException ex) {
                    throw new ApplicationContextException("Cannot initialize servlet context",
                            ex);
                }
            }
            initPropertySources();
        }

getEmbeddedServletContainerFactory 方法被调用时会尝试获取工厂bean名称,如果获取不到,则会抛出Unable to start EmbeddedWebApplicationContext due to missing的异常。

protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
            // Use bean names so that we don't consider the hierarchy
            String[] beanNames = getBeanFactory()
                    .getBeanNamesForType(EmbeddedServletContainerFactory.class);
            if (beanNames.length == 0) {
                throw new ApplicationContextException(
                        "Unable to start EmbeddedWebApplicationContext due to missing "
                                + "EmbeddedServletContainerFactory bean.");
            }
            if (beanNames.length > 1) {
                throw new ApplicationContextException(
                        "Unable to start EmbeddedWebApplicationContext due to multiple "
                                + "EmbeddedServletContainerFactory beans : "
                                + StringUtils.arrayToCommaDelimitedString(beanNames));
            }
            return getBeanFactory().getBean(beanNames[0],
                    EmbeddedServletContainerFactory.class);
        }

总结:通过对异常产生的原因分析,可以知道是因为引入了spring-web-xx.jar(xx为版本号),导致判断当前环境为web,最终尝试创建相关的Servlet容器工厂失败而导致。

最后分析maven依赖关系发现依赖的包dubbo-test-client存在对spring-web的间接依赖。

20191017100356\_1.png

解决方法

1.在dubbo-test-client中排除对spring-web jar的依赖。

<dependency>
          <groupId>xxx.xxx.xx</groupId>
          <artifactId>dubbo-test-client</artifactId>
          <version>1.0-SNAPSHOT</version>
          <exclusions>
            <exclusion>
              <groupId>org.springframework</groupId>
              <artifactId>spring-web</artifactId>
            </exclusion>
          </exclusions>
        </dependency>

2.引入spring-boot-starter-web,引入创建web容器所需要的相关依赖。

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
          </dependency>

参考

spring-boot 1.5.6 源码


来源:[]()

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> Spring-boot Unable to start EmbeddedWebApplicationContext 分析与解决方法

相关推荐