前言
在我们使用 SpringBoot
开发中,我们定义一接口是下面这样的
1 2 3 4 5 6 7 8
| @RestController public class UserController{ @GetMapping( name = "/getUserName") public String getUserName(){ return "Hello World"; } }
|
这时候我们思考一个问题,我们在浏览器上只输入了一个URL地址,怎么就能访问到这个接口的呢?于是乎就引出了
今天我们要讨论的话题。Spring中的Web接口资源是如何保存起来的?
一、Spring中的Web接口资源是如何保存起来的?
在我们学习之前我们可以先自己来进行思考一下。处理逻辑是什么样的?
- Spring容器解析
@RequestMapping
注解。当然这个注解又派生了其他的注解比如。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @RequestMapping(method = RequestMethod.POST) public @interface PostMapping {}
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @RequestMapping(method = RequestMethod.PUT) public @interface PutMapping {}
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @RequestMapping(method = RequestMethod.GET) public @interface GetMapping {}
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @RequestMapping(method = RequestMethod.DELETE) public @interface DeleteMapping {}
|
- 我们猜测Spring源码中一定会对
@RestController
和 @Controller
标记的类,里面的每个
Method进行处理,判断是否包含了上面的注解。注解那么多Spring肯定不会这样一个一个去处理。我们可以看到
上面的注解都使用了@AliasFor
注解。其中奥妙就在这里。看下面例子代码。
- 我们猜测Spring肯定对这些Method判断是否有
@RequestMapping
有注解。
1. @AliasFor使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @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); System.out.println(StringUtils.arrayToCommaDelimitedString(annotation.value())); System.out.println(StringUtils.arrayToCommaDelimitedString(annotation.produces())); System.out.println("是否包含RequestMapping: "+AnnotatedElementUtils.hasAnnotation(queryBbsPostAll,RequestMapping.class));
RequestMapping mergedAnnotation = AnnotatedElementUtils.getMergedAnnotation(queryBbsPostAll, RequestMapping.class); System.out.println(StringUtils.arrayToCommaDelimitedString(mergedAnnotation.value())); } }
|
可以看到只要使用下面代码就能把被@PostMapping
等等的注解都涵盖了。
1 2
| System.out.println("是否包含RequestMapping: "+AnnotatedElementUtils.hasAnnotation(queryBbsPostAll,RequestMapping.class)); RequestMapping mergedAnnotation = AnnotatedElementUtils.getMergedAnnotation(queryBbsPostAll, RequestMapping.class);
|
2. 解析请求Method
AbstractHandlerMethodMapping
实现 InitializingBean
。在当前 Bean
初始化时候会执行
afterPropertiesSet -> initHandlerMethods
从这里开始解析Web资源类的信息。请小伙伴们看下面的截图,截图中源码已经把类名也截上了,方便小伙伴们自己在根据截图看一遍源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
@Override public void afterPropertiesSet() { initHandlerMethods(); }
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
就行了。

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

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

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

到这里其实我们就把本篇的议题就说明清楚了。下面我们在看下SpringWeb是如何将http请求信息路由到具体的HandlerMethod的吧。
3. 最后串一下流程
看了前面的截图,我们知道Spring是如何把这些Web资源信息给保存起来的了。然后就看是DispatcherServlet
的逻辑了。
首先DispatcherServlet
是一个Servlet。Servlet相信大家都都知道就不重点说原理。 我们直接看
doService
-> doDispatch
方法

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


最后求关注,求订阅,谢谢你的阅读!

分享
新浪微博
微信
QQ空间
QQ好友
豆瓣
有道云笔记
取消