本文分析onRefresh方法。
首先调用父类的方法初始化主题源(themeSource)然后创建并启动WebServer。SpringBoot内置的Tomcat或者UndertowWebServer就是在这里实例化的。
【1】方法概览
ServletWebServerApplicationContext的onRefresh如下所示:
@Override protected void onRefresh() { super.onRefresh(); try { createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }
父类GenericWebApplicationContext的onRefresh如下所示,只是初始化了ThemeSource。
//GenericWebApplicationContext @Override protected void onRefresh() { this.themeSource = UiApplicationContextUtils.initThemeSource(this); }
【2】initThemeSource
抽象类UiApplicationContextUtils 其实只做了一件事,即实例化ThemeSource 。
public abstract class UiApplicationContextUtils { public static final String THEME_SOURCE_BEAN_NAME = "themeSource"; private static final Log logger = LogFactory.getLog(UiApplicationContextUtils.class); //检测是否存在themeSource,如果不存在则创建一个空的ThemeSource public static ThemeSource initThemeSource(ApplicationContext context) { if (context.containsLocalBean(THEME_SOURCE_BEAN_NAME)) { ThemeSource themeSource = context.getBean(THEME_SOURCE_BEAN_NAME, ThemeSource.class); // Make ThemeSource aware of parent ThemeSource. if (context.getParent() instanceof ThemeSource && themeSource instanceof HierarchicalThemeSource) { HierarchicalThemeSource hts = (HierarchicalThemeSource) themeSource; if (hts.getParentThemeSource() == null) { // Only set parent context as parent ThemeSource if no parent ThemeSource // registered already. hts.setParentThemeSource((ThemeSource) context.getParent()); } } if (logger.isDebugEnabled()) { logger.debug("Using ThemeSource [" + themeSource + "]"); } return themeSource; } else { // Use default ThemeSource to be able to accept getTheme calls, either // delegating to parent context's default or to local ResourceBundleThemeSource. HierarchicalThemeSource themeSource = null; if (context.getParent() instanceof ThemeSource) { themeSource = new DelegatingThemeSource(); themeSource.setParentThemeSource((ThemeSource) context.getParent()); } else { themeSource = new ResourceBundleThemeSource(); } if (logger.isDebugEnabled()) { logger.debug("Unable to locate ThemeSource with name '" + THEME_SOURCE_BEAN_NAME + "': using default [" + themeSource + "]"); } return themeSource; } } }
方法逻辑梳理如下:
- 尝试从容器中获取themeSource,如果存在则尝试为其设置ParentThemeSource;
- 如果容器中不存在themeSource则对parent进行判断:
- 如果parent不为null且是ThemeSource,则实例化DelegatingThemeSource并设置parent;
- 否则只是实例化ResourceBundleThemeSource;
本文这里只是实例化了ResourceBundleThemeSource。
【3】createWebServer
ServletWebServerApplicationContext
的createWebServer方法如下所示。
private void createWebServer() { // 默认是null WebServer webServer = this.webServer; //本文这里servletContext 也是null ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { ServletWebServerFactory factory = getWebServerFactory(); this.webServer = factory.getWebServer(getSelfInitializer()); } else if (servletContext != null) { try { // 如果servletContext 不为null,表示已经存在则启动 getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } // prepareRefresh中我们看到过,这里又出现了 initPropertySources(); }
ServletWebServerApplicationContext的getWebServerFactory方法如下所示,根据ServletWebServerFactory.class从容器检索得到beanNames。本文这里得到的是tomcatServletWebServerFactory,然后从容器中获取TomcatServletWebServerFactory bean实例。
protected ServletWebServerFactory getWebServerFactory() { // Use bean names so that we don't consider the hierarchy // 本文这里拿到的是tomcatServletWebServerFactory 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)); } return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class); }
得到TomcatServletWebServerFactory
实例后就会进行后续WebServer的实例化流程。本文这里暂不深入分析,将另起章节学习可以参考SpringBoot中是如何创建WebServer的。之后我们在控制台可以看到如下日志打印:
我们继续往下看,将触发initPropertySources。
initPropertySources
在前面prepareRefresh方法中我们看到过这个方法,这里又出现了。
// GenericWebApplicationContext @Override protected void initPropertySources() { ConfigurableEnvironment env = getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, null); } } //StandardServletEnvironment @Override public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) { WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig); } //WebApplicationContextUtils public static void initServletPropertySources(MutablePropertySources sources, @Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) { Assert.notNull(sources, "'propertySources' must not be null"); //servletContextInitParams String name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME; if (servletContext != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) { sources.replace(name, new ServletContextPropertySource(name, servletContext)); } //servletConfigInitParams name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME; if (servletConfig != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) { sources.replace(name, new ServletConfigPropertySource(name, servletConfig)); } }
与前者不同的是,这里servletContext不为null(servletConfig 为null)。故而这里将替换sources中的servletContextInitParams值,更新为new ServletContextPropertySource(name, servletContext)