当前位置: 首页 > news >正文

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

初探SpringMVC源码

在这里插入图片描述

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

一.基于Controller接口

1. 案例代码

引入相关的依赖

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.18</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
      <scope>provided</scope>
    </dependency>

然后创建自定义的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;
    }
}

然后添加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的name作为url进行查找 ,
                  需要在配置Handler时指定beanname(就是url) 所有的映射器都实现
         HandlerMapping接口。
     -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />

    <!-- 配置 Controller适配器 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>

    <bean name="/hello.action" class="com.boge.controller.UserController" />
</beans>

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

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

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>test</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>

  <!-- 配置前端控制器 -->
  <!-- contextConfigLocation配置springmvc加载的配置文件(配置处理器映射器、适配器等等)
  	如果不配置contextConfigLocation,
  	默认加载的是/WEB-INF/servlet名称-serlvet.xml(springmvc-servlet.xml)
  	 -->
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>


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

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

到这我们就清楚了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.案例讲解

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

<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>

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

@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/do1")
    @ResponseBody
    public String doSome1(){
        return "do1";
    }

    @RequestMapping("/do2")
    @ResponseBody
    public String doSome2(){
        return "do2";
    }
}

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

相关文章:

  • 上手Python之set(集合)
  • Visual Studio 2022开发Arduino详述
  • 【机器人定位引导中的机器视觉技术】
  • 零售商贩mysql表设计:主题信息表(theme)
  • 本文带你了解透彻云计算(前世,今生,未来)
  • ARM发布Cortex-X3和Cortex-A715
  • 麻雀算法极限学习机SSA-ELM回归预测及其MATLAB代码实现
  • 云原生DevOps篇:jenkins发送通知到企业微信机器人
  • LeetCode337:打家劫舍III
  • 【飞桨PaddleSpeech语音技术课程】— 语音识别-流式服务-模型部分
  • isomap降维算法--学习笔记
  • 【Linux】yum vim 基础工具的使用
  • QT学习_03_坐标系统和内存回收机制
  • cookies,session,token都是相对安全,并不能完全防窃取
  • 在Ubuntu22.04条件下,如何打开树莓派4B的串口
  • 【347天】每日项目总结系列085(2018.01.18)
  • Django 博客开发教程 8 - 博客文章详情页
  • es6--symbol
  • Java比较器对数组,集合排序
  • Java新版本的开发已正式进入轨道,版本号18.3
  • js
  • supervisor 永不挂掉的进程 安装以及使用
  • 阿里云Kubernetes容器服务上体验Knative
  • 工作踩坑系列——https访问遇到“已阻止载入混合活动内容”
  • 构建工具 - 收藏集 - 掘金
  • 关于 Cirru Editor 存储格式
  • 简单基于spring的redis配置(单机和集群模式)
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 聊聊directory traversal attack
  • 我有几个粽子,和一个故事
  • 原生JS动态加载JS、CSS文件及代码脚本
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • #1015 : KMP算法
  • #经典论文 异质山坡的物理模型 2 有效导水率
  • $.ajax()方法详解
  • (4)Elastix图像配准:3D图像
  • (java)关于Thread的挂起和恢复
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (vue)页面文件上传获取:action地址
  • (笔记)Kotlin——Android封装ViewBinding之二 优化
  • (翻译)Quartz官方教程——第一课:Quartz入门
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (篇九)MySQL常用内置函数
  • (十八)devops持续集成开发——使用docker安装部署jenkins流水线服务
  • (学习日记)2024.03.25:UCOSIII第二十二节:系统启动流程详解
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (转)c++ std::pair 与 std::make
  • (转)关于多人操作数据的处理策略
  • (转)拼包函数及网络封包的异常处理(含代码)
  • ***php进行支付宝开发中return_url和notify_url的区别分析
  • .gitignore
  • .NET 设计一套高性能的弱事件机制
  • .NET/C# 使窗口永不获得焦点
  • .Net6支持的操作系统版本(.net8已来,你还在用.netframework4.5吗)