当前位置: 代码网 > it编程>编程语言>Java > SpringBoot内置Tomcat启动方式

SpringBoot内置Tomcat启动方式

2024年12月11日 Java 我要评论
一、tomcat相关配置类如何加载的?在springboot项目中,我们只需要引入spring-boot-starter-web依赖,启动服务成功,我们一个web服务就搭建好了,没有明显的看到tomc

一、tomcat相关配置类如何加载的?

在springboot项目中,我们只需要引入spring-boot-starter-web依赖,启动服务成功,我们一个web服务就搭建好了,没有明显的看到tomcat。

其实打开spring-boot-starter-web依赖,我们可以看到:依赖了tomcat。

1.进入springboot启动类

我们加入springboot最核心的注解@springbootapplication,源码如下图:重点看注解@enableautoconfiguration,

2.进入注解@enableautoconfiguration

如下图:该注解通过@import注解导入了autoconfigurationimportselector类。

其实这个类,就是导入通过加载配置文件,加载了很多工厂方法的配置类。

3.进入autoconfigurationimportselector类

首先调用selectimport()方法,在该方法中调用了 getautoconfigurationentry()方法,在之中又调用了getcandidateconfigurations()方法, getcandidateconfigurations()方法就去meta-inf/spring.factory配置文件中加载相关配置类。

详细讲解如下:也就是下图的,方法1调用方法2,方法2调用方法3:

到了这里加载了 meta-inf/spring.factories文件:

4.我们看到

加载了servletwebserverfactoryautoconfiguration这个配置类,web工厂配置类。

@configuration(proxybeanmethods = false)
@autoconfigureorder(ordered.highest_precedence)
@conditionalonclass(servletrequest.class)
@conditionalonwebapplication(type = type.servlet)
@enableconfigurationproperties(serverproperties.class)
@import({ servletwebserverfactoryautoconfiguration.beanpostprocessorsregistrar.class,
		servletwebserverfactoryconfiguration.embeddedtomcat.class,
		servletwebserverfactoryconfiguration.embeddedjetty.class,
		servletwebserverfactoryconfiguration.embeddedundertow.class })
public class servletwebserverfactoryautoconfiguration {
  ...
}

从这个配置工厂类,我们看出通过@import注解加载了tomcat,jetty,undertow三个web服务器的配置类。

由于没有导入jetty和undertow的相关jar包,这两个类实例的不会真正的加载。

5.进入embeddedtomcat类

创建了tomcatservletwebserverfactory类的对象。

@configuration(proxybeanmethods = false)
class servletwebserverfactoryconfiguration {

	@configuration(proxybeanmethods = false)
	@conditionalonclass({ servlet.class, tomcat.class, upgradeprotocol.class })
	@conditionalonmissingbean(value = servletwebserverfactory.class, search = searchstrategy.current)
	static class embeddedtomcat {

		@bean
		tomcatservletwebserverfactory tomcatservletwebserverfactory(
				objectprovider<tomcatconnectorcustomizer> connectorcustomizers,
				objectprovider<tomcatcontextcustomizer> contextcustomizers,
				objectprovider<tomcatprotocolhandlercustomizer<?>> protocolhandlercustomizers) {
			tomcatservletwebserverfactory factory = new tomcatservletwebserverfactory();
			factory.gettomcatconnectorcustomizers()
					.addall(connectorcustomizers.orderedstream().collect(collectors.tolist()));
			factory.gettomcatcontextcustomizers()
					.addall(contextcustomizers.orderedstream().collect(collectors.tolist()));
			factory.gettomcatprotocolhandlercustomizers()
					.addall(protocolhandlercustomizers.orderedstream().collect(collectors.tolist()));
			return factory;
		}
	}

6.进入tomcatservletwebserverfactory类

关注getwebserver()方法:

	@override
	public webserver getwebserver(servletcontextinitializer... initializers) {
		if (this.disablembeanregistry) {
			registry.disableregistry();
		}
		//实例化一个tomcat
		tomcat tomcat = new tomcat();
		file basedir = (this.basedirectory != null) ? this.basedirectory : createtempdir("tomcat");
		//设置tomcat的工作临时目录
		tomcat.setbasedir(basedir.getabsolutepath());
		//默认使用http11nioprotocal实例化connector
		connector connector = new connector(this.protocol);
		connector.setthrowonfailure(true);
		//给service添加connector
		tomcat.getservice().addconnector(connector);
		customizeconnector(connector);
		tomcat.setconnector(connector);
		//关闭热部署
		tomcat.gethost().setautodeploy(false);
		//配置engine
		configureengine(tomcat.getengine());
		for (connector additionalconnector : this.additionaltomcatconnectors) {
			tomcat.getservice().addconnector(additionalconnector);
		}
		preparecontext(tomcat.gethost(), initializers);
		// 实例化tomcatwebserver时会将dispatcherservlet以及一些filter添加到tomcat中
		return gettomcatwebserver(tomcat);
	}

getwebserver()方法在当前类,调用了gettomcatwebserver()方法,其实又new tomcatwebserver()对象:

