HandlerAdapter字面上的意思就是处理适配器,它的作用用一句话概括就是调用具体的方法对用户发来的请求来进行处理。当handlerMapping获取到执行请求的controller时,DispatcherServlte会根据controller对应的controller类型来调用相应的HandlerAdapter来进行处理。
在贴源码之前先说一下HandlerAdapter处理的大体流程,这样就有一个大体的掌握。大体流程有三步:
1.DispatcherServlte会根据配置文件信息注册HandlerAdapter,如果在配置文件中没有配置,那么DispatcherServlte会获取HandlerAdapter的默认配置,如果是读取默认配置的话,DispatcherServlte会读取DispatcherServlte.properties文件,该文件中配置了三种HandlerAdapter:HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter。DispatcherServlte会将这三个HandlerAdapter对象存储到它的handlerAdapters这个集合属性中,这样就完成了HandlerAdapter的注册。
2.DispatcherServlte会根据handlerMapping传过来的controller与已经注册好了的HandlerAdapter一一匹配,看哪一种HandlerAdapter是支持该controller类型的,如果找到了其中一种HandlerAdapter是支持传过来的controller类型,那么该HandlerAdapter会调用自己的handle方法,handle方法运用Java的反射机制执行controller的具体方法来获得ModelAndView,例如SimpleControllerHandlerAdapter是支持实现了controller接口的控制器,如果自己写的控制器实现了controller接口,那么SimpleControllerHandlerAdapter就会去执行自己写控制器中的具体方法来完成请求。
下面是我自己写的代码。
1.自己写的controller,就是我们自己写的控制器
- package com.wangbiao.springMVC;
- import javax.servlet.ServletContext;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.springframework.web.servlet.ModelAndView;
- import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
- public class HelloWorld extends MultiActionController{
- public ModelAndView sayHelloWorld(HttpServletRequest request, HttpServletResponse response) {
- String param = request.getParameter("param");
- System.out.println("springMVC测试:helloWorld;"+param);
- ModelAndView mv = new ModelAndView();
- mv.addObject("content", "springMVC HelloWorld:"+param);
- mv.setViewName("springMVC/helloWorld");
- ServletContext ctx = this.getServletContext();
- return mv;
- }
- }
package com.wangbiao.springMVC; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.multiaction.MultiActionController; public class HelloWorld extends MultiActionController{ public ModelAndView sayHelloWorld(HttpServletRequest request, HttpServletResponse response) { String param = request.getParameter("param"); System.out.println("springMVC测试:helloWorld;"+param); ModelAndView mv = new ModelAndView(); mv.addObject("content", "springMVC HelloWorld:"+param); mv.setViewName("springMVC/helloWorld"); ServletContext ctx = this.getServletContext(); return mv; } }
2.SpringMVC配置文件
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns="http://www.springframework.org/schema/beans"
- xmlns:mvc="http://www.springframework.org/schema/mvc"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.0.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
- <!-- handlerMapping -->
- <bean id="beanNameUrlMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
- <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
- <property name="mappings">
- <props>
- <prop key="/springMVC.d">/HelloWorld</prop>
- </props>
- </property>
- </bean>
- <bean name="/HelloWorld" class="com.wangbiao.springMVC.HelloWorld">
- <property name="methodNameResolver">
- <ref local="methodNameResolver"/>
- </property>
- </bean>
- <!-- 在url中对应具体的方法,通过m后面带的参数来确定方法 -->
- <bean id="methodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
- <property name="paramName"><value>m</value></property>
- <property name="defaultMethodName"><value>execute</value></property>
- </bean>
- <!--视图解析器-->
- <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
- <!-- webroot到一指定文件夹文件路径 -->
- <property name="prefix" value="/"/>
- <!-- 视图名称后缀 -->
- <property name="suffix" value=".jsp"/>
- </bean>
- </beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <!-- handlerMapping --> <bean id="beanNameUrlMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/springMVC.d">/HelloWorld</prop> </props> </property> </bean> <bean name="/HelloWorld" class="com.wangbiao.springMVC.HelloWorld"> <property name="methodNameResolver"> <ref local="methodNameResolver"/> </property> </bean> <!-- 在url中对应具体的方法,通过m后面带的参数来确定方法 --> <bean id="methodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver"> <property name="paramName"><value>m</value></property> <property name="defaultMethodName"><value>execute</value></property> </bean> <!--视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- webroot到一指定文件夹文件路径 --> <property name="prefix" value="/"/> <!-- 视图名称后缀 --> <property name="suffix" value=".jsp"/> </bean> </beans>
下面是源码
1.DispatcherServlet注册HandlerAdapter。DispatcherServlet的initHandlerAdapters方法,红色标记的部分是关键。由于在配置文件中没有对HandlerAdapter的相关配置,所以DispatcherServlet获取到的HandlerAdapter是三个默认的HandlerAdapter对象,分别是HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter,并将这三个对象存入handlerAdapter属性中去。
- private void initHandlerAdapters(ApplicationContext context) {
- this.handlerAdapters = null;
- if (this.detectAllHandlerAdapters) {
- // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
- Map<String, HandlerAdapter> matchingBeans =
- BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
- if (!matchingBeans.isEmpty()) {
- this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
- // We keep HandlerAdapters in sorted order.
- OrderComparator.sort(this.handlerAdapters);
- }
- }
- else {
- try {
- HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
- this.handlerAdapters = Collections.singletonList(ha);
- }
- catch (NoSuchBeanDefinitionException ex) {
- // Ignore, we'll add a default HandlerAdapter later.
- }
- }
- // Ensure we have at least some HandlerAdapters, by registering
- // default HandlerAdapters if no other adapters are found.
- if (this.handlerAdapters == null) {
- this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
- if (logger.isDebugEnabled()) {
- logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
- }
- }
- }
private void initHandlerAdapters(ApplicationContext context) { this.handlerAdapters = null; if (this.detectAllHandlerAdapters) { // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts. Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values()); // We keep HandlerAdapters in sorted order. OrderComparator.sort(this.handlerAdapters); } } else { try { HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class); this.handlerAdapters = Collections.singletonList(ha); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerAdapter later. } } // Ensure we have at least some HandlerAdapters, by registering // default HandlerAdapters if no other adapters are found. if (this.handlerAdapters == null) { this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default"); } } }
2.根据handlerMapping传过来的Handler对象与DispatcherServlet集合属性handlerAdapter中的HandlerAdapter一一匹配,如果有支持Handler对象的HandlerAdapter,那么HandlerAdapter就会调用自己的handle方法处理请求。
- protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
- HttpServletRequest processedRequest = request;
- HandlerExecutionChain mappedHandler = null;
- int interceptorIndex = -1;
- try {
- ModelAndView mv;
- boolean errorView = false;
- try {
- processedRequest = checkMultipart(request);
- // Determine handler for the current request.
- mappedHandler = getHandler(processedRequest, false);
- if (mappedHandler == null || mappedHandler.getHandler() == null) {
- noHandlerFound(processedRequest, response);
- return;
- }
- // Determine handler adapter for the current request.
- 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()) {
- String requestUri = urlPathHelper.getRequestUri(request);
- logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
- }
- if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
- return;
- }
- }
- // Apply preHandle methods of registered interceptors.
- HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
- if (interceptors != null) {
- for (int i = 0; i < interceptors.length; i++) {
- HandlerInterceptor interceptor = interceptors[i];
- if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
- triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
- return;
- }
- interceptorIndex = i;
- }
- }
- // Actually invoke the handler.
- mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
- // Do we need view name translation?
- if (mv != null && !mv.hasView()) {
- mv.setViewName(getDefaultViewName(request));
- }
- // Apply postHandle methods of registered interceptors.
- if (interceptors != null) {
- for (int i = interceptors.length - 1; i >= 0; i--) {
- HandlerInterceptor interceptor = interceptors[i];
- interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
- }
- }
- }
- catch (ModelAndViewDefiningException ex) {
- logger.debug("ModelAndViewDefiningException encountered", ex);
- mv = ex.getModelAndView();
- }
- catch (Exception ex) {
- Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
- mv = processHandlerException(processedRequest, response, handler, ex);
- errorView = (mv != null);
- }
- // Did the handler return a view to render?
- if (mv != null && !mv.wasCleared()) {
- render(mv, processedRequest, response);
- if (errorView) {
- WebUtils.clearErrorRequestAttributes(request);
- }
- }
- else {
- if (logger.isDebugEnabled()) {
- logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
- "': assuming HandlerAdapter completed request handling");
- }
- }
- // Trigger after-completion for successful outcome.
- triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
- }
- catch (Exception ex) {
- // Trigger after-completion for thrown exception.
- triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
- throw ex;
- }
- catch (Error err) {
- ServletException ex = new NestedServletException("Handler processing failed", err);
- // Trigger after-completion for thrown exception.
- triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
- throw ex;
- }
- finally {
- // Clean up any resources used by a multipart request.
- if (processedRequest != request) {
- cleanupMultipart(processedRequest);
- }
- }
- }
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; int interceptorIndex = -1; try { ModelAndView mv; boolean errorView = false; try { processedRequest = checkMultipart(request); // Determine handler for the current request. mappedHandler = getHandler(processedRequest, false); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. 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()) { String requestUri = urlPathHelper.getRequestUri(request); logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // Apply preHandle methods of registered interceptors. HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); if (interceptors != null) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) { triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); return; } interceptorIndex = i; } } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // Do we need view name translation? if (mv != null && !mv.hasView()) { mv.setViewName(getDefaultViewName(request)); } // Apply postHandle methods of registered interceptors. if (interceptors != null) { for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv); } } } catch (ModelAndViewDefiningException ex) { logger.debug("ModelAndViewDefiningException encountered", ex); mv = ex.getModelAndView(); } catch (Exception ex) { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(processedRequest, response, handler, ex); errorView = (mv != null); } // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { render(mv, processedRequest, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isDebugEnabled()) { logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + "': assuming HandlerAdapter completed request handling"); } } // Trigger after-completion for successful outcome. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); } catch (Exception ex) { // Trigger after-completion for thrown exception. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); throw ex; } catch (Error err) { ServletException ex = new NestedServletException("Handler processing failed", err); // Trigger after-completion for thrown exception. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); throw ex; } finally { // Clean up any resources used by a multipart request. if (processedRequest != request) { cleanupMultipart(processedRequest); } } }
getHandlerAdapter方法
- 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 +
- "]: Does your handler implement a supported interface like Controller?");
- }
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 + "]: Does your handler implement a supported interface like Controller?"); }
HandlerAdapter接口
- /*
- * Copyright 2002-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.springframework.web.servlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- /**
- * MVC framework SPI interface, allowing parameterization of core MVC workflow.
- *
- * <p>Interface that must be implemented for each handler type to handle a request.
- * This interface is used to allow the {@link DispatcherServlet} to be indefinitely
- * extensible. The DispatcherServlet accesses all installed handlers through this
- * interface, meaning that it does not contain code specific to any handler type.
- *
- * <p>Note that a handler can be of type <code>Object</code>. This is to enable
- * handlers from other frameworks to be integrated with this framework without
- * custom coding, as well as to allow for annotation handler objects that do
- * not obey any specific Java interface.
- *
- * <p>This interface is not intended for application developers. It is available
- * to handlers who want to develop their own web workflow.
- *
- * <p>Note: HandlerAdaptger implementators may implement the
- * {@link org.springframework.core.Ordered} interface to be able to specify a
- * sorting order (and thus a priority) for getting applied by DispatcherServlet.
- * Non-Ordered instances get treated as lowest priority.
- *
- * @author Rod Johnson
- * @author Juergen Hoeller
- * @see org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
- * @see org.springframework.web.servlet.handler.SimpleServletHandlerAdapter
- */
- public interface HandlerAdapter {
- /**
- * Given a handler instance, return whether or not this HandlerAdapter can
- * support it. Typical HandlerAdapters will base the decision on the handler
- * type. HandlerAdapters will usually only support one handler type each.
- * <p>A typical implementation:
- * <p><code>
- * return (handler instanceof MyHandler);
- * </code>
- * @param handler handler object to check
- * @return whether or not this object can use the given handler
- */
- boolean supports(Object handler);
- /**
- * Use the given handler to handle this request.
- * The workflow that is required may vary widely.
- * @param request current HTTP request
- * @param response current HTTP response
- * @param handler handler to use. This object must have previously been passed
- * to the <code>supports</code> method of this interface, which must have
- * returned <code>true</code>.
- * @throws Exception in case of errors
- * @return ModelAndView object with the name of the view and the required
- * model data, or <code>null</code> if the request has been handled directly
- */
- ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
- /**
- * Same contract as for HttpServlet's <code>getLastModified</code> method.
- * Can simply return -1 if there's no support in the handler class.
- * @param request current HTTP request
- * @param handler handler to use
- * @return the lastModified value for the given handler
- * @see javax.servlet.http.HttpServlet#getLastModified
- * @see org.springframework.web.servlet.mvc.LastModified#getLastModified
- */
- long getLastModified(HttpServletRequest request, Object handler);
- }
/* * Copyright 2002-2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.web.servlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * MVC framework SPI interface, allowing parameterization of core MVC workflow. * * <p>Interface that must be implemented for each handler type to handle a request. * This interface is used to allow the {@link DispatcherServlet} to be indefinitely * extensible. The DispatcherServlet accesses all installed handlers through this * interface, meaning that it does not contain code specific to any handler type. * * <p>Note that a handler can be of type <code>Object</code>. This is to enable * handlers from other frameworks to be integrated with this framework without * custom coding, as well as to allow for annotation handler objects that do * not obey any specific Java interface. * * <p>This interface is not intended for application developers. It is available * to handlers who want to develop their own web workflow. * * <p>Note: HandlerAdaptger implementators may implement the * {@link org.springframework.core.Ordered} interface to be able to specify a * sorting order (and thus a priority) for getting applied by DispatcherServlet. * Non-Ordered instances get treated as lowest priority. * * @author Rod Johnson * @author Juergen Hoeller * @see org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter * @see org.springframework.web.servlet.handler.SimpleServletHandlerAdapter */ public interface HandlerAdapter { /** * Given a handler instance, return whether or not this HandlerAdapter can * support it. Typical HandlerAdapters will base the decision on the handler * type. HandlerAdapters will usually only support one handler type each. * <p>A typical implementation: * <p><code> * return (handler instanceof MyHandler); * </code> * @param handler handler object to check * @return whether or not this object can use the given handler */ boolean supports(Object handler); /** * Use the given handler to handle this request. * The workflow that is required may vary widely. * @param request current HTTP request * @param response current HTTP response * @param handler handler to use. This object must have previously been passed * to the <code>supports</code> method of this interface, which must have * returned <code>true</code>. * @throws Exception in case of errors * @return ModelAndView object with the name of the view and the required * model data, or <code>null</code> if the request has been handled directly */ ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; /** * Same contract as for HttpServlet's <code>getLastModified</code> method. * Can simply return -1 if there's no support in the handler class. * @param request current HTTP request * @param handler handler to use * @return the lastModified value for the given handler * @see javax.servlet.http.HttpServlet#getLastModified * @see org.springframework.web.servlet.mvc.LastModified#getLastModified */ long getLastModified(HttpServletRequest request, Object handler); }
再来看一下自己写的控制器HelloWorld继承了MultiActionController。MultiActionController又继承了AbstractController,AbstractController实现了Controller。这样就看DispatcherServlet属性中的HandlerApater谁支持Controller类型的处理器了。在运行的过程中发现SimpleControllerHandlerAdapter是支持Controller类型的控制器的。
来看一下SimpleControllerHandlerAdapter的代码
- public class SimpleControllerHandlerAdapter implements HandlerAdapter {
- public boolean supports(Object handler) {
- return (handler instanceof Controller);
- }
- public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
- throws Exception {
- return ((Controller) handler).handleRequest(request, response);
- }
- public long getLastModified(HttpServletRequest request, Object handler) {
- if (handler instanceof LastModified) {
- return ((LastModified) handler).getLastModified(request);
- }
- return -1L;
- }
- }
public class SimpleControllerHandlerAdapter implements HandlerAdapter { public boolean supports(Object handler) { return (handler instanceof Controller); } public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller) handler).handleRequest(request, response); } public long getLastModified(HttpServletRequest request, Object handler) { if (handler instanceof LastModified) { return ((LastModified) handler).getLastModified(request); } return -1L; } }
再看一下Controller源码,Controller接口只有一个handleRequest方法
- public interface Controller {
- /**
- * Process the request and return a ModelAndView object which the DispatcherServlet
- * will render. A <code>null</code> return value is not an error: It indicates that
- * this object completed request processing itself, thus there is no ModelAndView
- * to render.
- * @param request current HTTP request
- * @param response current HTTP response
- * @return a ModelAndView to render, or <code>null</code> if handled directly
- * @throws Exception in case of errors
- */
- ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
- }
public interface Controller { /** * Process the request and return a ModelAndView object which the DispatcherServlet * will render. A <code>null</code> return value is not an error: It indicates that * this object completed request processing itself, thus there is no ModelAndView * to render. * @param request current HTTP request * @param response current HTTP response * @return a ModelAndView to render, or <code>null</code> if handled directly * @throws Exception in case of errors */ ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception; }
再看看实现了Controller接口的AbstractController类
- public abstract class AbstractController extends WebContentGenerator implements Controller {
- private boolean synchronizeOnSession = false;
- /**
- * Set if controller execution should be synchronized on the session,
- * to serialize parallel invocations from the same client.
- * <p>More specifically, the execution of the <code>handleRequestInternal</code>
- * method will get synchronized if this flag is "true". The best available
- * session mutex will be used for the synchronization; ideally, this will
- * be a mutex exposed by HttpSessionMutexListener.
- * <p>The session mutex is guaranteed to be the same object during
- * the entire lifetime of the session, available under the key defined
- * by the <code>SESSION_MUTEX_ATTRIBUTE</code> constant. It serves as a
- * safe reference to synchronize on for locking on the current session.
- * <p>In many cases, the HttpSession reference itself is a safe mutex
- * as well, since it will always be the same object reference for the
- * same active logical session. However, this is not guaranteed across
- * different servlet containers; the only 100% safe way is a session mutex.
- * @see org.springframework.web.servlet.mvc.AbstractController#handleRequestInternal
- * @see org.springframework.web.util.HttpSessionMutexListener
- * @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession)
- */
- public final void setSynchronizeOnSession(boolean synchronizeOnSession) {
- this.synchronizeOnSession = synchronizeOnSession;
- }
- /**
- * Return whether controller execution should be synchronized on the session.
- */
- public final boolean isSynchronizeOnSession() {
- return this.synchronizeOnSession;
- }
- public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- // Delegate to WebContentGenerator for checking and preparing.
- checkAndPrepare(request, response, this instanceof LastModified);
- // Execute handleRequestInternal in synchronized block if required.
- if (this.synchronizeOnSession) {
- HttpSession session = request.getSession(false);
- if (session != null) {
- Object mutex = WebUtils.getSessionMutex(session);
- synchronized (mutex) {
- return handleRequestInternal(request, response);
- }
- }
- }
- return handleRequestInternal(request, response);
- }
- /**
- * Template method. Subclasses must implement this.
- * The contract is the same as for <code>handleRequest</code>.
- * @see #handleRequest
- */
- protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
- throws Exception;
- }
public abstract class AbstractController extends WebContentGenerator implements Controller { private boolean synchronizeOnSession = false; /** * Set if controller execution should be synchronized on the session, * to serialize parallel invocations from the same client. * <p>More specifically, the execution of the <code>handleRequestInternal</code> * method will get synchronized if this flag is "true". The best available * session mutex will be used for the synchronization; ideally, this will * be a mutex exposed by HttpSessionMutexListener. * <p>The session mutex is guaranteed to be the same object during * the entire lifetime of the session, available under the key defined * by the <code>SESSION_MUTEX_ATTRIBUTE</code> constant. It serves as a * safe reference to synchronize on for locking on the current session. * <p>In many cases, the HttpSession reference itself is a safe mutex * as well, since it will always be the same object reference for the * same active logical session. However, this is not guaranteed across * different servlet containers; the only 100% safe way is a session mutex. * @see org.springframework.web.servlet.mvc.AbstractController#handleRequestInternal * @see org.springframework.web.util.HttpSessionMutexListener * @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession) */ public final void setSynchronizeOnSession(boolean synchronizeOnSession) { this.synchronizeOnSession = synchronizeOnSession; } /** * Return whether controller execution should be synchronized on the session. */ public final boolean isSynchronizeOnSession() { return this.synchronizeOnSession; } public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { // Delegate to WebContentGenerator for checking and preparing. checkAndPrepare(request, response, this instanceof LastModified); // Execute handleRequestInternal in synchronized block if required. if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { return handleRequestInternal(request, response); } } } return handleRequestInternal(request, response); } /** * Template method. Subclasses must implement this. * The contract is the same as for <code>handleRequest</code>. * @see #handleRequest */ protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception; }
再看一下继承了AbstractController的MultiActionController,MultiActionController中有对AbstractController的handleRequestInternal的实现
- protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- try {
- String methodName = this.methodNameResolver.getHandlerMethodName(request);
- return invokeNamedMethod(methodName, request, response);
- }
- catch (NoSuchRequestHandlingMethodException ex) {
- return handleNoSuchRequestHandlingMethod(ex, request, response);
- }
- }
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { try { String methodName = this.methodNameResolver.getHandlerMethodName(request); return invokeNamedMethod(methodName, request, response); } catch (NoSuchRequestHandlingMethodException ex) { return handleNoSuchRequestHandlingMethod(ex, request, response); } }
可以看出在MultiActionController的handleRequestInternal方法中分为两步,第一步是找寻执行该请求的方法名,第二步是调用invokeNamedMethod方法。
invokeNamedMethod方法源码
- protected final ModelAndView invokeNamedMethod(
- String methodName, HttpServletRequest request, HttpServletResponse response) throws Exception {
- Method method = this.handlerMethodMap.get(methodName);
- if (method == null) {
- throw new NoSuchRequestHandlingMethodException(methodName, getClass());
- }
- try {
- Class[] paramTypes = method.getParameterTypes();
- List<Object> params = new ArrayList<Object>(4);
- params.add(request);
- params.add(response);
- if (paramTypes.length >= 3 && paramTypes[2].equals(HttpSession.class)) {
- HttpSession session = request.getSession(false);
- if (session == null) {
- throw new HttpSessionRequiredException(
- "Pre-existing session required for handler method '" + methodName + "'");
- }
- params.add(session);
- }
- // If last parameter isn't of HttpSession type, it's a command.
- if (paramTypes.length >= 3 &&
- !paramTypes[paramTypes.length - 1].equals(HttpSession.class)) {
- Object command = newCommandObject(paramTypes[paramTypes.length - 1]);
- params.add(command);
- bind(request, command);
- }
- //执行该方法
- Object returnValue = method.invoke(this.delegate, params.toArray(new Object[params.size()]));
- return massageReturnValueIfNecessary(returnValue);
- }
- catch (InvocationTargetException ex) {
- // The handler method threw an exception.
- return handleException(request, response, ex.getTargetException());
- }
- catch (Exception ex) {
- // The binding process threw an exception.
- return handleException(request, response, ex);
- }
- }
protected final ModelAndView invokeNamedMethod( String methodName, HttpServletRequest request, HttpServletResponse response) throws Exception { Method method = this.handlerMethodMap.get(methodName); if (method == null) { throw new NoSuchRequestHandlingMethodException(methodName, getClass()); } try { Class[] paramTypes = method.getParameterTypes(); List<Object> params = new ArrayList<Object>(4); params.add(request); params.add(response); if (paramTypes.length >= 3 && paramTypes[2].equals(HttpSession.class)) { HttpSession session = request.getSession(false); if (session == null) { throw new HttpSessionRequiredException( "Pre-existing session required for handler method '" + methodName + "'"); } params.add(session); } // If last parameter isn't of HttpSession type, it's a command. if (paramTypes.length >= 3 && !paramTypes[paramTypes.length - 1].equals(HttpSession.class)) { Object command = newCommandObject(paramTypes[paramTypes.length - 1]); params.add(command); bind(request, command); } //执行该方法 Object returnValue = method.invoke(this.delegate, params.toArray(new Object[params.size()])); return massageReturnValueIfNecessary(returnValue); } catch (InvocationTargetException ex) { // The handler method threw an exception. return handleException(request, response, ex.getTargetException()); } catch (Exception ex) { // The binding process threw an exception. return handleException(request, response, ex); } }
根据方法名在MultiActionController的方法集合属性handlerMethodMap中寻找对应的方法对象,然后执行该方法对象,执行该方法对象后获得一个object的返回值,通过massageReturnValueIfNecessary方法判断这个返回值的类型,如果这个值的返回类型是ModelAndView类型,就返回ModelAndView。到此我们找到响应请求的方法并执行获得了返回值。
虽然总体走完了,但是有两个地方还没有说,1如何根据用户发来的url请求来确定url中哪一段是执行该请求的方法名;2.确定方法名后是怎么找到该方法的。
MultiActionController中有一个构造函数,registerHandlerMethods(this.delegate);方法就是对我们所写的controller中的方法的注册。
- public MultiActionController() {
- this.delegate = this;
- registerHandlerMethods(this.delegate);
- // We'll accept no handler methods found here - a delegate might be set later on.
- }
public MultiActionController() { this.delegate = this; registerHandlerMethods(this.delegate); // We'll accept no handler methods found here - a delegate might be set later on. }
registerHandlerMethods方法
this.delegate其实就是继承了MultiActionController的控制对象,比如HelloWorld继承了MultiActionController,那么传过来的就是HelloWorld对象,就会将HelloWorld对象中的所有方法放到MultiActionController的handlerMethodMap属性中去了。
- private void registerHandlerMethods(Object delegate) {
- this.handlerMethodMap.clear();
- this.lastModifiedMethodMap.clear();
- this.exceptionHandlerMap.clear();
- // Look at all methods in the subclass, trying to find
- // methods that are validators according to our criteria
- Method[] methods = delegate.getClass().getMethods();
- for (Method method : methods) {
- // We're looking for methods with given parameters.
- if (isExceptionHandlerMethod(method)) {
- registerExceptionHandlerMethod(method);
- }
- else if (isHandlerMethod(method)) {
- registerHandlerMethod(method);
- registerLastModifiedMethodIfExists(delegate, method);
- }
- }
- }
private void registerHandlerMethods(Object delegate) { this.handlerMethodMap.clear(); this.lastModifiedMethodMap.clear(); this.exceptionHandlerMap.clear(); // Look at all methods in the subclass, trying to find // methods that are validators according to our criteria Method[] methods = delegate.getClass().getMethods(); for (Method method : methods) { // We're looking for methods with given parameters. if (isExceptionHandlerMethod(method)) { registerExceptionHandlerMethod(method); } else if (isHandlerMethod(method)) { registerHandlerMethod(method); registerLastModifiedMethodIfExists(delegate, method); } } }
在配置文件中有这样一段代码
- <!-- 在url中对应具体的方法,通过m后面带的参数来确定方法 -->
- <bean id="methodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
- <property name="paramName"><value>m</value></property>
- <property name="defaultMethodName"><value>execute</value></property>
- </bean>
<!-- 在url中对应具体的方法,通过m后面带的参数来确定方法 --> <bean id="methodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver"> <property name="paramName"><value>m</value></property> <property name="defaultMethodName"><value>execute</value></property> </bean>
ParameterMethodNameResolver这个类的作用就是根据url链接中带的参数来确定执行该url的方法名是什么。在ioc容器初始ParameterMethodNameResolver的时候,容器会将“m”这个参数赋值给ParameterMethodNameResolver的属性paramName,然后ParameterMethodNameResolver会根据url中m后面跟的参数来获取方法名
- public String getHandlerMethodName(HttpServletRequest request) throws NoSuchRequestHandlingMethodException {
- String methodName = null;
- // Check parameter names where the very existence of each parameter
- // means that a method of the same name should be invoked, if any.
- if (this.methodParamNames != null) {
- for (String candidate : this.methodParamNames) {
- if (WebUtils.hasSubmitParameter(request, candidate)) {
- methodName = candidate;
- if (logger.isDebugEnabled()) {
- logger.debug("Determined handler method '" + methodName +
- "' based on existence of explicit request parameter of same name");
- }
- break;
- }
- }
- }
- // Check parameter whose value identifies the method to invoke, if any.
- if (methodName == null && this.paramName != null) {
- methodName = request.getParameter(this.paramName);
- if (methodName != null) {
- if (logger.isDebugEnabled()) {
- logger.debug("Determined handler method '" + methodName +
- "' based on value of request parameter '" + this.paramName + "'");
- }
- }
- }
- if (methodName != null && this.logicalMappings != null) {
- // Resolve logical name into real method name, if appropriate.
- String originalName = methodName;
- methodName = this.logicalMappings.getProperty(methodName, methodName);
- if (logger.isDebugEnabled()) {
- logger.debug("Resolved method name '" + originalName + "' to handler method '" + methodName + "'");
- }
- }
- if (methodName != null && !StringUtils.hasText(methodName)) {
- if (logger.isDebugEnabled()) {
- logger.debug("Method name '" + methodName + "' is empty: treating it as no method name found");
- }
- methodName = null;
- }
- if (methodName == null) {
- if (this.defaultMethodName != null) {
- // No specific method resolved: use default method.
- methodName = this.defaultMethodName;
- if (logger.isDebugEnabled()) {
- logger.debug("Falling back to default handler method '" + this.defaultMethodName + "'");
- }
- }
- else {
- // If resolution failed completely, throw an exception.
- throw new NoSuchRequestHandlingMethodException(request);
- }
- }
- return methodName;
- }
public String getHandlerMethodName(HttpServletRequest request) throws NoSuchRequestHandlingMethodException { String methodName = null; // Check parameter names where the very existence of each parameter // means that a method of the same name should be invoked, if any. if (this.methodParamNames != null) { for (String candidate : this.methodParamNames) { if (WebUtils.hasSubmitParameter(request, candidate)) { methodName = candidate; if (logger.isDebugEnabled()) { logger.debug("Determined handler method '" + methodName + "' based on existence of explicit request parameter of same name"); } break; } } } // Check parameter whose value identifies the method to invoke, if any. if (methodName == null && this.paramName != null) { methodName = request.getParameter(this.paramName); if (methodName != null) { if (logger.isDebugEnabled()) { logger.debug("Determined handler method '" + methodName + "' based on value of request parameter '" + this.paramName + "'"); } } } if (methodName != null && this.logicalMappings != null) { // Resolve logical name into real method name, if appropriate. String originalName = methodName; methodName = this.logicalMappings.getProperty(methodName, methodName); if (logger.isDebugEnabled()) { logger.debug("Resolved method name '" + originalName + "' to handler method '" + methodName + "'"); } } if (methodName != null && !StringUtils.hasText(methodName)) { if (logger.isDebugEnabled()) { logger.debug("Method name '" + methodName + "' is empty: treating it as no method name found"); } methodName = null; } if (methodName == null) { if (this.defaultMethodName != null) { // No specific method resolved: use default method. methodName = this.defaultMethodName; if (logger.isDebugEnabled()) { logger.debug("Falling back to default handler method '" + this.defaultMethodName + "'"); } } else { // If resolution failed completely, throw an exception. throw new NoSuchRequestHandlingMethodException(request); } } return methodName; }
当找到了方法名后,就会去MultiActionController属性handlerMethodMap中根据方法名找方法对象。再执行这个方法就好了。
再来看看是如何从handlerMethodMap集合中找到方法并执行方法的
- protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- try {
- String methodName = this.methodNameResolver.getHandlerMethodName(request);
- return invokeNamedMethod(methodName, request, response);
- }
- catch (NoSuchRequestHandlingMethodException ex) {
- return handleNoSuchRequestHandlingMethod(ex, request, response);
- }
- }
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { try { String methodName = this.methodNameResolver.getHandlerMethodName(request); return invokeNamedMethod(methodName, request, response); } catch (NoSuchRequestHandlingMethodException ex) { return handleNoSuchRequestHandlingMethod(ex, request, response); } }
- protected final ModelAndView invokeNamedMethod(
- String methodName, HttpServletRequest request, HttpServletResponse response) throws Exception {
- Method method = this.handlerMethodMap.get(methodName);
- if (method == null) {
- throw new NoSuchRequestHandlingMethodException(methodName, getClass());
- }
- try {
- Class[] paramTypes = method.getParameterTypes();
- List<Object> params = new ArrayList<Object>(4);
- params.add(request);
- params.add(response);
- if (paramTypes.length >= 3 && paramTypes[2].equals(HttpSession.class)) {
- HttpSession session = request.getSession(false);
- if (session == null) {
- throw new HttpSessionRequiredException(
- "Pre-existing session required for handler method '" + methodName + "'");
- }
- params.add(session);
- }
- // If last parameter isn't of HttpSession type, it's a command.
- if (paramTypes.length >= 3 &&
- !paramTypes[paramTypes.length - 1].equals(HttpSession.class)) {
- Object command = newCommandObject(paramTypes[paramTypes.length - 1]);
- params.add(command);
- bind(request, command);
- }
- Object returnValue = method.invoke(this.delegate, params.toArray(new Object[params.size()]));
- return massageReturnValueIfNecessary(returnValue);
- }
- catch (InvocationTargetException ex) {
- // The handler method threw an exception.
- return handleException(request, response, ex.getTargetException());
- }
- catch (Exception ex) {
- // The binding process threw an exception.
- return handleException(request, response, ex);
- }
- }
protected final ModelAndView invokeNamedMethod( String methodName, HttpServletRequest request, HttpServletResponse response) throws Exception { Method method = this.handlerMethodMap.get(methodName); if (method == null) { throw new NoSuchRequestHandlingMethodException(methodName, getClass()); } try { Class[] paramTypes = method.getParameterTypes(); List<Object> params = new ArrayList<Object>(4); params.add(request); params.add(response); if (paramTypes.length >= 3 && paramTypes[2].equals(HttpSession.class)) { HttpSession session = request.getSession(false); if (session == null) { throw new HttpSessionRequiredException( "Pre-existing session required for handler method '" + methodName + "'"); } params.add(session); } // If last parameter isn't of HttpSession type, it's a command. if (paramTypes.length >= 3 && !paramTypes[paramTypes.length - 1].equals(HttpSession.class)) { Object command = newCommandObject(paramTypes[paramTypes.length - 1]); params.add(command); bind(request, command); } Object returnValue = method.invoke(this.delegate, params.toArray(new Object[params.size()])); return massageReturnValueIfNecessary(returnValue); } catch (InvocationTargetException ex) { // The handler method threw an exception. return handleException(request, response, ex.getTargetException()); } catch (Exception ex) { // The binding process threw an exception. return handleException(request, response, ex); } }
HandlerAdapter,大家都叫它适配处理器,就是适配不同的处理器,将他们封装起来调用同一个借口方法,这样DispatcherServlet就只需要调用接口方法,而不需要在DispatcherServlet判断调用哪一个具体的HandlerAdapter的实现类了。
当时看到自己项目里面的所有的处理器都是实现了MultiActionController的Controller,再去看MultiActionController的源码时,发现MultiActionController实现了Controller接口,Controller接口只有一个handleRequest方法,我想DispatcherServlet直接用Controller的handleRequest方法执行具体请求就行了,何必还要用HandlerAdapter将Controller封装起来,再在HandlerAdapter的handle方法里执行Controller的handleRequest方法呢,这不是多此一举?
只怪自己目光短浅,由于用的是配置的方式来做项目的,而且平时自己写的Controller只继承了MultiActionController,以为Controller接口就是所有的处理器的接口,眼里就只有Controller了。
今天再来看源码,发现处理器根本就不只有Controller这一种。还有HttpRequestHandler,Servlet等处理器。下面来介绍一下几种适配器对应的处理器以及这些处理器的作用
1. AnnotationMethodHandlerAdapter主要是适配注解类处理器,注解类处理器就是我们经常使用的@Controller的这类处理器
2. HttpRequestHandlerAdapter主要是适配静态资源处理器,静态资源处理器就是实现了HttpRequestHandler接口的处理器,这类处理器的作用是处理通过SpringMVC来访问的静态资源的请求。
3.SimpleControllerHandlerAdapter是Controller处理适配器,适配实现了Controller接口或Controller接口子类的处理器,比如我们经常自己写的Controller来继承MultiActionController,那么自己写的这些Controller就会由SimpleControllerHandlerAdapter来适配
4.SimpleServletHandlerAdapter是Servlet处理适配器,适配实现了Servlet接口或Servlet的子类的处理器,我们不仅可以在web.xml里面配置Servlet,其实也可以用SpringMVC来配置Servlet,不过这个适配器很少用到,而且SpringMVC默认的适配器没有他,默认的是前面的三种。