本文將分析SpringMVC自身的建立過程。web
SpringMVC核心Servlet的繼承結構如圖spring
能夠看到Servlet的繼承機構中一共有5個類GenericServlet,HttpServlet在Java中,剩下的三個類HttpServlet,HttpServlet,HttpServlet是SpringMVC中的,中點介紹着三個類的建立過程。app
這三個類直接實現三個接口EnvironmentCapable,EnvironmentAware,ApplicationContextAware其中XXXAware在Spring裏表示對XXX能夠感知,也就是你想要Spring的一下東西,能夠經過實現XXXAware接口告訴Spring,Spring看到後就會給你送過了,而接收的方式是經過實現接口的惟一方法setXXX。如FrameworkServlet實現了ApplicationContextAware接口,會自動調用以下方法:ide
@Override public void setApplicationContext(ApplicationContext applicationContext) { if (this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) { this.webApplicationContext = (WebApplicationContext) applicationContext; this.webApplicationContextInjected = true; } }
EnvironmentCapable是指擁有Environment的能力,也就是能夠提供Environment,實現這個接口就是告訴Spring它能夠提供Environment,須要的時候就會調用getEnvironment工具
@Override public ConfigurableEnvironment getEnvironment() { if (this.environment == null) { this.environment = createEnvironment(); } return this.environment; }
protected ConfigurableEnvironment createEnvironment() { return new StandardServletEnvironment(); }
@Override public final void init() throws ServletException { if (logger.isTraceEnabled()) { logger.trace("Initializing servlet '" + getServletName() + "'"); } // 將servlet中配置的參數封裝到pvs變量中,requiredProperties爲必須參數 若是沒有將報異常 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); //模板方法,能夠在子類調用,作一些初始化操做,bw表明DispatcherServlet initBeanWrapper(bw); //將配置的初始值設置到bw表明DispatcherServlet bw.setPropertyValues(pvs, true); } catch (BeansException ex) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); } throw ex; } } // 模板方法,子類初始化入口 initServletBean(); if (logger.isTraceEnabled()) { logger.trace("Servlet '" + getServletName() + "' configured successfully"); } }
首先將Servlet中的配置參數使用BeanWarpper設置到DispatcherServlet的相關屬性,而後調用模板方法,子類經過這個方法初始化,BeanWarpper是Spring提供的一個用來操做JavaBean屬性的工具,使用它能夠直接修改一個對象的屬性。post
從HttpServletBean中可知,FrameworkServlet的初始化入口方法應該是initServletBean其代碼以下ui
@Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (logger.isInfoEnabled()) { logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { this.webApplicationContext = initWebApplicationContext(); // 模板方法 initFrameworkServlet(); } catch (ServletException | RuntimeException ex) { logger.error("Context initialization failed", ex); throw ex; } if (logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } }
這裏核心代碼就兩句,一個是初始化WebApplicationContext,另外一句用於初始化FrameworkServlet,也是模板方法,子類覆蓋在這裏面作一些初始化操做。好像子類並無使用它,因此他的主要職責仍是初始化WebApplicationContextthis
protected WebApplicationContext initWebApplicationContext() { // 獲取rootContext WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; // 1.若是已經經過構造方法設置了webApplicationContext,主要是Servlet3.0環境中 if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { //2.查看容器是否已經存在了,當webApplicationContext已經存在ServletContext中時, // 經過配置在Servlet中的contextAttribute參數獲取 wac = findWebApplicationContext(); } if (wac == null) { // 3.若是webApplicationContext尚未建立,則建立一個 wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { //當ContextRefreshedEvent事件沒有觸發時調用次方法,模板方法,能夠在子類重寫 onRefresh(wac); } if (this.publishContext) { // 將webApplicationContext保持到ServletContext中 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (logger.isTraceEnabled()) { logger.trace("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute [" + attrName + "]"); } } return wac; }
initWebApplicationContext主要作了三件事:spa
1.獲取spring的跟容器rootContext。debug
默認狀況下spring會將本身的容器設置成ServletContext的一個屬性,key定義以下
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
因此獲取跟容器代碼爲:
Object attr = sc.getAttribute(attrName);
2.設置webApplicationContext並根據狀況調用onRefresh方法。
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { //建立類型 即XmlWebApplicationContext.class Class<?> contextClass = getContextClass(); if (logger.isTraceEnabled()) { logger.trace("Servlet '" + getServletName() + "' will create custom WebApplicationContext context of class '" + contextClass.getName() + "'" + ", parent context [" + parent + "]"); } if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } //具體建立實例化 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); //設置環境 wac.setEnvironment(getEnvironment()); //設置父容器 wac.setParent(parent); String configLocation = getContextConfigLocation(); if (configLocation != null) { //將設置的configLocation參數傳給wac wac.setConfigLocation(configLocation); } configureAndRefreshWebApplicationContext(wac); return wac; }
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // The application context id is still set to its original default value // -> assign a more useful id based on available information if (this.contextId != null) { wac.setId(this.contextId); } else { // Generate default id... wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName()); } } wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); // 添加ContextRefreshEvent事件的監聽器 wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); // The wac environment's #initPropertySources will be called in any case when the context // is refreshed; do it eagerly here to ensure servlet property sources are in place for // use in any post-processing or initialization that occurs below prior to #refresh ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); } //後置處理器 postProcessWebApplicationContext(wac); applyInitializers(wac); wac.refresh(); }
3.將webApplicationContext設置到ServletContext中。
onRefresh是DispatcherServlet的入口方法,onRefresh調用了initStrategies,在initStrategies中調用9個初始化方法,就是初始化9個組件,後面會詳細介紹。
@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); if (logger.isDebugEnabled()) { if (this.disableLoggingRequestDetails) { logger.debug("Logging request parameters and headers is OFF."); } else { logger.warn("\n\n" + "!!!!!!!!!!!!!!!!!!!\n" + "Logging request parameters (DEBUG) and headers (TRACE) may show sensitive data.\n" + "If not in development, use the DispatcherServlet property \"disableLoggingRequestDetails=true\",\n" + "or lower the log level.\n" + "!!!!!!!!!!!!!!!!!!!\n"); } } }
SpringMVC的建立過程一個有三個層次,分別是HttpServletBean,FramworkSerlet和DispatcherServlet.
HttpServletBean的做用是將Servlet中的配置參數設置到相應的屬性;
FramworkSerlet的做用是初始化webApplicationContext;
DispatcherServlet的做用是初始化自身的9個組件
總的來講結構簡單實現複雜。