	protected tomcatwebserver gettomcatwebserver(tomcat tomcat) {
		return new tomcatwebserver(tomcat, getport() >= 0);
	}

7.进入tomcatwebserver类

这个类才是真正的做tomcat启动的类:

(1)构造方法:调用了initialize()方法

public tomcatwebserver(tomcat tomcat, boolean autostart) {
		assert.notnull(tomcat, "tomcat server must not be null");
		this.tomcat = tomcat;
		this.autostart = autostart;
		initialize();
	}

(2)进入initialize()方法,这个方法:this.tomcat.start(),启动tomcat容器了

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

				// tomcat在这里启动了
				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);
			}
		}
	}

二、getwebserver()的调用分析,也就是tomcat何时启动的

上面分析了tomcat的配置到启动的方法,我们现在来分析,tomcat是何时启动的。

1.首先进入springboot启动类的run方法

	public static void main(string[] args) {
		springapplication.run(springbootmytestapplication.class, args);
	}

最终调用了本类的一个同名方法:

public configurableapplicationcontext run(string... args) {
		//记录程序运行时间
		stopwatch stopwatch = new stopwatch();
		stopwatch.start();
		// configurableapplicationcontext spring 的上下文
		configurableapplicationcontext context = null;
		collection<springbootexceptionreporter> exceptionreporters = new arraylist<>();
		configureheadlessproperty();
		//【1、获取并启动监听器】
		springapplicationrunlisteners listeners = getrunlisteners(args);
		listeners.starting();
		try {
			applicationarguments applicationarguments = new defaultapplicationarguments(args);
			//【2、构造应用上下文环境】
			configurableenvironment environment = prepareenvironment(listeners, applicationarguments);
			//处理需要忽略的bean
			configureignorebeaninfo(environment);
			//打印banner
			banner printedbanner = printbanner(environment);
			///【3、初始化应用上下文】
			context = createapplicationcontext();
			//实例化springbootexceptionreporter.class,用来支持报告关于启动的错误
			exceptionreporters = getspringfactoriesinstances(springbootexceptionreporter.class,
					new class[] { configurableapplicationcontext.class }, context);
			//【4、刷新应用上下文前的准备阶段】
			preparecontext(context, environment, listeners, applicationarguments, printedbanner);
			//【5、刷新应用上下文】
			refreshcontext(context);
			//【6、刷新应用上下文后的扩展接口】
			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;
	}

这个方法大概做了以下几件事:

  • 1)获取并启动监听器 通过加载meta-inf/spring.factories 完成了 springapplicationrunlistener实例化工作
  • 2)构造容器环境,简而言之就是加载系统变量,环境变量,配置文件
  • 3)创建容器
  • 4)实例化springbootexceptionreporter.class,用来支持报告关于启动的错误
  • 5)准备容器
  • 6) 刷新容器
  • 7)刷新容器后的扩展接口

2.那么内置tomcat启动源码

就是隐藏在上面第六步:refreshcontext方法里面,该方法最终会调 用到abstractapplicationcontext类的refresh()方法,进入refreshcontext()方法,如图:

	private void refreshcontext(configurableapplicationcontext context) {
		refresh(context);
		if (this.registershutdownhook) {
			try {
				context.registershutdownhook();
			}
			catch (accesscontrolexception ex) {
				// not allowed in some environments.
			}
		}
	}

refreshcontext()调用了refresh()方法:

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

        }
    }

refresh()方法调用了this.onrefresh():

	@override
	protected void onrefresh() {
		super.onrefresh();
		try {
			//核心方法:会获取嵌入式的servlet容器工厂,并通过工厂来获取servlet容器
			createwebserver();
		}
		catch (throwable ex) {
			throw new applicationcontextexception("unable to start web server", ex);
		}
	}

如下面的代码:createwebserver() 方法调用了一个factory.getwebserver()。

	private void createwebserver() {
		webserver webserver = this.webserver;
		servletcontext servletcontext = getservletcontext();
		if (webserver == null && servletcontext == null) {
			//先获取嵌入式servlet容器工厂
			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()方法,下一步就是创建tomcatwebserver对象,创建该对象,就在构造方法启动了tomcat。详细代码在第一部分有。

总结

tomcat启动流程

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com