一、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启动流程

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