5 分钟彻底理解 Spring MVC(上)

简介: 概述Sping MVC 正式的名字为 Spring Web MVC,是 Spring Framework 框架中的其中一个模块,基于 Servlet API 构建,同时使用 MVC 的架构模式,主要用以简化传统的 Servlet + JSP 进行 web 开发的工作。

概述


Sping MVC 正式的名字为 Spring Web MVC,是 Spring Framework 框架中的其中一个模块,基于 Servlet API 构建,同时使用 MVC 的架构模式,主要用以简化传统的 Servlet + JSP 进行 web 开发的工作。


MVC 架构模式


Spring MVC 基于 MVC 模式,因此理解 Spring MVC 需要先对 MVC 模式有所了解。


传统 MVC 架构模式


MVC 即 Model-View-Controller 是软件开发中一种常用的架构模式,将软件系统分为三层:模型(Model)、视图(View)、控制器(Controller),各部分根据职责进行分离,使程序的结构更为直观,增加了程序的可扩展性、可维护性、可复用性。可以用如下的图形来表示三者之间的关系。


image.png


模型(Model):模型封装了数据及对数据的操作,可以直接对数据库进行访问,不依赖视图和控制器,也就是说模型并不关注数据如何展示,只负责提供数据。GUI 程序模型中数据的变化一般会通过观察者模式通知视图,而在 web 中则不会这样。

视图(View):视图从模型中拉取数据,只负责展示,没有具体的程序逻辑。

控制器(Controller):控制器用于控制程序的流程,将模型中的数据展示到视图中。

Java Web MVC 架构模式


上世纪 90 年代,随着互联网的发展,基于浏览器的 B/S 模式随之流行,最初浏览器向服务器请求的都是一些静态的资源,如 HTML,CSS 等,为了支持根据用户的请求动态的获取资源,Java 提出了 Servlet 规范。


此时 Servlet 可以说是一个大杂烩,浏览器接收的 HTML 都是通过 Servelt 一行一行的输出,比较繁琐,并且写后端代码的程序员还要熟悉前端技术,为了解决这个问题,sun 公司又借鉴 ASP 提出了 JSP。


JSP 和 HTML 相似,只是在 JSP 文件中可以嵌入 Java 代码,减少了直接使用 Servlet 产生的大量冗余代码。此时 JSP 同时充当模型、视图、控制器的角色,为了解决前后端代码仍然揉在一起的问题,Java Web MVC 模式后来被提出,JavaBean 充当模型、JSP 充当视图,Servlet 充当控制器,流程如下图所示。


7.png


浏览器的请求先经过 Servlet,Servlet 控制整个流程,使用 JavaBean 查询并存储数据,然后携带 JavaBean 中的数据到 JSP 页面中,这个就是 Java 中早期的 Web MVC 架构模式了。


Spring MVC 架构模式


Spring MVC 架构模式对 Java Web 中的 MVC 架构模式加以扩展,将控制器拆分为前端控制器 DispatcherServlet 和后端控制器 Controller,将 Model 拆分成业务层(Service) 和数据访问层(Respository),并且支持不同的视图,如 JSP、FreeMarker 等,设计更为灵活,请求处理流程如下。


8.png



浏览器的请求先经过 DispatcherServlet,DispatcherServlet 负责分发请求,因此 DispatcherServlet 又被称为前端控制器。DispatcherServlet 其后的 Controller 又被称为后端控制器,Controller 可以选择性的调用 Service、Repository 实现业务逻辑,DispatcherServlet 拿到 Controller 提供的模型和视图后,进行渲染并返回给浏览器。当然了,这里只是为了方便理解 Spring MVC 描述的大概流程,具体流程会在后文介绍。


Hello,Spring MVC


虽然现在 SpringBoot 已经成为主流,但是我仍然想从单纯的 Spring MVC 讲起,因为 SpringBoot 也只是在 Spring Framework 其上添加了一些自动化的配置,这些自动化的配置会让我们忽略背后的技术原理。


几年的 Spring 的教程中都会提出使用 Spring MVC 首先需要去 Spring 官网下载一大堆的依赖,而现在有了 maven 之后再也不必关系这些乱七八糟的依赖及其依赖关系。如果你不了解 maven,建议先去了解 maven 后再回头看下面的内容。


Spring MVC 依赖引入


新建 maven 项目,并引入 Spring MVC 的依赖,注意这里引入的版本号是 5.2.6,Spring Framework 5 开始对 JDK 版本的要求是 1.8 及以上。完整的 pom 内容如下。


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.zzuhkp</groupId>
    <artifactId>mvc-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <finalName>mvc-demo</finalName>
    </build>
</project>


DispatcherServlet 声明


传统的 Java Web 项目使用 Servlet 处理请求,Spring MVC 遵循了 Servlet 规范,提供了一个名称为 DispatcherServlet 的 Servlet 类,使用 Spring MVC 需要先声明这个 Servlet。


