为什么Springboot Main方法运行完成后不会自动关闭

文章开始前。先看一篇知乎文章 。

在熟悉了一些知识后,再看这篇文章

有了这两篇文章,就能明白,为什么对于Web容器(这里以Tomcat为例)来说,我们没有写main 方法程序就能启动了

原因就在于 Tomcat 中存在 org.apache.catalina.startup.Bootstrap.main()。

但是通常来讲,我们自己写了一个main方法,执行完成是会自动关闭的。比如:

    public static void main(String[] args) {
        System.out.println("==========");
    }

运行上面一段代码,打印完成之后,程序就会关闭了,那么为什么Tomcat的main方法并没有自动关闭呢?

这里我使用SpringBoot做一个web应用,WebServer使用Tomcat 来验证,为什么Springboot Main方法运行完成后不会自动关闭.

Springboot 版本为 1.5.6.RELEASE 。

启动 Springboot应用,很自然的找到 SpringApplication.refreshContext() 方法。进入该方法。即会调用同类的refresh()方法

SpringApplication.java

    protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        // 调用 AbstractApplicationContext的refresh()
       ((AbstractApplicationContext)applicationContext).refresh();
    }

再接着调用AbstractApplicationContext.refresh()方法,该方法中调用了onRefresh()方法。

AbstractApplicationContext.java

public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                // 受保护方法,由子类实现
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                // 完成refresh(最后会打印Tomcat port和发布通知)
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

而 onRefresh()方法是个受保护方法。

    protected void onRefresh() throws BeansException {
    }

可以看到实现该方法的子类。因此一下子我们就看到了。有哪些实现该方法的类。

进入到EmbeddedWebApplicationContext.onRefresh()方法中。它首先调用了一次父级方法,然后调用了同类的createEmbeddedServletContainer()方法,

   private void createEmbeddedServletContainer() {
        EmbeddedServletContainer localContainer = this.embeddedServletContainer;
        ServletContext localServletContext = this.getServletContext();
        if (localContainer == null && localServletContext == null) {
            EmbeddedServletContainerFactory containerFactory = this.getEmbeddedServletContainerFactory();
            // 注意 containerFactory.getEmbeddedServletContainer() 方法,其也是有多个实现的,这里选择与Tomcat相关的实现
            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();
    }

需要注意的是containerFactory.getEmbeddedServletContainer() 存在多个实现。

这里正好又对应了springboot支持的webserver种类,分别是jetty/tomcat/undertow。

这里就直接进入 TomcatEmbeddedServletContainerFactory.getEmbeddedServletContainer()方法。这是个嵌入式的Servlet容器工厂类,那么它的作用应该就是创建容器。

TomcatEmbeddedServletContainerFactory.java

    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);
    }

可以看到工厂类直接就new 了一个Servlet容器对象。

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

那么看看TomcatEmbeddedServletContainer类的构造方法干了哪些事情吧。

TomcatEmbeddedServletContainer.java
   public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {
        this.monitor = new Object();
        this.serviceConnectors = new HashMap();
        Assert.notNull(tomcat, "Tomcat Server must not be null");
        this.tomcat = tomcat;
        this.autoStart = autoStart;
        // 重点关注
        this.initialize();
    }
private void initialize() throws EmbeddedServletContainerException {
        logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
        synchronized(this.monitor) {
            try {
                this.addInstanceIdToEngineName();

                try {
                    this.removeServiceConnectors();
                    // 1.重点关注 
                    this.tomcat.start();
                    this.rethrowDeferredStartupExceptions();
                    Context context = this.findContext();

                    try {
                        ContextBindings.bindClassLoader(context, this.getNamingToken(context), this.getClass().getClassLoader());
                    } catch (NamingException var5) {
                    }
                    // 2.重点关注
                    this.startDaemonAwaitThread();
                } catch (Exception var6) {
                    containerCounter.decrementAndGet();
                    throw var6;
                }
            } catch (Exception var7) {
                throw new EmbeddedServletContainerException("Unable to start embedded Tomcat", var7);
            }

        }
    }

找到TomcatEmbeddedServletContainer.initialize()方法,我们看到有两个start方法。我们暂且称之为 方法1 和 方法2。

