SpringMVC的基础知识

SpringMVC的简介

MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分

M:Model,模型层 ,指工程中的JavaBean,作用是处理数据

JavaBean分为两类:

一类称为实体类Bean:专门存储业务数据的,如 Student、User 等

一类称为业务处理 Bean:指 Service 或 Dao 对象,专门用于处理业务逻辑和数据访问。

V:View,视图层 ,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据

C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器

MVC的工作流程: 用户通过视图层 发送请求到服务器,在服务器中请求被Controller接收,Controller调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器

SpringMVC入门案例

HelloWorld实现对首页的访问

Controller

java 复制代码
// @RequestMapping注解:处理请求和控制器方法之间的映射关系
// @RequestMapping注解的value属性可以通过请求地址匹配请求,/表示的当前工程的上下文路径
// localhost:8080/springMVC/
@RequestMapping("/")
public String index() {
//设置视图名称
return "index";
}

通过超链接跳转到指定页面

java 复制代码
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>首页</h1>
<a th:href="@{/hello}">HelloWorld</a><br/>
</body>
</html>

在请求控制器中创建处理请求的方法

java 复制代码
@RequestMapping("/hello")
public String HelloWorld() {
return "target";
}

浏览器发送请求,若请求地址符合前端控制器的url-pattern,该请求就会被前端控制器

DispatcherServlet处理。前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器,将请求地址和控制器中@RequestMapping注解的value属性值进行匹配,若匹配成功,该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称 ,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,通过Thymeleaf对视图进行渲染,最终转发到视图所对应页面

SpringMVC获取请求参数

SpringMVC获取从客户端发送到服务器的HTTP请求的相关参数
通过ServletAPI获取

将HttpServletRequest作为控制器方法的形参,此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象

示例

java 复制代码
@RequestMapping("/testParam")
public String testParam(HttpServletRequest request){
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username:"+username+",password:"+password);
return "success";
}

对应前端

java 复制代码
<html>
<head>
    <title>Test Form</title>
</head>
<body>
    <form action="/testParam" method="post">
        <label for="username">Username:</label>
        <input type="text" id="username" name="username"><br><br>
        <label for="password">Password:</label>
        <input type="password" id="password" name="password"><br><br>
        <input type="submit" value="Submit">
    </form>
</body>
</html>

通过控制器方法的形参获取请求参数

在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在DispatcherServlet中就会将请求参数赋值给相应的形参

java 复制代码
@RequestMapping("/testParam")
public String testParam(String username, String password){
System.out.println("username:"+username+",password:"+password);
return "success";
}

@RequestParam

java 复制代码
@RequestParam是将请求参数和控制器方法的形参创建映射关系
@RequestParam注解一共有三个属性:
value:指定为形参赋值的请求参数的参数名
required:设置是否必须传输此请求参数,默认值为true
若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置defaultValue属性,则页面报错400:Required String parameter 'xxx' is not present;若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为null
defaultValue:不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值
为""时,则使用默认值为形参赋值

@RequestHeader

@RequestHeader是将请求头信息和控制器方法的形参创建映射关系

@RequestHeader注解一共有三个属性:value、required、defaultValue,用法同@RequestParam
@CookieValue

@CookieValue是将cookie数据和控制器方法的形参创建映射关系

@CookieValue注解一共有三个属性:value、required、defaultValue,用法同@RequestParam
通过POJO获取请求参数

可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值

java 复制代码
<form th:action="@{/testpojo}" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
性别:<input type="radio" name="sex" value="男">男<input type="radio"
name="sex" value="女">女<br>
年龄:<input type="text" name="age"><br>
邮箱:<input type="text" name="email"><br>
<input type="submit">
</form>
java 复制代码
@RequestMapping("/testpojo")
public String testPOJO(User user){
System.out.println(user);
return "success";
}

@RequestMapping注解的params属性

@RequestMapping注解的params属性通过请求的请求参数匹配请求映射

java 复制代码
@RequestMapping(
value = {"/testRequestMapping", "/test"}
,method = {RequestMethod.GET, RequestMethod.POST}
,params = {"username","password!=123456"}
)
public String testRequestMapping(){
return "success";
}

注:若当前请求满足@RequestMapping注解的value和method属性,但是不满足params属性,此时页面回报错400:Parameter conditions "username, password!=123456" not met for actual request parameters: username={admin}, password={123456}
SpringMVC支持路径中的占位符

SpringMVC路径中的占位符常用于RESTful风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping注解的value属性中通过占位符{xxx}表示传输的数据,在通过@PathVariable注解,将占位符所表示的数据赋值给控制器方法的形参

java 复制代码
@RequestMapping("/testRest/{id}/{username}")
public String testRest(@PathVariable("id") String id, @PathVariable("username")String username){
	System.out.println("id:"+id+",username:"+username);
	return "success";
}

使用ModelAndView向request域对象共享数据

