文章开始前。先看一篇知乎文章 。
在熟悉了一些知识后,再看这篇文章。
有了这两篇文章,就能明白,为什么对于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());
}
}
}