DispatcherServlet 整合了 IOC 容器,所有处理 Web 请求的组件都存至 IOC 容器中,然后使用这些 bean 处理控制整个请求过程。


有两种声明 DispatcherServlet 的方式,第一种方式是直接在类路径下的/WEB-INF/web.xml文件中配置。


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
          http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>


第二种方式基于 Servlet 3.0 提出的 ServletContainerInitializer 接口,Servlet 容器会从类路径中查找实现了这个接口的类,并在启动时回调这个接口中的方法,Spring MVC 已经将这个接口实现为 SpringServletContainerInitializer,在其内部调用了 WebApplicationInitializer 接口完成初始化,因此实现 WebApplicationInitializer 接口再添加 DispatcherServlet 也可以,和上述 xml 等效的 java 代码如下。


public class MvcXmlInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        XmlWebApplicationContext context = new XmlWebApplicationContext();
        DispatcherServlet dispatcher = new DispatcherServlet(context);
        Dynamic dynamic = servletContext.addServlet("dispatcher", dispatcher);
        dynamic.addMapping("/");
        dynamic.setLoadOnStartup(1);
    }
}


除了上述用户自定义的 WebApplicationInitializer,Spring 还自定义了一个支持注解配置的抽象实现类 AbstractAnnotationConfigDispatcherServletInitializer,这个类会自动向 Servlet 上下文中注册 DispatcherServlet,实现这个类然后指定配置类即可。


public class MvcAnnotationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{MvcConfig.class};
    }
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}


这里我们使用 web.xml 配置进行演示,我们配置的 DispatcherServlet 声明的映射路径是/,因此,所有的请求都会到达 DispatcherServlet,然后再分派给不同的处理器处理。


Spring 上下文配置


Spring MVC 使用 IOC 容器存储处理请求的组件,包括处理器在内的所有自定义的与 Web 请求有关的组件都需要添加到 Spring 的配置中。


Spring 上下文配置文件指定


DispatcherServlet 初始化时默认使用的容器是 XmlWebApplicationContext,虽然 Spring 预留了扩展点用于修改容器类型,非必要情况下还是建议不要修改,这个容器默认情况下会使用类路径下/WEB-INF/{servlet-name}-servlet.xml文件作为容器的配置文件,我们声明的 DispatcherServlet 名为 dispatcher,因此我们创建/WEB-INF/dispatcher-servlet.xml文件作为容器的配置。另外还可以使用 Servlet 的初始化参数 configLocation 指定 Spring 容器配置文件路径。


    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>


Spring 上下文配置文件内容


Spring 配置文件内容如下。


<?xml version="1.0" encoding="UTF-8"?>
<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 id="/hellohandler" class="com.zzuhkp.mvc.handler.HelloSpringMVCHttpRequestHandler"/>
</beans>


这里声明了一个类型为 HelloSpringMVCHttpRequestHandler 的 bean,其 id 为请求路径/hellohandler,这个类的定义如下。


public class HelloSpringMVCHttpRequestHandler implements HttpRequestHandler {
    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        response.getWriter().write("Hello,HelloSpringMVCHttpRequestHandler");
    }
}


这样配置的目的是希望当/hellohandler请求到达时,能够使用我们提供的 HelloSpringMVCHttpRequestHandler 处理请求。

到了这里,将项目发布到 Tomcat,我这里使用的 Tomcat 版本号是 9.0.54,可以看到效果如下。


image.png


HandlerMapping 配置


那为什么将处理器的 bean id 配置为请求路径就可以使用这个处理器进行处理呢?Spring MVC 为了灵活的查找处理器内部使用了 HandlerMapping 将请求映射到处理器,Spring 默认情况下会使用BeanNameUrlHandlerMapping映射请求,这个映射器将请求路径作为 id 查找处理器。除了默认情况下使用的这个映射器,我们还可以配置 SimpleUrlHandlerMapping 映射器,和上述等效的 Spring 配置如下。


<?xml version="1.0" encoding="UTF-8"?>
<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 id="hellohandler" class="com.zzuhkp.mvc.handler.HelloSpringMVCHttpRequestHandler"/>
    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="urlMap">
            <map>
                <entry key="/hellohandler" value-ref="hellohandler"/>
            </map>
        </property>
    </bean>
