关联博文:
AbstractApplicationContext中refresh方法详解
Spring中refresh分析之prepareRefresh方法详解
Spring中refresh分析之obtainFreshBeanFactory方法详解
Spring中refresh分析之prepareBeanFactory方法详解
Spring中refresh分析之postProcessBeanFactory方法详解
Spring中refresh分析之invokeBeanFactoryPostProcessors方法详解
Spring中refresh分析之registerBeanPostProcessors方法详解
Spring中refresh分析之initMessageSource方法详解
Spring中refresh分析之initApplicationEventMulticaster方法详解
Spring中refresh分析之onRefresh方法详解
Spring中refresh分析之registerListeners方法详解
Spring中refresh分析之finishBeanFactoryInitialization方法详解
Spring中refresh分析之finishRefresh方法详解
接上文Spring中refresh分析之initApplicationEventMulticaster方法详解我们分析过initApplicationEventMulticaster后,本文分析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)