Concern public number: IT elder brother, read a dry goods technical article every day, a year later you will find a different self.
@restController and @Controller annotations
RestController, by default, only provides restful interface return values. For controllers that do not need to return pages, RestController annotations are used.
The @restController source code is as follows.
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Controller@ResponseBodypublic @interface RestController { /** * The value may indicate a suggestion for a logical component name, * to be turned into a Spring bean in case of an autodetected component. * @return the suggested component name, If any * @since 4.0.1 */ String value() default ""; }Copy the code
The way @RestController is written depends on annotation combinations. @RestController is annotated by @Controller and @responseBody to indicate that @RestController has annotation semantics from both, So @RestController has one more @responseBody semantics for annotation processing than @Controller, that’s the difference between @RestController and @Controller, That’s why the return values of @restController are all converted JSON.
So the summary is: @restController = @Controller + @responseBody;
@responseBody Handling of annotations
Now that we know that @restController differs from @Controller by adding @responseBody semantics, let’s take a look at how @responseBody is handled.
First, you know that @responseBody is an annotation that handles the return value of a method. If you are familiar with Spring MVC processing, you know that after obtaining the HandlerMethod according to the requestURL mapping, the object that dispatches the request method according to the HandlerMethod is the HandlerAdapter, and the method call ends. The schedule object for return value processing is also a HandlerAdapter. Therefore, handling of the @responseBody annotation should also be done in the HandlerAdapter.
In RequestMappingHandlerAdapter# invokeHandlerMethod method, there are several of the more important code
/ / create method invocation object ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod (handlerMethod); / /... / / set the return value processor invocableMethod. SetHandlerMethodReturnValueHandlers (enclosing returnValueHandlers); / /... / / call the method invocableMethod. InvokeAndHandle (webRequest, mavContainer);Copy the code
ReturnValueHandlers is a variable that sounds like returnValueHandlers, but is actually a collection of handlers that handle return values. The process is complete by first creating an object that calls a method, then injecting the handler, and finally calling the method.
We can also analyze if we have a handler initialized for the @responseBody annotation.
ResponseBody Annotation handler initialization
A search for the returnValueHandlers initialization shows this call chain:
-
RequestMappingHandlerAdapter#afterPropertiesSet
-
handlers = RequestMappingHandlerAdapter#getDefaultReturnValueHandlers
returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers)
Copy the code
So it is in RequestMappingHandlerAdapter bean initialization is complete, will return the processor’s initialization, In RequestMappingHandlerAdapter# getDefaultReturnValueHandlers within a method implementation, the code is as follows.
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() { List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>(); // Single-purpose return value types handlers.add(new ModelAndViewMethodReturnValueHandler()); handlers.add(new ModelMethodProcessor()); handlers.add(new ViewMethodReturnValueHandler()); handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters())); handlers.add(new StreamingResponseBodyReturnValueHandler()); handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice)); handlers.add(new HttpHeadersReturnValueHandler()); handlers.add(new CallableMethodReturnValueHandler()); handlers.add(new DeferredResultMethodReturnValueHandler()); handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory)); // Annotation-based return value types handlers.add(new ModelAttributeMethodProcessor(false)); / / @ ResponseBody annotation processor handlers. Add (new RequestResponseBodyMethodProcessor (getMessageConverters (), this.contentNegotiationManager, this.requestResponseBodyAdvice)); // Multi-purpose return value types handlers.add(new ViewNameMethodReturnValueHandler()); handlers.add(new MapMethodProcessor()); // Custom return value types if (getCustomReturnValueHandlers() ! = null) { handlers.addAll(getCustomReturnValueHandlers()); } // Catch-all if (! CollectionUtils.isEmpty(getModelAndViewResolvers())) { handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers())); } else { handlers.add(new ModelAttributeMethodProcessor(true)); } return handlers; }Copy the code
Can see very processor, RequestResponseBodyMethodProcessor is @ ResponseBody processor.
ResponseBody Annotation handler call
Into the method call invocableMethod. InvokeAndHandle (webRequest, mavContainer)/ServletInvocableHandlerMethod# invokeAndHandle, continue to make calls, Trace the call chain as follows.
-
ServletInvocableHandlerMethod#invokeAndHandle
-
this.returnValueHandlers.handleReturnValue/HandlerMethodReturnValueHandlerComposite#handleReturnValue
HandlerMethodReturnValueHandlerComposite# handleReturnValue code as shown below.
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws the Exception {/ / select a suitable processor HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null) { throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName()); } / / process the return value handler. HandleReturnValue (returnValue returnType, mavContainer, webRequest); }Copy the code
So, it basically picks the target processor out of all the processors and processes the return value. Enter the HandlerMethodReturnValueHandlerComposite# selectHandler
private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) { boolean isAsyncValue = isAsyncReturnValue(value, returnType); for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { if (isAsyncValue && ! (handler instanceof AsyncHandlerMethodReturnValueHandler)) { continue; } / / whether the processor support the if (handler. SupportsReturnType (returnType)) {return handler. } } return null; }Copy the code
RequestResponseBodyMethodProcessor# supportsReturnType, the code is as follows.
public boolean supportsReturnType(MethodParameter returnType) { return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class)); }Copy the code
So obviously if you have @responseBody on your class or if you have a method on your class, it fits the handler, and @RestController has @responseBody semantics that fits, So for RequestResponseBodyMethodProcessor# handleReturnValue return value processing.
Concern public number: IT elder brother, read a dry goods technical article every day, a year later you will find a different self.