Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the return value handling in the spring webmvc request processing process?

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/02 Report--

In this issue, the editor will bring you about the return value processing in the spring webmvc request processing process. The article is rich in content and analyzed and described from a professional point of view. I hope you can get something after reading this article.

Spring-webmvc request processing flow-return value processing

Explain the RequestMappingHandlerAdapter used after version 3.2.x, which replaces AnnotationMethodHandlerAdapter.

RequestMappingHandlerAdapter

Since version 3. 2, RequestMappingHandlerAdapter has been introduced to replace AnnotationMethodHandlerAdapter processing. Let's also analyze this thing here.

Because it is also a HandlerAdapter, the previous processing flow is the same, and the getHandlerAdapter of servlet returns RequestMappingHandlerAdapter instead of AnnotationMethodHandlerAdapter at this time.

After we get the HandlerAdapter, let's go straight to the ha.handle () method and start the analysis.

/ / RequestMAppingHandlerAdapter.javaprotected final ModelAndView handleInternal (HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {/ /... Do some checking return invokeHandleMethod (request, response, handlerMethod);} private ModelAndView invokeHandleMethod (HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest = new ServletWebRequest (request, response); WebDataBinderFactory binderFactory = getDataBinderFactory (handlerMethod); ModelFactory modelFactory = getModelFactory (handlerMethod, binderFactory); / / get the controller method ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod (handlerMethod, binderFactory) that we need to execute / / Container ModelAndViewContainer mavContainer = new ModelAndViewContainer () for later construction of mv; mavContainer.addAllAttributes (RequestContextUtils.getInputFlashMap (request)); modelFactory.initModel (webRequest, mavContainer, requestMappingMethod); mavContainer.setIgnoreDefaultModelOnRedirect (this.ignoreDefaultModelOnRedirect); / /. This paragraph is the method of handling / / calling controller for asynchronous requests and handling mavContainer requestMappingMethod.invokeAndHandle (webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted ()) {return null;} return getModelAndView (mavContainer, modelFactory, webRequest);} private ModelAndView getModelAndView (ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {modelFactory.updateModel (webRequest, mavContainer) / / judge: if the current request has been processed, null if (mavContainer.isRequestHandled ()) {return null;} / / if the request has not been processed, the page may need to be returned, start searching, processing and return mav ModelMap model = mavContainer.getModel (); ModelAndView mav = new ModelAndView (mavContainer.getViewName (), model) If (! mavContainer.isViewReference ()) {mav.setView ((View) mavContainer.getView ());} if (model instanceof RedirectAttributes) {Map flashAttributes = ((RedirectAttributes) model) .getFlashAttributes (); HttpServletRequest request = webRequest.getNativeRequest (HttpServletRequest.class); RequestContextUtils.getOutputFlashMap (request) .putAll (flashAttributes);} return mav;}

Go straight in. When you come to the handleInternal () method of RequestMappingHAndlerAdapter, don't panic, according to the usual style of spring, this TM is certainly not the core method, he just does some checks. The method finally calls invokeHandleMethod (). In this method, some required parameters are obtained, such as the requested controller layer method, parameters. Then call the ServletInvocableHandlerMethod object invokeAndHandle method.

Call the business method and process the return value / / ServletInvocableHandlerMethod.javapublic final void invokeAndHandle (ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object...) ProvidedArgs) throws Exception {/ / actually calls the controller method and gets the result Object returnValue = invokeForRequest (webRequest, mavContainer, providedArgs); / / sets the response status setResponseStatus (webRequest) / / if the return value is null, the request does not return any result. Setting mavContainer.setRequestHandled (true) / / to true indicates that the request has been processed and will not be processed later. The if (returnValue = = null) {if (isRequestNotModified (webRequest) | | hasResponseStatus () | | mavContainer.isRequestHandled ()) {mavContainer.setRequestHandled (true); return will be mentioned later. }} else if (StringUtils.hasText (this.responseReason)) {mavContainer.setRequestHandled (true); return;} / / is set to false, indicating that the current request has not yet been processed mavContainer.setRequestHandled (false); try {/ / calls all default and custom return value parsers to process the returned results this.returnValueHandlers.handleReturnValue (returnValue, getReturnValueType (returnValue), mavContainer, webRequest) } catch (Exception ex) {/ /...} throw ex;}}

