{"id":797,"date":"2012-03-11T18:47:53","date_gmt":"2012-03-11T17:47:53","guid":{"rendered":"http:\/\/doanduyhai.wordpress.com\/?p=797"},"modified":"2012-03-11T18:47:53","modified_gmt":"2012-03-11T17:47:53","slug":"spring-mvc-part-i-request-handling","status":"publish","type":"post","link":"https:\/\/www.doanduyhai.com\/blog\/?p=797","title":{"rendered":"Spring MVC part I: Request Handling"},"content":{"rendered":"<p>Recently I changed my view technology from JSF to Spring MVC. I used the latest release (<strong>3.1.RELEASE<\/strong>) of the framework and there are some significant changes compared to the older 2.5.x versions<\/p>\n<p><!--more--><\/p>\n<p>The most important changes I believe were the introduction of the <strong>&lt;mvc&gt;<\/strong> namespace (supposedly to simplify developers&#8217; life) and the intensive use of annotations for request mappings.<\/p>\n<p>&nbsp;<\/p>\n<h1>I Configuration<\/h1>\n<p>Below is a simple XML configuration file <strong>my-spring-mvc.xml<\/strong> for the Spring MVC servlet:<\/p>\n<pre class=\"brush: xml; highlight: [9,13]; title: ; wrap-lines: false; notranslate\" title=\"\">\n\t&amp;lt;tx:annotation-driven \/&amp;gt;\n\t\n\t&amp;lt;context:component-scan base-package=&amp;quot;com.doan.onlinelibrary&amp;quot; \/&amp;gt;\n\t\n\t&amp;lt;mvc:resources location=&amp;quot;\/resources\/img\/&amp;quot; mapping=&amp;quot;\/resources\/img\/**&amp;quot; \/&amp;gt; \n\t&amp;lt;mvc:resources location=&amp;quot;\/resources\/css\/&amp;quot; mapping=&amp;quot;\/resources\/css\/**&amp;quot; \/&amp;gt;\n\t&amp;lt;mvc:resources location=&amp;quot;\/resources\/js\/&amp;quot; mapping=&amp;quot;\/resources\/js\/**&amp;quot; \/&amp;gt;\n\t\n\t&amp;lt;bean class=&amp;quot;org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping&amp;quot;\/&amp;gt;\n\t\n\t&amp;lt;bean id=&amp;quot;customJacksonViewAwareMessageConverter&amp;quot; class=&amp;quot;com.doan.onlinelibrary.json.converter.JacksonViewAwareHttpMessageConverter&amp;quot;\/&amp;gt;\n\t\n\t&amp;lt;bean class=&amp;quot;org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter&amp;quot; &amp;gt;\n\t\t &amp;lt;property name=&amp;quot;messageConverters&amp;quot;&amp;gt;\n            &amp;lt;list&amp;gt;\n            \t&amp;lt;ref bean=&amp;quot;customJacksonViewAwareMessageConverter&amp;quot;\/&amp;gt;\n            \t &amp;lt;bean class = &amp;quot;org.springframework.http.converter.StringHttpMessageConverter&amp;quot;&amp;gt;\n                \t&amp;lt;property name=&amp;quot;supportedMediaTypes&amp;quot; value = &amp;quot;text\/plain;charset=UTF-8&amp;quot; \/&amp;gt;\n            \t&amp;lt;\/bean&amp;gt;\n\t\t\t&amp;lt;\/list&amp;gt;\n\t\t &amp;lt;\/property&amp;gt;\n\t&amp;lt;\/bean&amp;gt;\n\n\t...\n\t...\n\t&amp;lt;!-- Config for messages, templates &amp;amp; views --&amp;gt;\n<\/pre>\n<p>I skipped on purpose the configuration for message bundles, template &amp; view resolvers because they are out of the scope of this post.<\/p>\n<p>Please notice at <strong>lines 9 &amp; 13<\/strong> the declaration of the <strong>DefaultAnnotationHandlerMapping<\/strong> and <strong>AnnotationMethodHandlerAdapter<\/strong> beans. Both of them are playing key roles for request mapping in Spring MVC.<\/p>\n<p>This configuration file for Spring MVC should be declared in the <strong>web.xml<\/strong> as a servlet:<\/p>\n<pre class=\"brush: xml; highlight: [9,13]; title: ; wrap-lines: false; notranslate\" title=\"\">\n\t&amp;lt;servlet&amp;gt;\n\t\t&amp;lt;servlet-name&amp;gt;my-spring-mvc&amp;lt;\/servlet-name&amp;gt;\n\t\t&amp;lt;servlet-class&amp;gt;org.springframework.web.servlet.DispatcherServlet &amp;lt;\/servlet-class&amp;gt;\n\t\t&amp;lt;init-param&amp;gt;\n\t\t\t&amp;lt;param-name&amp;gt;contextConfigLocation&amp;lt;\/param-name&amp;gt;\n\t\t\t&amp;lt;param-value&amp;gt;\n\t\t\t\t\/WEB-INF\/classes\/my-spring-mvc.xml\n\t\t\t&amp;lt;\/param-value&amp;gt;\n\t\t&amp;lt;\/init-param&amp;gt;\t\n\t\t&amp;lt;load-on-startup&amp;gt;1&amp;lt;\/load-on-startup&amp;gt;\n\t&amp;lt;\/servlet&amp;gt;\n\n\t&amp;lt;servlet-mapping&amp;gt;\n\t \t&amp;lt;servlet-name&amp;gt;my-spring-mvc&amp;lt;\/servlet-name&amp;gt;\n\t\t&amp;lt;url-pattern&amp;gt;\/&amp;lt;\/url-pattern&amp;gt;\n\t&amp;lt;\/servlet-mapping&amp;gt;\t\n<\/pre>\n<h1>II Request handlers listing<\/h1>\n<p>The <strong>DefaultAnnotationHandlerMapping<\/strong> extends the <strong>AbstractDetectingUrlHandlerMapping<\/strong> class which has a method of interest: <em>detectHandlers()<\/em><\/p>\n<pre class=\"brush: java; highlight: [3,4,8]; title: ; wrap-lines: false; notranslate\" title=\"\">\nprotected void detectHandlers() throws BeansException {\n\t...\n\tString[] beanNames = (this.detectHandlersInAncestorContexts ?\n\t\tBeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :\tgetApplicationContext().getBeanNamesForType(Object.class));\n\n\t\/\/ Take any bean name that we can determine URLs for.\n\tfor (String beanName : beanNames) {\n\t\tString[] urls = determineUrlsForHandler(beanName);\n\t\tif (!ObjectUtils.isEmpty(urls)) {\n\t\t\t\/\/ URL paths found: Let's consider it a handler.\n\t\t\tregisterHandler(urls, beanName);\n\t\t}\n\t\t...\n\t\t...\n\t}\n<\/pre>\n<p>At <strong>lines 3 &amp; 4<\/strong>, it simply list all beans within the current application context (<strong>the one for the Spring MVC servlet<\/strong>). Optionally it will also detect all beans in parent application context if the flag <strong>detectHandlersInAncestorContexts<\/strong> was set to true.<\/p>\n<p>At l<strong>ine 8<\/strong>, the <em>determineUrlsForHandler()<\/em> method is called. All the job is done there.<\/p>\n<pre class=\"brush: java; highlight: [3,7,9,12,23,24,36,41]; title: ; wrap-lines: false; notranslate\" title=\"\">\nprotected String[] determineUrlsForHandler(String beanName) {\n\tApplicationContext context = getApplicationContext();\n\tClass&amp;lt;?&amp;gt; handlerType = context.getType(beanName);\n\tRequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);\n\tif (mapping != null) {\n\t\t\/\/ @RequestMapping found at type level\n\t\tthis.cachedMappings.put(handlerType, mapping);\n\t\tSet&amp;lt;String&amp;gt; urls = new LinkedHashSet&amp;lt;String&amp;gt;();\n\t\tString[] typeLevelPatterns = mapping.value();\n\t\tif (typeLevelPatterns.length &amp;gt; 0) {\n\t\t\t\/\/ @RequestMapping specifies paths at type level\n\t\t\tString[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType, true);\n\t\t\tfor (String typeLevelPattern : typeLevelPatterns) {\n\t\t\t\tif (!typeLevelPattern.startsWith(&amp;quot;\/&amp;quot;)) {\n\t\t\t\t\ttypeLevelPattern = &amp;quot;\/&amp;quot; + typeLevelPattern;\n\t\t\t\t}\n\t\t\t\tboolean hasEmptyMethodLevelMappings = false;\n\t\t\t\tfor (String methodLevelPattern : methodLevelPatterns) {\n\t\t\t\t\tif (methodLevelPattern == null) {\n\t\t\t\t\t\thasEmptyMethodLevelMappings = true;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tString combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);\n\t\t\t\t\t\taddUrlsForPath(urls, combinedPattern);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (hasEmptyMethodLevelMappings ||\n\t\t\t\t\t\torg.springframework.web.servlet.mvc.Controller.class.isAssignableFrom(handlerType)) {\n\t\t\t\t\taddUrlsForPath(urls, typeLevelPattern);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn StringUtils.toStringArray(urls);\n\t\t}\n\t\telse {\n\t\t\t\/\/ actual paths specified by @RequestMapping at method level\n\t\t\treturn determineUrlsForHandlerMethods(handlerType, false);\n\t\t}\n\t}\n\telse if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {\n\t\t\/\/ @RequestMapping to be introspected at method level\n\t\treturn determineUrlsForHandlerMethods(handlerType, false);\n\t}\n\telse {\n\t\treturn null;\n\t}\n}\n<\/pre>\n<p><strong>Line 3<\/strong>, we check whether the current class has the <strong>@RequestMapping<\/strong> annotation at <strong>class level<\/strong><\/p>\n<p>If so, the class is put into a mapping cache at <strong>line 7<\/strong><\/p>\n<p><strong>Line 9<\/strong>, we extract all the URL paths defined in the <strong>@RequestMapping<\/strong> annotation (for example<em> \/pages\/Admin<\/em>)<\/p>\n<p>At line 12, we list all URL mappings at method level. If we had a mapping like:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\n@Controller\n@RequestMapping(&amp;quot;\/pages\/Books&amp;quot;)\npublic class BooksController\n{\n\t@RequestMapping(value = &amp;quot;search&amp;quot;, method = RequestMethod.GET)\n\tpublic String setupForm(Model model) {...}\n\n\t@RequestMapping(value = &amp;quot;search&amp;quot;, method = RequestMethod.POST)\n\tpublic String doSearch(Model model) {...}\n\n\t@RequestMapping(value = &amp;quot;clearSearch&amp;quot;, method = RequestMethod.GET)\n\tpublic String clear(Model model) {...}\n}\n<\/pre>\n<p>it will result in <em>methodLevelPatterns<\/em> = <strong>{&#8220;search&#8221;,&#8221;clearSearch&#8221;}<\/strong><\/p>\n<p>At <strong>line 23<\/strong>, all method level URLs are combined with class level URL pattern so we get <em>combinedPattern<\/em> = <strong>{&#8220;\/pages\/Books\/search&#8221;,&#8221;\/pages\/Books\/clearSearch&#8221;}<\/strong><\/p>\n<p>Then, at <strong>line 24<\/strong>, these URLs are added in an URL set <em>urls<\/em><\/p>\n<p><strong>Lines 36 &amp; 41<\/strong> correspond to the case where URLs are defined <strong>directly at method level<\/strong> and not class level.<br \/>\n&nbsp;<\/p>\n<h1>III Requests handling<\/h1>\n<p>Now that the URL mapping is established, let&#8217;s focus on the <strong>AnnotationMethodHandlerAdapter<\/strong>.<\/p>\n<p>The main entry for request handling starts with the method <em>handle(HttpServletRequest request, HttpServletResponse response, Object handler)<\/em><\/p>\n<pre class=\"brush: java; highlight: [8,12,17,21,26,31]; title: ; wrap-lines: false; notranslate\" title=\"\">\npublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) \nthrows Exception \n{\n\tClass&amp;lt;?&amp;gt; clazz = ClassUtils.getUserClass(handler);\n\tBoolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);\n\tif (annotatedWithSessionAttributes == null) {\n\t\tannotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);\n\t\tthis.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);\n\t}\n\tif (annotatedWithSessionAttributes) {\n\t\t\/\/ Always prevent caching in case of session attribute management.\n\t\tcheckAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);\n\t\t\/\/ Prepare cached set of session attributes names.\n\t}\n\telse {\n\t\t\/\/ Uses configured default cacheSeconds setting.\n\t\tcheckAndPrepare(request, response, true);\n\t}\n\n\t\/\/ Execute invokeHandlerMethod in synchronized block if required.\n\tif (this.synchronizeOnSession) {\n\t\tHttpSession session = request.getSession(false);\n\t\tif (session != null) {\n\t\t\tObject mutex = WebUtils.getSessionMutex(session);\n\t\t\tsynchronized (mutex) {\n\t\t\t\treturn invokeHandlerMethod(request, response, handler);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn invokeHandlerMethod(request, response, handler);\n}\n<\/pre>\n<p>At <strong>line 8<\/strong>, the handler class is put in a cache if it is annotated with <strong>@SessionAttribute<\/strong>.<br \/>\nAt <strong>lines 12 &amp; 17<\/strong>, the request is checked against supported HTTP methods (GET, POST, PUT, &#8230;). If the request HTTP method is not supported, a <strong>HttpRequestMethodNotSupportedException<\/strong> will be raised.<br \/>\nAt <strong>line 21<\/strong>, if the &#8220;<strong>synchronizeOnSession<\/strong>&#8221; property was set to <strong>true<\/strong> for the handler, the processing will be synchonized against a session mutex<\/p>\n<p>In all cases, the real processing is delegated to the method <em>invokeHandlerMethod()<\/em><\/p>\n<pre class=\"brush: java; highlight: [7,9,10,11]; title: ; wrap-lines: false; notranslate\" title=\"\">\nprotected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)\tthrows Exception {\n\n\tServletHandlerMethodResolver methodResolver = getMethodResolver(handler);\n\tMethod handlerMethod = methodResolver.resolveHandlerMethod(request);\n\tServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);\n\tServletWebRequest webRequest = new ServletWebRequest(request, response);\n\tExtendedModelMap implicitModel = new BindingAwareModelMap();\n\n\tObject result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);\n\tModelAndView mav =methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);\n\tmethodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);\n\treturn mav;\n}\n<\/pre>\n<p> At <strong>line 7<\/strong>, a <strong>BindingAwareModelMap<\/strong> is created. This is simply a plain HashMap object, acting as an implicit Model object that will be injected to the request handler.<\/p>\n<p>At this point, it is usefull to remind our readers that Spring MVC offers a quite large list of method argument types for all request handler methods annotated with <strong>@RequestMapping<\/strong>: <a href=\"http:\/\/static.springsource.org\/spring\/docs\/current\/spring-framework-reference\/html\/mvc.html#mvc-ann-arguments\" title=\"http:\/\/static.springsource.org\/spring\/docs\/current\/spring-framework-reference\/html\/mvc.html#mvc-ann-arguments\" target=\"_blank\">supported argument types<\/a>. The argument matching and mapping is performed by the method <em>invokeHandlerMethod()<\/em> at <strong>line 9<\/strong><\/p>\n<p> At <strong>line 10<\/strong>, the Model and View lookup is done by <em>getModelAndView()<\/em> and finally Spring binds the model values to the view in the <em>updateModelAttributes()<\/em> method (<strong>line 11<\/strong>).<br \/>\n&nbsp;<\/p>\n<p> To be continued &#8230;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently I changed my view technology from JSF to Spring MVC. I used the latest release (3.1.RELEASE) of the framework and there are some significant changes compared to the older 2.5.x versions<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[31,19],"tags":[],"_links":{"self":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/797"}],"collection":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=797"}],"version-history":[{"count":0,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/797\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=797"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=797"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=797"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}