org.apache.catalina.LifecycleException: A child container failed during start

      1.问题产生

         在开发一个需求的时候需要引用二方包,引入后起来报下面的错误,项目是springboot-1.4.5,下面是截取部分日志,关键日志  Unable to start embedded Tomcat  该异常出现在TomcatEmbeddedServletContainer类中

2020-04-23 17:20:34.217 [main ] [,,] ERROR o.s.boot.SpringApplication.reportFailure(840) - Application startup failed
org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.boot.context.embedded.EmbeddedServletContainerException: Unable to start embedded Tomcat
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:137)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:536)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:762)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:372)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1187)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1176)
at com.jd.pop.order.mqnew.start.AppStart.main(AppStart.java:24)
Caused by: org.springframework.boot.context.embedded.EmbeddedServletContainerException: Unable to start embedded Tomcat
at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.initialize(TomcatEmbeddedServletContainer.java:123)
at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.<init>(TomcatEmbeddedServletContainer.java:84)
at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory.getTomcatEmbeddedServletContainer(TomcatEmbeddedServletContainerFactory.java:546)
at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory.getEmbeddedServletContainer(TomcatEmbeddedServletContainerFactory.java:177)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.createEmbeddedServletContainer(EmbeddedWebApplicationContext.java:164)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:134)
... 16 common frames omitted
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardServer[-1]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
at org.apache.catalina.startup.Tomcat.start(Tomcat.java:343)
at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.initialize(TomcatEmbeddedServletContainer.java:99)
... 21 common frames omitted
org.apache.catalina.core.StandardService.startInternal(StandardService.java:422)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 25 common frames omitted
Caused by: org.apache.catalina.LifecycleException: A child container failed during start

    2 解决方案

       引入的二方包排除掉servlet-api依赖(注意:假如你的idea默认没有servlet-api这个jar的话,可以引用servlet-api的时候可以设置scope=provided,因为scope=provided 则可以认为这个provided是目标容器已经provide这个artifact。也就是不会出现在WEB-INFO/lib目录下)

   <exclusion>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
    </exclusion>

     3 打断点分析SpringBoot中Tomcat加载过程

      我们知道SpringBoot的加载过程,先在异常出现的类打断点,既 TomcatEmbeddedServletContainer#initialize这里,然后我先从入口开始

     1启动main函数 SpringApplication.run(AppStart.class, args);

     2 public ConfigurableApplicationContext run(String... args) 进入该方法,下面有3段重要的方法,我们的重点在refresh方法,这里会发现有装载Tomcat的过程,SpringBoot通过创建一个spring的容器,把各种外部依赖的beanDefination注册到spring这个容器中 最后调用refresh方法进行Bean的初始化

       this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); 
       this.refreshContext(context); 
       this.afterRefresh(context, applicationArguments)

    3 通过debug进去,发现进入到这个方法,AbstractApplicationContext#refresh,方法里接着调用this.onRefresh();方法定义如下,是个抽象方法,也就是由子类进行实现

 protected void onRefresh() throws BeansException {
    }

   4继续debug发现子类是这个EmbeddedWebApplicationContext,代码如下,调用私有方法createEmbeddedServletContainer

   protected void onRefresh() {
        super.onRefresh();

        try {
            this.createEmbeddedServletContainer();
        } catch (Throwable var2) {
            throw new ApplicationContextException("Unable to start embedded container", var2);
        }
    }


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

        this.initPropertySources();
    }

  5 debug接着走会发现获取 EmbeddedServletContainerFactory,这个接口定义如下,实现类如下

public interface EmbeddedServletContainerFactory {
    EmbeddedServletContainer getEmbeddedServletContainer(ServletContextInitializer... var1);
}

6 接着进入到这个Tomcat实现类TomcatEmbeddedServletContainerFactory#getEmbeddedServletContainer,这个方法里有 this.getTomcatEmbeddedServletContainer(tomcat);

   public EmbeddedServletContainer getEmbeddedServletContainer(ServletContextInitializer... initializers) {
        Tomcat tomcat = new Tomcat();
        File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        Connector connector = new Connector(this.protocol);
        tomcat.getService().addConnector(connector);
        this.customizeConnector(connector);
        tomcat.setConnector(connector);
        tomcat.getHost().setAutoDeploy(false);
        this.configureEngine(tomcat.getEngine());
        Iterator var5 = this.additionalTomcatConnectors.iterator();

        while(var5.hasNext()) {
            Connector additionalConnector = (Connector)var5.next();
            tomcat.getService().addConnector(additionalConnector);
        }

        this.prepareContext(tomcat.getHost(), initializers);
        return this.getTomcatEmbeddedServletContainer(tomcat);
    }

   protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(Tomcat tomcat) {
        return new TomcatEmbeddedServletContainer(tomcat, this.getPort() >= 0);
    }

7最终进入到我们的异常出现的类TomcatEmbeddedServletContainer#initialize

 8就这么个调试过程,这是一个循环渐进的过程,在解决问题的同时,也能顺便了解下springBoot装载Tomcat的过程,顺便也可以了解下Tomcat的源码,放一张网上的Tomcat的架构图

9 控制配置类的加载顺序

有些场景下我们可以通过AutoConfigureAfter,AutoConfigureBefore指定配置的加载顺序
@Configuration
@AutoConfigureAfter(value = {JMQProducerAutoConfigurationArchiveOne.class, SpringBeanUtil.class})
@AutoConfigureBefore(name  = {"classpath:spring-config-service.xml"})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值