As you can see from the comments, first call the business logic to get the return result, and then make some judgment on the return value and simply deal with it. The returned results are further processed through the returnValueHandlers object. This is an object of type HandlerMethodReturnValueHandlerComposite. Continue to follow.

Public void handleReturnValue (Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {/ / gets the handler that can process the return value. Of course, by traversing, you can see the method HandlerMethodReturnValueHandler handler = getReturnValueHandler (returnType); / / if there is no suitable return value processor, an error Assert.notNull will be reported (handler, "Unknown return value type [" + returnType.getParameterType (). GetName () + "]") / / use the return value processor to process the returned result handler.handleReturnValue (returnValue, returnType, mavContainer, webRequest);} private HandlerMethodReturnValueHandler getReturnValueHandler (MethodParameter returnType) {/ / traverse all handler, including custom for (HandlerMethodReturnValueHandler returnValueHandler: returnValueHandlers) {/ /. If (returnValueHandler.supportsReturnType (returnType)) {/ / if the handler can handle the current return value, it returns the handler return returnValueHandler;}} return null;}

Here, take a look at all the default registered processors. See if RequestResponseBodyMethodProcessor is very kind, by the way, he is dealing with @ ResponseBody annotations. If you like it, you can see the supportsReturnType () method of this class.

Instead of following here, the familiar RequestResponseBodyMethodProcessor processor will set mavContainer.setRequestHandled (true) when processing the result; it means that the processing is finished.

GetModelAndView ()

When you're done, go back to the invokeHandleMethod () method of RequestMappingHandlerAdapter. This method finally returns getModelAndView ()

From this method, we can see that if the current request has been processed (the value of mavContainer.isRequestHandled () is true), then null is not returned without further processing, otherwise spring will continue to process the current request and try to return a ModelAndView.

Custom HandlerMethodReturnValueHandler

Now that the custom return value handler is mentioned above, where is this customization? Where is the return value processor registered? I'll be right back!

Custom return value handler

The return value processor needs to implement HandlerMethodReturnValueHandler

Public class MyCustomReturnValueHandler implements HandlerMethodReturnValueHandler {@ Override public boolean supportsReturnType (MethodParameter returnType) {/ / determine whether the method contains a custom annotation MyResonse or the returned result is a specified type of return returnType.getMethodAnnotation (MyResponse.class)! = null | | ResponseResult.class.isAssignableFrom (returnType.getParameterType ()) } @ Override public void handleReturnValue (Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {/ / indicates that the request has been processed, and spring will no longer process mavContainer.setRequestHandled (true); HttpServletResponse response = webRequest.getNativeResponse (HttpServletResponse.class); response.setCharacterEncoding ("UTF-8"); response.setContentType ("application/json;charset=UTF-8") Response.getWriter () .println (JSON.toJSONString (returnValue));}

Controller, my annotations and return value types here all meet the requirements of the above processor, in fact, only one is fine.

@ RequestMapping ("/ testValueHandler") @ MyResponsepublic ResponseResult testValueHandler () {return new ResponseResult (0, "success");}

Springmvc.xml

Customize the initialization process of the return result processor

You can see from the xml that it is the custom label mvc:return-value-handlers, which tracks MvcNamespaceHandler.

/ / MvcNamespaceHandler.javapublic void init () {/ / parse the parser registerBeanDefinitionParser of the custom tag ("annotation-driven", new AnnotationDrivenBeanDefinitionParser ());}

Enter the parse method of AnnotationDrivenBeanDefinitionParser, and the custom one is handled here. Let's take a look.

/ / AnnotationDrivenBeanDefinitionParserpublic BeanDefinition parse (Element element, ParserContext parserContext) {/ /... / get the custom return value processor ManagedList returnValueHandlers = getReturnValueHandlers (element, parserContext); / /... / define RequestMappingAdapterHandler RootBeanDefinition handlerAdapterDef = new RootBeanDefinition (RequestMappingHandlerAdapter.class); / /. If (returnValueHandlers! = null) {/ / sets the property of the custom return value handler customReturnValueHandlers handlerAdapterDef.getPropertyValues () .add ("customReturnValueHandlers", returnValueHandlers);} / /... / / registers the defined RequestMappingAdapterHandler as the bean parserContext.registerComponent of spring (new BeanComponentDefinition (handlerAdapterDef, handlerAdapterName)) / /...} / / parse the custom tag return-value-handlersprivate ManagedList getReturnValueHandlers (Element element, ParserContext parserContext) {Element handlersElement = DomUtils.getChildElementByTagName (element, "return-value-handlers"); if (handlersElement! = null) {return extractBeanSubElements (handlersElement, parserContext);} return null;}

Just look at the notes and don't explain. At this point, the custom return value processor has been registered, and it is already a bean of spring. Now let's take a look at RequestMappingHandlerAdapter.

This guy implements InitializingBean. Let's take a look at the afterPropertiesSet method.

Public void afterPropertiesSet () {/ /...} if (this.returnValueHandlers = = null) {/ / gets all the return value processors, although the method name is getDefaultReturnVanlueHandler, which actually includes a custom processor List handlers = getDefaultReturnValueHandlers (); this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite (). AddHandlers (handlers);} initControllerAdviceCache ();} private List getDefaultReturnValueHandlers () {List handlers = new ArrayList () / / default return value processor / / Single-purpose return value types handlers.add (new ModelAndViewMethodReturnValueHandler ()); handlers.add (new ModelMethodProcessor ()); handlers.add (new ViewMethodReturnValueHandler ()); handlers.add (new HttpEntityMethodProcessor (getMessageConverters (), this.contentNegotiationManager)); handlers.add (new CallableMethodReturnValueHandler ()); handlers.add (new DeferredResultMethodReturnValueHandler ()); handlers.add (new AsyncTaskMethodReturnValueHandler (this.beanFactory)) / / Annotation-based return value types handlers.add (new ModelAttributeMethodProcessor (false)); handlers.add (new RequestResponseBodyMethodProcessor (getMessageConverters (), this.contentNegotiationManager)); / / Multi-purpose return value types handlers.add (new ViewNameMethodReturnValueHandler ()); handlers.add (new MapMethodProcessor ()); / / Custom return value processors, which are the if (getCustomReturnValueHandlers ()! = null) {handlers.addAll (getCustomReturnValueHandlers ()) processed by parse before. } / / add other default return value processor / / Catch-all if (! CollectionUtils.isEmpty (getModelAndViewResolvers () {handlers.add (new ModelAndViewResolverMethodReturnValueHandler (getModelAndViewResolvers ();} else {handlers.add (new ModelAttributeMethodProcessor (true));} return handlers;}

As can be seen from above, although our custom return value processor has been put into it, will we definitely use it? Not necessarily. You can see that all the return value processors are not sorted, so we can't control this order. First, give a debug diagram:

As we can see from the figure, our custom handler is indeed loaded, but since spring does not sort this handlers, our customization is placed behind, and we cannot control this order (other coquettish methods can be implemented, which is not considered here). So there is a situation where our return value can be processed by one of the previous 11 processors, so it's not our custom return value processor. To take a simple example, for example, if your return type is String, it will not be processed by ViewNameMethodReturnValueHandler; if the returned type is Map, it will be processed by MapMethodProcessor and can be tested by yourself. So our return value is actually very important. So to use our custom processor, we first have to make it impossible for the previous processor to process our return results.

It is usually possible to implement a generic return result entity, or a tag interface (empty interface), so that other return value processors cannot handle the returned value type, so it is our turn to return value handlers. You can also return your favorite entity type, or you can use annotations as in the example. However, because the return values of a project are usually defined, and the return types are all in the same format, such comparisons tend to use the same return entity, or use an interface tag.

Over...

This is what the return value processing is in the spring webmvc request processing process shared by Xiaobian. If you happen to have similar doubts, please refer to the above analysis to understand. If you want to know more about it, you are welcome to follow the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report