Spring MVC是我们Web编程的基础,目前有很多应用都在用Spring MVC,而学习Spring MVC的核心在于流程和组件。MVC不是Spring/Java独有的,它只是一种架构的方法。满足高内聚低耦合的特点。
1、认识MVC架构
MVC的架构主要将应用拆分为模型层(Model)、控制层(Controller)和视图层(View)。
目前大部分的应用都是如下图这样的MVC结构:
这里需要注意的是Spring MVC架构中阿拉伯数字的顺序是执行的顺序。这里再具体进行说明。
- 视图:主要是JSON、JSP、H5、安卓、IoS等,主要满足和用户交互。可以让用户输入,或者展示数据给用户看。
- 控制器:是一个特殊的Java类,它来接收用户请求,控制流程,和提供响应方式。
- 模型层:主要提供数据的操作,而数据主要来源于数据库/NoSQL等,一般模型层分为业务层和数据层,之所以这样分,是因为业务层需要夹带数据库事务。
2、Spring MVC概述
和传统的Java Web围绕Servlet一样,Spring MVC是围绕DispatcherServlet来工作的,也就是Spring MVC的核心是DispatcherServlet。但是Spring MVC会提供一些组件来完成整体的工作。整体的流程如下图所示。
上图就是Spring MVC的全流程,注意并非全部请求都需要全流程进行响应(未来我们会学习到)。
- 这里的HandlerMapping主要是找到URI和处理器的映射关系,这里的处理器是一个包装了控制器逻辑的组件,而控制器由我们提供。也就是Spring MVC会根据这层关系通过URI找到对应的控制器,起到路由的作用。这里HandlerMapping返回的HandlerExecutionChain包含了拦截器和处理器,也就是我们可以对处理器进行拦截。
- HandlerAdapter显然就是一个适配器,通过它去运行处理器。运行了之后,它会返回ModelAndView(模型和视图),从名称就可以看到存在视图和模型两种信息。
- 当返回ModelAndView后,Spring MVC会根据视图名称去定位视图(比如JSP等),这步是由视图解析器去完成的。
- 然后将数据渲染到视图中,完成流程。
其实在Spring MVC内部,已经帮助我们初始化了一些组件,关于这点,在我们引入spring-boot-starter-web后,可以看到spring-webmvc-x.y.z.jar中的包org.springframework.web.servlet,它里面有一个文件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
# Flash管理器,不常用,后续不再论述
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
上面就是Spring MVC会帮助我们自动初始化的组件。所以一般,我们需要做的事情比较少。
3、初探Spring MVC
这里我们需要引入spring MVC和|Thymeleaf两个包,因此可以使用Maven引入它们。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
这里因为了Thymeleaf作为模板,这样就可以编写对应的模板了,你只需要将模板文件放在项目的/resources/templates目录下。下面我们编写一个Thymeleaf模板,名称为user_details.html,代码如下。
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>欢迎学习Spring MVC</title>
</head>
<body>
<!-- “th:text”代表获取请求属性的值来渲染页面 -->
<span th:text="${user.userName}"/> ,欢迎学习Spring MVC!
</body>
</html>
请注意到th:text="${user.userName}"的写法是要从数据模型中取出数据渲染,所以我们需要在控制器的ModelAndView中添加数据模型。为此需要添加一个控制器,代码如下。
package com.csdn.mvc.chapter1.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller // 控制器
public class UserController {
@RequestMapping("/user/details") // 路由,让请求能映射到处理器
public ModelAndView userDetails(Long id) {
var mav = new ModelAndView();
// 视图名称,可以指向渲染的页面
mav.setViewName("user_details");
var user = new User();
user.setId(id);
user.setUserName("user_name_" + id);
user.setNote("note_" + id);
// 添加数据模型
mav.addObject(user);
return mav;
}
class User {
private Long id;
private String userName;
private String note;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
}
讲述开发的过程:
- 这个类被标注为@Controller,这样它就是Spring MVC中的控制器。
- @RequestMapping要做的事情是将请求的路径映射到处理器(处理器会包装控制器的方法)上。
- 控制器方法会创建和处理ModelAndView对象,它包含视图名称和数据模型,注意视图名称和我们的user_details.html文件名保持一致,同时将文件放在/resources/templates目录下,这样就能映射上。而数据模型则绑定了一个用户对象。
- 最后返回ModelAndView对象。
再次确认视图user_details.html的路径,如下图。
然后修改Spring Boot的启动文件,如下。
package com.csdn.mvc.chapter1.main;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 指定扫描的包,确保控制器能扫描到
@SpringBootApplication(scanBasePackages = "com.csdn.mvc.chapter1")
public class Chapter1Application {
public static void main(String[] args) {
SpringApplication.run(Chapter1Application.class, args);
}
}
这个文件只需要保证能够扫描到我们开发的控制器即可。
然后运行它,请求路径:
http://localhost:8080/user/details?id=12
得到如下结果,可见运行已经成功了。
4、再分析结果
学习Spring MVC最重要的是组件和流程,所以一定要熟悉它们,这里再分析我们上面的结果,来给大家增加印象,一定要好好体会分析的过程。
首先是请求的路径能够映射到处理器(它会包装控制器和其方法),因此这里在UserController.java中使用了@RequestMapping,这就是一种HandlerMapping的机制。Spring就会把方法包装为处理器了,然后通过路径能映射到这个处理器上。
控制器需要做的是确定自己的数据模型和视图(ModelAndView),从而完成业务功能。
ModelAndView需要给出视图名称和数据模型。它会根据视图名称通过映射得到具体的视图位置,这个过程需要一个视图解析器(ViewResolver)去定位,而视图的作用是将控制器中的数据模型渲染出来,展示给用户查看。
整个流程,如下图所示。
到这里,我们只是完成简单的Spring MVC,而核心是流程和组件,后续我们还会深入的介绍Spring MVC,期待下一篇哦。