SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门ITeye - 凯时娱乐

SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门ITeye

2019年04月04日13时12分00秒 | 作者: 怀绿 | 标签: 装备,办法,完成 | 浏览: 2148

 

刚触摸SpringMVC,对它的xml文件装备一向比较模模糊糊,最近花了一点时刻略微看了下源代码,再加上调试,开端逐步了解它,网上的相似的内容有许多,写本文主要是自己加深一下了解。本文适宜用过SpringMVC的开发者,言归正传,首要建立一个最简略的工程体会一下。

该工程是根据maven的,pom装备不再阐明,所运用的spring版别4.0.5。
首要是web.xml文件装备,最简略的装备

 !DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" 
 web-app 
 display-name Archetype Created Web Application /display-name 
 servlet 
 servlet-name mvc /servlet-name 
 servlet-class org.springframework.web.servlet.DispatcherServlet /servlet-class 
 load-on-startup 1 /load-on-startup 
 /servlet 
 servlet-mapping 
 servlet-name mvc /servlet-name 
 url-pattern /* /url-pattern 
 /servlet-mapping 
 /web-app 

然后是mvc-servlet.xml文件的装备,上面装备DispatcherServlet会默许加载[servlet-name]-servlet.xml文件。关于我的装备,会去加载mvc-servlet.xml文件。
mvc-servlet.xml文件的内容:

 ?xml version="1.0" encoding="UTF-8" ? 
 beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
 http://www.springframework.org/schema/mvc
 http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
 http://www.springframework.org/schema/util
 http://www.springframework.org/schema/util/spring-util-2.0.xsd
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context-3.2.xsd" 
 bean name="/index" /bean 
 bean 
 property name="templateLoaderPath" value="/WEB-INF/views" / 
 property name="defaultEncoding" value="utf-8" / 
 property name="freemarkerSettings" 
 props 
 prop key="locale" zh_CN /prop 
 /props 
 /property 
 /bean 
 bean 
 property name="suffix" value=".html" / 
 property name="contentType" value="text/html;charset=utf-8" / 
 property name="requestContextAttribute" value="request" / 
 property name="exposeRequestAttributes" value="true" / 
 property name="exposeSessionAttributes" value="true" / 
 /bean 
 /beans 

在该装备中界说了一个HomeAction的Bean。内容为:

package com.lg.mvc;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class HomeAction implements Controller{
 @Override
 public ModelAndView handleRequest(HttpServletRequest request,
 HttpServletResponse response) throws Exception {
 return new ModelAndView("hello");

这是最原始的mvc做法,要承继Controller接口,先从原始的说起,终究再过渡到@Controller和@RequestMapping注解式的装备。它在mvc-serlet.xml文件中的装备有一个要害的特点name="/index"。
WEB-INF/view目录下有一个简略的hello.html,内容为:

 html 
 head 
 /head 
 body 
 hello lg !
 /body 
 /html 

至此该工程就写完了,布置到tomcat中,项目途径为/,运转一下。
拜访 http://localhost:8080/index


至此整个工程就算建立成功了。

下面就要说说原理了。
用过python Django结构的都知道Django关于拜访办法的装备便是,一个url途径和一个函数配对,你拜访这个url,就会直接调用这个函数,简略明了。关于java的面向目标来说,就要分两步走。第一步首要要找到是哪个目标,即handler,本工程的handler则是HomeAction目标。第二步要找到拜访的函数,即HomeAction的handleRequest办法。所以就呈现了两个源码接口 HandlerMapping和HandlerAdapter,前者担任第一步,后者担任第二步。借用网上的SpringMVC架构图。


HandlerMapping接口的完成(只举了我知道的几个) :

BeanNameUrlHandlerMapping :经过比照url和bean的name找到对应的目标
SimpleUrlHandlerMapping :也是直接装备url和对应bean,比BeanNameUrlHandlerMapping功用更多
DefaultAnnotationHandlerMapping : 主要是针对注解装备@RequestMapping的,已过期
RequestMappingHandlerMapping :替代了上面一个

HandlerAdapter 接口完成:

HttpRequestHandlerAdapter : 要求handler完成HttpRequestHandler接口,该接口的办法为 void handleRequest(HttpServletRequest request, HttpServletResponse response)也便是 handler有必要有一个handleRequest办法

SimpleControllerHandlerAdapter:要求handler完成Controller接口,该接口的办法为ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response),也便是本工程选用的

AnnotationMethodHandlerAdapter :和上面的DefaultAnnotationHandlerMapping配对运用的,也已过期

RequestMappingHandlerAdapter : 和上面的RequestMappingHandlerMapping配对运用,针对@RequestMapping

先简略的说下这个工程的流程,拜访http://localhost:8080/index首要由DispatcherServlet进行转发,经过BeanNameUrlHandlerMapping(含有 /index- HomeAction的装备),找到了HomeAction,然后再拿HomeAction和每个adapter进行适配,因为HomeAction完成了Controller接口,所以终究会有SimpleControllerHandlerAdapter来完成对HomeAction的handleRequest办法的调度。然后就顺畅的履行了咱们想要的办法,后边的内容不在本节中阐明。

了解了大约流程,然后就需要看源代码了。
首要便是SpringMVC的进口类,DispatcherServlet,它完成了Servlet接口,不再详细说DispatcherServlet的细节,否则又是一大堆的内容。每次恳求都会调用它的doService- doDispatch,咱们重视的要点就在doDispatch办法中。

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找到对应的handler
 // Determine handler for the current request.
 mappedHandler = getHandler(processedRequest);
 if (mappedHandler  null || mappedHandler.getHandler()  null) {
 noHandlerFound(processedRequest, response);
 return;
 // Determine handler adapter for the current request.
 //这是第二步,找到适宜的HandlerAdapter,然后由它来调度履行handler的办法
 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
 // Process last-modified header, if supported by the handler.
 String method = request.getMethod();
 boolean isGet = "GET".equals(method);
 if (isGet || "HEAD".equals(method)) {
 long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
 if (logger.isDebugEnabled()) {
 logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
 if (new ServletWebRequest(request, response).checkNotModified(lastModified) isGet) {
 return;
 if (!mappedHandler.applyPreHandle(processedRequest, response)) {
 return;
 try {
 // Actually invoke the handler.
 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
 finally {
 if (asyncManager.isConcurrentHandlingStarted()) {
 return;
 applyDefaultViewName(request, mv);
 mappedHandler.applyPostHandle(processedRequest, response, mv);
 catch (Exception ex) {
 dispatchException = ex;
 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
 catch (Exception ex) {
 triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
 catch (Error err) {
 triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
 finally {
 if (asyncManager.isConcurrentHandlingStarted()) {
 // Instead of postHandle and afterCompletion
 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
 return;
 // Clean up any resources used by a multipart request.
 if (multipartRequestParsed) {
 cleanupMultipart(processedRequest);

第一步详细检查:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
 for (HandlerMapping hm : this.handlerMappings) {
 if (logger.isTraceEnabled()) {
 logger.trace(
 "Testing handler map [" + hm + "] in DispatcherServlet with name " + getServletName() + "");
 HandlerExecutionChain handler = hm.getHandler(request);
 if (handler != null) {
 return handler;
 return null;

能够看到便是经过遍历一切已注册的HandlerMapping来找到对应的handler,然后构建出一个HandlerExecutionChain,它包含了handler和HandlerMapping自身的一些拦截器,如下

public class HandlerExecutionChain {
 private final Object handler;
 private HandlerInterceptor[] interceptors;
 private List HandlerInterceptor interceptorList;
 //其他代码省掉

其间HandlerMapping的getHandler完成:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
 Object handler = getHandlerInternal(request);
 if (handler  null) {
 handler = getDefaultHandler();
 if (handler  null) {
 return null;
 // Bean name or resolved handler?
 if (handler instanceof String) {
 String handlerName = (String) handler;
 handler = getApplicationContext().getBean(handlerName);
 return getHandlerExecutionChain(handler, request);

这儿的getHandlerInternal(request)是个笼统办法,由详细的HandlerMapping来完成,获取到的handler假如为空,则获取默许装备的handler,假如handler为String类型,则表明这个则会去Spring容器里边去找这样姓名的bean。
再看下BeanNameUrlHandlerMapping的getHandlerInternal(request)的详细完成(经过一系列的接口规划,之后再好美观看这个规划,到BeanNameUrlHandlerMapping这只用完成该办法中的一部分),如下

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
 * Checks name and aliases of the given bean for URLs, starting with "/".
 @Override
 protected String[] determineUrlsForHandler(String beanName) {
 List String urls = new ArrayList String 
 if (beanName.startsWith("/")) {
 urls.add(beanName);
 String[] aliases = getApplicationContext().getAliases(beanName);
 for (String alias : aliases) {
 if (alias.startsWith("/")) {
 urls.add(alias);
 return StringUtils.toStringArray(urls);

这儿面注释说,bean的name有必要以/最初,它才处理,将信息存储在Map String, Object handlerMap中,关于本工程来说便是{/index:HomeAction目标}。
至此这儿完成了第一步,下面开端第二步,即办法HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());的详细完成:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
 for (HandlerAdapter ha : this.handlerAdapters) {
 if (logger.isTraceEnabled()) {
 logger.trace("Testing handler adapter [" + ha + "]");
 if (ha.supports(handler)) {
 return ha;
 throw new ServletException("No adapter for handler [" + handler +
 "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");

遍历一切的HandlerAdapter,判别他们是否支撑这个handler。
咱们来看下HttpRequestHandlerAdapter的supports(handler)办法:

public class HttpRequestHandlerAdapter implements HandlerAdapter {
 @Override
 public boolean supports(Object handler) {
 //便是判别handler是否完成了HttpRequestHandler接口
 return (handler instanceof HttpRequestHandler);
 @Override
 public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
 throws Exception {
 //若handler完成了HttpRequestHandler接口,则调用该接口的办法,履行咱们在该办法中写的事务逻辑
 ((HttpRequestHandler) handler).handleRequest(request, response);
 return null;
 @Override
 public long getLastModified(HttpServletRequest request, Object handler) {
 if (handler instanceof LastModified) {
 return ((LastModified) handler).getLastModified(request);
 return -1L;

同理SimpleControllerHandlerAdapter也是这样相似的逻辑

public class SimpleControllerHandlerAdapter implements HandlerAdapter {
 @Override
 public boolean supports(Object handler) {
 return (handler instanceof Controller);
 @Override
 public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
 throws Exception {
 return ((Controller) handler).handleRequest(request, response);
 @Override
 public long getLastModified(HttpServletRequest request, Object handler) {
 if (handler instanceof LastModified) {
 return ((LastModified) handler).getLastModified(request);
 return -1L;

剩下两个AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter就比较复杂,我也没看。
依照本工程的装备,则SimpleControllerHandlerAdapter是支撑HomeAction的,然后就会履行SimpleControllerHandlerAdapter的handle(processedRequest, response, mappedHandler.getHandler())办法。本质上就会调用HomeAction完成Controller接口的办法。至此就剖析完了。
了解进程了之后,然后便是最重要的也是常常装备出问题的当地。DispatcherServlet的handlerMappings和handlerAdapters的来历问题。

DispatcherServlet初始化的时分,会调用一个办法如下:

protected void initStrategies(ApplicationContext context) {
 initMultipartResolver(context);
 initLocaleResolver(context);
 initThemeResolver(context);
//初始化一些HandlerMapping
 initHandlerMappings(context);
//初始化一些HandlerAdapter
 initHandlerAdapters(context);
 initHandlerExceptionResolvers(context);
 initRequestToViewNameTranslator(context);
 initViewResolvers(context);
 initFlashMapManager(context);

这儿能够看到,它会初始化一些HandlerMapping和HandlerAdapter,这两个办法非常重要,了解了这两个办法你就会知道,装备不对问题出在哪里,下面详细看下这两个办法:

private void initHandlerMappings(ApplicationContext context) {
 this.handlerMappings = null;
 if (this.detectAllHandlerMappings) {
 // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
 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.
 OrderComparator.sort(this.handlerMappings);
 else {
 try {
 HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
 this.handlerMappings = Collections.singletonList(hm);
 catch (NoSuchBeanDefinitionException ex) {
 // Ignore, well add a default HandlerMapping later.
 // Ensure we have at least one HandlerMapping, by registering
 // a default HandlerMapping if no other mappings are found.
 if (this.handlerMappings  null) {
 this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
 if (logger.isDebugEnabled()) {
 logger.debug("No HandlerMappings found in servlet " + getServletName() + ": using default");

detectAllHandlerMappings是DispatcherServlet的一个特点,你是能够在web.xml中装备的,默许是true,假如为true,则会去从本工程mvc-servlet.xml文件中去勘探一切完成了HandlerMapping的bean,假如有,则参加DispatcherServlet的handlerMappings中。假如detectAllHandlerMappings为false,则直接去容器中找id="handlerMapping"且完成了HandlerMapping的bean.假如以上都没找到,则会去加载默许的HandlerMapping。

/** Detect all HandlerMappings or just expect "handlerMapping" bean? */
 private boolean detectAllHandlerMappings = true;

