上一篇筆記中記錄了下 Root WebApplicationContext 的初始化代碼.這一篇來看 Servlet WebApplicationContext 的初始化代碼git
DispatcherServlet
是另外一個須要在 web.xml
中配置的類, Servlet WebApplicationContext 就由它來建立和初始化.github
HttpServletBean
簡單繼承了 HttpServlet
, 負責將 init-param 中的參數注入到當前 Servlet
實例的屬性中, 而且爲子類提供了增長 requiredProperties 的能力. HttpServletBean
並不依賴於 Spring 容器.web
來看一下它的 init()
方法:spring
public final void init() throws ServletException { // Set bean properties from init parameters. // 從 ServletConfig 中取出初始化參數到 PropertyValues。ServletConfigPropertyValues 的構造器中將會檢查是否缺失了必要屬性 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { // 將 servlet 對象包裝成 BeanWrapper ,從而可以以 Spring 的方式(反射)來注入參數 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); // 註冊 PropertyEditor,遇到 Resource 類型的屬性時,用 ResourceEditor 解析 bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); // 初始化 BeanWrapper,空方法 initBeanWrapper(bw); // 注入屬性,忽略沒有 setter 的屬性 bw.setPropertyValues(pvs, true); } catch (BeansException ex) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); } throw ex; } } // Let subclasses do whatever initialization they like. // 由子類實現初始化邏輯 initServletBean(); }
private static class ServletConfigPropertyValues extends MutablePropertyValues { public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties) throws ServletException { // 將 requiredProperties 拷貝到新的 Set missingProps Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ? new HashSet<>(requiredProperties) : null); // 將 ServletConfig 中的初始化參數取出,添加到 MutablePropertyValues 中 Enumeration<String> paramNames = config.getInitParameterNames(); while (paramNames.hasMoreElements()) { String property = paramNames.nextElement(); Object value = config.getInitParameter(property); addPropertyValue(new PropertyValue(property, value)); if (missingProps != null) { missingProps.remove(property); } } // Fail if we are still missing properties. if (!CollectionUtils.isEmpty(missingProps)) { // 存在必須出現的條件沒出現 throw new ServletException( "Initialization from ServletConfig for servlet '" + config.getServletName() + "' failed; the following required properties were missing: " + StringUtils.collectionToDelimitedString(missingProps, ", ")); } } }
FrameworkServlet
是一個更具體的 Servlet 基類. 它有如下兩個功能:spring-mvc
WebApplicationContext
實例.FrameworkServlet
重寫了 HttpServletBean
的 initServletBean()
方法, 這個方法會在 全部 servlet 的屬性被注入以後執行, 來看一下代碼:mvc
protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'"); if (logger.isInfoEnabled()) { logger.info("Initializing Servlet '" + getServletName() + "'"); } long startTime = System.currentTimeMillis(); try { // 初始化 webApplicationContext this.webApplicationContext = initWebApplicationContext(); // 在容器被加載後執行,由子類來實現一些必要的初始化 initFrameworkServlet(); } catch (ServletException | RuntimeException ex) { logger.error("Context initialization failed", ex); throw ex; } // 略去打印日誌的部分 ... }
initWebApplicationContext()
方法會初始化並返回一個容器:app
protected WebApplicationContext initWebApplicationContext() { // 獲取 Root WebApplicationContext WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it // 一個上下文已經被注入進來 wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { // 若是是 ConfigurableWebApplicationContext, ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // 沒有激活,設置父容器,配置而且刷新容器 if (cwac.getParent() == null) { cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // 嘗試從 ServletContext 中獲取一個容器 wac = findWebApplicationContext(); } if (wac == null) { // 建立一個新的容器並初始化 wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // 沒有觸發過刷新時間 synchronized (this.onRefreshMonitor) { // 手動觸發刷新事件 onRefresh(wac); } } if (this.publishContext) { // Publish the context as a servlet context attribute. // 將容器發佈到 ServletContext 的屬性上 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; }
onRefresh()
方法供子類來重寫, DispatcherServlet
重寫了這個方法來初始化 MVC 中的一些組件:webapp
@Override protected void onRefresh(ApplicationContext context) { initStrategies(context); } protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
initWebApplicationContext()
方法調用的其餘方法其實和 ContextLoader
中的方法比較相似, 這裏就再也不放上來了, 有興趣的能夠訪問個人源碼註釋ide