springmvc源码解析

「爱情、让人受尽委屈。」 2022-04-10 02:23 334阅读 0赞

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dmbG92ZWphdmE_size_16_color_FFFFFF_t_70

总结:

首先请求进入DispatcherServlet 由DispatcherServlet 从HandlerMappings中提取对应的Handler

此时只是获取到了对应的Handle,然后得去寻找对应的适配器,即:HandlerAdapter
拿到对应HandlerAdapter时,这时候开始调用对应的Handler处理业务逻辑了(这时候实际上已经执行完了我们 的Controller) 执行完成之后返回一个ModeAndView

这时候交给我们的ViewResolver通过视图名称查找出对应的视图然后返回
最后 渲染视图 返回渲染后的视图 —>响应请求

实例:

在pom.xml文件上添加spring-mvc的依赖

  1. <dependencies>
  2. <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
  3. <dependency>
  4. <groupId>org.springframework</groupId>
  5. <artifactId>spring-webmvc</artifactId>
  6. <version>5.0.8.RELEASE</version>
  7. </dependency>
  8. <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
  9. <dependency>
  10. <groupId>javax.servlet</groupId>
  11. <artifactId>javax.servlet-api</artifactId>
  12. <version>3.1.0</version>
  13. <scope>provided</scope>
  14. </dependency>
  15. </dependencies>
  16. 添加App类
  17. import org.springframework.context.annotation.ComponentScan;
  18. import org.springframework.context.annotation.Configuration;
  19. /**
  20. * @author wufei
  21. * @create 2018-12-24 9:37
  22. **/
  23. @ComponentScan("com.wf.spring.mvc")
  24. @Configuration
  25. public class App {
  26. }

添加DispatcherServlet类

  1. /**
  2. * 添加DispatcherServlet
  3. * @author wufei
  4. * @create 2018-12-24 9:47
  5. **/
  6. public class MyWebApplicationInitializer implements WebApplicationInitializer {
  7. @Override
  8. public void onStartup(ServletContext servletCxt) {
  9. // Load Spring web application configuration
  10. AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
  11. ac.register(AppConfig.class);
  12. ac.refresh();
  13. // Create and register the DispatcherServlet
  14. DispatcherServlet servlet = new DispatcherServlet(ac);
  15. ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
  16. registration.setLoadOnStartup(1);
  17. registration.addMapping("/app/*");
  18. }
  19. }
  20. /**
  21. * 第一种方式注册controller
  22. * 使用bean的形成注册controller
  23. * @author wufei
  24. * @create 2018-12-24 9:58
  25. **/
  26. @Component("/test")
  27. public class BeanController implements Controller{
  28. @Override
  29. public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
  30. System.out.println("test");
  31. return null;
  32. }
  33. }
  34. /**
  35. * 第二种方式注册controller
  36. * annotation形式注册controller
  37. * RequestMappingHandlerMapping
  38. * @author wufei
  39. * @create 2018-12-24 15:10
  40. **/
  41. @Controller
  42. public class AnnotationController {
  43. @RequestMapping("/test1")
  44. public String test(){
  45. System.out.println("test1");
  46. return "test1";
  47. }
  48. }

源码分析:

首先我们看下DispatcherServlet继承关系图:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dmbG92ZWphdmE_size_16_color_FFFFFF_t_70 1

从图中可以看出DispatcherServlet最终继承的是个HttpServlet,即DispatcherServlet就是个Servlet。由此我们可以找到Servlet的doGet、doPost、service重要方法。在FrameworkServlet类中可以看到

  1. protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2. HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
  3. if (httpMethod != HttpMethod.PATCH && httpMethod != null) {
  4. super.service(request, response);
  5. } else {
  6. this.processRequest(request, response);
  7. }
  8. }
  9. protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  10. this.processRequest(request, response);
  11. }
  12. protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  13. this.processRequest(request, response);
  14. }

从三个方法上可以看出,都是调用了processRequest(request, response);方法,然后我们继续跟踪processRequest方法上

我们可以看到doService方法

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dmbG92ZWphdmE_size_16_color_FFFFFF_t_70 2

该方法是个抽象方法,由DispatcherServlet类实现该方法的具体逻辑。发现有个重要的关键代码方法this.doDispatch(request, response);

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dmbG92ZWphdmE_size_16_color_FFFFFF_t_70 3