java 复制代码
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
	/**
	* ModelAndView有Model和View的功能
	* Model主要用于向请求域共享数据
	* View主要用于设置视图,实现页面跳转
	*/
	ModelAndView mav = new ModelAndView();
	//向请求域共享数据
	mav.addObject("testScope", "hello,ModelAndView");
	//设置视图,实现页面跳转
	mav.setViewName("success");
	return mav;
}

使用Model向request域对象共享数据

java 复制代码
@RequestMapping("/testModel")
public String testModel(Model model){
	model.addAttribute("testScope", "hello,Model");
	return "success";
}

使用map向request域对象共享数据

java 复制代码
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map){
	map.put("testScope", "hello,Map");
	return "success";
}

使用ModelMap向request域对象共享数据

java 复制代码
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){
	modelMap.addAttribute("testScope", "hello,ModelMap");
	return "success";
}

Model、ModelMap、Map的关系

Model、ModelMap、Map类型的参数其实本质上都是BindingAwareModelMap 类型的

转发视图

当控制器方法中所设置的视图名称以"forward:"为前缀时,创建InternalResourceView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:"去掉,剩余部

分作为最终路径通过转发的方式实现跳转

java 复制代码
@RequestMapping("/testForward")
public String testForward(){
	return "forward:/testHello";
}

重定向视图

SpringMVC中默认的重定向视图是RedirectView 当控制器方法中所设置的视图名称以"redirect:"为前缀时,创建RedirectView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"去掉,剩余部分作为最终路径通过重定向的方式实现跳转

java 复制代码
@RequestMapping("/testRedirect")
public String testRedirect(){
	return "redirect:/testHello";
}

SpringMVC处理ajax请求

@RequestBody

@RequestBody可以获取请求体信息,使用@RequestBody注解标识控制器方法的形参,当前请求的请

求体就会为当前注解所标识的形参赋值

@RequestBody获取json格式的请求参数

在使用了axios发送ajax请求之后,浏览器发送到服务器的请求参数有两种格式:

1、name=value&name=value...,此时的请求参数可以通过request.getParameter()获取,对应SpringMVC中,可以直接通过控制器方法的形参获取此类请求参数

2、{key:value,key:value,...},此时无法通过request.getParameter()获取,之前我们使用操作json的相关jar包gson或jackson处理此类请求参数,可以将其转换为指定的实体类对象或map集合。在SpringMVC中,直接使用@RequestBody注解标识控制器方法的形参即可将此类请求参数转换为java对象

java 复制代码
<input type="button" value="测试@RequestBody获取json格式的请求参数"
@click="testRequestBody()"><br>
<script type="text/javascript" th:src="@{/js/vue.js}"></script>
<script type="text/javascript" th:src="@{/js/axios.min.js}"></script>
<script type="text/javascript">
var vue = new Vue({
	el:"#app",
	methods:{
	testRequestBody(){
	axios.post(
	"/SpringMVC/test/RequestBody/json",
	{username:"admin",password:"123456"}
	).then(response=>{
	console.log(response.data);
	});
}
}
});
</script>
java 复制代码
@RequestMapping("/test/RequestBody/json")
public void testRequestBody(@RequestBody Map<String, Object> map,
	HttpServletResponse response) throws IOException {
	System.out.println(map);
	//{username=admin, password=123456}
	//response.getWriter()获取了响应的PrintWriter对象,然后使用print方法发送字符串。这将是发送回客户端的数据。
	response.getWriter().print("hello,axios");
}

HttpServletResponse response:这个参数允许方法与HTTP响应交互,例如发送数据回客户端。
@ResponseBody

@ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器

java 复制代码
@RequestMapping("/testResponseBody")
public String testResponseBody(){
	//此时会跳转到逻辑视图success所对应的页面
	return "success";
}
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
	//此时响应浏览器数据success
	return "success";
}

@ResponseBody响应浏览器json数据

java 复制代码
<input type="button" value="测试@ResponseBody响应浏览器json格式的数据"
@click="testResponseBody()"><br>
<script type="text/javascript" th:src="@{/js/vue.js}"></script>
<script type="text/javascript" th:src="@{/js/axios.min.js}"></script>
<script type="text/javascript">
	var vue = new Vue({
	el:"#app",
	methods:{
	testResponseBody(){
	axios.post("/SpringMVC/test/ResponseBody/json").then(response=>{
	console.log(response.data);
		});
	}
	}
	});
</script>
java 复制代码
//响应浏览器list集合
@RequestMapping("/test/ResponseBody/json")
@ResponseBody
public List<User> testResponseBody(){
	User user1 = new User(1001,"admin1","123456",23,"男");
	User user2 = new User(1002,"admin2","123456",23,"男");
	User user3 = new User(1003,"admin3","123456",23,"男");
	List<User> list = Arrays.asList(user1, user2, user3);
	return list;
}
//响应浏览器map集合
@RequestMapping("/test/ResponseBody/json")
@ResponseBody
public Map<String, Object> testResponseBody(){
	User user1 = new User(1001,"admin1","123456",23,"男");
	User user2 = new User(1002,"admin2","123456",23,"男");
	User user3 = new User(1003,"admin3","123456",23,"男");
	Map<String, Object> map = new HashMap<>();
	map.put("1001", user1);
	map.put("1002", user2);
	map.put("1003", user3);
	return map;
}
//响应浏览器实体类对象
@RequestMapping("/test/ResponseBody/json")
@ResponseBody
public User testResponseBody(){
	return user;
}

拦截器

SpringMVC中的拦截器用于拦截控制器方法的执行

SpringMVC中的拦截器需要实现HandlerInterceptor

SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置:
拦截器的三个抽象方法

SpringMVC中的拦截器有三个抽象方法:
preHandle :控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法
postHandle :控制器方法执行之后执行postHandle()
afterCompletion :处理完视图和模型数据,渲染视图完毕之后执行afterCompletion()
多个拦截器的执行顺序

①若每个拦截器的preHandle()都返回true此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:preHandle()会按照配置的顺序执行,而postHandle()和afterCompletion()会按照配置的反序执行

②若某个拦截器的preHandle()返回了false

preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterCompletion()会执行

异常处理器

SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver

HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver和SimpleMappingExceptionResolver
基于注解的异常处理

java 复制代码
@ControllerAdvice
public class ExceptionController {
//@ExceptionHandler用于设置所标识方法处理的异常
	@ExceptionHandler(ArithmeticException.class)
//ex表示当前请求处理中出现的异常对象
	public String handleArithmeticException(Exception ex, Model model){
	model.addAttribute("ex", ex);
	return "error";
	}
}

注解配置SpringMVC

使用配置类和注解代替web.xml和SpringMVC配置文件的功能
创建SpringConfig配置类,代替spring的配置文件

java 复制代码
@Configuration
public class SpringConfig {
//ssm整合之后,spring的配置信息写在此类中
}

创建WebConfig配置类,代替SpringMVC的配置文件

java 复制代码
@Configuration
//扫描组件
@ComponentScan("com.atguigu.mvc.controller")
//开启MVC注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
//使用默认的servlet处理静态资源
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer
configurer) {
	configurer.enable();
}
//配置文件上传解析器
@Bean
public CommonsMultipartResolver multipartResolver(){
	return new CommonsMultipartResolver();
}
//配置拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
	FirstInterceptor firstInterceptor = new FirstInterceptor();
	registry.addInterceptor(firstInterceptor).addPathPatterns("/**");
}

SpringMVC执行流程

  • DispatcherServlet:前端控制器,不需要工程师开发,由框架提供作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求
  • HandlerMapping:处理器映射器,不需要工程师开发,由框架提供作用:根据请求的url、method等信息查找Handler,即控制器方法
  • Handler:处理器,需要工程师开发
    作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理
  • HandlerAdapter:处理器适配器,不需要工程师开发,由框架提供
    作用:通过HandlerAdapter对处理器(控制器方法)进行执行
  • ViewResolver:视图解析器,不需要工程师开发,由框架提供
    作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、 RedirectView
  • View:视图 作用:将模型数据通过页面展示给用户

SpringMVC的执行流程

  1. 用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获。
  2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:
    a) 不存在
    i. 再判断是否配置了mvc:default-servlet-handler
    ii. 如果没配置,则控制台报映射查找不到,客户端展示404错误
    b) 存在则执行下面的流程
  3. 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及
    Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回。
  4. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。
  5. 如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(...)方法【正向】
  6. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。
    在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
    a) HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定
    的响应信息
    b) 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
    c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
    d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
  7. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。
  8. 此时将开始执行拦截器的postHandle(...)方法【逆向】。
  9. 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行
    HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model
    和View,来渲染视图。
  10. 渲染视图完毕执行拦截器的afterCompletion(...)方法【逆向】。
  11. 将渲染结果返回给客户端。
相关推荐
懒羊羊不懒@11 分钟前
Java基础语法—最小单位、及注释
java·c语言·开发语言·数据结构·学习·算法
ss27315 分钟前
手写Spring第4弹: Spring框架进化论:15年技术变迁:从XML配置到响应式编程的演进之路
xml·java·开发语言·后端·spring
DokiDoki之父26 分钟前
MyBatis—增删查改操作
java·spring boot·mybatis
兩尛43 分钟前
Spring面试
java·spring·面试
Java中文社群1 小时前
服务器被攻击!原因竟然是他?真没想到...
java·后端
Full Stack Developme1 小时前
java.nio 包详解
java·python·nio
零千叶1 小时前
【面试】Java JVM 调优面试手册
java·开发语言·jvm
代码充电宝1 小时前
LeetCode 算法题【简单】290. 单词规律
java·算法·leetcode·职场和发展·哈希表
li3714908902 小时前
nginx报400bad request 请求头过大异常处理
java·运维·nginx
摇滚侠2 小时前
Spring Boot 项目, idea 控制台日志设置彩色
java·spring boot·intellij-idea