Discuz! Board
标题: spring mvc DispatcherServlet详解 [打印本页]
作者: zangcf 时间: 2016-2-25 19:01
标题: spring mvc DispatcherServlet详解
四个部分内容,详解了整个SpringMVC的请求过程。
[attach]83[/attach]
作者: zangcf 时间: 2016-2-25 19:11
标题: spring mvc DispatcherServlet详解之一---处理请求深入解析
要深入理解spring mvc的工作流程,就需要先了解spring mvc的架构:
[attach]84[/attach]
从上图可以看到 前端控制器DispatcherServlet在其中起着主导作用,理解了DispatcherServlet 就完全可以说弄清楚了spring mvc。
为了加深对spring mvc的整个工作流程的理解,本文从分析DispatcherServlet的工作过程来一窥spring mvc的整个面貌。
1. 初始化
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context); //文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析;
initLocaleResolver(context); //本地化解析
initThemeResolver(context); //主题解析
initHandlerMappings(context); //通过HandlerMapping,将请求映射到处理器
initHandlerAdapters(context); //通过HandlerAdapter支持多种类型的处理器
initHandlerExceptionResolvers(context); //如果执行过程中遇到异常将交给HandlerExceptionResolver来解析
initRequestToViewNameTranslator(context); //直接解析请求到视图名
initViewResolvers(context); //通过ViewResolver解析逻辑视图名到具体视图实现
initFlashMapManager(context); //flash映射管理器
}
单个resolverinitMultipartResolver,initLocaleResolver,initThemeResolver,initRequestToViewNameTranslator,initFlashMapManager 这五个初始化方法流程相同,都是使用
context.getBean(String name, Class<FlashMapManager> requiredType)的方式获取到相应的Resolver。以initMultipartResolver为例,见如下:/**
* Initialize the MultipartResolver used by this class.
* <p>If no bean is defined with the given name in the BeanFactory for this namespace,
* no multipart handling is provided.
*/
private void initMultipartResolver(ApplicationContext context) {
try {
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("Using MultipartResolver [" + this.multipartResolver + "]");
}
}
catch (NoSuchBeanDefinitionException ex) {
// Default is no multipart resolver.
this.multipartResolver = null;
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
"': no multipart request handling provided");
}
}
}
多个resolver
initHandlerMappings,initHandlerAdapters,initHandlerExceptionResolvers,initViewResolvers 获取方式相同,使用:
BeanFactoryUtils.beansOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<HandlerMapping> type, boolean includeNonSingletons, boolean allowEagerInit)
的方式获取到相应的Resolver。以initHandlerMappings为例,见如下:/** * Initialize the HandlerMappings used by this class. * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace, * we default to BeanNameUrlHandlerMapping. */ 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, 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) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } } }
那么深入看一下BeanFactoryUtils.beansOfTypeIncludingAncestors 究竟做了什么?返回指定类型和子类型的所有bean,若该bean factory 是一个继承类型的beanFactory,这个方法也会获取祖宗factory中定义的指定类型的bean。/** * Return all beans of the given type or subtypes, also picking up beans defined in * ancestor bean factories if the current bean factory is a HierarchicalBeanFactory. * The returned Map will only contain beans of this type. * <p>Does consider objects created by FactoryBeans if the "allowEagerInit" flag is set, * which means that FactoryBeans will get initialized. If the object created by the * FactoryBean doesn't match, the raw FactoryBean itself will be matched against the * type. If "allowEagerInit" is not set, only raw FactoryBeans will be checked * (which doesn't require initialization of each FactoryBean). * <p><b>Note: Beans of the same name will take precedence at the 'lowest' factory level, * i.e. such beans will be returned from the lowest factory that they are being found in, * hiding corresponding beans in ancestor factories.</b> This feature allows for * 'replacing' beans by explicitly choosing the same bean name in a child factory; * the bean in the ancestor factory won't be visible then, not even for by-type lookups. * @param lbf the bean factory * @param type type of bean to match * @param includeNonSingletons whether to include prototype or scoped beans too * or just singletons (also applies to FactoryBeans) * @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and * <i>objects created by FactoryBeans</i> (or by factory methods with a * "factory-bean" reference) for the type check. Note that FactoryBeans need to be * eagerly initialized to determine their type: So be aware that passing in "true" * for this flag will initialize FactoryBeans and "factory-bean" references. * @return the Map of matching bean instances, or an empty Map if none * @throws BeansException if a bean could not be created */ public static <T> Map<String, T> beansOfTypeIncludingAncestors( ListableBeanFactory lbf, Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException {
Assert.notNull(lbf, "ListableBeanFactory must not be null"); Map<String, T> result = new LinkedHashMap<String, T>(4); result.putAll(lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit)); if (lbf instanceof HierarchicalBeanFactory) { HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf; if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) { Map<String, T> parentResult = beansOfTypeIncludingAncestors( (ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit); for (Map.Entry<String, T> entry : parentResult.entrySet()) { String beanName = entry.getKey(); if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) { result.put(beanName, entry.getValue()); } } } } return result; }
2. 提供服务
我们来看看这个servlet是如何提供服务的?
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
doDispatch(request, response);
}
finally {
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
return;
}
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
从上面我们可以看到,提供服务只要分4步:
1. 保存现场。保存request 熟悉的快照,以便能在必要时恢复。
2. 将框架需要的对象放入request中,以便view和handler使用。
3. 请求分发服务.
4. 恢复现场。
其中最重要的是请求分发服务:
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
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);
// 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 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);
}
}
}
分发过程如下:
1. 判断是否设置了multipart resolver,设置的话转换为multipart request,没有的话则继续下面的步骤。
2. 根据当前request,获取hangdler。
3. 根据当前request,获取HandlerAdapter。
4. 如果支持http请求头,处理 last-modified header请求头。
5. 应用已注册interceptor的preHandle方法
6. HandlerAdapter处理请求。
7. 设置默认视图。
8. 应用已注册interceptor的postHandle方法。
9. 处理异常或者视图渲染。
小结:
DispatherServlet整个过程的细节一章之内很难描述的面面俱到,只能分析部分流程,想了解更具体的实现需要从源代码中去寻找。
作者: zangcf 时间: 2016-2-25 19:30
标题: spring mvc DispatcherServlet详解之二---request通过Controller获取Model...
整个spring mvc的架构如下图所示:
[attach]85[/attach]
上篇文件讲解了DispatcherServlet通过request获取控制器Controller的过程,现在来讲解DispatcherServletDispatcherServlet的第二步:通过request从Controller获取ModelAndView。
DispatcherServlet调用Controller的过程:
DispatcherServlet.java
doService()--->doDispatch()--->handlerAdapter的handle()方法
try {// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
}
最常用的实现了HandlerAdapter接口是SimpleControllerHandlerAdapter类,该类将
两个不兼容的类:DispatcherServlet 和Controller 类连接到一起。Adapter to use the plain {@link Controller} workflow interface with the generic {@link org.springframework.web.servlet.DispatcherServlet}. Supports handlers that implement the {@link LastModified} interface. <p>This is an SPI class, not used directly by application code.类之间的转换代码如下所示,调用了Controller类的handleRequest()方法来处理请求:@Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller) handler).handleRequest(request, response); }重量级人物控制器Controller开始闪亮登场,Controller是一个基本的接口,它接受request和response,从这点上来说,它有点像servlet,但不同之处在于它在mvc模式流程中起作用,它和struts中的Action作用类似。继承该接口的控制器或者类应该保证是线程安全的,可复用的,能够在一个应用生命周期中处理大量的request。为了使Controller的配置更便捷,通常使用javaBeans来继承Controller。Controller的handleRequest()方法处理请求,并返回ModelAndView给DispatcherServlet去渲染render。/** * Base Controller interface, representing a component that receives * {@code HttpServletRequest} and {@code HttpServletResponse} * instances just like a {@code HttpServlet} but is able to * participate in an MVC workflow. Controllers are comparable to the * notion of a Struts {@code Action}. * * <p>Any implementation of the Controller interface should be a * <i>reusable, thread-safe</i> class, capable of handling multiple * HTTP requests throughout the lifecycle of an application. To be able to * configure a Controller easily, Controller implementations are encouraged * to be (and usually are) JavaBeans. * </p> * * <p><b><a name="workflow">Workflow</a></b></p> * * <p> * After a <cde>DispatcherServlet</code> has received a request and has * done its work to resolve locales, themes and suchlike, it then tries * to resolve a Controller, using a * {@link org.springframework.web.servlet.HandlerMapping HandlerMapping}. * When a Controller has been found to handle the request, the * {@link #handleRequest(HttpServletRequest, HttpServletResponse) handleRequest} * method of the located Controller will be invoked; the located Controller * is then responsible for handling the actual request and - if applicable - * returning an appropriate * {@link org.springframework.web.servlet.ModelAndView ModelAndView}. * So actually, this method is the main entrypoint for the * {@link org.springframework.web.servlet.DispatcherServlet DispatcherServlet} * which delegates requests to controllers.</p> * * <p>So basically any <i>direct</i> implementation of the Controller interface * just handles HttpServletRequests and should return a ModelAndView, to be further * interpreted by the DispatcherServlet. Any additional functionality such as * optional validation, form handling, etc should be obtained through extending * one of the abstract controller classes mentioned above.</p> * * <p><b>Notes on design and testing</b></p> * * <p>The Controller interface is explicitly designed to operate on HttpServletRequest * and HttpServletResponse objects, just like an HttpServlet. It does not aim to * decouple itself from the Servlet API, in contrast to, for example, WebWork, JSF or Tapestry. * Instead, the full power of the Servlet API is available, allowing Controllers to be * general-purpose: a Controller is able to not only handle web user interface * requests but also to process remoting protocols or to generate reports on demand.</p> * * <p>Controllers can easily be tested by passing in mock objects for the * HttpServletRequest and HttpServletResponse objects as parameters to the * {@link #handleRequest(HttpServletRequest, HttpServletResponse) handleRequest} * method. As a convenience, Spring ships with a set of Servlet API mocks * that are suitable for testing any kind of web components, but are particularly * suitable for testing Spring web controllers. In contrast to a Struts Action, * there is no need to mock the ActionServlet or any other infrastructure; * HttpServletRequest and HttpServletResponse are sufficient.</p> * * <p>If Controllers need to be aware of specific environment references, they can * choose to implement specific awareness interfaces, just like any other bean in a * Spring (web) application context can do, for example:</p> * <ul> * <li>{@code org.springframework.context.ApplicationContextAware}</li> * <li>{@code org.springframework.context.ResourceLoaderAware}</li> * <li>{@code org.springframework.web.context.ServletContextAware}</li> * </ul> * * <p>Such environment references can easily be passed in testing environments, * through the corresponding setters defined in the respective awareness interfaces. * In general, it is recommended to keep the dependencies as minimal as possible: * for example, if all you need is resource loading, implement ResourceLoaderAware only. * Alternatively, derive from the WebApplicationObjectSupport base class, which gives * you all those references through convenient accessors - but requires an * ApplicationContext reference on initialization. * * <p>Controllers can optionally implement the {@link LastModified} interface.
*/Controller接口的抽象实现类为:AbstractController,它通过互斥锁(mutex)来保证线程安全。
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); }}handleRequestInternal()为抽象方法,留待具体实现类来实现。它的直接子类有:AbstractUrlViewController, MultiActionController, ParameterizableViewController, ServletForwardingController, ServletWrappingControlle
在web.xml中有时候定义节点<welcome-list>index.html</welcome-list>等,这种简单的请,Controller是如何实现的呢?我们来看看UrlFilenameViewController,它是Controller的一个间接实现,实现了AbstractUrlViewController。它把url的虚拟路径转换成一个view的名字,然后返回这个view。
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); String viewName = getViewNameForRequest(request); if (logger.isDebugEnabled()) { logger.debug("Returning view name '" + viewName + "' for lookup path [" + lookupPath + "]"); } return new ModelAndView(viewName, RequestContextUtils.getInputFlashMap(request));}
复杂Controller实现
一个可以处理多种请求类型的Controller实现:MultiActionController。它类似于struts中的DispatcherAction,但更灵活,而且支持代理。
/** * {@link org.springframework.web.servlet.mvc.Controller Controller} * implementation that allows multiple request types to be handled by the same * class. Subclasses of this class can handle several different types of * request with methods of the form * * <pre class="code">public (ModelAndView | Map | String | void) actionName(HttpServletRequest request, HttpServletResponse response, [,HttpSession] [,AnyObject]);</pre> * * A Map return value indicates a model that is supposed to be passed to a default view * (determined through a {@link org.springframework.web.servlet.RequestToViewNameTranslator}). * A String return value indicates the name of a view to be rendered without a specific model. * * <p>May take a third parameter (of type {@link HttpSession}) in which an * existing session will be required, or a third parameter of an arbitrary * class that gets treated as the command (that is, an instance of the class * gets created, and request parameters get bound to it) * * <p>These methods can throw any kind of exception, but should only let * propagate those that they consider fatal, or which their class or superclass * is prepared to catch by implementing an exception handler. * * <p>When returning just a {@link Map} instance view name translation will be * used to generate the view name. The configured * {@link org.springframework.web.servlet.RequestToViewNameTranslator} will be * used to determine the view name. * * <p>When returning {@code void} a return value of {@code null} is * assumed meaning that the handler method is responsible for writing the * response directly to the supplied {@link HttpServletResponse}. * * <p>This model allows for rapid coding, but loses the advantage of * compile-time checking. It is similar to a Struts {@code DispatchAction}, * but more sophisticated. Also supports delegation to another object. * * <p>An implementation of the {@link MethodNameResolver} interface defined in * this package should return a method name for a given request, based on any * aspect of the request, such as its URL or an "action" parameter. The actual * strategy can be configured via the "methodNameResolver" bean property, for * each {@code MultiActionController}. * * <p>The default {@code MethodNameResolver} is * {@link InternalPathMethodNameResolver}; further included strategies are * {@link PropertiesMethodNameResolver} and {@link ParameterMethodNameResolver}. * * <p>Subclasses can implement custom exception handler methods with names such * as: * * <pre class="code">public ModelAndView anyMeaningfulName(HttpServletRequest request, HttpServletResponse response, ExceptionClass exception);</pre> * * The third parameter can be any subclass or {@link Exception} or * {@link RuntimeException}. * * <p>There can also be an optional {@code xxxLastModified} method for * handlers, of signature: * * <pre class="code">public long anyMeaningfulNameLastModified(HttpServletRequest request)</pre> * * If such a method is present, it will be invoked. Default return from * {@code getLastModified} is -1, meaning that the content must always be * regenerated. * * <p><b>Note that all handler methods need to be public and that * method overloading is <i>not</i> allowed.</b> * * <p>See also the description of the workflow performed by * {@link AbstractController the superclass} (in that section of the class * level Javadoc entitled 'workflow'). * * <p><b>Note:</b> For maximum data binding flexibility, consider direct usage of a * {@link ServletRequestDataBinder} in your controller method, instead of relying * on a declared command argument. This allows for full control over the entire * binder setup and usage, including the invocation of {@link Validator Validators} * and the subsequent evaluation of binding/validation errors.*/
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); }
)
处理返回结果,要么返回null要么返回ModelAndView实例。当返回一个Map类型时,ModelAndView实例包装的Map类型。 /** * Processes the return value of a handler method to ensure that it either returns * {@code null} or an instance of {@link ModelAndView}. When returning a {@link Map}, * the {@link Map} instance is wrapped in a new {@link ModelAndView} instance. */ @SuppressWarnings("unchecked") private ModelAndView massageReturnValueIfNecessary(Object returnValue) { if (returnValue instanceof ModelAndView) { return (ModelAndView) returnValue; } else if (returnValue instanceof Map) { return new ModelAndView().addAllObjects((Map<String, ?>) returnValue); } else if (returnValue instanceof String) { return new ModelAndView((String) returnValue); } else { // Either returned null or was 'void' return. // We'll assume that the handle method already wrote the response. return null; } }
小结:
DispatcherServlet接受一个请求,然后解析完locales, themes等后,通过HadlerMapping解析控制器Controller去处理请求。
找到Controller后,出发当前controller的handleRequest()方法,此controller负责真正处理请求,然后一个ModelAndView实例。
DispatcherServlet 代理此Controller,接收返回结果,然后进行渲染。
| 欢迎光临 Discuz! Board (http://47.89.242.157:9000/bbs/discuz/) |
Powered by Discuz! X3.2 |