首页>>后端>>SpringBoot->Embedded Servlet Container 是怎样启动的

Embedded Servlet Container 是怎样启动的

时间:2023-11-30 本站 点击:0

传统Java Web开发中,开发者需要独立部署Servlet容器,比如Tomcat,并将应用程序打成war包放入容器才能运行,这多多少少有点繁琐且不方便调试,嵌入式Servlet容器的出现改变了这个局面。当使用嵌入式Servlet容器,我们不再需要任何外部设施的支持,应用程序本身就是一个可以独立运行的个体。作为解放生产力的典型代表,SpringBoot默认采用Embedded Tomcat来启动Web应用程序,我们今天就来探究一下吧(基于SpringBoot 2.3.0)。

@SpringBootApplicationpublicclassApplication{publicstaticvoidmain(String[]args){SpringApplication.run(Application.class,args);}}

这段代码相信大家都不陌生,它是应用程序的入口,一切的一切都要从这里开始。我们直接跟进SpringApplication.run(...),来到

publicstaticConfigurableApplicationContextrun(Class<?>[]primarySources,String[]args){returnnewSpringApplication(primarySources).run(args);}

静态的run(...)函数创建了一个SpringApplication的实例,并调用了它的run(...)方法,继续跟进SpringApplication的构造函数

//无关逻辑已删除publicSpringApplication(ResourceLoaderresourceLoader,Class<?>...primarySources){//根据类路径下是否有对应的class文件来判定当前的运行环境//我们引入了spring-boot-starter-web,因此类型被推断为SERVLETthis.webApplicationType=WebApplicationType.deduceFromClasspath();}

这里我们主要关注SpringBoot是如何推断应用程序的类型的,简单来说是基于类路径下是否存在指定的class文件来判定的,在我们的例子里类型被推断为WebApplicationType.SERVLET。接着来到run(...)实例方法

//无关逻辑已删除publicConfigurableApplicationContextrun(String...args){ConfigurableApplicationContextcontext=null;try{//1.创建ApplicationContextcontext=createApplicationContext();//2.刷新ApplicationContextrefreshContext(context);}catch(Throwableex){thrownewIllegalStateException(ex);}returncontext;}

省去无关逻辑后,run(...)方法主要做了两件事:

创建ApplicationContext

刷新ApplicationContext

嗯?嵌入式Tomcat这就启动起来了?看来奥秘就隐藏在这两步之中。查看这两步的源码,很容易知道ApplicationContext的具体类型是AnnotationConfigServletWebServerApplicationContext,而刷新无非是调用了它的refresh()方法。

AnnotationConfigServletWebServerApplicationContext

观察AnnotationConfigServletWebServerApplicationContext的继承树,可以看到,红圈外的是我们非常熟悉的、在传统Web环境下使用的ApplicationContext;红圈内的部分呢,单看名字也能猜到是我们要重点研究的对象——WebServerApplicaitonContext

再深入一点儿AnnotationConfigServletWebServerApplicationContext,它继承自ServletWebServerApplicationContext,并且在父类的基础上提供了对Component Scan的支持和对@Configuration配置类的读取、解析。

