您的位置:首页 > 移动开发 > WebAPP

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

2017-08-06 11:40 761 查看

异常触发背景

项目中依赖了第三方的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的间接依赖。



解决方法

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 源码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  springboot spring dubbo
相关文章推荐