进入doDispatch方法里面

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dmbG92ZWphdmE_size_16_color_FFFFFF_t_70 4

看到两个最重要的方法,getHandler(处理器)和getHandlerAdapter(适配器)

问题1 handler从哪来的呢

我们先查看getHandler方法里面的代码

  1. @Nullable
  2. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  3. if (this.handlerMappings != null) {
  4. Iterator var2 = this.handlerMappings.iterator();
  5. while(var2.hasNext()) {
  6. HandlerMapping hm = (HandlerMapping)var2.next();
  7. if (this.logger.isTraceEnabled()) {
  8. this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'");
  9. }
  10. HandlerExecutionChain handler = hm.getHandler(request);
  11. if (handler != null) {
  12. return handler;
  13. }
  14. }
  15. }
  16. return null;
  17. }
  18. 是在迭代handlerMappings,而handlerMappings是个List集合
  19. @Nullable
  20. private List<HandlerMapping> handlerMappings;

handlerMapping存放两个值,一个是org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,一个是org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dmbG92ZWphdmE_size_16_color_FFFFFF_t_70 5

问题2 handlerMapping是什么?

我们端个点,查看下具体是什么

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dmbG92ZWphdmE_size_16_color_FFFFFF_t_70 6

如图所示:是两个我们不认识的东西,我们继续往下看,

  1. HandlerExecutionChain handler = hm.getHandler(request);
  2. if (handler != null) {
  3. return handler;
  4. }

从mappings获取一个handler,然后返回了。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dmbG92ZWphdmE_size_16_color_FFFFFF_t_70 7

我们发现 handler就是我们的controll类的方法,我们以Bean的形式注册的Controller 可以从这个BeanNameUrlHandlerMapping里面 获取到对应的Handler ; 这里 我们是不是对于这个HandlerMapping有了懵懂的了解了

下面我们以Annotation方式注册的Controller,就会被第二个RequestMappingHandlerMapping获取对应的handler。如图所示:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dmbG92ZWphdmE_size_16_color_FFFFFF_t_70 8

问题3 什么是适配器

首先我们跟踪下代码

  1. HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
  2. protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
  3. if (this.handlerAdapters != null) {
  4. Iterator var2 = this.handlerAdapters.iterator();
  5. while(var2.hasNext()) {
  6. HandlerAdapter ha = (HandlerAdapter)var2.next();
  7. if (this.logger.isTraceEnabled()) {
  8. this.logger.trace("Testing handler adapter [" + ha + "]");
  9. }
  10. if (ha.supports(handler)) {
  11. return ha;
  12. }
  13. }
  14. }
  15. throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
  16. }

从代码上可以看出是迭代handlerAdapters,而这个从哪获取到的呢?也是从配置文件上获取的DispatcherServlet.properties

  1. org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
  2. org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
  3. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

使用Annotation方式注册的Controller使用的是RequestMappingHandlerAdapter适配器

20181224180023611.png

使用bean的形成注册controller使用的是SimpleControllerHandlerAdapter适配器

最后执行mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 执行业务逻辑了。输出了test1

20181224180740192.png

然后返回mv,mv就是试图了,然后渲染页面。

适配器就是对应不同的handler有不同的解决方案

总结
其实我们的SpringMVC关键的概念就在于Handler(处理器) 和Adapter(适配器)
通过一个关键的HandlerMappings 找到合适处理你的Controller的Handler 然后再通过HandlerAdapters找到一 个合适的HandlerAdapter 来执行Handler即Controller里面的逻辑。 最后再返回ModlAndView…

发表评论

表情:
评论列表 (有 0 条评论,334人围观)

还没有评论,来说两句吧...

相关阅读

    相关 SpringMVC

    首先上时序图,帮助理解整个解析过程和执行过程 ![SpringMVC启动过程及执行请求过程][SpringMVC] 准备 Spring版本:5.0.8 解

    相关 SpringMVC请求过程

    对SpringMVC从请求到最后响应输出的过程结合源码进行了一个总结梳理,其中对于Spring底层代码理解还不到位,难免有不准确的地方,欢迎指正,结合源码梳理整个流程的流转图: