SpringBoot之@Controller和@RequestMapping的实现原理解读

 更新时间:2025年04月19日 09:26:58   作者:西魏陶渊明  
这篇文章主要介绍了SpringBoot之@Controller和@RequestMapping的实现原理,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

SpringBoot之@Controller和@RequestMapping的实现原理

干货分享,SpringBoot中Web接口资源是如何被管理起来呢?

一个请求,通过在浏览器上输入了一个url,是如何被SpringWeb匹配到对应的方法的呢?

带着这个疑问我们来学习本篇。

浏览器的请求,是如何被映射到后端服务的方法上呢?

后端服务使用SpringBoot只使用了一个注解就提供了web服务的实现原理是什么呢?

@RestController
public class TestController {

    @GetMapping("/name")
    public String name(HttpServletRequest request){
        return request.toString();
    }
}

带着上面的疑问,小编通过源码的方式带你一看究竟吧。 为了能让各位童鞋更好的更容易的理解。第一趴我们先来补充点知识点。

一、注解派生概念

在java体系中,类是可以被继承,接口可以被实现。但是注解没有这些概念,而是有一个派生的概念。举例,注解A。被标记了在注解B头上,那么我们可以说注解B就是注解A的派生。

下面我们举一个例子:

@RestController
public class PostController {

    @ApiOperation(value = "查询Bbs所有文章")
    @PostMapping(value = "/query/bbs/posts", produces = MediaType.APPLICATION_JSON_VALUE)
    public Result<PostAllResponse> queryBbsPostAll(@RequestBody PostAllSelectRequest postAllSelectRequest) {
        return postBiz.queryBbsPostAll(postAllSelectRequest);
    }

    public static void main(String[] args) {
        Method queryBbsPostAll = ClassUtils.getMethod(PostController.class, "queryBbsPostAll",PostAllSelectRequest.class);
        PostMapping annotation = AnnotationUtils.findAnnotation(queryBbsPostAll, PostMapping.class);
        ///query/bbs/posts
        System.out.println(StringUtils.arrayToCommaDelimitedString(annotation.value()));
        //application/json
        System.out.println(StringUtils.arrayToCommaDelimitedString(annotation.produces()));
        //是否包含RequestMapping: true
        System.out.println("是否包含RequestMapping: "+AnnotatedElementUtils.hasAnnotation(queryBbsPostAll,RequestMapping.class));

        RequestMapping mergedAnnotation = AnnotatedElementUtils.getMergedAnnotation(queryBbsPostAll, RequestMapping.class);
        ///query/bbs/posts
        System.out.println(StringUtils.arrayToCommaDelimitedString(mergedAnnotation.value()));
    }
}
  • queryBbsPostAll是用了PostMapping类进行标记是一个POST资源
  • 我们通过main方法里面可以知道。通过反射我们能拿到Method上的PostMapping注解信息。
  • 但是看这一行AnnotatedElementUtils.hasAnnotation(queryBbsPostAll,RequestMapping.class)为什么也是true呢?

没错因为PostMapping是RequestMapping的派生注解。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.POST)
public @interface PostMapping {
   ...
}

请记住这个小的知识点,后面的逻辑会用到。因为下面后有大量的源码,为了方便标注,小编使用截图的形式,在截图上会加上注释信息。 ​

二、进入正题,跟进源码解析请求Method

通过跟进源码,我们会发现这样一个类。AbstractHandlerMethodMapping。其实现了实现 InitializingBean接口。在当前 Bean初始化时候会执行afterPropertiesSet -> initHandlerMethods。

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
        initHandlerMethods();
    }
    
    /**
  * Scan beans in the ApplicationContext, detect and register handler methods.
  * @see #getCandidateBeanNames()
  * @see #processCandidateBean
  * @see #handlerMethodsInitialized
  */
    protected void initHandlerMethods() {
        for (String beanName : getCandidateBeanNames()) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                processCandidateBean(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }
}

RequestMappingHandlerMapping解析Method上的RequestMapping信息

isHandler 方法判断是否是web资源类。当一个类被标记了 @Controller 或者@RequestMapping。 注意 @RestController 是@Controller的派生类。所以这里只用判断 @Controller 或者@RequestMapping就行了。

 @Override
 protected boolean isHandler(Class<?> beanType) {
  return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
    AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
 }

detectHandlerMethods方法就是真正开始解析Method的逻辑。通过解析Method上的 @RequestMapping或者其他派生的注解。生成请求信息。 注意这个请求信息里面也是有很多逻辑的不过不是本篇讨论的重点,就不说了。稍微提一下。根据规则来匹配url逻辑就在这里面。

这里我们能看到源码里拿到了Method并拿到了执行这个Method的实例Bean。在这里封装成了HandlerMethod并注册到了MappingRegistry中。

在注册的过程中把RequestMapping中的路径信息同事也放到一个urlLookup中。key是url,value是Mapping信息。

到这里其实我们就把本篇的议题就说明清楚了。下面我们在看下SpringWeb是如何将http请求信息路由到具体的HandlerMethod的吧。

三、最后串一下请求流程

看了前面的截图,我们知道Spring是如何把这些Web资源信息给保存起来的了。然后就看是DispatcherServlet的逻辑了。 首先DispatcherServlet 是一个Servlet。Servlet相信大家都都知道就不重点说原理。 我们直接看doService -> doDispatch 方法

根据请求路径,找到从Mapping信息,然后根据Mapping信息匹配到具体的HandlerMethod。 ok本篇内容就到这里。谢谢大家。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 一文带你了解如何正确使用Java中的字符串常量池

    一文带你了解如何正确使用Java中的字符串常量池

    研究表明,Java堆中对象占据最大比重的就是字符串对象,所以弄清楚字符串知识对学习Java很重要。本文主要重点聊聊字符串常量池,希望对大家有所帮助
    2022-12-12
  • Java拆分Word文档的两种实用方案详解

    Java拆分Word文档的两种实用方案详解

    在日常开发中,我们经常会遇到需要处理大型 Word 文档的场景,本文介绍一种基于 Java 的自动化处理方式,利用一个基于 Java 的 Word 文档处理库,通过两种不同的策略来拆分 Word 文档,感兴趣的小伙伴可以了解下
    2026-05-05
  • Maven dependency中的scope案例讲解

    Maven dependency中的scope案例讲解

    Maven的一个哲学是惯例优于配置(Convention Over Configuration), Maven默认的依赖配置项中,scope的默认值是compile,本文给大家介绍Maven dependency中的scope案例讲解,感兴趣的朋友跟随小编一起看看吧
    2024-02-02
  • java Runnable接口创建线程

    java Runnable接口创建线程

    这篇文章主要介绍了java Runnable接口创建线程的相关资料,需要的朋友可以参考下
    2017-07-07
  • Java中关键字synchronized的使用方法详解

    Java中关键字synchronized的使用方法详解

    synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块,下面这篇文章主要给大家介绍了关于Java中synchronized使用的相关资料,需要的朋友可以参考下
    2021-08-08
  • Mybatis中SqlMapper配置的扩展与应用详细介绍(1)

    Mybatis中SqlMapper配置的扩展与应用详细介绍(1)

    这篇文章主要介绍了Mybatis中SqlMapper配置的扩展与应用(1)的相关资料,非常不错具有参考借鉴价值,需要的朋友可以参考下
    2016-11-11
  • SpringBoot如何实现并发任务并返回结果

    SpringBoot如何实现并发任务并返回结果

    这篇文章主要介绍了SpringBoot如何实现并发任务并返回结果问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • SpringBoot 优雅停机实现原理与最佳实践指南

    SpringBoot 优雅停机实现原理与最佳实践指南

    文章介绍了Spring Boot在2.3版本中引入的优雅停机机制,详细解释了优雅停机的概念、核心组件与层级关系、Spring Boot 2.3前后的区别,以及在生产环境中的最佳实践,强调了启用优雅停机的重要性,以提高系统的高可用性和稳定性
    2026-04-04
  • 浅谈Java泛型通配符解决了泛型的许多诟病(如不能重载)

    浅谈Java泛型通配符解决了泛型的许多诟病(如不能重载)

    下面小编就为大家带来一篇浅谈Java泛型通配符解决了泛型的许多诟病(如不能重载)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-10-10
  • Java中怎样处理空指针异常

    Java中怎样处理空指针异常

    程序中的变量是 null,就意味着它没有引用指向或者说没有指针。这时,我们对这个变量进行任何操作,都必然会引发空指针异常,本文主要介绍了Java中怎样处理空指针异常,感兴趣的可以了解一下
    2022-05-05

最新评论