SpringBoot是如何保证服务启动后不自动停止的

一般项目执行后,当程序结束会自动关闭程序。但Springboot项目,启动后,只要不发生error错误,一般不会自动停止。

这是为什么呢?

简单Java阻止停止

为了保证一个服务能够持续有效地对外提供服务,一般会有相应的处理方式,比如:

  • 服务器上的守护进程脚本

但是,在Java代码层面,除了shell脚本之外,还有一种很特别的方式,保证服务不会执行后停止。

  • 死循环!文雅点叫自旋锁。

比如下面的例子:

public class ThreadDemo2 {
    public static volatile boolean isDead = false;
    public static void main(String[] args) throws InterruptedException {
        while (!isDead){
            Thread.sleep(4000);
        }
    }
}

程序执行后,会进入自旋锁的代码逻辑中,每隔4s,检查一下isDead值。

如果isDead一直为false,那么程序将不会自动停止。

但是,设置在主线程中,此处自旋锁触发后,导致后续代码并不会继续执行,影响到后面的逻辑处理,显然是不可取的。

如果,单独开辟一个新的线程,去处理这个活,主线程依旧去执行别的逻辑呢?

public class TestThread {
    public static volatile boolean isDead = false;

    public static void main(String[] args) throws InterruptedException {
        //        Thread thread = new Thread(new Runnable() {
        //            @Override
        //            public void run() {
        //                System.out.println("---------");
        //                try {
        //                    while (!isDead){
        //                        Thread.sleep(10000L);
        //                    }
        //                } catch (InterruptedException e) {
        //                    e.printStackTrace();
        //                }
        //            }
        //        });

        Thread thread = new Thread("container-1") {

            @Override
            public void run() {
                System.out.println("---------");
                try {
                    while (!isDead) {
                        Thread.sleep(4000L);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        };
        thread.setDaemon(false);

        thread.start();

        // 主线程暂停40s
        Thread.sleep(40000L);
        // 变更状态
        isDead = true;
    }
}

单独设定一个非守护进程的线程,去干这个活,主线程依旧可以继续执行其他的事。

基于这个思想,接下来看看源码中Springboot底层是如何实现的。

Springboot 底层实现

以Springboot默认整合Tomcat为例。

查看SpringApplication.run

Springboot的项目执行,依据的是run方法,其中的实现方式如下:

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}

继续查看run方法。

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment);
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                                                         new Class[] { ConfigurableApplicationContext.class }, context);
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 暂时不看别的逻辑,就看 refreshContext
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

其他逻辑暂时不看,就看其中的refreshContext(context);

refreshContext(context)

private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

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

org.springframework.context.support.AbstractApplicationContext#refresh

在其中,定义了synchronized保证了启动加载时的线程安全性问题:

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

其中定义程序是否保活的逻辑,在onRefresh()中。

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh

@Override
protected void onRefresh() {
    super.onRefresh();
    try {
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}

createWebServer()主要是构建服务容器,这里以tomcat为例。点击进去查看其实现: 

private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null && servletContext == null) {
        ServletWebServerFactory factory = getWebServerFactory();
        this.webServer = factory.getWebServer(getSelfInitializer());
    }
    else if (servletContext != null) {
        try {
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    initPropertySources();
}

在服务容器创建时,就会定义其保活线程,查看getWebServer处理逻辑: 

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    if (this.disableMBeanRegistry) {
        Registry.disableRegistry();
    }
    Tomcat tomcat = new Tomcat();
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    prepareContext(tomcat.getHost(), initializers);
    return getTomcatWebServer(tomcat);
}

查看tomcatweb服务生成方式逻辑: 

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    this.tomcat = tomcat;
    this.autoStart = autoStart;
    initialize();
}

查看init逻辑:

private void initialize() throws WebServerException {
    logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
    synchronized (this.monitor) {
        try {
            addInstanceIdToEngineName();

            Context context = findContext();
            context.addLifecycleListener((event) -> {
                if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
                    // Remove service connectors so that protocol binding doesn't
                    // happen when the service is started.
                    removeServiceConnectors();
                }
            });

            // Start the server to trigger initialization listeners
            this.tomcat.start();

            // We can re-throw failure exception directly in the main thread
            rethrowDeferredStartupExceptions();

            try {
                ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
            }
            catch (NamingException ex) {
                // Naming is not enabled. Continue
            }

            // Unlike Jetty, all Tomcat threads are daemon threads. We create a
            // blocking non-daemon to stop immediate shutdown
            startDaemonAwaitThread();
        }
        catch (Exception ex) {
            stopSilently();
            destroySilently();
            throw new WebServerException("Unable to start embedded Tomcat", ex);
        }
    }
}

可以看到,在tomcat的服务容器构建完成后,其中调用了一个startDaemonAwaitThread();开启了一个守护线程? 

查看其中的逻辑:

private void startDaemonAwaitThread() {
    Thread awaitThread = new Thread("container-" + (containerCounter.get())) {

        @Override
        public void run() {
            TomcatWebServer.this.tomcat.getServer().await();
        }

    };
    awaitThread.setContextClassLoader(getClass().getClassLoader());
    awaitThread.setDaemon(false);
    awaitThread.start();
}

开启了一个新的非守护类型的线程,去执行了await()。

查看await()的实现逻辑:

红框中的代码逻辑,是否似曾相识!

整体思维逻辑

SpringApplication.run
 --> 
refreshContext
 -- >
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#refresh 
-- >
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#createWebServer 
-- >
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
 ->
org.springframework.boot.web.embedded.tomcat.TomcatWebServer#TomcatWebServer(org.apache.catalina.startup.Tomcat, boolean)
 -->
 org.springframework.boot.web.embedded.tomcat.TomcatWebServer#initialize
  -->
  org.springframework.boot.web.embedded.tomcat.TomcatWebServer#startDaemonAwaitThread

 

posted @   残城碎梦  阅读(1394)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
历史上的今天:
2021-11-09 压力测试工具jmeter
点击右上角即可分享
微信分享提示