一、MVC 模式简介
在软件开发的广袤天地中,MVC 模式宛如一座明亮的灯塔,指引着开发者构建高效、可维护的应用程序。Spring MVC 作为基于 Spring 框架的重要 web 开发模块,更是将 MVC 模式的优势发挥得淋漓尽致,堪称 Servlet 的强力升级版。
1.1 MVC 各组件职责
- Model(模型层):这一层如同应用的数据大脑,包含了工程中的 Java Bean。Java Bean 又细分为两类:实体类 Bean,就像一个个数据保险箱,专门用于存储业务数据,比如在一个电商系统中,商品信息、用户信息等实体类就肩负着存储这些关键数据的重任;业务处理 Bean,它可以是 Servlet 或者 Dao 对象,主要负责处理业务逻辑以及与数据持久化相关的操作,例如在处理用户注册业务时,相关的 Servlet 或 Dao 对象会对用户输入的数据进行验证、存储等操作。
- View(视图层):视图层是应用与用户直接对话的窗口,项目中的 html 或 jsp 等页面便是其主要载体。它的核心作用在于与用户进行交互,将数据以直观、友好的方式展示给用户,同时收集用户的输入信息。比如在一个新闻网站中,用户看到的新闻列表页面、新闻详情页面等都是视图层的体现,用户可以在这些页面上浏览新闻内容、进行评论等操作。
- Controller(控制层):控制层在整个 MVC 架构中扮演着交通枢纽的角色,在 Spring MVC 框架里由 Controller 担当此重任,其前身是 Servlet。它的主要职责是接收来自用户的请求,对请求进行分析和处理,然后根据业务逻辑调用相应的模型层组件进行数据处理,最后将处理结果返回给视图层进行展示。例如在一个在线购物系统中,当用户点击 "添加商品到购物车" 按钮时,对应的 Controller 会接收到这个请求,调用相关的业务逻辑方法处理添加商品的操作,最后返回给用户一个操作结果页面。
MVC 思想并非特定编程语言或者 web 应用所独有的,它是一种广泛适用的规范。通过将一个应用清晰地划分为 Model、View 和 Controller 三个基本部分,这三个部分以最小的耦合协同工作,极大地提高了应用的可扩展性和可维护性。就像一辆汽车,发动机(Model)负责提供动力,仪表盘(View)负责展示信息,方向盘和变速箱(Controller)负责控制方向和速度,它们各司其职又协同配合,共同保证汽车的正常运行。
二、SpringMVC 入门案例搭建
2.1 创建 WEB 工程与引入依赖
搭建 SpringMVC 项目的第一步,是创建一个 WEB 工程。就像搭建一座房子,首先要打好地基。创建好工程后,我们需要引入开发所需的 jar 包,这些 jar 包就如同建房所需的各种建筑材料。通过在项目的pom.xml
文件中进行如下配置来引入依赖:
xml
<properties>
<spring.version>5.0.2.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!--SpringMVC核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--servlet API依赖,用于与Servlet容器交互,provided范围表示由容器提供,打包时不会包含-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<!--日志依赖,用于记录应用运行时的日志信息,方便调试和监控-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>3.0.9.RELEASE</version>
</dependency>
</dependencies>
2.2 配置 web.xml
接下来,我们需要对web.xml
配置文档进行更改。web.xml
就像是一个交通指挥中心,负责配置整个 web 应用的一些关键信息。首先,我们要确保web - app
标签的配置正确,指定合适的版本和相关命名空间:
xml
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
</web-app>
2.3 配置核心控制器 DispatcherServlet
Spring MVC 的核心是基于原生 servlet 的强大前端控制器 DispatcherServlet,它就像一个智能的交通警察,对所有的请求和响应进行统一处理。在web.xml
中配置 DispatcherServlet 的过程如下:
xml
<!--在web.xml中配置Spring提供的过滤器类,用于设置字符编码,确保请求和响应的字符编码一致,避免乱码问题-->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--不拦截所有是html的页面请求,让服务器默认的Servlet来处理html请求,提高性能和兼容性-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<!--配置前端控制器DispatcherServlet,对浏览器发送的请求进行统一处理-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--加载springmvc.xml配置文件的位置和名称,配置的是Spring配置,contextConfigLocation是固定的参数名,classpath表示类路径,即Java和resources文件夹所在路径,springmvc.xml是配置文件的名称,需要确保该文件存在于指定路径下-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--配置启动加载,load-on-startup的值为1表示在服务器启动时就加载该Servlet,值越小优先级越高,这样可以确保在应用启动时就完成相关的初始化工作,提高应用的响应速度-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--开启项目时打开的页面,设置欢迎文件列表,当用户访问根路径时,服务器会优先查找并返回该文件,这里设置为index.html,方便用户快速进入应用的起始页面-->
<welcome-file-list>
<welcome-file>/index.html</welcome-file>
</welcome-file-list>
2.4 编写相关页面
我们需要编写index.html
和suc.html
页面。index.html
就像是应用的大门,用户从这里进入应用,它提供了一个简单的界面,包含一个指向/SpringMVCDemo/hello.do
的超链接:
html
<html>
<head>
<meta charset="utf-8">
<title>入门程序</title>
</head>
<body>
<h3>入门</h3>
<a href="/SpringMVCDemo/hello.do">入门程序</a>
</body>
</html>
suc.html
则是用于展示处理结果的页面,它使用了 Thymeleaf 模板引擎来动态渲染数据:
html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>成功</title>
</head>
<body>
<h1>Hello <b th:text="${msg}"></b></h1>
</body>
<script>
</script>
</html>
2.5 编写 Controller 类和方法
创建一个HelloController
类,使用@Controller
注解将其交给 IOC 容器进行管理,就像把一把钥匙交给管理员,让管理员来管理这个类的生命周期和依赖注入等操作。在这个类中,编写一个处理请求的方法sayHello
,使用@RequestMapping
注解来映射请求路径:
java
//把当前类交给IOC容器进行管理
@Controller
public class HelloController {
/**
* 处理超链接发送出来的请求
* @param model 用于向视图传递数据的模型对象
* @return 返回视图名称
*/
@RequestMapping(path = "/hello.do")
public String sayHello(Model model){
System.out.println("入门方法执行了2...");
// 向模型中添加属性msg与值,可以在html页面中取出并渲染
model.addAttribute("msg","hello,SpringMVC");
// 配置了视图解析器后,直接返回视图名称suc,视图解析器会根据配置找到对应的实际页面
return "suc";
}
}
2.6 编写 springmvc.xml 配置文件
springmvc.xml
是 Spring MVC 的重要配置文件,它就像一个指挥家,协调着各个组件的工作。在这个文件中,我们进行了如下配置:
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.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--配置spring创建容器时要扫描的包,指定为com.qcby包及其子包,这样Spring容器会自动扫描该包下的所有组件,例如带有@Controller、@Service等注解的类,将其注册到容器中进行管理-->
<context:component-scan base-package="com.qcby"></context:component-scan>
<!--处理映射器,BeanNameUrlHandlerMapping会根据处理器的Bean名称来映射请求,例如如果一个Controller的Bean名称是helloController,那么它可以处理以/helloController为路径的请求,这里使用默认配置即可满足简单的请求映射需求-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--处理器适配器,SimpleControllerHandlerAdapter用于适配实现了Controller接口的处理器,它负责调用处理器的handleRequest方法来处理请求,在Spring MVC中,不同类型的处理器需要不同的适配器来进行适配和调用,这里使用的是简单控制器适配器,适用于简单的Controller实现场景-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--配置视图解析器,ThymeleafViewResolver用于解析Thymeleaf模板视图,order属性设置视图解析器的优先级,值越小优先级越高;characterEncoding属性设置字符编码为UTF-8,确保视图渲染时的字符编码正确;templateEngine属性引用了一个名为templateEngine的Bean,用于进行模板引擎相关的配置和操作,这里使用Thymeleaf模板引擎来渲染视图,Thymeleaf具有强大的模板语法和功能,方便进行页面的动态渲染和数据展示等操作-->
<bean id="viewResolver" class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine" ref="templateEngine"/>
</bean>
<!-- templateEngine配置,SpringTemplateEngine是Thymeleaf的Spring集成模板引擎,templateResolver属性引用了一个名为templateResolver的Bean,用于配置模板解析器,模板解析器负责根据配置的前缀、后缀等信息来查找和加载模板文件,例如这里配置的SpringResourceTemplateResolver会从/html/目录下查找以.html为后缀的模板文件,然后使用Thymeleaf的模板语法进行渲染和处理,最终生成可展示的视图页面-->
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver"/>
</bean>
<bean id="templateResolver" class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/html/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML5"/>
</bean>
<!-- JSON View配置,MappingJackson2JsonView用于将模型数据转换为JSON格式并返回给客户端,在一些需要返回JSON数据的场景,例如进行前后端分离开发时,前端通过AJAX请求获取数据,后端可以使用这个视图来将数据以JSON格式返回,方便前端进行数据处理和展示等操作,这里只是配置了这个视图组件,在实际使用时,需要在Controller方法中进行相应的设置来返回JSON数据,例如可以使用@ResponseBody注解结合这个视图来实现JSON数据的返回功能-->
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
</bean>
<!-- 配置spring开启注解mvc的支持 ,默认就是开启的 ,要想让其他组件(不包含映射器、适配器、处理器)生效就必须需要配置了,例如一些自定义的拦截器、消息转换器等组件,如果不开启注解mvc支持,这些组件可能无法正常工作,开启后,Spring MVC会自动扫描和注册相关的注解驱动组件,使得应用能够支持更多的功能和特性,提高开发效率和应用的灵活性等。例如可以使用@PathVariable、@RequestParam等注解来方便地处理请求参数等操作,这些注解的生效就依赖于开启注解mvc支持。-->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
完成上述所有配置后,启动 Tomcat 服务器进行测试。如果一切配置正确,当用户访问index.html
页面并点击 "入门程序" 超链接时,会触发HelloController
中的sayHello
方法,最终跳转到suc.html
页面并展示 "hello,SpringMVC" 的信息。
三、SpringMVC 原理剖析
3.1 SpringMVC 中心控制器 DispatcherServlet
Spring 的 web 框架就像是一个精心设计的交响乐团队,而 DispatcherServlet 则是这个团队中的指挥家。它的核心作用是将请求精准地发送到不同的处理器,确保整个应用的流程顺畅运行。在启动 Tomcat 服务器时,由于配置了load - on - startup
标签,DispatcherServlet 对象会被创建,同时它会加载springmvc.xml
配置文件,就像指挥家在演出前会仔细研究乐谱一样,DispatcherServlet 通过加载配置文件来了解整个应用的架构和流程。
3.2 入门案例的执行流程
3.3 SpringMVC 官方提供图形入门案例中的组件分析
-
启动服务器与初始化 :当 Tomcat 服务器启动时,因为
load - on - startup
配置为 1,DispatcherServlet 对象被创建,并且它会根据contextConfigLocation
参数的值加载springmvc.xml
配置文件。在这个过程中,Spring 容器被初始化,各种组件被创建并注册到容器中,就像搭建一个舞台,准备好各种道具和演员。 -
创建 Controller 对象 :由于开启了注解扫描,
@Controller
注解标记的HelloController
对象会被 Spring 容器创建,就像导演挑选演员并让他们做好演出准备。 -
请求到达与处理 :当用户从
index.jsp
(这里假设index.html
重定向到了index.jsp
,实际情况中也可以直接从index.html
发起请求)发送请求时请求会如同传递接力棒一般,率先抵达 DispatcherServlet 这个核心控制器。DispatcherServlet 会仔细审视请求的路径,依据配置在
HelloController
类中sayHello
方法上的@RequestMapping
注解,精准地找到需要执行的具体方法。这就好比在一个大型图书馆中,根据索引目录快速定位到所需的书籍。 -
视图解析与页面查找 :
sayHello
方法执行完毕后会返回一个视图名称(在这里是suc
)。此时,视图解析器开始发挥关键作用,它会依据在springmvc.xml
中配置的规则去寻找对应的实际页面。以本案例的配置来说,视图解析器ThymeleafViewResolver
会在/html/
目录下查找名为suc.html
的文件。这类似于按照地图的指引,在城市的特定区域找到目标建筑。 -
页面渲染与响应 :Tomcat 服务器找到
suc.html
页面后,会根据 Thymeleaf 模板引擎的规则对其进行渲染。在渲染过程中,sayHello
方法通过model.addAttribute("msg","hello,SpringMVC")
设置的msg
属性值会被动态地填充到页面中对应的位置。最终,渲染好的页面作为响应返回给用户,用户在浏览器中就能看到展示了 "hello,SpringMVC" 信息的页面,完成一次完整的请求处理流程,就像一场精彩的演出完美落幕,观众欣赏到了最终的成果。 -
前端控制器(DispatcherServlet):作为整个 SpringMVC 架构的入口和核心调度者,DispatcherServlet 负责接收所有来自客户端的请求,并将这些请求分发给后续的组件进行处理。它就像一个大型商场的总服务台,所有顾客的咨询和需求都首先汇聚到这里,然后由总服务台的工作人员将需求分配到相应的部门去解决。
-
处理器映射器(HandlerMapping) :处理器映射器的任务是根据请求的信息(例如请求的 URL),找到对应的处理器(Handler)。在我们的案例中,使用的
BeanNameUrlHandlerMapping
会根据处理器的 Bean 名称来映射请求。可以将其想象成一个智能的导航系统,根据输入的目的地信息,规划出到达目的地的路线,这里的目的地就是请求要访问的处理器。 -
处理器(Handler) :处理器是真正处理请求业务逻辑的组件,在我们的案例中就是
HelloController
中的sayHello
方法。它负责接收请求参数,调用相关的业务逻辑方法进行处理,然后返回处理结果。好比工厂中的工人,根据订单要求进行生产加工,最终产出产品。 -
处理器适配器(HandlerAdapter) :由于不同类型的处理器可能有不同的接口和调用方式,处理器适配器的作用就是将 DispatcherServlet 传递过来的请求适配到具体的处理器上进行调用。在案例中使用的
SimpleControllerHandlerAdapter
用于适配实现了Controller
接口的处理器。它类似于一个万能插头转换器,将不同规格的插头(请求)转换为能插入特定插座(处理器)的形式,确保请求能够正确地被处理器处理。 -
视图解析器(ViewResolver) :视图解析器的职责是根据处理器返回的视图名称,找到对应的实际视图资源(如 JSP 页面、Thymeleaf 模板等),并将其解析为可以返回给客户端的视图对象。在本案例中,
ThymeleafViewResolver
根据配置的前缀/html/
和后缀.html
,以及视图名称suc
,找到suc.html
页面并进行解析。它就像一个翻译官,将抽象的视图名称翻译为具体的、可展示的页面资源。 -
视图(View) :视图是最终呈现给用户的界面,它负责将模型中的数据展示出来。在我们的案例中,
suc.html
就是视图,通过 Thymeleaf 模板语法将model
中的msg
数据展示在页面上。视图就像是一幅精美的画作,将各种元素(数据)组合在一起,呈现给观众(用户)欣赏。