本工程因为没有装备HandlerMapping,所以它会去加载默许的,下面看看默许的装备是什么

protected T List T getDefaultStrategies(ApplicationContext context, Class T strategyInterface) {
 String key = strategyInterface.getName();
//defaultStrategies存储了默许的装备
 String value = defaultStrategies.getProperty(key);
 if (value != null) {
 String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
 List T strategies = new ArrayList T (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 DispatcherServlets default strategy class [" + className +
 "] for interface [" + key + "]", ex);
 catch (LinkageError err) {
 throw new BeanInitializationException(
 "Error loading DispatcherServlets default strategy class [" + className +
 "] for interface [" + key + "]: problem with class file or dependent class", err);
 return strategies;
 else {
 return new LinkedList T 

持续看看defaultStrategies是怎么初始化的:

private static final Properties defaultStrategies;
 static {
 // Load default strategy implementations from properties file.
 // This is currently strictly internal and not meant to be customized
 // by application developers.
 try {
//这儿的DEFAULT_STRATEGIES_PATH便是DispatcherServlet.properties
 ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
 defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
 catch (IOException ex) {
 throw new IllegalStateException("Could not load DispatcherServlet.properties: " + ex.getMessage());

这儿运用静态代码块来加载装备文件DispatcherServlet.properties,它所在位置便是和DispatcherServlet同一目录下面的,如下图所示:



该默许的装备文件的内容如下:

# Default implementation classes for DispatcherServlets strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
#这儿便是默许的HandlerMapping的装备
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
 org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
#这儿便是默许的HandlerAdapter的装备
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

也便是说,当你什么都没有装备时,默许会加载以上的装备。正是因为有了上述默许装备的BeanNameUrlHandlerMapping(它要求name有必要是以/最初的),它才会存储咱们在mvc-servlet.xml中装备的 bean name="/index" /bean ,相同正是因为有了SimpleControllerHandlerAdapter(因为handler完成了Controller接口,所以它的support办法支撑咱们的handler),才会调度履行HomeAction的handleRequest办法。

版权声明
本文来源于网络,版权归原作者所有,其内容与观点不代表凯时娱乐立场。转载文章仅为传播更有价值的信息,如采编人员采编有误或者版权原因,请与我们联系,我们核实后立即修改或删除。

猜您喜欢的文章