前言 在上一篇博文()中,我们分析了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); } } }
总结&比较