深入理解SpringMVC工作原理,像大牛一样手写SpringMVC框架

引言

   对于SpringMVC相信诸位并不陌生,这是Java开发过程中使用最频繁的框架,在你的项目中可能不一定用MyBatis,但绝对会使用SpringMVC,因为操作数据库还有Hibernate、JPA等其他ORM框架选择,但SpringMVC这个框架在其领域中,可谓是独领风骚,因此在面试中也会常常问到一些与之相关的面试题,其中最为经典的则是那道:

SpringMVC在启动后是如何工作的?(工作原理)

对于这题的答案,相信大家在“Java面试八股文”中绝对背过,但之前大多数小伙伴应该也只是死记,并未真正的理解其核心原理,那本篇的目的就在于让诸位真正的掌握SpringMVC原理。当然,为了更好的理解,咱们也不会以之前分析底层时的那种源码方式,对其进行长篇概述,本次则使用一种新的方式来对其进行原理讲解。

那新的方式是什么呢?那就是自己手写框架,真正的理解就是自己能够把轮子重新造一次,这原本源码的方式更加形象,也能够更加让我们对其原理印象深刻。

在之后有可能会写的《源码分析》专题中,会再次详细剖析一些常用开源框架的源码实现,同时为了加深对每个技术栈的理解,在剖析清楚源码实现后,也会以本文这种形式,对框架进行迷你版的手写实战,因此本文也算是一个新的尝试。

一、SpringMVC框架的概述与回忆

   SpringMVCSpring家族中的元老之一,它是一个基于MVC三层架构模式的Web应用框架,它的出现也一统了JavaWEB应用开发的项目结构,从而避免将所有业务代码都糅合在同一个包下的复杂情况。在该框架中通过把Model、View、Controller分离,如下:

  • M/Model模型:由service、dao、entityJavaBean构成,主要负责业务逻辑处理。
  • V/View视图:负责向用户进行界面的展示,由jsp、html、ftl....等组成。
  • C/Controller控制器:主要负责接收请求、调用业务服务、根据结果派发页面。

SpringMVC贯彻落实了MVC思想,以分层工作的模式,把整个较为复杂的web应用拆分成逻辑清晰的几部分,从很大程度上也简化了开发工作,减少了团队协作开发时的出错几率。

   回想最初的servlet开发,或者说最初我们学习Java时,如稚子般的操作,当时也不会划分模块、划分包,所有代码一股脑的全都放在少数的几个包下。但不知从何时起,慢慢的,每当有一个新的项目需求出现时,我们都会先对其划分模块,再划分层次,SpringMVC这个框架已经让每位Java开发彻底将MVC思想刻入到了DNA中,无论是最初的单体开发,亦或是如今主流的分布式、微服务开发,相信大家都已经遵守着这个思想。

SpringMVC框架的设计,是以请求为驱动,围绕Servlet设计的,将请求发给控制器,然后通过模型对象,分派器来展示请求结果的视图。SpringMVC的核心类是DispatcherServlet,它是一个Servlet子类,顶层是实现的Servlet接口。

当然,此刻暂且避开其原理不谈,先回想最初的SpringMVC是如何使用的呢?一起来看看。

1.1、SpringMVC的使用方式

   对于SpringMVC框架的原生使用方式,估计大部分小伙伴都已经忘了,尤其是近些年SpringBoot框架的流行,由于其简化配置的特性,让我们几乎无需再关注最初那些繁杂的XML配置。

说到这块就引起了我早些年那些痛苦的回忆,在SpringBoot还未那么流行之前,几乎所有的配置都是基于XML来弄的,而且每当引入一个新的技术栈,都需要配置一大堆文件,比如Spring、SpringMVC、MyBatis、Shiro、Quartz、EhCache....,这个整合过程无疑是痛苦的。

但随着后续的SpringBoot流行,这些问题则无需开发者再关注,不过成也SpringBoot,败也SpringBoot,尤其是近几年新入行的Java程序员,正是由于未曾有过之前那种繁重的XML配置经历,因此对于application.yml中很多技术栈的配置项也并不是特别理解,项目开发中需要引入一个新的技术栈时,几乎靠在网上copy他人的配置信息,也就成了“知其然而不知其所以然”,这对后续想要深入研究底层也成了一道新的屏障。

就此打住,感慨也不多说了,咱们先来回忆回忆最初SpringMVC的使用方式:基于最普通的maven-web工程构建。

在使用SpringMVC框架时,一般会首先配置它的核心文件:springmvc-servlet.xml,如下:

<?xml version="1.0" encoding="UTF-8"?>
<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/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 
		http://www.springframework.org/schema/mvc
		http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
    <!-- 通过context:component-scan元素扫描指定包下的控制器-->
    <!-- 扫描com.xxx.xxx及子孙包下的控制器(扫描范围过大,耗时)-->
    <context:component-scan base-package="com.xxx.controller"/>
    
    <!-- ViewResolver -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- viewClass需要在pom中引入两个包:standard.jar and jstl.jar -->
        <property name="viewClass"
                  value="org.springframework.web.servlet.view.JstlView"></property>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    
    <!-- 省略其他配置...... -->
</beans>

springmvc-servlet.xml这个核心配置文件中,最重要的其实是配置Controller类所在的路径,即包扫描的路径,以及配置一个视图解析器,主要用于解析请求成功之后的视图数据。

OK~,配置好了springmvc-servlet.xml文件后,紧接着我们会再修改maven-web项目核心文件web.xml中的配置项:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <!-- 再这里会添加一个SpringMVC的servlet配置项 -->
  <servlet>
  <!-- 首先指定SpringMVC核心控制器所在的位置 -->
    <servlet-name>SpringMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- DispatcherServlet启动时,从哪个文件中加载组件的初始化信息 -->
    <!--此参数可以不配置,默认值为:/WEB-INF/springmvc-servlet.xml-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/springmvc-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <!--web.xml 3.0的新特性,是否支持异步-->
    <!--<async-supported>true</async-supported>-->
  </servlet>
  <!-- 配置路由匹配规则,/ 代表匹配所有,类似于nginx的location规则 -->
  <servlet-mapping>
    <servl
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值