前言 在上一篇博文()中,我们分析了spring boot的整体启动流程。主要分为两部分:1)是创建SpringApplication;2)是SpringApplication的run方法,核心流程是创建WebApplicationContext,并完成这个context的refresh。 通过上篇博文,我们无法得知真正的web server(Jetty or Tomcat)是何时以及如何启动的。本篇博文会着重分析spring boot中是如何嵌入web server并将其启动成功的。我们会以webApplicationType为SERVLET的servlet web application举例进行分析。
配置web server web server在spring boot中是以嵌入的方式配置和运行的。spring boot中默认嵌入的web server是Tomcat,那我们就以Jetty为例进行分析。如果想要改变默认的Tomcat并切换为Jetty,需要对pom依赖进行如下的修改:1)去掉spring-boot-starter-tomcat的依赖 2)增加spring-boot-starter-jetty的依赖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <dependency >     <groupId > org.springframework.boot</groupId >  	<artifactId > spring-boot-starter-web</artifactId >  	<exclusions >  		<exclusion >  			<groupId > org.springframework.boot</groupId >  			<artifactId > spring-boot-starter-tomcat</artifactId >  		</exclusion >  	</exclusions >  </dependency > <dependency > 	<groupId > org.springframework.boot</groupId >  	<artifactId > spring-boot-starter-jetty</artifactId >  </dependency > 
 
创建ApplicationContext 本文是以webApplicationType=SERVLET 来进行举例,根据上篇博文中创建ApplicationContext的分析,可以看到对应创建的ApplicationContext类型为DEFAULT_WEB_CONTEXT_CLASS,class类型为"org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 protected  ConfigurableApplicationContext createApplicationContext ()   {	Class<?> contextClass = this .applicationContextClass; 	if  (contextClass == null ) { 		try  { 			switch  (this .webApplicationType) { 			case  SERVLET: 				contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS); 				break ; 			case  REACTIVE: 				contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); 				break ; 			default : 				contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); 			} 		} 		catch  (ClassNotFoundException ex) { 			throw  new  IllegalStateException( 					"Unable create a default ApplicationContext, "  					+ "please specify an ApplicationContextClass" , 					ex); 		} 	} 	return  (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); } 
 
AnnotationConfigServletWebServerApplicationContext 由上可知,spring boot中创建的ApplicationContext是一个AnnotationConfigServletWebServerApplicationContext实例。下图描述了对应的类关系图,其中用红色的圆圈标注的类属于spring-boot这个package,其余的类属于spring-context和spring-web这两个package。
从继承关系上看,AnnotationConfigServletWebServerApplicationContext继承了类ServletWebServerApplicationContext并实现了接口类AnnotationConfigRegistry。
摘抄了一段AnnotationConfigServletWebServerApplicationContext的javadoc描述,它是一个可以将注解类作为context的input的一个ServletWebServerApplicationContext。在实现方式上,可以通过指定class进行注册也可以通过扫描package注册。由此可见,启动servlet web。application的核心实现在于类ServletWebServerApplicationContext中。
1 2 3 4 5 6 * {@link  ServletWebServerApplicationContext} that accepts annotated classes as input - in * particular {@link  org.springframework.context.annotation.Configuration @Configuration } * -annotated classes, but also plain {@link  Component @Component } classes and JSR-330  * compliant classes using {@code  javax.inject} annotations. Allows for  registering * classes one by one  (specifying class names as config location)  as well as for  classpath  * scanning  (specifying base packages as config location) . 
 
摘抄一下该类的重要的私有变量和方法进行简单分析。
首先看下两个私有变量AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner,前者用于对annotated bean classes的注册,后者用于对指定classpath进行扫描。
再来看一下两个方法register(Class<?>... annotatedClasses) 和 scan(String... basePackages),这两个方法是对接口AnnotationConfigRegistry的实现。前者是手动注册annotatedClasses,后者是完成对basePackages的扫描。后边的两个方法prepareRefresh()和postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)是对父类方法的覆盖。特别是在方法postProcessBeanFactory中,会调用reader.register(Class<?>... annotatedClasses)方法将所有的annotatedClasses进行注册。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public  class  AnnotationConfigServletWebServerApplicationContext 	extends  ServletWebServerApplicationContext  implements  AnnotationConfigRegistry   {	private  final  AnnotatedBeanDefinitionReader reader; 	private  final  ClassPathBeanDefinitionScanner scanner; 	private  final  Set<Class<?>> annotatedClasses = new  LinkedHashSet<>(); 	 	@Override      public  final  void  register (Class<?>... annotatedClasses)   {     	Assert.notEmpty(annotatedClasses,     				"At least one annotated class must be specified" );     	this .annotatedClasses.addAll(Arrays.asList(annotatedClasses));     }          @Override      public  final  void  scan (String... basePackages)   {     	Assert.notEmpty(basePackages, "At least one base package must be specified" );     	this .basePackages = basePackages;     }          @Override      protected  void  prepareRefresh ()   {     	this .scanner.clearCache();     	super .prepareRefresh();     }          @Override      protected  void  postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory)   {     	super .postProcessBeanFactory(beanFactory);     	if  (this .basePackages != null  && this .basePackages.length > 0 ) {     		this .scanner.scan(this .basePackages);     	}     	if  (!this .annotatedClasses.isEmpty()) {     		this .reader.register(ClassUtils.toClassArray(this .annotatedClasses));     	}     } } 
 
