Spring Boot-源码阅读-如何启动内置web容器(三)
说明
在Spring Boot-源码阅读-启动主流程(一) <8-13>触发 容器初始化是AnnotationConfigServletWebServerApplicationContext
类图
<1>
在<8-16>处触发容器的refresh
/** * 优雅关闭 * @param context */ static final SpringApplicationShutdownHook shutdownHook = new SpringApplicationShutdownHook(); private void refreshContext(ConfigurableApplicationContext context) { if (this.registerShutdownHook) { shutdownHook.registerApplicationContext(context); } //<2>调用容器的refresh方法进入spring生命周期 refresh(context); }
<2>
更多spring生命周期代码可以参考:https://www.cnblogs.com/LQBlog/p/13954302.html
org.springframework.context.support.AbstractApplicationContext#refresh
@Override public void refresh() throws BeansException, IllegalStateException { /* *加锁 防止在初始化过程中,继续调用初始化和 销毁的方法 */ synchronized (this.startupShutdownMonitor) { //初始化容器的准备工作 // 将 active 属性设置为 true,closed 属性设置为 false,它们都是 AtomicBoolean 类型 prepareRefresh(); //将会初始化 BeanFactory、加载 Bean、注册 Bean 等等。只是将需要初始化的bean通过BeanDefinition封装 //BeanDefinition封装了如何初始化bean ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //为beanFactory设置创建bean的ClassLoader 添加几个 BeanPostProcessor,手动注册几个特殊的 bean 供后续使用 prepareBeanFactory(beanFactory); try { /** *这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化 *通过beanFactory我们可以获取并改变BeanDefinition 的属性改变将要创建bean的信息 * 钩子方法 目前空实现 */ postProcessBeanFactory(beanFactory); /** * 实例化并调用所有已注册的 BeanFactoryPostProcessor的实现类 * 这里是spring提供的扩展展点。我们可以通过实现BeanFactoryPostProcessor 在回调拿到beanFactory * 我们可以往里面注册BeanDefinition或者改变Definition的值 * 如何注册一个BeanDefinition: *可以参考:org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition使用 *BeanDefinitionReaderUtils#registerBeanDefinition 注册 使用例子可参考:附录 *典型例子可以参考Import原理 * */ invokeBeanFactoryPostProcessors(beanFactory); /** * 初始化 BeanPostProcessor的实现类 注意这里并没有调用 只是先初始化这个接口的实现类到容器 * 1.实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务 * 2.实例化、依赖注入、初始化完毕时执行 */ registerBeanPostProcessors(beanFactory); //初始化当前 ApplicationContext 的 MessageSource 国际化相关 initMessageSource(); /** * 初始化当前 ApplicationContext 的事件广播器 先在容器中找 如果没有则创建默认的 * ApplicationEventMulticaster 这个接口可以管理很多个ApplicationListener对象。并将事件发布给这些监听器 * 可参考附录用法 */ initApplicationEventMulticaster(); //<3>被子类重写了 onRefresh(); // 注册事件监听器,监听器需要实现 ApplicationListener 接口 registerListeners(); /** * 核心方法 * 初始化所有的 singleton bean BeanDefinition isLazyInit为 lazy-init 的除外 */ finishBeanFactoryInitialization(beanFactory); /** * 完成刷新Context,主要调用org.springframework.context.LifecycleProcessor接口onRefresh方法,发布事件ContextRefreshedEvent事件 */ finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // 销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源 destroyBeans(); // 将active 设置为false 表示此容器不可用 cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { //重置Spring核心中的常见内核缓存,因为我们可能不再需要单例bean的元数据了 resetCommonCaches(); } } }
<3>
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
@Override protected void onRefresh() { //调用父类的onRefresh super.onRefresh(); try { //<4>构建web createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }
<4>
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#createWebServer
private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); //触发创建 if (webServer == null && servletContext == null) { StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create"); //<5>获得ServletWebServerFactory 如tomcat netty或者自定实现 ServletWebServerFactory factory = getWebServerFactory(); createWebServer.tag("factory", factory.getClass().toString()); //<6>构建webserver this.webServer = factory.getWebServer(getSelfInitializer()); createWebServer.end(); //实现了SmartLifecycle接口 在容器加载完毕启动web 在容器关闭 关闭web getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer)); getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer)); } else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); }
<5>
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#getWebServerFactory
protected ServletWebServerFactory getWebServerFactory() { // 获得ServletWebServerFactory实现类的beanName String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class); if (beanNames.length == 0) { throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing " + "ServletWebServerFactory bean."); } if (beanNames.length > 1) { throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple " + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); } //调用getBean完成初始化 TomcatServletWebServerFactory JettyServletWebServerFactory默认是TomcatServletWebServerFactory return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class); }
<6>
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
@Override public WebServer getWebServer(ServletContextInitializer... initializers) { if (this.disableMBeanRegistry) { Registry.disableRegistry(); } Tomcat tomcat = new Tomcat(); File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); connector.setThrowOnFailure(true); tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this.additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); return getTomcatWebServer(tomcat); }