</beans>
目录
相关文章
|
3月前
|
前端开发 Java 微服务
《深入理解Spring》:Spring、Spring MVC与Spring Boot的深度解析
Spring Framework是Java生态的基石,提供IoC、AOP等核心功能;Spring MVC基于其构建,实现Web层MVC架构;Spring Boot则通过自动配置和内嵌服务器,极大简化了开发与部署。三者层层演进,Spring Boot并非替代,而是对前者的高效封装与增强,适用于微服务与快速开发,而深入理解Spring Framework有助于更好驾驭整体技术栈。
|
10月前
|
前端开发 Java 测试技术
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestParam
本文介绍了 `@RequestParam` 注解的使用方法及其与 `@PathVariable` 的区别。`@RequestParam` 用于从请求中获取参数值(如 GET 请求的 URL 参数或 POST 请求的表单数据),而 `@PathVariable` 用于从 URL 模板中提取参数。文章通过示例代码详细说明了 `@RequestParam` 的常用属性,如 `required` 和 `defaultValue`,并展示了如何用实体类封装大量表单参数以简化处理流程。最后,结合 Postman 测试工具验证了接口的功能。
558 0
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestParam
|
10月前
|
JSON 前端开发 Java
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestBody
`@RequestBody` 是 Spring 框架中的注解,用于将 HTTP 请求体中的 JSON 数据自动映射为 Java 对象。例如,前端通过 POST 请求发送包含 `username` 和 `password` 的 JSON 数据,后端可通过带有 `@RequestBody` 注解的方法参数接收并处理。此注解适用于传递复杂对象的场景,简化了数据解析过程。与表单提交不同,它主要用于接收 JSON 格式的实体数据。
900 0
|
10月前
|
前端开发 Java 微服务
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@PathVariable
`@PathVariable` 是 Spring Boot 中用于从 URL 中提取参数的注解,支持 RESTful 风格接口开发。例如,通过 `@GetMapping(&quot;/user/{id}&quot;)` 可以将 URL 中的 `{id}` 参数自动映射到方法参数中。若参数名不一致,可通过 `@PathVariable(&quot;自定义名&quot;)` 指定绑定关系。此外,还支持多参数占位符,如 `/user/{id}/{name}`,分别映射到方法中的多个参数。运行项目后,访问指定 URL 即可验证参数是否正确接收。
561 0
|
10月前
|
JSON 前端开发 Java
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestMapping
@RequestMapping 是 Spring MVC 中用于请求地址映射的注解,可作用于类或方法上。类级别定义控制器父路径,方法级别进一步指定处理逻辑。常用属性包括 value(请求地址)、method(请求类型,如 GET/POST 等,默认 GET)和 produces(返回内容类型)。例如:`@RequestMapping(value = &quot;/test&quot;, produces = &quot;application/json; charset=UTF-8&quot;)`。此外,针对不同请求方式还有简化注解,如 @GetMapping、@PostMapping 等。
488 0
|
10月前
|
JSON 前端开发 Java
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RestController
本文主要介绍 Spring Boot 中 MVC 开发常用的几个注解及其使用方式,包括 `@RestController`、`@RequestMapping`、`@PathVariable`、`@RequestParam` 和 `@RequestBody`。其中重点讲解了 `@RestController` 注解的构成与特点:它是 `@Controller` 和 `@ResponseBody` 的结合体,适用于返回 JSON 数据的场景。文章还指出,在需要模板渲染(如 Thymeleaf)而非前后端分离的情况下,应使用 `@Controller` 而非 `@RestController`
409 0
|
6月前
|
前端开发 Java API
Spring Cloud Gateway Server Web MVC报错“Unsupported transfer encoding: chunked”解决
本文解析了Spring Cloud Gateway中出现“Unsupported transfer encoding: chunked”错误的原因,指出该问题源于Feign依赖的HTTP客户端与服务端的`chunked`传输编码不兼容,并提供了具体的解决方案。通过规范Feign客户端接口的返回类型,可有效避免该异常,提升系统兼容性与稳定性。
406 0
|
6月前
|
SQL Java 数据库连接
Spring、SpringMVC 与 MyBatis 核心知识点解析
我梳理的这些内容,涵盖了 Spring、SpringMVC 和 MyBatis 的核心知识点。 在 Spring 中,我了解到 IOC 是控制反转,把对象控制权交容器;DI 是依赖注入,有三种实现方式。Bean 有五种作用域,单例 bean 的线程安全问题及自动装配方式也清晰了。事务基于数据库和 AOP,有失效场景和七种传播行为。AOP 是面向切面编程,动态代理有 JDK 和 CGLIB 两种。 SpringMVC 的 11 步执行流程我烂熟于心,还有那些常用注解的用法。 MyBatis 里,#{} 和 ${} 的区别很关键,获取主键、处理字段与属性名不匹配的方法也掌握了。多表查询、动态
183 0
|
6月前
|
JSON 前端开发 Java
第05课:Spring Boot中的MVC支持
第05课:Spring Boot中的MVC支持
292 0
|
12月前
|
SQL Java 数据库连接
对Spring、SpringMVC、MyBatis框架的介绍与解释
Spring 框架提供了全面的基础设施支持,Spring MVC 专注于 Web 层的开发,而 MyBatis 则是一个高效的持久层框架。这三个框架结合使用,可以显著提升 Java 企业级应用的开发效率和质量。通过理解它们的核心特性和使用方法,开发者可以更好地构建和维护复杂的应用程序。
657 29