一、相关核心注解
1、@RestController
-
组合注解:
= @Controller + @ResponseBody -
作用:将类标记为控制器,且所有方法的返回值直接写入 HTTP 响应体(非视图解析)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @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 (or empty String otherwise)
* @since 4.0.1
*/
@AliasFor(annotation = Controller.class)
String value() default "";
}
其实Controller 也是Component 的一中.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
/**
* Alias for {@link Component#value}.
*/
@AliasFor(annotation = Component.class)
String value() default "";
}
2、 @GetMapping
-
组合注解:
= @RequestMapping(method = RequestMethod.GET) -
作用:将 HTTP GET 请求映射到特定方法。
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {
}
二、相关图解
1、流程图解

2、代码解析
2.1 DispatcherServlet(总控制器)
处理所有的请求入口:
public class DispatcherServlet extends FrameworkServlet {
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.
// 1. 通过HandlerMapping查找处理器
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 2. 获取HandlerAdapter (适配器模式)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 3. 执行处理器方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new ServletException("Handler dispatch failed: " + err, err);
}
// 4. 处理结果(渲染视图或写入响应)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new ServletException("Handler processing failed: " + err, err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
asyncManager.setMultipartRequestParsed(multipartRequestParsed);
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed || asyncManager.isMultipartRequestParsed()) {
cleanupMultipart(processedRequest);
}
}
}
}
}
2.2 RequestMappingHandlerMapping
扫描 @RestController 和 @RequestMapping
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware {
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
RequestMappingInfo.Builder builder = RequestMappingInfo
// 解析@RequestMapping注解生成路由元数据
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name());
if (customCondition != null) {
builder.customCondition(customCondition);
}
return builder.options(this.config).build();
}
}
2.3 RequestMappingHandlerAdapter
调用控制器方法
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
// Obtain wrapped response to enforce lifecycle rule from Servlet spec, section 2.3.3.4
response = asyncWebRequest.getNativeResponse(HttpServletResponse.class);
ServletWebRequest webRequest = (asyncWebRequest instanceof ServletWebRequest ?
(ServletWebRequest) asyncWebRequest : new ServletWebRequest(request, response));
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
invocableMethod.setMethodValidator(this.methodValidator);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
Object[] resultContext = asyncManager.getConcurrentResultContext();
Assert.state(resultContext != null && resultContext.length > 0, "Missing result context");
mavContainer = (ModelAndViewContainer) resultContext[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 通过反射执行目标方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 处理@ResponseBody返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
}
2.4 HttpMessageConverter
处理返回值转换(JSON 序列化)
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
implements HandlerMethodReturnValueHandler {
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
Object body;
Class<?> valueType;
Type targetType;
if (value instanceof CharSequence) {
body = value.toString();
valueType = String.class;
targetType = String.class;
}
else {
body = value;
valueType = getReturnValueType(body, returnType);
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
}
if (isResourceType(value, returnType)) {
outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
outputMessage.getServletResponse().getStatus() == 200) {
Resource resource = (Resource) value;
try {
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
body = HttpRange.toResourceRegions(httpRanges, resource);
valueType = body.getClass();
targetType = RESOURCE_REGION_LIST_TYPE;
}
catch (IllegalArgumentException ex) {
outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
}
}
}
MediaType selectedMediaType = null;
MediaType contentType = outputMessage.getHeaders().getContentType();
boolean isContentTypePreset = contentType != null && contentType.isConcrete();
if (isContentTypePreset) {
if (logger.isDebugEnabled()) {
logger.debug("Found 'Content-Type:" + contentType + "' in response");
}
selectedMediaType = contentType;
}
else {
HttpServletRequest request = inputMessage.getServletRequest();
List<MediaType> acceptableTypes;
try {
acceptableTypes = getAcceptableMediaTypes(request);
}
catch (HttpMediaTypeNotAcceptableException ex) {
int series = outputMessage.getServletResponse().getStatus() / 100;
if (body == null || series == 4 || series == 5) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring error response content (if any). " + ex);
}
return;
}
throw ex;
}
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
if (body != null && producibleTypes.isEmpty()) {
throw new HttpMessageNotWritableException(
"No converter found for return value of type: " + valueType);
}
List<MediaType> compatibleMediaTypes = new ArrayList<>();
determineCompatibleMediaTypes(acceptableTypes, producibleTypes, compatibleMediaTypes);
// For ProblemDetail, fall back on RFC 9457 format
if (compatibleMediaTypes.isEmpty() && ProblemDetail.class.isAssignableFrom(valueType)) {
determineCompatibleMediaTypes(this.problemMediaTypes, producibleTypes, compatibleMediaTypes);
}
if (compatibleMediaTypes.isEmpty()) {
if (logger.isDebugEnabled()) {
logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
}
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(producibleTypes);
}
return;
}
MimeTypeUtils.sortBySpecificity(compatibleMediaTypes);
for (MediaType mediaType : compatibleMediaTypes) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using '" + selectedMediaType + "', given " +
acceptableTypes + " and supported " + producibleTypes);
}
}
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
ResolvableType targetResolvableType = null;
// 选择匹配的Converter(如MappingJackson2HttpMessageConverter)
for (HttpMessageConverter converter : this.messageConverters) {
ConverterType converterTypeToUse = null;
if (converter instanceof GenericHttpMessageConverter genericConverter) {
if (genericConverter.canWrite(targetType, valueType, selectedMediaType)) {
converterTypeToUse = ConverterType.GENERIC;
}
}
else if (converter instanceof SmartHttpMessageConverter smartConverter) {
targetResolvableType = getNestedTypeIfNeeded(ResolvableType.forMethodParameter(returnType));
if (smartConverter.canWrite(targetResolvableType, valueType, selectedMediaType)) {
converterTypeToUse = ConverterType.SMART;
}
}
else if (converter.canWrite(valueType, selectedMediaType)){
converterTypeToUse = ConverterType.BASE;
}
if (converterTypeToUse != null) {
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(), inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
switch (converterTypeToUse) {
case BASE -> converter.write(body, selectedMediaType, outputMessage);
case GENERIC -> ((GenericHttpMessageConverter) converter).write(body, targetType, selectedMediaType, outputMessage);
case SMART -> ((SmartHttpMessageConverter) converter).write(body, targetResolvableType, selectedMediaType, outputMessage, null);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
return;
}
}
}
if (body != null) {
Set<MediaType> producibleMediaTypes =
(Set<MediaType>) inputMessage.getServletRequest()
.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {
throw new HttpMessageNotWritableException(
"No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
}
throw new HttpMediaTypeNotAcceptableException(getSupportedMediaTypes(body.getClass()));
}
}
}
三、实例代码分享
1. controller 部分
@RestController //组合注解:@Controller + @ResponseBody
public class HelloController {
@Autowired
private User user;
@Autowired
private Article article;
@Value("${dynamic.property:default}")
private String dynamicProperty;
@GetMapping(value = "/hello") // 路径match
public String hello() {
System.out.println(article);
System.out.println(user);
return String.format("1.0.0 config: %s", dynamicProperty) ;
}
}
2. Spring Boot 自动配置关键点
-
WebMvcAutoConfiguration
自动配置RequestMappingHandlerMapping、RequestMappingHandlerAdapter等组件。 -
JacksonAutoConfiguration
配置Jackson2ObjectMapperBuilder -
HttpMessageConvertersAutoConfiguration
自动配置 JSON 转换器
MappingJackson2HttpMessageConverter
WebMvcConfigurationSupport#addDefaultHttpMessageConverters
if (jackson2Present) {
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
// 默认转换器
messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
3、图解

四、总结思考
-
路由注册
Spring 启动时扫描@RestController,解析@GetMapping生成路由映射表。 -
请求分发
DispatcherServlet根据 URL 查找匹配的HandlerMethod。 -
方法执行
RequestMappingHandlerAdapter通过反射调用目标方法。 -
响应处理
返回值通过HttpMessageConverter序列化为 JSON 并写入响应
说明:
spingboot 3.5.0, springweb 6.2.7
2万+

被折叠的 条评论
为什么被折叠?