方法1 最后会调用LifecycleBase.start()方法。

LifecycleBase.java

   public final synchronized void start() throws LifecycleException {
        if (!LifecycleState.STARTING_PREP.equals(this.state) && !LifecycleState.STARTING.equals(this.state) && !LifecycleState.STARTED.equals(this.state)) {
            if (this.state.equals(LifecycleState.NEW)) {
                this.init();
            } else if (this.state.equals(LifecycleState.FAILED)) {
                this.stop();
            } else if (!this.state.equals(LifecycleState.INITIALIZED) && !this.state.equals(LifecycleState.STOPPED)) {
                this.invalidTransition("before_start");
            }

            try {
                this.setStateInternal(LifecycleState.STARTING_PREP, (Object)null, false);
                this.startInternal();
                if (this.state.equals(LifecycleState.FAILED)) {
                    this.stop();
                } else if (!this.state.equals(LifecycleState.STARTING)) {
                    this.invalidTransition("after_start");
                } else {
                    this.setStateInternal(LifecycleState.STARTED, (Object)null, false);
                }

            } catch (Throwable var2) {
                ExceptionUtils.handleThrowable(var2);
                this.setStateInternal(LifecycleState.FAILED, (Object)null, false);
                throw new LifecycleException(sm.getString("lifecycleBase.startFail", new Object[]{this.toString()}), var2);
            }
        } else {
            if (log.isDebugEnabled()) {
                Exception e = new LifecycleException();
                log.debug(sm.getString("lifecycleBase.alreadyStarted", new Object[]{this.toString()}), e);
            } else if (log.isInfoEnabled()) {
                log.info(sm.getString("lifecycleBase.alreadyStarted", new Object[]{this.toString()}));
            }

        }
    }

此方法会多次递归调用,经历过一连串的调用后。会进入ContainerBase.startInternal()方法。

  protected synchronized void startInternal() throws LifecycleException {
        this.logger = null;
        this.getLogger();
        Cluster cluster = this.getClusterInternal();
        if (cluster instanceof Lifecycle) {
            ((Lifecycle)cluster).start();
        }

        Realm realm = this.getRealmInternal();
        if (realm instanceof Lifecycle) {
            ((Lifecycle)realm).start();
        }

        Container[] children = this.findChildren();
        List<Future<Void>> results = new ArrayList();

        for(int i = 0; i < children.length; ++i) {
            results.add(this.startStopExecutor.submit(new ContainerBase.StartChild(children[i])));
        }

        boolean fail = false;
        Iterator i$ = results.iterator();

        while(i$.hasNext()) {
            Future result = (Future)i$.next();

            try {
                result.get();
            } catch (Exception var9) {
                log.error(sm.getString("containerBase.threadedStartFailed"), var9);
                fail = true;
            }
        }

        if (fail) {
            throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"));
        } else {
            if (this.pipeline instanceof Lifecycle) {
                ((Lifecycle)this.pipeline).start();
            }

            this.setState(LifecycleState.STARTING);
            // 重点关注
            this.threadStart();
        }
    }

查看 threadStart()方法。该方法很简单就是创建了一个线程,并执行。

    protected void threadStart() {
        if (this.thread == null) {
            if (this.backgroundProcessorDelay > 0) {
                this.threadDone = false;
                String threadName = "ContainerBackgroundProcessor[" + this.toString() + "]";
                // 重点看线程类
                this.thread = new Thread(new ContainerBase.ContainerBackgroundProcessor(), threadName);
                // 设置为守护线程
                this.thread.setDaemon(true);
                this.thread.start();
            }
        }
    }


我们看看线程类是什么ContainerBase.ContainerBackgroundProcessor 里面是一个while循环。

  public void run() {
            Throwable t = null;
            String unexpectedDeathMessage = ContainerBase.sm.getString("containerBase.backgroundProcess.unexpectedThreadDeath", new Object[]{Thread.currentThread().getName()});

            try {
                while(!ContainerBase.this.threadDone) {
                    try {
                        Thread.sleep((long)ContainerBase.this.backgroundProcessorDelay * 1000L);
                    } catch (InterruptedException var8) {
                    }

                    if (!ContainerBase.this.threadDone) {
                        this.processChildren(ContainerBase.this);
                    }
                }
            } catch (Error | RuntimeException var9) {
                t = var9;
                throw var9;
            } finally {
                if (!ContainerBase.this.threadDone) {
                    ContainerBase.log.error(unexpectedDeathMessage, t);
                }

            }

        }