publicclassAnnotationConfigServletWebServerApplicationContextextendsServletWebServerApplicationContextimplementsAnnotationConfigRegistry{//读取并解析@Configuration配置类privatefinalAnnotatedBeanDefinitionReaderreader;//扫描指定包下的所有组件privatefinalClassPathBeanDefinitionScannerscanner;//restofcodesareomitted...}

这部分功能是通过代理给AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner来实现的。这两兄弟也是我们的老朋友了,而且和嵌入式Servlet容器的启动也没啥关系,我们就不多说了。

WebServerApplicaitonContext

接下来我们的重心就落在WebServerApplicaitonContext上了,先来看一下它的定义

/***实现此接口的ApplicationContext要负责对嵌入式WebServer的创建和生命周期管理**Interfacetobeimplementedby{@linkApplicationContextapplicationcontexts}that*createandmanagethelifecycleofanembedded{@linkWebServer}.*/publicinterfaceWebServerApplicationContextextendsApplicationContext{/***返回当前ApplicationContext创建的WebServer,并通过返回的WebServer引用来管理其生命周期*/WebServergetWebServer();/***命名空间,当应用程序中有多个WebServer在运行时可以用来避免歧义*/StringgetServerNamespace();staticbooleanhasServerNamespace(ApplicationContextcontext,StringserverNamespace){return(contextinstanceofWebServerApplicationContext)&&ObjectUtils.nullSafeEquals(((WebServerApplicationContext)context).getServerNamespace(),serverNamespace);}}//子接口,可以对ApplicationContext进行配置publicinterfaceConfigurableWebServerApplicationContextextendsConfigurableApplicationContext,WebServerApplicationContext{/***设置命名空间*/voidsetServerNamespace(StringserverNamespace);}

再看看它关联的WebServer的定义

/***代表一个已经配置好了的WebServer,比如Tomcat、Jetty、Netty**Simpleinterfacethatrepresentsafullyconfiguredwebserver(forexampleTomcat,*Jetty,Netty).Allowstheservertobe{@link#start()started}and{@link#stop()*stopped}.*/publicinterfaceWebServer{/***启动服务器*/voidstart()throwsWebServerException;/***停止服务器*/voidstop()throwsWebServerException;/***返回服务器监听的端口*/intgetPort();/***优雅关闭*/defaultvoidshutDownGracefully(GracefulShutdownCallbackcallback){callback.shutdownComplete(GracefulShutdownResult.IMMEDIATE);}}

WebServer是对嵌入式Servlet容器的抽象,并且它代表的是一个已经完全配置好了的Servlet容器。换句话说,终端用户不需要关注容器的具体细节,只需要知道怎么启动或关闭它;而WebServerApplicationContext负责创建WebServer,并作为终端用户在合适的时机启动或关闭它(也就是管理其生命周期)。

ServletWebServerApplicationContext

ServletWebServerApplicationContext实现了WebServerApplicationContext接口,而它对WebServer的创建和管理都浓缩在它自身的刷新进程之中,也就是ConfigurableApplicationContext#refresh()被调用的时候。具体地说,ServletWebServerApplicationContext覆写了onRefresh(...)钩子方法,这个方法的调用时机是:

BeanFactory初始化完毕

BeanDefinition解析完成

Non-Lazy Init类型的Bean还未初始化

@OverrideprotectedvoidonRefresh(){super.onRefresh();try{//创建WebServercreateWebServer();}catch(Throwableex){thrownewApplicationContextException("Unabletostartwebserver",ex);}}privatevoidcreateWebServer(){WebServerwebServer=this.webServer;ServletContextservletContext=getServletContext();//webServer==null:还没有创建WebServer实例//servletContext==null:没有使用外部容器if(webServer==null&&servletContext==null){//1.获取一个创建WebServer的工厂ServletWebServerFactoryfactory=getWebServerFactory();//2.通过工厂创建WebServerthis.webServer=factory.getWebServer(getSelfInitializer());//3.监听ApplicationContext的生命周期//在ApplicationContext#stop()时优雅关闭WebServergetBeanFactory().registerSingleton("webServerGracefulShutdown",newWebServerGracefulShutdownLifecycle(this.webServer));//4.监听ApplicationContext的生命周期//在Non-LazyInit类型的Bean都初始化了之后启动WebServer//在ApplicationContext#stop()时关闭WebServergetBeanFactory().registerSingleton("webServerStartStop",newWebServerStartStopLifecycle(this,this.webServer));}//外部容器elseif(servletContext!=null){try{getSelfInitializer().onStartup(servletContext);}catch(ServletExceptionex){thrownewApplicationContextException("Cannotinitializeservletcontext",ex);}}//初始化ServletContextPropertySource//将ServletContext的init-parameters暴露到Environment中initPropertySources();}

创建WebServer的时机是在Non-Lazy Init类型的Bean初始化之前,通过获取BeanFactory中唯一的一个ServletWebServerFactory来执行创建。注意这里携带了一个参数——getSelfInitializer(),这个参数很重要,我们后面再说。

紧接着往BeanFactory中注册了两个SmartLifecycle类型的组件来管理WebServer的生命周期,其中一个用于优雅关闭WebServer,另一个用于启动或停止WebServer

classWebServerGracefulShutdownLifecycleimplementsSmartLifecycle{privatefinalWebServerwebServer;privatevolatilebooleanrunning;WebServerGracefulShutdownLifecycle(WebServerwebServer){this.webServer=webServer;}@Overridepublicvoidstart(){this.running=true;}@Overridepublicvoidstop(){thrownewUnsupportedOperationException("Stopmustnotbeinvokeddirectly");}@Overridepublicvoidstop(Runnablecallback){//优雅关闭webServerthis.running=false;this.webServer.shutDownGracefully((result)->callback.run());}@OverridepublicbooleanisRunning(){returnthis.running;}}classWebServerStartStopLifecycleimplementsSmartLifecycle{privatefinalServletWebServerApplicationContextapplicationContext;privatefinalWebServerwebServer;privatevolatilebooleanrunning;WebServerStartStopLifecycle(ServletWebServerApplicationContextapplicationContext,WebServerwebServer){this.applicationContext=applicationContext;this.webServer=webServer;}@Overridepublicvoidstart(){//启动webServerthis.webServer.start();this.running=true;this.applicationContext.publishEvent(newServletWebServerInitializedEvent(this.webServer,this.applicationContext));}@Overridepublicvoidstop(){//关闭webServerthis.webServer.stop();}@OverridepublicbooleanisRunning(){returnthis.running;}@OverridepublicintgetPhase(){//控制对#stop()的调用在WebServerGracefulShutdownLifecycle#stop(Runnable)之后returnInteger.MAX_VALUE-1;}}

SmartLifecyclespring-context定义的基础组件,本篇的主题并不是它。不过为了方便理清调用顺序,这里还是简单说一下:它是由LifecycleProcessor驱动的,在Non-Lazy Init类型的Bean都初始化了之后,ApplicationContext会回调LifecycleProcessor#onRefresh(),并在其中对SmartLifecycle进行处理。

//源码位于AbstractApplicationContextprotectedvoidfinishRefresh(){//Clearcontext-levelresourcecaches(suchasASMmetadatafromscanning).clearResourceCaches();//初始化LifecycleProcessorinitLifecycleProcessor();//回调LifecycleProcessor#onRefresh()//在onRefresh()中逐个调用SmartLifecycle#start()//当然,这里还有一些过滤条件,我们就不细说了getLifecycleProcessor().onRefresh();//发布ContextRefreshedEventpublishEvent(newContextRefreshedEvent(this));//ParticipateinLiveBeansViewMBean,ifactive.LiveBeansView.registerApplicationContext(this);}

至此,嵌入式Servlet容器是如何启动的就分析完了。

ServletContextInitializer

前面提过,ServletWebServerFactory在创建WebServer时会携带一个参数——getSelfInitializer(),它的类型是ServletContextInitializer

publicstaticConfigurableApplicationContextrun(Class<?>[]primarySources,String[]args){returnnewSpringApplication(primarySources).run(args);}0

ServletContextInitializer的作用类似于ServletContainerInitializer,后者是Servlet API提供的标准初始化器。我们同样可以在ServletContextInitializer 中对ServletContext进行配置,区别在于它的生命周期由BeanFactory管理而不是Servlet容器。

publicstaticConfigurableApplicationContextrun(Class<?>[]primarySources,String[]args){returnnewSpringApplication(primarySources).run(args);}1

第 1、2 和 第 3 步比较简单,大家自己看看吧,我们重点来看看第 4 步。

publicstaticConfigurableApplicationContextrun(Class<?>[]primarySources,String[]args){returnnewSpringApplication(primarySources).run(args);}2

ServletContextInitializerBeans在初始化的时候会检索出BeanFactory中所有的RegistrationBean;如果BeanFactory中还存在原生的ServletFilterServlet Listener类型的Bean,则将它们包装成对应的RegistrationBean,最后对所有的RegistrationBean进行排序。我们就以ServletRegistrationBean来看看它是如何实现向ServletContext中添加Servlet的吧。

从继承树可以看到,ServletRegistrationBean同样实现了ServletContextInitializer,查看其onStartup(...)方法

publicstaticConfigurableApplicationContextrun(Class<?>[]primarySources,String[]args){returnnewSpringApplication(primarySources).run(args);}3

DynamicRegistrationBean实现了register(...)方法

publicstaticConfigurableApplicationContextrun(Class<?>[]primarySources,String[]args){returnnewSpringApplication(primarySources).run(args);}4

addRegistration(...)最终由ServletRegistrationBean实现

publicstaticConfigurableApplicationContextrun(Class<?>[]primarySources,String[]args){returnnewSpringApplication(primarySources).run(args);}5

不过是直接使用ServletContext.addServlet(...)动态添加了一个Servlet,再无其它。

后记

我们分析了嵌入式Servlet容器是何时创建和启动的,却没有提它是如何创建的。以Tomcat为例,对应的TomcatWebServer封装了Tomcat API,提供了对ConnectorErrorPage等一些列组件的配置,只不过我不太熟这些容器的架构,就不瞎BB了~~


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/SpringBoot/2598.html