• 是时候掌握SpringMVC源码了-初探篇


    初探SpringMVC源码

    在这里插入图片描述

      上篇文章我们通过手写分析了SpringMVC中的Controller的两种实现方式。接下来我们来看看在SpringMVC中具体是如何使用的。

    一.基于Controller接口

    1. 案例代码

    引入相关的依赖

    
        <dependency>
          <groupId>org.springframeworkgroupId>
          <artifactId>spring-webmvcartifactId>
          <version>5.3.18version>
        dependency>
        
        <dependency>
          <groupId>javax.servletgroupId>
          <artifactId>javax.servlet-apiartifactId>
          <version>3.0.1version>
          <scope>providedscope>
        dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    然后创建自定义的Controller

    /**
     * 自定义控制器
     * 必须实现Controller接口
     * @author dpb【波波烤鸭】
     *
     */
    public class UserController implements Controller{
    
        @Override
        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
            System.out.println("本方法被调用了...");
            ModelAndView view = new ModelAndView();
            view.setViewName("/index.jsp");
            return view;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    然后添加SpringMVC的配置文件

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        
        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
    
        
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter">bean>
    
        <bean name="/hello.action" class="com.boge.controller.UserController" />
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    我们需要在配置文件中配置对应的 HandlerMappingHandlerAdapter

    最后在web.xml中配置下前端控制器就可以了

    
    
      test
      
        index.html
        index.htm
        index.jsp
        default.html
        default.htm
        default.jsp
      
    
      
      
      
        springmvc
        org.springframework.web.servlet.DispatcherServlet
        
          contextConfigLocation
          classpath:spring-mvc.xml
        
      
    
      
        springmvc
        /
      
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    然后就可以启动服务来访问了

    2. 源码分析

      通过上面的案例分析,我们可以看的出来在 web.xml中配置了一个 Servlet当用户请求到来的时候都会拦截处理。所以我们通过DispatcherServlet来作为入口分析。

    image.png

    既然是一个Servlet。我们就需要分析对应的生命周期的方法

    image.png

    2.1 init方法

      init方法中会完成相关的初始化操作。我们来看看完成了哪些初始化的操作。我们可以直接进入到FrameworkServlet的 initServletBean()方法中查看 initWebApplicationContext()方法中。

    image.png

    进入后:

    image.png

    上面的IoC容器的初始化过程前面介绍Spring的时候重点讲解过,先不关注。直接看 onRefresh方法

    image.png

    我们看下 initHandlerMapping方法。

    image.png

    上面的代码我们可以看到逻辑很简单,会先读取xml文件中配置的HandlerMapping类型的信息,如果没有就读取默认的。案例中我们配置的有

    image.png

    如果没有配置的逻辑为就会读取 DispatcherServlet.properties 文件中内容

    image.png

    DispatcherServlet.properties中的信息为:

    # Default implementation classes for DispatcherServlet's strategy interfaces.
    # Used as fallback when no matching beans are found in the DispatcherServlet context.
    # Not meant to be customized by application developers.
    
    org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
    
    org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
    
    org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
    	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
    	org.springframework.web.servlet.function.support.RouterFunctionMapping
    
    org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
    	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
    	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
    	org.springframework.web.servlet.function.support.HandlerFunctionAdapter
    
    
    org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
    	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
    	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
    
    org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
    
    org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
    
    org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    到这我们就清楚了init方法做了两件事情

    1. 完成了IoC的初始化
    2. 完成了SpringMVC核心组件的初始化

    2.2 service方法

      service方法是在用户请求到来的时候触发的。也就是具体处理请求的方法。我们来看下,直接进入到doDispatch方法中

    image.png

    image.png

    获取对应的适配器

    image.png

    调用ha.handle方法

    image.png

    然后就进入到了自定义的控制器中了。

    image.png

    2.3 destory方法

      在destory方法中的处理就很简单。完成IoC容器的关闭操作

        public void destroy() {
            this.getServletContext().log("Destroying Spring FrameworkServlet '" + this.getServletName() + "'");
            if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) {
                ((ConfigurableApplicationContext)this.webApplicationContext).close();
            }
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    二、基于注解的方式

    1.案例讲解

      注解方式的使用我们需要在配置文件中添加相关的标签来开启

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
    		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    
        <!-- 开启注解 -->
        <mvc:annotation-driven></mvc:annotation-driven>
        <!-- 开启扫描 -->
        <context:component-scan base-package="com.boge.controller"></context:component-scan>
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    然后我们就可以在自定义控制器中通过@Controller注解和@RequestMapping注解来做方法级别的映射

    @Controller
    @RequestMapping("/user")
    public class UserController {
    
        @RequestMapping("/do1")
        @ResponseBody
        public String doSome1(){
            return "do1";
        }
    
        @RequestMapping("/do2")
        @ResponseBody
        public String doSome2(){
            return "do2";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.源码分析

      我们可以看到添加了一个标签后就开启了注解的使用方式,那么他的内部是怎么执行的呢?因为是自定义标签,所以在SpringMVC中会提供相关的解析器。这块我们可以看下源码中的内容

    image.png

    看到提供了一个处理器MvcNamespaceHandler。

    image.png

      可以看到,对应的标签都映射了对应的解析器,也就是解析到这个标签的时候,会找到对应的解析器来解析操作。然后我们进入到 AnnotationDrivenBeanDefinitionParser中可以看看具体的解析逻辑。

    image.png

    image.png

    可以看到。在这块完成了核心的 HandlerMappingHandlerAdapter注入到了容器中。那么在DispatcherServlet中处理请求的时候就会通过对应的Handler来处理了。

    这块的串联是在IoC的加载解析xml文件的逻辑中处理的。

    image.png

    image.png

    三、SpringBoot项目

      然后我们来看下载SpringBoot项目中是怎么自动装配SpringMVC框架的,首先我们找到对应的配置类

    image.png

    同时我们也需要关注下这个配置类

    image.png

    在这个配置类中注入的HandlerMapping和HandlerAdapter的具体实现类

    image.png

    image.png

    同时也注入了DispatcherServlet

    image.png

  • 相关阅读:
    葡萄糖-聚乙二醇-二茂铁Ferrocene-PEG-Glucose
    src实战-两处nacos未授权访问
    PCB设计时如何选择合适的叠层方案
    双层神经网络实现非线性回归——机器学习
    分析常见限流算法及手写三种(计数器、漏斗、令牌桶)代码实现
    PIE-Engine 教程:水稻面积提取2—监督分类(宿迁市)
    微服务常见面试题
    【面试专线】【基础知识】【JAVA】基础(三)(简答版)
    Python之网络编程
    公关世界杂志公关世界杂志社公关世界编辑部2022年第14期目录
  • 原文地址:https://blog.csdn.net/qq_38526573/article/details/127588657