SpringMVC的好用離不開其功能強大的各個組件,相互獨立易於擴展,默認實現就已經能支持絕大多數狀況了。但在組件可使用前,都須要進行初始化。在DispatcherServlet初始化最後,觸發了全部必要組件的策略化加載,支持動態地對全部組件進行配置。另外,在各個組件自身的初始化時,也完成了各自運行環境的準備。java
上一章最後,在servlet子上下文完成建立,調用了模板擴展方法OnRefresh,它在FrameworkServlet中僅僅只是個空方法,但在其子類DispatcherServlet中則相當重要,它是一切組件的起源。web
DispatcherServlet.java protected void onRefresh(ApplicationContext context) { initStrategies(context); }
初始化全部策略,實際上是指各個組件能夠經過策略動態地進行配置。spring
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); }
以上基本將SpringMVC中的主要組件都羅列出來了,其中最重要的當是HandlerMapping,HandlerAdapter和ViewResolver。因爲各組件的初始化策略方式類似,咱們以HandlerMapping來介紹。mvc
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; // 是否查找全部HandlerMapping標識 if (this.detectAllHandlerMappings) { // 從上下文(包含全部父上下文)中查找HandlerMapping類型的Bean Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { // 根據指定名稱獲取HandlerMapping對象 HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } // 確保至少有一個HandlerMapping,若是沒能找到,註冊一個默認的 if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } } }
策略的邏輯很簡單:有一個標識,是否查找全部HandlerMapping(默認爲true)。若是爲是,則從上下文(包括全部父上下文)中查詢類型爲HandlerMapping的Bean,並進行排序。若是爲否,則從上下文中按指定名稱去尋找。若是都沒有找到,提供一個默認的實現。這個默認實現從DispatcherServlet同級目錄的DispatcherServlet.properties中加載獲得。app
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
能夠看到SpringMVC爲每一個組件都提供了默認的實現,一般狀況下都可以知足需求。若是你想對某個組件進行定製,能夠經過spring的xml文件或者@Configuration類中的@Bean來實現。好比常配置的視圖解析器:cors
xml方式jsp
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/"></property> <property name="suffix" value=".jsp"></property> </bean>
編碼方式ide
[@Bean](https://my.oschina.net/bean) public ViewResolver viewResolver(){ return new InternalResourceViewResolver("/WEB-INF/", ".jsp"); }
因爲其餘組件的初始化方式徹底一致,這裏就不贅述了。須要關注的一點是,當匹配到合適的組件時,都會經過Spring的方式實例化組件。而有些組件在實例化時也會對自身運行環境進行初始化。ui
在使用SpringMVC時,須要在xml文件中添加一行註解this
<mvc:annotation-driven />
或者在配置類上增長註解@EnableWebMvc,才能使SpringMVC的功能徹底開啓。咱們以xml的配置爲例,看看它都作了什麼。根據spring對namespace的解析策略找到MvcNamespaceHandler類,在其init方法中
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
直接看AnnotationDrivenBeanDefinitionParser的parse方法,上下有一百多行,瀏覽一遍,主要就是註冊各類組件和內部須要的解析器和轉換器。找到HandlerMapping組件的實現
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
於是實際用的HandlerMapping實現即爲RequestMappingHandlerMapping。其抽象基類AbstractHandlerMethodMapping實現了InitializingBean接口。
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean
當RequestMappingHandlerMapping對象初始化時,會調用InitializingBean接口的afterPropertiesSet方法。在此方法中完成了Controller方法同請求url的映射表。
public void afterPropertiesSet() { initHandlerMethods(); } protected void initHandlerMethods() { if (logger.isDebugEnabled()) { logger.debug("Looking for request mappings in application context: " + getApplicationContext()); } // 默認只從當前上下文中查詢全部beanName String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); for (String beanName : beanNames) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { Class<?> beanType = null; try { beanType = getApplicationContext().getType(beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex); } } // 若是類上有@Controller或@RequestMapping註解,則進行解析 if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } } } handlerMethodsInitialized(getHandlerMethods()); }
對於類上有@Controller或@RequestMapping註解,都進行了detect。
protected void detectHandlerMethods(final Object handler) { Class<?> handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass()); final Class<?> userType = ClassUtils.getUserClass(handlerType); // 方法內省器,用於發現@RequestMapping註解的方法 Map<Method, T> methods = MethodIntrospector.selectMethods(userType, new MethodIntrospector.MetadataLookup<T>() { @Override public T inspect(Method method) { try { return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } } }); if (logger.isDebugEnabled()) { logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods); } // 遍歷全部有效方法,封裝方法爲可執行的Method,註冊到URL-Controller方法映射表 for (Map.Entry<Method, T> entry : methods.entrySet()) { Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType); T mapping = entry.getValue(); registerHandlerMethod(handler, invocableMethod, mapping); } }
方法內省器中的內部類調用的getMappingForMethod方法爲抽象方法,實如今RequestMappingHandlerMapping中。
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = createRequestMappingInfo(method); if (info != null) { RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); if (typeInfo != null) { info = typeInfo.combine(info); } } return info; } private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); }
解析方法上的@RequestMapping註解返回RequestMappingInfo,其實就是請求映射信息。而在上面的detect方法最後,註冊URL-Controller方法映射表由registerHandlerMethod方法完成。
protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); }
mappingRegistry是AbstractHandlerMethodMapping的核心構成,主要做用就是維護HandlerMethod的映射關係,以及提供映射的查詢方法。其register方法的實現以下:
public void register(T mapping, Object handler, Method method) { // 加鎖,保證一致性 this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); assertUniqueMethodMapping(handlerMethod, mapping); if (logger.isInfoEnabled()) { logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod); } // 添加mapping->HandlerMethod的映射 this.mappingLookup.put(mapping, handlerMethod); List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { // 添加url->mapping的映射 this.urlLookup.add(url, mapping); } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } // 添加mapping->MappingRegistration的映射 this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } }
註冊過程增長了三個個映射關係,一個是mapping->HandlerMethod的映射,一個是url->mapping的映射,一個是mapping->MappingRegistration的映射。經過前兩個映射,能夠將請求定位到肯定的Controller的方法上,最後一個映射保留全部Mapping註冊信息,用於unregister。而方法加鎖則是保證全部映射的一致性。
至此,請求URL和Controller方法之間的關係就初始化完成了。
在使用SpringMVC時,對Controller中方法的參數和返回值的處理都很是的方便。咱們知道,經常使用類型的參數不須要任何額外配置,SpringMVC便可完美轉換,而返回值既能夠是ModelAndView, 也能夠是String,或者是HashMap,或者是ResponseEntity,多種方式簡單配置,完美兼容。它是怎麼作到的呢,經過一系列的轉換器來完成的。而這些轉換器也是須要初始化到運行環境中的, 誰的運行環境, HandlerAdapter的。
一樣SpringMVC提供了一個默認的強大實現,RequestMappingHandlerAdapter,一樣在<mvc:annotation-driven />
中定義。它也實現了InitializingBean接口。
public void afterPropertiesSet() { // Do this first, it may add ResponseBody advice beans // 初始化Controller通用切面 initControllerAdviceCache(); // 初始化參數解析器 if (this.argumentResolvers == null) { List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers(); this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } // 初始化InitBinder解析器 if (this.initBinderArgumentResolvers == null) { List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers(); this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } // 初始化返回值處理器 if (this.returnValueHandlers == null) { List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers(); this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers); } }
每一個組件都經過內置默認的實現,咱們主要來看參數解析器和返回值處理器兩個。
參數解析器
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>(); // Annotation-based argument resolution resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new PathVariableMethodArgumentResolver()); resolvers.add(new PathVariableMapMethodArgumentResolver()); resolvers.add(new MatrixVariableMethodArgumentResolver()); resolvers.add(new MatrixVariableMapMethodArgumentResolver()); resolvers.add(new ServletModelAttributeMethodProcessor(false)); resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); resolvers.add(new RequestHeaderMapMethodArgumentResolver()); resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new SessionAttributeMethodArgumentResolver()); resolvers.add(new RequestAttributeMethodArgumentResolver()); // Type-based argument resolution resolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RedirectAttributesMethodArgumentResolver()); resolvers.add(new ModelMethodProcessor()); resolvers.add(new MapMethodProcessor()); resolvers.add(new ErrorsMethodArgumentResolver()); resolvers.add(new SessionStatusMethodArgumentResolver()); resolvers.add(new UriComponentsBuilderMethodArgumentResolver()); // Custom arguments if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } // Catch-all resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); resolvers.add(new ServletModelAttributeMethodProcessor(true)); return resolvers; }
你們根據解析器名稱大概能夠推測出其做用,好比@RequestParam解析器,@PathVariable解析器,及@RequestBody和@ResponseBody解析器等等。SpringMVC強大的參數解析能力其實來源於豐富的內置解析器。
另外一個返回值處理器的初始化
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() { List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>(); // Single-purpose return value types handlers.add(new ModelAndViewMethodReturnValueHandler()); handlers.add(new ModelMethodProcessor()); handlers.add(new ViewMethodReturnValueHandler()); handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters())); handlers.add(new StreamingResponseBodyReturnValueHandler()); handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice)); handlers.add(new HttpHeadersReturnValueHandler()); handlers.add(new CallableMethodReturnValueHandler()); handlers.add(new DeferredResultMethodReturnValueHandler()); handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory)); // Annotation-based return value types handlers.add(new ModelAttributeMethodProcessor(false)); handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice)); // Multi-purpose return value types handlers.add(new ViewNameMethodReturnValueHandler()); handlers.add(new MapMethodProcessor()); // Custom return value types if (getCustomReturnValueHandlers() != null) { handlers.addAll(getCustomReturnValueHandlers()); } // Catch-all if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) { handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers())); } else { handlers.add(new ModelAttributeMethodProcessor(true)); } return handlers; }
一樣內置了對多種返回類型,返回方式的處理器,才支撐起豐富便捷的使用。
本章主要介紹了SpringMVC的web組件的初始化,支持外部配置的策略化方式。另外就HandlerMapping和HandlerAdapter兩個重要組件的運行環境的初始化簡要了解,知曉請求url同Controller方法的映射關係的創建過程,以及請求處理器Handler運行時初始化的各類解析器,以支持其強大的處理過程。下一章咱們去探究一個請求如何在SpringMVC各組件中進行流轉。