由此可见,AnnotationConfigServletWebServerApplicationContext仅仅是附加了注册注解类的功能实现,具体核心的实现在ServletWebServerApplicationContext类。
ServletWebServerApplicationContext webServer启动流程 webServer的启动跟异常关闭过程跟ServletWebServerApplicationContext覆盖父类GenericWebApplicationContext的5个方法postProcessBeanFactory、refresh、onRefresh、finishRefresh、onClose。除refresh方法之外,另外4个方法都会在父抽象类AbstractApplicationContext的refresh方法中执行,具体的执行次序及作用可以详见对该类的分析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 @Override protected  void  postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory)   {	beanFactory.addBeanPostProcessor( 		new  WebApplicationContextServletContextAwareProcessor(this )); 	beanFactory.ignoreDependencyInterface(ServletContextAware.class); } @Override public  final  void  refresh ()  throws  BeansException, IllegalStateException  {	try  { 		super .refresh(); 	} 	catch  (RuntimeException ex) { 		stopAndReleaseWebServer(); 		throw  ex; 	} } @Override protected  void  onRefresh ()   {	super .onRefresh(); 	try  { 		createWebServer(); 	} 	catch  (Throwable ex) { 		throw  new  ApplicationContextException("Unable to start web server" , ex); 	} } @Override protected  void  finishRefresh ()   {	super .finishRefresh(); 	WebServer webServer = startWebServer(); 	if  (webServer != null ) { 		publishEvent(new  ServletWebServerInitializedEvent(webServer, this )); 	} } @Override protected  void  onClose ()   {	super .onClose(); 	stopAndReleaseWebServer(); } 
 
1)首先会被调用的是postProcessBeanFactory方法,方法会执行两步。
2)按照步骤,继续会被调用的是onRefresh方法,在onRefresh方法中会调用createWebServer方法,该方法会创建一个webServer。
3)再次被调用的会是finishRefresh,在该方法中会调用startWebServer,真正将webServer进行启动。启动成功之后,会创建一个ServletWebServerInitializedEvent的事件并将其发布出去。
4)最后关闭的方法,在ApplicationContext关闭时也会调用stopAndReleaseWebServer将对应的webServer关闭掉。
总结一下,ServletWebServerApplicationContext通过方法覆盖将创建、启动、关闭webServer的操作嵌入到了AbstractApplicationContext的refresh执行流程中,将webServer的生命周期融入到应用上下文中。
下面,我们就具体分析一下流程中具体的创建、启动和关闭的详细过程。
createWebServer 看一下createWebServer的方法实现,核心的代码实现在第5、6行。 在这篇博文里不会对具体代码实现进行分析,只会大概描述下流程,后续会在单独的博文中具体进行分析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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(); } 
 
第一步是通过调用getWebServerFactory()方法获取一个类型为ServletWebServerFactory的工厂实例。如果是Jetty容器的话会返回JettyServletWebServerFactory,tomcat的话会返回TomcatServletWebServerFactory。
第二步是调用ServletWebServerFactory的getWebServer(ServletContextInitializer... initializers)创建一个webServer。通过方法的javadoc描述,可以看出该方法返回的是一个完全配置好但处于暂停状态的webServer实例。
1 2 3 4 5 6 7 8 9 10 11 12 @FunctionalInterface public  interface  ServletWebServerFactory   {	 	WebServer getWebServer (ServletContextInitializer... initializers)  ; } 
 
方法的参数ServletContextInitializer也非常重要,这些初始化器需要在webServer启动之前得以执行。这是初始化器的真正作用是干什么的呢?
通过方法的javadoc描述可以看出,这个初始化器会帮助初始化ServletContext相关的任意的servlets, filters, listeners,context-params, attributes 。设计这个interface的主要好处就是可以将ServletContextInitializer通过Spring来管理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @FunctionalInterface public  interface  ServletContextInitializer   {	 	void  onStartup (ServletContext servletContext)  throws  ServletException ; } 
 
通过以上分析可见创建webServer的核心代码实现在ServletWebServerFactory以及ServletContextInitializer,后续会在文章里拿Jetty作为例子进行详细分析。
startWebServer 通过上节的分析,我们可以知道createWebServer创建的webServer处于配置好但是暂停的状态。startWebServer方法会调用webServer.start()将webServer进行启动。
1 2 3 4 5 6 7 private  WebServer startWebServer ()   {	WebServer webServer = this .webServer; 	if  (webServer != null ) { 		webServer.start(); 	} 	return  webServer; } 
 
stopAndReleaseWebServer 会调用webServer.stop()将webServer进行关闭。
1 2 3 4 5 6 7 8 9 10 11 12 private  void  stopAndReleaseWebServer ()   {	WebServer webServer = this .webServer; 	if  (webServer != null ) { 		try  { 			webServer.stop(); 			this .webServer = null ; 		} 		catch  (Exception ex) { 			throw  new  IllegalStateException(ex); 		} 	} } 
 
总结&比较