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各组件中进行流转。