方法1结束。

让我们回到方法2。回到TomcatEmbeddedServletContainer.initialize()方法。该方法最终调用了本类的 startDaemonAwaitThread()方法。

    private void startDaemonAwaitThread() {
        Thread awaitThread = new Thread("container-" + containerCounter.get()) {
            public void run() {
                // 新线程逻辑。重点关注
                TomcatEmbeddedServletContainer.this.tomcat.getServer().await();
            }
        };
        awaitThread.setContextClassLoader(this.getClass().getClassLoader());
        // 设置为非守护线程
        awaitThread.setDaemon(false);
        awaitThread.start();
    }

看一下await 方法都干了什么吧!

 public void await() {

.........

  while(!this.stopAwait) {
     try {
           Thread.sleep(10000L);
         } catch (InterruptedException var64) {
        }
       }
..........
}

省略了部分代码。

此时我们就已经明白了,维持main方法不退出是因为有方法2的存在,为什么方法1也存在while循环,而不是方法1维持main方法不退出的呢?因为方法1的线程是守护线程。他们的区别

最后再回到AbstractApplicationContext.refresh()方法中,它又调用了本类的finishRefresh()方法,该方法publishEvent() 发布了一个事件

AbstractApplicationContext.java

    protected void finishRefresh() {
        this.initLifecycleProcessor();
        this.getLifecycleProcessor().onRefresh();
        // 重点关注。事件发布。监听模式
        this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)));
        LiveBeansView.registerApplicationContext(this);
    }

从而调用了EmbeddedWebApplicationContext.finishRefresh()方法,EmbeddedWebApplicationContext.finishRefresh()方法又调用了本类的startEmbeddedServletContainer()方法。

    protected void finishRefresh() {
        super.finishRefresh();
        // 重点关注
        EmbeddedServletContainer localContainer = this.startEmbeddedServletContainer();
        if (localContainer != null) {
            this.publishEvent(new EmbeddedServletContainerInitializedEvent(this, localContainer));
        }

    }


    private EmbeddedServletContainer startEmbeddedServletContainer() {
        // EmbeddedServletContainer 是一个接口
        EmbeddedServletContainer localContainer = this.embeddedServletContainer;
        if (localContainer != null) {
            // 重点关注
            localContainer.start();
        }

        return localContainer;
    }

startEmbeddedServletContainer()方法 又调用了EmbeddedServletContainer的start() 方法。由于EmbeddedServletContainer是一个接口。它的实现类有:


因此我们十分容易的找到了TomcatEmbeddedServletContainer类。

再来看TomcatEmbeddedServletContainer.start()方法。它打印出了
Tomcat started on port(s)

TomcatEmbeddedServletContainer.java

   public void start() throws EmbeddedServletContainerException {
        synchronized(this.monitor) {
            if (!this.started) {
                boolean var10 = false;

                try {
                    var10 = true;
                    this.addPreviouslyRemovedConnectors();
                    Connector connector = this.tomcat.getConnector();
                    if (connector != null && this.autoStart) {
                        this.startConnector(connector);
                    }

                    this.checkThatConnectorsHaveStarted();
                    this.started = true;
                    logger.info("Tomcat started on port(s): " + this.getPortsDescription(true));
                    var10 = false;
                } catch (ConnectorStartFailedException var11) {
                    this.stopSilently();
                    throw var11;
                } catch (Exception var12) {
                    throw new EmbeddedServletContainerException("Unable to start embedded Tomcat servlet container", var12);
                } finally {
                    if (var10) {
                        Context context = this.findContext();
                        ContextBindings.unbindClassLoader(context, this.getNamingToken(context), this.getClass().getClassLoader());
                    }
                }

                Context context = this.findContext();
                ContextBindings.unbindClassLoader(context, this.getNamingToken(context), this.getClass().getClassLoader());
            }
        }
    }

猜你喜欢

转载自blog.csdn.net/kanyun123/article/details/116660557