SpringBoot之@Controller和@RequestMapping的实现原理解读,
SpringBoot之@Controller和@RequestMapping的实现原理解读,
目录
- SpringBoot之@Controller和@RequestMapping的实现原理
- 一、注解派生概念
- 二、进入正题,跟进源码解析请求Method
- 三、最后串一下请求流程
- 总结
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本篇内容就到这里。谢谢大家。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持3672js教程。
您可能感兴趣的文章:- springboot全局异常处理方式@ControllerAdvice和@ExceptionHandler
- SpringBoot中的@ControllerAdvice注解原理详解
- SpringBoot中@ControllerAdvice注解的使用方法
- SpringBoot中的@ControllerAdvice使用方法详细解析
- SpringBoot的@ControllerAdvice处理全局异常详解
- Springboot之@Controller注解不生效问题及解决
用户点评