Spring中refresh分析之onRefresh方法详解

简介: Spring中refresh分析之onRefresh方法详解

本文分析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的。之后我们在控制台可以看到如下日志打印:

41f99a525324421e9a5483bfb798ee07.png

我们继续往下看,将触发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)


目录
相关文章
|
16天前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
146 73
|
1月前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
57 14
|
2月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
42 1
|
3月前
|
存储 安全 Java
|
2月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
38 1
|
3月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
753 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
3月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
280 2
|
2月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
40 0
|
4月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
207 5
|
4月前
|
Java 应用服务中间件 Spring
IDEA 工具 启动 spring boot 的 main 方法报错。已解决
IDEA 工具 启动 spring boot 的 main 方法报错。已解决
100 4