什么是Spring MVC 其实应该说 什么是 MVC ?
Model 数据, View 视图, Controller 控制器。啪!三个东西合在一起,MVC就出来了。
这么简单? 没错,其实就是这么简单。
当然如果你对MVC不太熟悉的话还是乖乖往下看吧。
其实MVC就是处理Web请求的一种框架模式,我们思考一下用户请求的流程:
对于用户来说其实也就这三个步骤,但是对于服务端来说需要做很多,这里我画了一张图供大家理解。这里其实忽略了很多 Tomcat 本身已经为我们做的,而且 Tomcat 并不仅仅只有 Host,Context。
我来解释一下,用户发送请求的 url 其实对应着很多东西。
比如说 localhost ,当然这个就是 ip 地址。这个 ip 地址对应着 Tomcat 里面的 Host (站点) 层。
Context 代表着一个 web 应用,还记得当初写 Servlet 项目的时候有一个 webapp 文件夹(里面还有个WEB-INF,最里面是web.xml)吗?也可以理解为当初写的 servlet 项目就是一个 web 应用,而用户通过 ip 地址的端口映射去找到了这个应用。
这时候我们已经通过 ip 和端口寻找到了指定的 web 应用,我们知道一个 web 应用中存在多个 servlet ,而我们如何去寻找每个请求对应的 servlet 呢? 答案还是 url ,我们通过后面的 /news 去web.xml里面寻找已经注册到应用中的 Servlet 类。
具体我再配合图中解释一下: 找到了指定的 web 应用之后,通过请求的路径 /news 去 web.xml 中寻找是否有对应的 标签,其中这个标签的子标签 标签的值需要匹配到请求的路径,这个时候 标签的值为 /news 正好匹配到了,所以我们获取了上面的标签的值然后再寻找是否有 标签的子标签 和这个值相等,如果有则获取到底下的 对应的类 并通过这个类去解析请求
总结来说就是 通过 url 从 web.xml 文件中寻找到匹配的 servlet 的类
其实这就是原生的 servlet ,那么 MVC 的影子在哪呢?
别急,你要记住的是 MVC 就是对 Servlet 的封装,想要理解 MVC 就必须理解 Servlet 和 MVC 与 Servlet 的关系 。
有没有发现这个 DispatcherServlet 其实就是一个 Servlet。也就是说 Spring MVC中最核心的部分其实就是一个 Servlet 。
我来简单解释一下相应的部分(先简单了解一下)
有没有想起来在 SSM 框架配置的时候在 web.xml 中的配置
<servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <!-- 把所以请求都交给DispatcherServlet处理--> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <!-- 拦截所有 --> <url-pattern>/</url-pattern> </servlet-mapping> 复制代码
好的,现在我们知道了 springMVC 中使用了一个 DispatcherServlet 去处理所有请求,而我们知道真正处理的肯定不是 DispatcherServlet ,而是具体我们在 Controller 层中写的带有 @Controller @RequestMapping 注解的类和底下的方法。 DispatcherServlet 只是一个为我们分发请求到具体处理器的一个分发Servlet 。
那么,这个 DispatcherServlet 具体怎么工作的呢?它是如何分发请求的呢? 且听我慢慢道来。
首先我先将这些组件简单化,并把一些不必要的先省略为了便于理解。
其实要 分发请求 和 处理请求并相应 ,我们可以肯定的是 我们需要使用一个映射关系Mapping来表示 url 和对应的 处理器,使用一个处理器Handler来处理对应的请求。这样,我们就出来了两个 最根本 的角色: HandlerMapping 和 Handler 。
我们再来强调一下这两者的工作。
这样,我们就可以再画出一个简单的流程图了。
有没有疑惑, 这个 HandlerMapping 集合从哪来?HandlerMapping 的类结构是啥样的?Handler的类结构又是什么样的?
如果有,那么就带着这些问题往下看。
首先,这个 handlerMapping 的集合从哪来的?甭说集合,连单个你都不知道从哪来。 那么我们就从源码中找答案吧。为了你省力,我直接告诉你,DispatcherServlet 中的 doDispatch 方法中进行了 分发的主要流程。
这里我给出了简化版的 doDispatch 方法
public void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception { // 通过request在处理器映射HandlerMapping中获取相应处理器 Object handler = getHandler(req); if (handler != null) { ... 调用handler的处理方法 } } 复制代码
那么这个 getHandler(request) 方法又是什么样的呢?这里我直接放 DispatcherServlet 类的源码
@Nullable // 这里返回的是 HandlerExecutionChain // 其实这是一个处理器和拦截器链的组合 // 现在你就理解为返回的是一个 handler protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { // 遍历 handlerMapping 调用它的getHanlder获取处理器 // 如果不为空直接返回 for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; } 复制代码
我们继续追踪 HandlerMapping 的getHandler(request) 方法。
其实进入源码你会发现,HandlerMapping 是一个接口,故这里给出一个简单的 HandlerMapping 接口代码,如果有能力可以去看源码。
public interface HandlerMapping { /** * 获取请求对应的处理器 * @param request 请求 * @return 处理器 * @throws Exception 异常 */ Object getHandler(HttpServletRequest request) throws Exception; } 复制代码
那么,具体的实现类又是什么呢?我们思考一下,这个mapping是一个请求和处理器的映射,它是如何存的?我们当初怎么做的?
想必,你已经有答案了,在我们使用 SSM 框架的时候我们是通过 给类和方法 配置相应的注解(@Controller,@ReqeustMapping)来建立相应的 url 和 处理器方法的映射关系的。
我们再回来看源码 在idea中 可以使用 ctrl+alt+B 来查看方法实现和类实现继承。我们查看 HandlerMapping 接口的 getHandler 方法的实现,我们会发现直接跳到了 AbstractHandlerMapping 这个抽象类的方法,我们查看该方法的源码
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 获取 handler 这其实是一个抽象方法 // 子类可以通过不同实现来获取handler // 例如通过 url 和 name的映射逻辑 // 通过 requestMapping 中的 url 和对应方法的映射逻辑 Object handler = getHandlerInternal(request); if (handler == null) { // 如果获取为null 的获取默认的处理器 // 这里子类也可以设置自己的默认处理器 handler = getDefaultHandler(); } // 如果还是没有则返回 这时候 DispatcherServlet会返回 404 if (handler == null) { return null; } // Bean name or resolved handler? // 如果返回的处理器是字符串 则认为它是一个beanName if (handler instanceof String) { String handlerName = (String) handler; // 通过beanName从IOC容器中获取相应的处理器 handler = obtainApplicationContext().getBean(handlerName); } // 下面是将处理器 和 拦截器封装成处理器执行链 HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (logger.isTraceEnabled()) { logger.trace("Mapped to " + handler); } else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) { logger.debug("Mapped to " + executionChain.getHandler()); } if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } // 返回处理器执行链 return executionChain; } 复制代码
如果其他的你看不懂,你只要理解我注释区域的代码就行了。我们从上面得到最重要的信息就是: 真正的handler获取是在子类实现的getHandlerInternal(request)中 ,那我们来看一下有哪些子类。
我们可以看到其中有 AbstractHandlerMethodMapping、AbstractUrlHandlerMapping、WelcomeHandlerMapping。
我们主要关注 AbstractHandlerMethodMapping (提供方法处理器) 和 AbstractUrlHandlerMapping(提供url对应处理器映射),这里为了不耽误时间,我们直接分析 AbstractHandlerMethodMapping ,它是注解方法的映射的一个抽象类。
@Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 获取请求的路径 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); this.mappingRegistry.acquireReadLock(); try { // 通过 lookupPath 来从中获取 HandlerMethod // 这个HandlerMethod 又是什么? // 先不用管 我们继续看lookupHandlerMethod源码 HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } } // 这里的逻辑稍微有些复杂 // 你只要知道它通过请求来匹配返回处理器方法 // 如果有多个处理器方法可以处理当前Http请求 那么返回最佳匹配的处理器 @Nullable protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // No choice but to go through all mappings... addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); } if (!matches.isEmpty()) { Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); matches.sort(comparator); Match bestMatch = matches.get(0); if (matches.size() > 1) { if (logger.isTraceEnabled()) { logger.trace(matches.size() + " matching mappings: " + matches); } if (CorsUtils.isPreFlightRequest(request)) { return PREFLIGHT_AMBIGUOUS_MATCH; } Match secondBestMatch = matches.get(1); if (comparator.compare(bestMatch, secondBestMatch) == 0) { Method m1 = bestMatch.handlerMethod.getMethod(); Method m2 = secondBestMatch.handlerMethod.getMethod(); String uri = request.getRequestURI(); throw new IllegalStateException( "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}"); } } request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod); handleMatch(bestMatch.mapping, lookupPath, request); // 返回最佳匹配 return bestMatch.handlerMethod; } else { return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } } 复制代码
到现在逻辑慢慢变得复杂起来,我们做一个小结: 在 DispatcherServlet 中我们通过遍历 handlerMapping 集合并调用它的 getHandler 方法来获取handler ,这个handler 是一个 Object (因为spring会整合其他框架的处理器,并使用这些处理器处理请求 所以这里选择Object)。而 HandlerMapping 仅仅是一个接口 为了方便 抽象类 AbstractHandlerMapping 实现了这个方法并且为子类提供了自定义获取handler的 getHandlerInternal(request) 方法。 对于我们通用方式注解来标识控制器方法和url请求路径的映射是通过 AbstractHandlerMethodMapping 来获取请求对应的HandlerMethod的 。
那么,疑问又来了,HandlerMethod是什么?
还记得刚刚上面的问题么, 这个 HandlerMapping 集合从哪来?HandlerMapping 的类结构是啥样的?Handler的类结构又是什么样的 ?
我们现在可以来回答一下Handler的类结构了,Handler是一个Object,为了第三方框架的处理器能够接入来处理请求,spring使用了Object,而对于注解形式来说 一个处理器是一个 HandlerMethod 。这里我给出 HandlerMethod 的简单实现形式,如果有能力可以查看源码。
@Data public class HandlerMethod { // bean 其实这个是标识 Controller 注解的类的对象 private Object bean; // 该对象的类型 private Class<?> beanType; // 该类上被标识RequestMapping注解的方法 private Method method; } 复制代码
在 HandlerMethod 中存放了控制器类和对应的方法。为什么要存放他们?你想一下,我们用@RequestMapping注解标识的方法不就是处理方法吗,HandlerMethod 中存放他们,到时候调用处理方法只需要通过反射调用这个bean的method就行了。如果不理解可以看一下我下面写的代码。
// 这里先不用管 ModelAndView public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ModelAndView modelAndView = null; HandlerMethod handlerMethod = ((HandlerMethod) handler); // 获取HandlerMethod的method Method method = handlerMethod.getMethod(); if (method != null) { // 通过反射调用方法并返回视图对象(这就是处理方法) modelAndView = (ModelAndView)method.invoke(BeanFactory.getBean(handlerMethod.getBeanType())); } return modelAndView; } 复制代码
再来看看上面的问题。 这个 HandlerMapping 集合从哪来?HandlerMapping 的类结构是啥样的?Handler的类结构又是什么样的
第三个问题解决了,第二个问题上面也解决了,那么第一个问题来了。
我们从一开始就只讨论了如何在HandlerMapping中取出handler 并且调用handler的处理方法,那么我们一开始遍历的这个handlerMappings集合到底从哪儿来,或者说 它是什么时候被初始化的 ?
这个时候,我们又得回到根源。我再来放这张图,不知道你们是否还记得
你能想到什么呢?我这里假设你对servlet还是有一些了解的。
我们知道 DispatcherServlet 是一个 servlet 。一个 servlet 肯定有init()方法 (还记得我上面讲的GenericServlet的作用吗?现在来了,如果不是很懂init(),建议去了解一下servlet的生命周期)。
我们可以大胆的猜测,对 handlerMappings 的初始化就是在 servlet 的初始化方法中进行的。
很遗憾我们没有能在 DispatcherServlet 中找到 init 方法,那么就找他爹,找不到再找他爷爷,曾爷爷。我们知道因为 DispatcherServlet 继承了 GenericServlet 所以我们需要找到 实现的 init() 无参方法。所以我们找到了 HttpServletBean 中重写的 init() 方法了
@Override // 这家伙还不允许被重写 final public final void init() throws ServletException { // Set bean properties from init parameters. // 将servlet配置信息存入bean的属性中 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) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); } throw ex; } } // 重点在这里 这里子类才可以自由发挥 // 该方法不能被重写是因为 上面的步骤是必须的 // 别忘了上面的步骤是 HttpServletBean 的职责 // 接下去继续看 // Let subclasses do whatever initialization they like. initServletBean(); } // 进入FrameworkServlet 查看实现的initServletBean方法 @Override protected final void initServletBean() throws ServletException { // log不用管 getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'"); if (logger.isInfoEnabled()) { logger.info("Initializing Servlet '" + getServletName() + "'"); } long startTime = System.currentTimeMillis(); // 重点来了 try { // 初始化容器和上下文 // 我们要记得现在在 FrameworkServlet中执行呢 // 我们进入initWebApplicationContext方法 this.webApplicationContext = initWebApplicationContext(); // 初始化FrameworkServlet 这里没给实现 子类也没给 // 所以不用管 initFrameworkServlet(); } // log不用管 catch (ServletException | RuntimeException ex) { logger.error("Context initialization failed", ex); throw ex; } if (logger.isDebugEnabled()) { String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data"; logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value); } if (logger.isInfoEnabled()) { logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms"); } } // 初始化容器和上下文 protected WebApplicationContext initWebApplicationContext() { // 查找是否有专门的根环境 先不用管 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; // 如果不存在专用根环境 通常我们不会走到这 先不用管 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) { // 查看是否在servlet中已经注册 // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // 自己创建一个 // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); } // 判断这个环境是否支持刷新 如果不支持 下面手动刷新 // 如果支持则前面已经刷新了 if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. synchronized (this.onRefreshMonitor) { // !!!!!!!!!!!!!!!!!!!!!重点 // DispacherServlet 就是在这里实现的 onRefresh(wac); } } if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; } // DispatcherServlet 重写了该方法 @Override protected void onRefresh(ApplicationContext context) { initStrategies(context); } // 一系列的初始化工作 protected void initStrategies(ApplicationContext context) { // 前面一些不用管 initMultipartResolver(context); // 地域 initLocaleResolver(context); // 主题 initThemeResolver(context); // 重点来了!!!! // 初始化HandlerMapping initHandlerMappings(context); // 初始化适配器 initHandlerAdapters(context); // 初始化异常处理 initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); } 复制代码
我们先暂停一下,理一下思路。
在 HttpServletBean 中重写了 GenericServlet 的 init() 无参方法开始初始化动作,其中HttpServletBean中先实现了 servlet 配置信息到 bean 属性信息的赋值,然后调用 initServletBean() 该方法是子类进行自定义初始化的方法。 FrameworkServlet 实现了该方法并且调用了 initWebApplicationContext() 方法进行了容器和上下文的初始化工作,并且其中调用了 onRefresh(ApplicationContext context) 方法。 这里FrameworkServlet没有做任何操作而是子类 DispatcherServlet 在其中调用了 initStrategies(context) 进行初始化工作。
好了我们继续看初始化 handlerMappings方法。
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. // 在应用上下文中寻找 handlerMappings Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { 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. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. // 前面不用管 其实一般我们使用默认的 if (this.handlerMappings == null) { // 这里是获取默认的handlerMappings this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } } protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) { String key = strategyInterface.getName(); // 获取defaultStrategies的内容 String value = defaultStrategies.getProperty(key); if (value != null) { // 解析相应内容并初始化 handlerMappings // 获取内容中的类名数组 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<>(); } } 复制代码
事情马上明了了,我们现在已经知道了 handlerMapping 是怎么加入队列中了( 获取到 defaultStrategies 的资源内容 遍历内容获取类名 并通过反射创建对象加入队列 ),所以我们可以大胆猜测 defaultStrategies 中藏着秘密,它肯定已经定义好了默认的 handlerMapping 的类名。
果不其然,我们来看代码
private static final Properties defaultStrategies; // 在静态块中已经加载了defaultStrategies static { // Load default strategy implementations from properties file. // This is currently strictly internal and not meant to be customized // by application developers. try { // 通过资源初始化defaultStrategies // 这里的资源路径 很重要!!!! ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException ex) { throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage()); } } // 在这里呀 DispatcherServlet.properties private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties"; 复制代码
我们去寻找一下 DispatcherServlet.properties 这个文件, 原来都给我们定义好了,我们可以看见默认的handlerMapping有两个。 BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping。
好了,我们现在终于可以总结一下了。
在我定义的简单的 DispatcherServlet 的 “同事”中,主要有 HandlerMapping 和 Handler。HandlerMapping 会在 DispatcherServlet 初始化的时候被在加载 ,然后在 DispatcherServlet 调用到执行方法 doDispatch() 的时候,会遍历 handlerMappings 集合获取对应的 handler。handler 是一个 Object(因为需要适配其他框架的处理器),在注解方式中是一个 HandlerMethod (里面存了Controller类的实例和method,在处理方法的时候使用反射调用该实例的method方法)。获取完 handler之后通过 处理器的处理方法返回一个视图对象并渲染页面。
其实对于“正宗”的MVC流程中,在遍历 handlerMappings 获取到相应的 handler 之后,其实并不是直接通过 handler 来执行处理方法的,而是通过 HandlerAdapter 来执行处理方法的。
这里我写了一个简单的适配器接口,源码也不复杂 你可以直接看源码
public interface HandlerAdapter { ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; boolean support(Object handler); } 复制代码
handleRequest 不必说,用来执行处理的,里面传进去一个 handler 肯定最终调用的 handler的执行方法。这是典型的适配器模式。
support 判断该handler是否被该适配器支持。
其实理解上面的过程了之后再加入一个适配器就不难了,我们主要思考一下为什么要加入适配器,我们知道 handler 是一个 Object 它的处理方法是不固定的,如果我们要在 DispatcherServlet 中通过 Handler 执行处理方法,那么就要做很多类型判断,这对于 DispatcherServlet 是非常难受的,所以需要通过适配器扩展。
这样我们可以写出一个简单的 doDispatch 方法了,有能力的可以查看源码
public void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception { // 通过request在处理器映射HandlerMapping中获取相应处理器 Object handler = getHandler(req); if (handler != null) { // 通过handler获取对应的适配器 HandlerAdapter handlerAdapter = getHandlerAdapter(handler); if (handlerAdapter != null) { // 通过适配器调用处理器的处理方法并返回ModelAndView视图对象 ModelAndView modelAndView = handlerAdapter.handleRequest(req, resp, handler); ... 处理视图并渲染 } } } 复制代码