年初面試時接觸到一道面試題,在聊到SpringMVC時提到了SpringMVC的開發者爲什麼要設計父子容器呢,又或者說是父子容器的設計有什麼更實際的做用呢?
首先要理解對於一個web應用,當其部署在web容器上時,容器會爲其提供一個全局上下文環境ServletContext,這個上下文環境將爲後續的Spring提供宿主環境。php
--在web.xml中配置,兩個重要的xml:applicationContext.xml和SpringMVC-conf.xml <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:applictionContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>dispatcher-servlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:springMVC-conf.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dispatcher-servlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
根據SpringMVC的官方解釋,父(根)容器主要包括一些基礎腳手架的bean,好比Pool、DataSource、Dao、Service。目的是在不一樣的Servlet實例之間共享。這些不一樣的bean能夠在子容器中重寫。
而子容器主要包括一些Controller、View等一些web相關的bean。 html
既然SpringMVC中同時包含Spring容器和SpringMVC容器,那麼這兩個容器都是在何時初始化呢?java
首先,根容器是經過ServletContext監聽器進行建立,默認的監聽器爲ContextLoaderListener,當web應用啓動時,會調用監聽器的contextInitialized方法。
那麼根容器的初始化就從ContextLoaderListener類提及吧,,Spring官方對該類的描述是啓動監聽器去啓動和關閉Spring的root WebApplicationContext(翻譯的實在有點蹩腳)。
web
public class ContextLoaderListener extends ContextLoader implements ServletContextListener { public ContextLoaderListener() { } public ContextLoaderListener(WebApplicationContext context) { super(context); } //===初始化root WebApplicationContext=== @Override public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); } @Override public void contextDestroyed(ServletContextEvent event) { closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); } }
//ContextLoader.java public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { //初始化Spring容器時若是發現servlet 容器中已存在根Spring容根器則拋出異常,證實rootWebApplicationContext只能有一個。 if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException( "Cannot initialize context because there is already a root application context present - " + "check whether you have multiple ContextLoader* definitions in your web.xml!"); } try { //建立webApplicationContext實例 if (this.context == null) { this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; if (!cwac.isActive()) { if (cwac.getParent() == null) { ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } //配置WebApplicationContext configureAndRefreshWebApplicationContext(cwac, servletContext); } } /** 把生成的webApplicationContext設置成root WebApplicationContext。保存在ServletContext上下文中。 下一步初始化MVC ApplicationContext時須要從ServletContext取出根上下文做爲其父上下文。 **/ servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } return this.context; } catch (RuntimeException | Error ex) { servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; } }
以上代碼主要完成兩個功能:建立實例WebApplicationContext實例、把所建立的WebApplicationContext設置爲根上下文,也就是設置成爲ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。面試
你們知道Servlet生命週期都是從init方法開始,desctory方法結束,由jvm負責垃圾回收。而DispatcherServlet也是一個普通的Servlet,先看一下DispatcherServlet的繼承關係圖,對整個繼承關係有個瞭解。
既然提及Servlet,那就從Servlet的初始化(init)方法入手spring
//HttpServletBean.java @Override public final void init() throws ServletException { 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())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { throw ex; } } //交給子類重寫 initServletBean(); } //FrameworkServlet.java @Override protected final void initServletBean() throws ServletException { try { this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException | RuntimeException ex) { throw ex; } } //FrameworkServlet.java //初始化MVC容器 protected WebApplicationContext initWebApplicationContext() { //從ServletContext取出根上下文 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { if (cwac.getParent() == null) { cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { wac = findWebApplicationContext(); } //若是尚未webApplicatioinContext,建立webApplicationContext if (wac == null) { wac = createWebApplicationContext(rootContext); } //子類自定義對servlet子上下文後續操做,在DispatcherServlet中實現 if (!this.refreshEventReceived) { synchronized (this.onRefreshMonitor) { //執行子類擴展方法onRefresh,在DispatcherServlet內初始化全部web相關組件 onRefresh(wac); } } //發佈servlet子上下文到ServletContext if (this.publishContext) { String attrName = getServletContextAttributeName(); //將servlet子上下文以org.springframework.web.servlet.FrameworkServlet.CONTEXT. + servletName的屬性名稱註冊到ServletContext中 getServletContext().setAttribute(attrName, wac); } return wac; } protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) { return createWebApplicationContext((ApplicationContext) parent); } protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { //獲取WebApplicationContext實現類,此處其實就是XmlWebApplicationContext Class<?> contextClass = getContextClass(); 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"); } //生成XmlWebApplicationContext實例 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setEnvironment(getEnvironment()); //設置根容器爲父容器 wac.setParent(parent); String configLocation = getContextConfigLocation(); if (configLocation != null) { //設置配置文件 wac.setConfigLocation(configLocation); } //配置webApplicationContext configureAndRefreshWebApplicationContext(wac); return wac; } protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { if (this.contextId != null) { wac.setId(this.contextId); } else { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName()); } } wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); } postProcessWebApplicationContext(wac); applyInitializers(wac); //開始處理bean wac.refresh(); }
上面的關鍵代碼都在FrameworkServlet類中,有幾個關鍵點:取除根上下文,建立子上下文並設置父上下文,完成刷新,把子上下文發佈到ServletContext中。 到這裏能夠說子容器(子上下文)已經建立完成。 並把其餘初始化web組件的相關工做交給onRefresh方法完成,由DispatcherServlet來重寫onRefresh方法,這就又回到了咱們熟悉的initStrategies方法。mvc
@Override protected void onRefresh(ApplicationContext context) { initStrategies(context); } protected void initStrategies(ApplicationContext context) { //文件上傳解析器 initMultipartResolver(context); //本地化解析器 initLocaleResolver(context); //主題解析器 initThemeResolver(context); //處理器映射器(url和Controller方法的映射) initHandlerMappings(context); //處理器適配器(實際執行Controller方法) initHandlerAdapters(context); //處理器異常解析器 initHandlerExceptionResolvers(context); //RequestToViewName解析器 initRequestToViewNameTranslator(context); //視圖解析器(視圖的匹配和渲染) initViewResolvers(context); //FlashMap管理者 initFlashMapManager(context); }
這裏咱們主要關注一下三個重要組件:HandlerMapping、HandlerAdapter、ViewResolver。分析這3個組件以前,咱們先看一下咱們的springMVC-conf.xml配置文件,mvc的配置文件中,咱們配置了兩行代碼:app
<context:component-scan base-package="com.zhangfei"/> <mvc:annotation-driven>
第二行代碼主要是添加了默認的HandleMapping,ViewResolver,HandleAdapter。咱們看看annotation-driven的源碼定義,根據spring自定義schema定義,咱們找到以下代碼,如圖所示:
該文件就一行代碼:cors
http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler
//MVC全部的標籤解析器都定義在此 public class MvcNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser()); registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser()); registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser()); registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser()); registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser()); } }
那麼經過分析AnnotationDrivenBeanDefinitionParser類,主要完成如下三大組件的裝配工做:
jvm
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; //這裏detectAllHandlerMappings默認值爲true,能夠經過配置文件設置爲false if (this.detectAllHandlerMappings) { //從上下文(包含父上下文)中查找全部HandlerMapping實現類 Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { //這裏只取固定的bean HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { } } /*** 確保至少有一個HandlerMapping,若是沒能找到,註冊一個默認的 默認規則在DispatcherServlet.properties中,這裏也就是取BeanNameUrlHandlerMapping、RequestMappingHandlerMapping ***/ if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); } }
private void initHandlerAdapters(ApplicationContext context) { this.handlerAdapters = null; if (this.detectAllHandlerAdapters) { //從上下文(包括父上下文)中查找全部HandlerAdapter實現類 Map<String, HandlerAdapter> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerAdapters = new ArrayList<>(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this.handlerAdapters); } } else { try { //這裏取bean名字爲handlerAdapter,類型爲HandlerAdapter的處理器適配器 HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class); this.handlerAdapters = Collections.singletonList(ha); } catch (NoSuchBeanDefinitionException ex) { } } /** 若是沒找到,則從默認規則裏取出指定的三個實現類:HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、RequestMappingHandlerAdapter **/ if (this.handlerAdapters == null) { this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class); } }
private void initViewResolvers(ApplicationContext context) { this.viewResolvers = null; if (this.detectAllViewResolvers) { //從上下文(包括父上下文)中查找全部ViewResolver實現類 Map<String, ViewResolver> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false); if (!matchingBeans.isEmpty()) { this.viewResolvers = new ArrayList<>(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this.viewResolvers); } } else { try { ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class); this.viewResolvers = Collections.singletonList(vr); } catch (NoSuchBeanDefinitionException ex) { } } /** 若是沒找到,則從默認規則裏取出指定的實現類:InternalResourceViewResolver **/ if (this.viewResolvers == null) { this.viewResolvers = getDefaultStrategies(context, ViewResolver.class); } }
三大組件的初始化最後判斷爲NULL時都會調用getDefaultStrategies方法,也就是從DispatcherServlet.properties中取出指定默認值。
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) { String key = strategyInterface.getName(); String value = defaultStrategies.getProperty(key); if (value != null) { String[] classNames = StringUtils.commaDelimitedListToStringArray(value); List<T> strategies = new ArrayList<>(classNames.length); for (String className : classNames) { try { Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader()); Object strategy = createDefaultStrategy(context, clazz); strategies.add((T) strategy); } catch (ClassNotFoundException ex) { throw new BeanInitializationException("Could not find DispatcherServlet's default strategy class [" + className +"] for interface [" + key + "]", ex); } catch (LinkageError err) { throw new BeanInitializationException("Unresolvable class definition for DispatcherServlet's default strategy class [" +className + "] for interface [" + key + "]", err); } } return strategies; } else { return new LinkedList<>(); } }
提到請求處理過程,咱們再來回顧一下Servlet生命週期,處理請求都放在service方法中處理,那麼也從DispatcherServlet的service方法入手。DispatcherServlet繼承FrameworkServlet,在FrameworkServlet中重寫了service、doGet、doPost、doPut、doDelete方法。
//FrameworkServlet.java @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); if (httpMethod == HttpMethod.PATCH || httpMethod == null) { processRequest(request, response); } else { super.service(request, response); } } @Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected final void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected final void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = buildLocaleContext(request); RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); //把新構造的LocaleContext對象和ServletRequestAttributes對象和當前請求線程綁定(後面要解除綁定) initContextHolders(request, localeContext, requestAttributes); try { //抽象方法,交給DispatcherServlet方法實現 doService(request, response); } catch (ServletException | IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { //重置LocaleContext和RequestAttributes對象,也就是解除LocaleContext對象和ServletRequestAttributes對象和當前請求線程的綁定 resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } //發佈ServletRequestHandledEvent事件 publishRequestHandledEvent(request, response, startTime, failureCause); } }
//DispatcherServlet.java @Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } //在當前request對象中填充4個屬性 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); if (this.flashMapManager != null) { FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); } try { //主要處理分發請求 doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } } protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); //調用handlerMapping獲取handlerChain mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } //獲取支持該handler解析的HandlerAdapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } //使用HandlerAdapter完成handler處理 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } //視圖處理(頁面渲染) applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
DispatcherServlet的doDispatch方法歸納起來大體就是如下幾點:首先根據當前請求路徑找到對應的HandlerMethod,一個HandlerMethod和若干個攔截器構造一個HandlerExecutionChain.經過HandlerExecutionChain獲得HandlerAdapter對象,經過執行HandlerAdapter的handle方法獲得ModelAndView對象,調用ModelAndView解析視圖,渲染視圖,Response結束。
http://www.javashuo.com/article/p-tdrvojaq-n.html
http://www.javashuo.com/article/p-tvgbymtf-bn.html
https://www.cnblogs.com/fangjian0423/p/springMVC-dispatcherServlet.html