spring mvc DispatcherServlet详解之二---request通过Controller获取ModelAndView过程
整個(gè)spring mvc的架構(gòu)如下圖所示:
上篇文件講解了DispatcherServlet通過request獲取控制器Controller的過程,現(xiàn)在來講解DispatcherServletDispatcherServlet的第二步:通過request從Controller獲取ModelAndView。
DispatcherServlet調(diào)用Controller的過程:
DispatcherServlet.java
doService()--->doDispatch()--->handlerAdapter的handle()方法
try {// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());}finally {if (asyncManager.isConcurrentHandlingStarted()) {return;}}最常用的實(shí)現(xiàn)了HandlerAdapter接口是SimpleControllerHandlerAdapter類,該類將
兩個(gè)不兼容的類: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.類之間的轉(zhuǎn)換代碼如下所示,調(diào)用了Controller類的handleRequest()方法來處理請求:
@Overridepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return ((Controller) handler).handleRequest(request, response);}重量級人物控制器Controller開始閃亮登場,Controller是一個(gè)基本的接口,它接受request和response,從這點(diǎn)上來說,它有點(diǎn)像servlet,但不同之處在于它在mvc模式流程中起作用,它和struts中的Action作用類似。繼承該接口的控制器或者類應(yīng)該保證是線程安全的,可復(fù)用的,能夠在一個(gè)應(yīng)用生命周期中處理大量的request。為了使Controller的配置更便捷,通常使用javaBeans來繼承Controller。
?
/*** 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的handleRequest()方法處理請求,并返回ModelAndView給DispatcherServlet去渲染render。
Controller接口的抽象實(shí)現(xiàn)類為:AbstractController,它通過互斥鎖(mutex)來保證線程安全。
/*** 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}* 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} 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 AbstractController#handleRequestInternal* @see org.springframework.web.util.HttpSessionMutexListener* @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession)*/線程安全實(shí)現(xiàn):
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()為抽象方法,留待具體實(shí)現(xiàn)類來實(shí)現(xiàn)。它的直接子類有:- AbstractUrlViewController, MultiActionController, ParameterizableViewController, ServletForwardingController, ServletWrappingController
簡單Controller實(shí)現(xiàn)
在web.xml中有時(shí)候定義節(jié)點(diǎn)<welcome-list>index.html</welcome-list>等,這種簡單的請,Controller是如何實(shí)現(xiàn)的呢?我們來看看UrlFilenameViewController,它是Controller的一個(gè)間接實(shí)現(xiàn),實(shí)現(xiàn)了AbstractUrlViewController。它把url的虛擬路徑轉(zhuǎn)換成一個(gè)view的名字,然后返回這個(gè)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));}
復(fù)雜Controller實(shí)現(xiàn)
一個(gè)可以處理多種請求類型的Controller實(shí)現(xiàn):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.*/?根據(jù)方法名決定處理的handler
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);}}觸發(fā)執(zhí)行方法:
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);}處理返回結(jié)果,要么返回null要么返回ModelAndView實(shí)例。當(dāng)返回一個(gè)Map類型時(shí),ModelAndView實(shí)例包裝的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;}}小結(jié):
? ?DispatcherServlet接受一個(gè)請求,然后解析完locales, themes等后,通過HadlerMapping解析控制器Controller去處理請求。
? ?找到Controller后,出發(fā)當(dāng)前controller的handleRequest()方法,此controller負(fù)責(zé)真正處理請求,然后一個(gè)ModelAndView實(shí)例。
? DispatcherServlet 代理此Controller,接收返回結(jié)果,然后進(jìn)行渲染。
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/p/4119314.html
總結(jié)
以上是生活随笔為你收集整理的spring mvc DispatcherServlet详解之二---request通过Controller获取ModelAndView过程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring mvc Dispatche
- 下一篇: spring aop 如何切面到mvc