文章目录

  • 前言
  • 简单Java阻止停止
  • Springboot 底层实现
  • 1、查看SpringApplication.run
  • 2、refreshContext(context)
  • 3、org.springframework.context.support.AbstractApplicationContext#refresh
  • 4、org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
  • 整体思维逻辑


前言

观众大姥爷们是否发现一个很有意思的现象:

一般项目执行后,当程序结束会自动关闭程序。
但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,那么程序将不会自动停止。

springboot后台会自动重启_tomcat


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

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

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为例。

1、查看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);

2、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();
}

3、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()中。

4、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()的实现逻辑:

springboot后台会自动重启_springboot后台会自动重启_02


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

整体思维逻辑

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