Spring MVC
**概述:**SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
特点:
- 基于MVC设计模型的请求驱动类型的轻量级Web框架(JSP是事件驱动)
- 可以集成其他的Web框架
- 它通过一套注解,让一个简单的Java类成为处理器
- 支持RESTful编程风格的请求
优点:
- 使用简单,开发便捷(相比于Servlet)
- 灵活性强
**作用:**起到控制器的作用,用来代替以前的Servlet开发
在原生Web开发中需要创建多个Servlet,使用SpringMVC只需要一个Servlet 以及原生Web开发的json格式数据转换也由SpringMVC完成了,无需程序员主动转换
# 五大组件
前端控制器:DispatcherServlet
用户请求到达前端控制器,它就相当于 MVC 模式中的 C,DispatcherServlet 是整个流程控制的中心,由 它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性。处理器映射器:HandlerMapping HandlerMapping
负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的 映射方式,例如:配置文件方式,实现接口方式,注解方式等。处理器适配器:HandlerAdapter
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理 器进行执行。处理器:Handler
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由 Handler 对具体的用户请求进行处理。视图解析器:View Resolver View Resolver
负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名,即 具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。视图:View SpringMVC
框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。最 常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程 序员根据业务需求开发具体的页面

①用户发送请求至前端控制器DispatcherServlet。
② DispatcherServlet收到请求调用HandlerMapping处理器映射器。
③处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
④ DispatcherServlet调用HandlerAdapter处理器适配器。
⑤ HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
⑥ Controller执行完成返回ModelAndView。
⑦ HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
⑧ DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
⑨ ViewReslover解析后返回具体View。
⑩ DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。DispatcherServlet响应用户。
# 使用步骤
导入坐标(SpringMVC+Servlet)
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>day36-01-springmvc-begin</artifactId>
<version>1.0-SNAPSHOT</version>
<name>day36-01-springmvc-begin</name>
<packaging>war</packaging>
<dependencies>
<!-- servlet包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- 导入springmvc的包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
**注意:**导入spring-webmvc坐标自动依赖spring相关坐标
定义处理请求的功能类(UserController)
//定义表现层控制器bean
@Controller
public class UserController {
// 设置映射路径为/save,即外部访问路径
@RequestMapping("/save")
// 设置当前操作返回结果为指定json数据(本质上是一个字符串信息)
// 在默认情况下,返回的字符串是服务器要跳转的地址 如:return "/index.jsp"
// @RequestMapping("/save",produces = "application/json;charset=utf-8")设置响应编码
@ResponseBody
public String save(){
System.out.println("user save ...");
return "{'info':'springmvc'}";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
**注意:**对于SpringMVC而言,Controller方法返回值默认表示要跳转的页面,没有对应的页面就会报错。如果不想跳转页面而是响应数据,那么就需要在方法上使用@ResponseBody注解。
编写SpringMVC配置类,加载处理请求的Bean。
//springmvc配置类,本质上还是一个spring配置类
@Configuration
@ComponentScan("com.itheima.controller")
public class SpringMvcConfig {
}
2
3
4
5
加载SpringMVC配置,并设置SpringMVC请求拦截的路径
/**
* Tomcat启动时自动加载的配置类,并且读取Spring容器和SpringMVC容器的配置类
*/
public class ServletConfig extends AbstractDispatcherServletInitializer {
/**
* 创建SpringMVC子容器
* @return
*/
@Override
protected WebApplicationContext createServletApplicationContext() {
// 创建一个注解方式的Web SpringMVC容器作为子容器
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
// 注册SpringMVC子容器配置类
ctx.register(SpringMvcConfig.class);
// 在Web容器中创建
return ctx;
}
/**
* 设置SpringMVC拦截的地址
* @return
*/
@Override
protected String[] getServletMappings() {
// 拦截所有的资源访问,交给SpringMVC处理
return new String[]{"/"};
}
/**
* 创建Spring父容器
* @return
*/
@Override
protected WebApplicationContext createRootApplicationContext() {
// 创建一个注解方式的Web Spring容器作为父容器
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
// 注册Spring父容器配置类
ctx.register(SpringConfig.class);
// 在Web容器中创建
return ctx;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
运行结果
# 相关常用注解
# @Controller注解
- 名称:@Controller
- 类型:类注解
- 位置:SpringMVC控制器类定义上方
- 作用:设定SpringMVC的核心控制器bean
- 范例
@Controller
public class UserController {
}
2
3
# @RequestMapping注解
- 名称:@RequestMapping
- 类型:方法注解
- 位置:SpringMVC控制器方法定义上方或者类上方
- 作用:设置当前控制器方法请求访问路径
- 范例
无参请求
@RequestMapping("/save")
public void save(){
System.out.println("user save ...");
}
2
3
4
注意:@RequestMapping注解还可以写到类上面,相当于模块的路径,如:/user/save
# GET请求传递普通参数
- 普通参数:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数
http://localhost:8080/commonParam?name=张三&age=12
//普通参数:请求参数与形参名称对应即可完成参数传递
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name ,int age){
System.out.println("普通参数传递 name ==> "+name);
System.out.println("普通参数传递 age ==> "+age);
return "{'module':'common param'}";
}
2
3
4
5
6
7
8
# POST请求传递普通参数
- 普通参数:form表单post请求传参,表单参数名与形参变量名相同,定义形参即可接收参数

//普通参数:请求参数与形参名称对应即可完成参数传递
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name ,int age){
System.out.println("普通参数传递 name ==> "+name);
System.out.println("普通参数传递 age ==> "+age);
return "{'module':'common param'}";
}
2
3
4
5
6
7
8
POST请求中文乱码处理
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
//乱码处理
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
2
3
4
5
6
7
8
9
10
在加载SpringMVC配置的配置类中指定字符过滤器。
# @RequestParam
- 名称:@RequestParam
- 类型:形参注解
- 位置:SpringMVC控制器方法形参定义前面
- 作用:绑定请求参数与处理器方法形参间的关系
- 参数:
- required:是否为必传参数,默认为true
- defaultValue:参数默认值
//普通参数:请求参数名与形参名不同时,使用@RequestParam注解关联请求参数名称与形参名称之间的关系
@RequestMapping("/commonParamDifferentName")
@ResponseBody
public String commonParamDifferentName(@RequestParam("name") String userName , int age){
System.out.println("普通参数传递 userName ==> "+userName);
System.out.println("普通参数传递 age ==> "+age);
return "{'module':'common param different name'}";
}
2
3
4
5
6
7
8
**注意:**默认将@RequestParam标注的形参视为必传的参数,如果没有传递则会报404
# @RequestBody
名称:@RequestBody
类型:形参注解
位置:SpringMVC控制器方法形参定义前面
作用:从请求体中获取数据封装到对象中,此注解一个处理器方法只能使用一次,
因为每次请求都只有一个请求体
范例:
@RequestMapping("/listParamForJson")
@ResponseBody
public String listParamForJson(@RequestBody List<String> likes){
System.out.println("list common(json)参数传递 list ==> "+likes);
return "{'module':'list common for json param'}";
}
2
3
4
5
6
# @PathVariable
- 名称:@PathVariable
- 类型:形参注解
- 位置:SpringMVC控制器方法形参定义前面
- 作用:绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应
@RequestMapping(value = "/users/{id}" ,method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id){
System.out.println("user delete..." + id);
return "{'module':'user delete'}";
}
2
3
4
5
6
# @RequestBody、@RequestParam、@PathVariable区别和应用
- 区别
@RequestParam用于接收url地址传参或表单传参,地址栏**?**后的参数
@RequestBody用于接收json数据,即**请求体**中的JSON字符串
@PathVariable用于接收路径参数,使用**/{参数名称}**描述路径参数 - 应用
后期开发中,发送请求参数超过1个,以json格式为主,@RequestBody应用较广
如果发送非json格式数据,选用@RequestParam接收请求参数
采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求路径变量,通常用于传递id值
# @ResponseBody
- 名称:@ResponseBody
- 类型:方法注解
- 位置:SpringMVC控制器方法定义上方
- 作用:设置当前控制器方法响应内容为当前返回值,无需解析
- 范例
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("user save ...");
return "{'info':'springmvc'}";
}
2
3
4
5
6
扩展: 如果将@ResponseBody放在类上,则相当于每个方法都添加了@ResponseBody注解 @RestController = @Controller + @ResponseBody
# @EnableWebMvc
- 名称:@EnableWebMvc
- 类型:配置类注解
- 位置:SpringMVC配置类定义上方
- 作用:开启WebMVC的多项组件功能:如开启自动转换json数据的支持,相当于使当前类继承WebMvcConfigurationSupport这个类,并且使所有的组件配置都设置为默认,如果已经定义了一个类继承WebMvcConfigurationSupport则不需要这个注解
- 范例:
@Configuration
@ComponentScan("com.itheima.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
2
3
4
5
@EnableWebMvc注解功能强大,该注解整合了多个功能,json数据进行自动类型转换只是其中之一
# @RestController
- 名称:@RestController
- 类型:类注解
- 位置:基于SpringMVC的RESTful开发控制器类定义上方
- 作用:设置当前控制器类为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能
@RestController //使用@RestController注解替换@Controller与@ResponseBody注解,简化书写
@RequestMapping("/books")
public class BookController {
//方法省略了没写
}
2
3
4
5
# SpringMVC设置对静态资源的放行
由于SpringMVC中的Web容器配置了SpringMVC的拦截地址为/,将所有的资源请求交给SpringMVC处理,但是静态资源不需要由SpringMVC处理,所以需要放行。
WebMvcConfigurationSupport:配置 springmvc 所需所有组件
这个类中会定义 springmvc 需要的所有组件,比如:RequestMapping、HandlerAdapter、HandlerInterceptor、HttpMessageConverter、HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler 等等,所以如果你感觉@EnableWebMvc 注解满足不了你的需求时,你可以直接继承这个类进行扩展。
方法:在config包编写类继承于WebMvcConfigurationSupport,中重写addResourceHandlers方法,在类上添加@Configuration注解。
- addResourceHandler映射的地址,/**表示包含子孙目录
- addResourceLocations表示物理存在的地址,即真实地址,必须以/结尾
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport{
// 配置静态资源处理器
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//当访问/pages/xxx时候,走/pages目录下的内容
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# SpringMVC拦截器
**概念:**拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行
**作用: **
- 在指定的方法调用前后执行预先设定的代码
- 阻止原始方法的执行
- 总结:增强
核心原理:AOP思想
# 拦截器和过滤器的区别
- 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
- 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强(主要是对控制器进行增强)

# 拦截器的定义
定义一个类,实现HandlerInterceptor接口
public class BookInterceptor implements HandlerInterceptor { //定义拦截器类,实现HandlerInterceptor接口
/**
* 前置增强/拦截:在控制器方法前执行
* @param request 请求对象
* @param response 响应对象
* @param handler HandlerMethod对象,用于获取控制器对象和方法
* @return 返回true,表示继续执行后面的控制器方法,返回false,后面的控制器方法不执行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("前置通知");
return true;
}
/**
* 后置增强/拦截:在控制器方法后执行
* @param request 请求对象
* @param response 响应对象
* @param handler HandlerMethod对象,用于获取控制器对象和方法
* @param modelAndView 获取控制器中返回的模型和视图对象(作用域中有哪些键和值,跳转到哪个页面去)
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("后置通知");
}
/**
* 最终增强/拦截:无论控制器方法是否抛出异常,都会执行
* @param request 请求对象
* @param response 响应对象
* @param handler HandlerMethod对象,用于获取控制器对象和方法
* @param ex 如果控制器中出现了异常,获取到那个异常
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("最终通知");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 拦截器的配置
方法1:【推荐】
注:@Configuration注解已经包含@Component的功能
- 在上面添加静态资源的配置类中重写addInterceptors方法
- 添加拦截器和多个拦截路径:/book和/book/**
- 要注入拦截器对象
- SpringMvcConfig需要添加扫包
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Override
protected void addInterceptors(InterceptorRegistry registry) {
//注解拦截器和拦截控制器方法请求地址
registry.addInterceptor(new BookInterceptor()).addPathPatterns("/book/*");
}
}
2
3
4
5
6
7
8
9
方法2:
使用标准接口WebMvcConfigurer简化开发(注意:侵入式较强,有背Spring的理念)【不推荐】
- 在SpringMvcConfig主配置类上实现WebMvcConfigurer接口,接口中全是默认方法
- 注入拦截器对象,重写addInterceptors方法
@Configuration
//同时扫描控制器和配置类所在的包
@ComponentScan({"com.itheima.controller","com.itheima.config"})
@EnableWebMvc // 使用实现接口的方式需要开启辅助功能
public class SpringMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new BookInterceptor()).addPathPatterns("/book/*");
}
}
2
3
4
5
6
7
8
9
10
11
注:与方式一两者只能选一种,不然会有冲突,如果方式一起作用会导致第二种方式的拦截器不起使用。
因为如果项目中出现了一次 extends WebMvcConfigurationSupport ,其他的 extends WebMvcConfigurationSupport 和 implements WebMvcConfigurer 会失效 。
# 拦截器执行流程

拦截器链的情况
- 当配置多个拦截器时,形成拦截器链
- 拦截器链的运行顺序参照拦截器添加顺序为准
- 当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行
- 当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作


# SpringMVC实现Servlet
在SpringMVC中,有两个应用上下文:RootApplicationContext、WebApplicationContext。
WebApplicationContext是DispatcherServlet专属的上下文,用来加载Controller、ViewResolver、HandlerMapping等web相关的Bean。
RootApplicationContext则是加载数据库、Service业务层等中间件中的Bean。其中,WebApplicationContext继承了RootApplicationContext中的所有Bean,以便在@Controller中注入@Service等依赖。WebApplicationContext是子容器可以使用父容器中的对象
在 Servlet 3.0 环境下,Servlet 容器会在 classpath 下搜索实现了 javax.servlet.ServletContainerInitializer 接口的任何类,找到之后用它来初始化 Servlet 容器。
Spring 实现了以上接口,实现类叫做 SpringServletContainerInitializer, 它会依次搜寻实现了 WebApplicationInitializer接口的任何类,并委派这个类实现配置。而AbstractDispatcherServletInitializer的父类则实现了WebApplicationInitializer接口。

# AbstractDispatcherServletInitializer类
- AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类
- AbstractDispatcherServletInitializer提供三个接口方法供用户实现
- createServletApplicationContext()方法,创建Servlet容器时,加载SpringMVC对应的bean并放入WebApplicationContext对象范围中,而WebApplicationContext的作用范围为ServletContext范围,即整个web容器范围。(相当于是加载了所有的服务器资源到上下文域中)
//加载springmvc配置类,产生springmvc容器(本质还是spring容器)
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
2
3
4
5
6
- getServletMappings()方法,设定SpringMVC对应的请求映射路径,设置为
/表示拦截所有请求,任意请求都将转入到SpringMVC进行处理。
//设置由springmvc控制器处理的请求映射路径
protected String[] getServletMappings() {
return new String[]{"/"};
}
2
3
4
- createRootApplicationContext()方法,如果创建Servlet容器时需要加载非SpringMVC对应的bean,即加载Spring容器中业务bean,使用这个方法进行,使用方式同createServletApplicationContext()
//加载spring配置类
protected WebApplicationContext createRootApplicationContext() {
return null;
}
2
3
4
# SpringMVC执行流程
启动服务器初始化过程
- 服务器启动,执行ServletConfig类(即继承了AbstractDispatcherServletInitializer的类),初始化web容器
- 执行createServletApplicationContext方法,创建了WebApplicationContext对象
- 加载SpringMvcConfig配置类
- 执行@ComponentScan加载对应的bean
- 加载UserController,每个@RequestMapping的名称对应一个具体的方法
- 执行getServletMappings方法,定义所有的请求都通过SpringMVC
单次请求过程
- 发送请求localhost/save
- web容器发现所有请求都经过SpringMVC,将请求交给SpringMVC处理
- 解析请求路径/save
- 由/save匹配执行对应的方法save()
- 执行save()
- 检测到有@ResponseBody直接将save()方法的返回值作为响应求体返回给请求方
# Spring整合SpringMVC的本质
Spring和SpringMVC框架的整合本质上是两个Spring容器的整合,因为我们知道SpringMVC框架本质也是一个Spring容器,所以这里所谓的整合就是在web容器中配置两个Spring容器,并让他们各司其职。
- Spring管理service,dao,事务等相关的组件的注入
- SpringMVC管理控制器相关的组件controller。
父子容器
- Spring容器对应的是父容器,SpringMVC容器对应的是子容器。从容器里面getBean的时候,先从本容器取,如果取不到再从父容器取。
- IoC容器体系中可以有多个子容器,但是父容器只有一个。不同的子容器之间不能共享bean,但是子容器都可以获得父容器中的bean信息。
- 父容器和子容器被初始化后会以属性的形式被存储在ServletContext上下文域中以供调用。因此处理器对象(子容器中)可以获取Service对象(父容器)并且注入,反过来业务对象不能获取处理器对象。
也就是说SpringMVC环境下一共存在三个容器:Web容器、SpringMVC容器、Spring容器
Web容器通过加载SpringMVC容器的Bean来访问资源,而SpringMVC通过使用Spring容器的Bean来完成业务功能
# Controller加载控制与业务bean加载控制
项目结构
SpringMVC相关bean(表现层bean):controller包下的类
Spring控制的bean:
- 业务bean(Service):service包下的类
- 功能bean(DataSource等):dao包下的类及
在加载Spring控制的bean的时候排除掉SpringMVC控制的bean。
避免Spring错误的加载到SpringMVC的bean,即重复加载SpringMVC加载过的Bean
SpringMVC相关bean加载控制
SpringMVC加载的bean对应的包均在com.itheima.controller包内
Spring相关bean加载控制
方式一:Spring加载的bean设定扫描范围为com.itheima,排除掉controller包内的bean
方式二:Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等【推荐】
方式三:不区分Spring与SpringMVC的环境,加载到同一个环境中【不推荐】
方式一代码实现
- 名称:@ComponentScan
- 类型:类注解
- 范例:Spring的配置文件
@Configuration
@ComponentScan(value = "com.itheima",
// 排除所有是Controller注解的类扫描
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
)
)
public class SpringConfig {
}
2
3
4
5
6
7
8
9
10
- 属性
- excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)与具体项(classes),type默认取值就是注解类型。@ComponentScan.Filter是注解内部的注解
- includeFilters:加载指定的bean,需要指定类别(type)与具体项(classes)
方式二的代码实现【简易版】
Spring 3.2 开始引入一个简易的 WebApplicationInitializer 实现类AbstractAnnotationConfigDispatcherServletInitializer。
Spring容器
@Configuration
@ComponentScan("cn.kk.service")
public class SpringConfig {
}
2
3
4
5
SpringMVC容器
@Configuration
@ComponentScan("cn.kk.controller")
public class SpringMvcConfig {
}
2
3
4
Web容器
/**
* Tomcat启动时自动加载的配置类,并且读取Spring容器和SpringMVC容器的配置类
*/
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 创建SpringMVC子容器
* @return
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
/**
* 创建Spring父容器
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
/**
* 设置SpringMVC拦截的地址
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 请求与响应参数传递
# 请求参数传递
# 普通参数
- 普通参数:当请求参数名与形参变量名不同,使用@RequestParam绑定参数关系

//普通参数:请求参数名与形参名不同时,使用@RequestParam注解关联请求参数名称与形参名称之间的关系
@RequestMapping("/commonParamDifferentName")
@ResponseBody
public String commonParamDifferentName(@RequestParam("name") String userName , int age){
System.out.println("普通参数传递 userName ==> "+userName);
System.out.println("普通参数传递 age ==> "+age);
return "{'module':'common param different name'}";
}
2
3
4
5
6
7
8
- 名称:@RequestParam
- 类型:形参注解
- 位置:SpringMVC控制器方法形参定义前面
- 作用:绑定请求参数与处理器方法形参间的关系
- 参数:
- required:是否为必传参数
- defaultValue:参数默认值
# POJO类型参数
- POJO参数:请求参数名与形参对象属性名相同,定义POJO类型形参即可接收参数
public class User {
private String name;
private int age;
//省略getter/setter/toString()方法
}
2
3
4
5
//POJO参数:请求参数与形参对象中的属性对应即可完成参数传递
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
System.out.println("pojo参数传递 user ==> "+user);
return "{'module':'pojo param'}";
}
2
3
4
5
6
7
# 嵌套POJO类型参数
- POJO对象中包含POJO对象

public class User {
private String name;
private int age;
private Address address;
//同学们自己添加getter/setter/toString()方法
}
public class Address {
private String province;
private String city;
}
2
3
4
5
6
7
8
9
10
- 嵌套POJO参数:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数
//嵌套POJO参数:嵌套属性按照层次结构设定名称即可完成参数传递
@RequestMapping("/pojoContainPojoParam")
@ResponseBody
public String pojoContainPojoParam(User user){
System.out.println("pojo嵌套pojo参数传递 user ==> "+user);
return "{'module':'pojo contain pojo param'}";
}
2
3
4
5
6
7
**注意:**请求参数key的名称要和pojo中属性的名称一致,否则无法封装
# 数组类型参数
- 数组参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型即可接收参数

//数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中
@RequestMapping("/arrayParam")
@ResponseBody
public String arrayParam(String[] likes){
System.out.println("数组参数传递 likes ==> "+ Arrays.toString(likes));
return "{'module':'array param'}";
}
2
3
4
5
6
7
# 集合类型参数
- 集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系

// 集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes){
System.out.println("集合参数传递 likes ==> "+ likes);
return "{'module':'list param'}";
}
2
3
4
5
6
7
# json数据传递
注意:使用json数据传递报415错误则是未导入json的依赖
# json数据参数介绍
- json普通数组(["","","",...])
- json对象({key:value,key:value,...})
- json对象数组([{key:value,...},{key:value,...}])
注意:json参数传递需要开启自动转换json数据的支持,@EnableWebMvc或定义类继承WebMvcConfigurationSupport
@Configuration
@ComponentScan("com.itheima.controller")
//开启webmvc组件
@EnableWebMvc
public class SpringMvcConfig {
}
2
3
4
5
6
# 传递json对象
- POJO参数:json数据与形参对象属性名相同,定义POJO类型形参即可接收参数

//POJO参数:json格式
//1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
//2.使用@RequestBody注解将外部传递的json数据映射到形参的实体类对象中,要求属性名称一一对应
@RequestMapping("/pojoParamForJson")
@ResponseBody
public String pojoParamForJson(@RequestBody User user){
System.out.println("pojo(json)参数传递 user ==> "+user);
return "{'module':'pojo for json param'}";
}
2
3
4
5
6
7
8
9
# 传递json对象数组
- POJO集合参数:json数组数据与集合中每个对象的属性名相同,定义List类型形参即可接收参数
public class User {
private String name;
private Integer age;
}
2
3
4
//集合参数:json格式
//1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
//2.使用@RequestBody注解将外部传递的json数组数据映射到形参的保存实体类对象的集合对象中,要求属性名称一一对应
@RequestMapping("/listPojoParamForJson")
@ResponseBody
public String listPojoParamForJson(@RequestBody List<User> list){
System.out.println("list pojo(json)参数传递 list ==> "+list);
return "{'module':'list pojo for json param'}";
}
2
3
4
5
6
7
8
9
# 日期类型参数传递
示例
- 日期类型数据基于系统不同格式也不尽相同
2088-08-18
2088/08/18
08/18/2088 - 接收形参时,根据不同的日期格式设置不同的接收方式

使用@DateTimeFormat注解设置日期类型数据格式,默认格式yyyy/MM/dd
//日期参数 http://localhost:80/dataParam?date=2088/08/08&date1=2088-08-18&date2=2088/08/28 8:08:08
@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date,
@DateTimeFormat(pattern="yyyy-MM-dd") Date date1,
@DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss") Date date2){
System.out.println("参数传递 date ==> "+date);
System.out.println("参数传递 date1(yyyy-MM-dd) ==> "+date1);
System.out.println("参数传递 date2(yyyy/MM/dd HH:mm:ss) ==> "+date2);
return "{'module':'data param'}";
}
2
3
4
5
6
7
8
9
10
11
@DateTimeFormat注解介绍
- 名称:@DateTimeFormat
- 类型:形参注解
- 位置:SpringMVC控制器方法形参前面
- 作用:设定日期时间型数据格式
- 属性:pattern:指定日期时间格式字符串
工作原理
- 其内部依赖Converter接口
public interface Converter<S, T> {
@Nullable
T convert(S var1);
}
2
3
4
- 请求参数年龄数据(String→Integer)
- json数据转对象(json → POJO)
- 日期格式转换(String → Date)
注意:
传递日期类型参数必须在配置类上使用@EnableWebMvc注解。其功能之一:根据类型匹配对应的类型转换器。
# 响应参数传递
# 跳转/响应页面
@Controller
public class UserController {
//响应页面/跳转页面
//返回值为String类型,设置返回值为页面名称,即可实现页面跳转
@RequestMapping("/toJumpPage")
public String toJumpPage(){
System.out.println("跳转页面");
return "page.jsp";
}
}
2
3
4
5
6
7
8
9
10
11
# 响应文本数据
//响应文本数据
//返回值为String类型,设置返回值为任意字符串信息,即可实现返回指定字符串信息,需要依赖@ResponseBody注解
@RequestMapping("/toText")
@ResponseBody
public String toText(){
System.out.println("返回纯文本数据");
return "response text";
}
2
3
4
5
6
7
8
# 响应JSON对象
//响应POJO对象
//返回值为实体类对象,设置返回值为实体类类型,即可实现返回对应对象的json数据,需要依赖@ResponseBody注解和@EnableWebMvc注解
@RequestMapping("/toJsonPOJO")
@ResponseBody
public User toJsonPOJO(){
System.out.println("返回json对象数据");
User user = new User();
user.setName("zhangsan");
user.setAge(15);
return user;
}
2
3
4
5
6
7
8
9
10
11
# 响应JSON对象集合
//响应POJO集合对象
//返回值为集合对象,设置返回值为集合类型,即可实现返回对应集合的json数组数据,需要依赖@ResponseBody注解和@EnableWebMvc注解
@RequestMapping("/toJsonList")
@ResponseBody
public List<User> toJsonList(){
System.out.println("返回json集合数据");
User user1 = new User();
user1.setName("李四");
user1.setAge(15);
User user2 = new User();
user2.setName("张三");
user2.setAge(12);
List<User> userList = new ArrayList<User>();
userList.add(user1);
userList.add(user2);
return userList;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
注意:需要添加jackson-databind依赖以及在SpringMvcConfig配置类上添加@EnableWebMvc注解
# 向请求域中存放数据

# RESTful风格编程
特点
- 每一个URI代表1种资源
- 客户端使用GET、POST、PUT、DELETE四个表示操作方式的动词对服务端资源进行操作
- GET用来获取资源
- POST用来新建资源
- PUT用来更新资源
- DELETE用来删除资源
访问风格对比
注意:
- 上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范
- 描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users、books、accounts……
示例
@Controller
public class UserController {
// 设置当前请求方法为POST,表示REST风格中的添加操作,并且只对该请求类型做出响应
@RequestMapping(value = "/users",method = RequestMethod.POST)
@ResponseBody
public String save(){
System.out.println("user save...");
return "{'module':'user save'}";
}
//设置当前请求方法为DELETE,表示REST风格中的删除操作,并且只对该请求类型做出响应
//@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同
@RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id){
System.out.println("user delete..." + id);
return "{'module':'user delete'}";
}
//设置当前请求方法为PUT,表示REST风格中的修改操作,并且只对该请求类型做出响应
@RequestMapping(value = "/users",method = RequestMethod.PUT)
@ResponseBody
public String update(@RequestBody User user){
System.out.println("user update..."+user);
return "{'module':'user update'}";
}
//设置当前请求方法为GET,表示REST风格中的查询操作,并且只对该请求类型做出响应
//@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同
@RequestMapping(value = "/users/{id}" ,method = RequestMethod.GET)
@ResponseBody
public String getById(@PathVariable Integer id){
System.out.println("user getById..."+id);
return "{'module':'user getById'}";
}
//设置当前请求方法为GET,表示REST风格中的查询操作,并且只对该请求类型做出响应
@RequestMapping(value = "/users",method = RequestMethod.GET)
@ResponseBody
public String getAll(){
System.out.println("user getAll...");
return "{'module':'user getAll'}";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# @PathVariable
- 名称:@PathVariable
- 类型:形参注解
- 位置:SpringMVC控制器方法形参定义前面
- 作用:绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应

# 快速开发
| 注解 | 作用 |
|---|---|
| @PathVariable | 放在方法的形参前面,用来说明这个参数从地址的路径上获取 |
| @PostMapping | @RequestMapping(method = RequestMethod.POST) 限制使用POST提交 |
| @GetMapping | @RequestMapping(method = RequestMethod.GET) 限制使用GET提交 |
| @DeleteMapping | @RequestMapping(method = RequestMethod.DELETE) 限制使用DELETE提交 |
| @PutMapping | @RequestMapping(method = RequestMethod.PUT) 限制使用PUT提交 |
- 名称:@GetMapping @PostMapping @PutMapping @DeleteMapping
- 类型:方法注解
- 位置:基于SpringMVC的RESTful开发控制器方法定义上方
- 作用:设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如@GetMapping对应GET请求
- 属性:
value(默认):请求访问路径
示例
@Controller
@RequestMapping("/books")
public class BookController {
// @RequestMapping( method = RequestMethod.POST)
@PostMapping//使用@PostMapping简化Post请求方法对应的映射配置
public String save(@RequestBody Book book){
System.out.println("book save..." + book);
return "{'module':'book save'}";
}
// @RequestMapping(value = "/{id}" ,method = RequestMethod.DELETE)
@DeleteMapping("/{id}") //使用@DeleteMapping简化DELETE请求方法对应的映射配置
public String delete(@PathVariable Integer id){
System.out.println("book delete..." + id);
return "{'module':'book delete'}";
}
// @RequestMapping(method = RequestMethod.PUT)
@PutMapping //使用@PutMapping简化Put请求方法对应的映射配置
public String update(@RequestBody Book book){
System.out.println("book update..."+book);
return "{'module':'book update'}";
}
// @RequestMapping(value = "/{id}" ,method = RequestMethod.GET)
@GetMapping("/{id}") //使用@GetMapping简化GET请求方法对应的映射配置
public String getById(@PathVariable Integer id){
System.out.println("book getById..."+id);
return "{'module':'book getById'}";
}
// @RequestMapping(method = RequestMethod.GET)
@GetMapping //使用@GetMapping简化GET请求方法对应的映射配置
public String getAll(){
System.out.println("book getAll...");
return "{'module':'book getAll'}";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 表现层数据封装
由于业务方法的返回值五花八门,查询所有是集合,查询单个是对象,新增删除修改返回一个布尔类型,在前后端分离的环境中,这是极其不利于前端解析的。

# 定义Result类封装响应结果
/**
* 封装结果信息,属性可以根据需求增加或减少
*/
@Data
public class Result {
//描述统一格式中的数据
private Object data;
//描述统一格式中的响应编码,用于区分操作(成功/失败),可以简化配置0或1表示成功失败
private Integer code;
//描述统一格式中的消息,可选属性
private String msg;
public Result() {
}
//状态码,数据
public Result(Integer code, Object data) {
this.data = data;
this.code = code;
}
//状态码,数据,信息
public Result(Integer code, Object data, String msg) {
this.data = data;
this.code = code;
this.msg = msg;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Code类封装响应编码
使用接口来封装好处在,常量默认就是使用public static final修饰,并且无法实例化,使用枚举也可以
//状态码
public interface Code {
Integer SAVE_OK = 20011;
Integer DELETE_OK = 20021;
Integer UPDATE_OK = 20031;
Integer GET_OK = 20041;
Integer SAVE_ERR = 20010;
Integer DELETE_ERR = 20020;
Integer UPDATE_ERR = 20030;
Integer GET_ERR = 20040;
Integer SYSTEM_ERR = 50001;
Integer SYSTEM_TIMEOUT_ERR = 50002;
Integer SYSTEM_UNKNOW_ERR = 59999;
Integer BUSINESS_ERR = 60002;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Code类的常量设计也不是固定的,可以根据需要自行增减,例如将查询再进行细分为GET_OK,GET_ALL_OK,GET_PAGE_OK
示例
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@PostMapping
public Result save(@RequestBody Book book) {
boolean flag = bookService.save(book);
//参数1:如果为真,返回添加成功,否则返回失败。参数2:数据 true或false
return new Result(flag ? Code.SAVE_OK:Code.SAVE_ERR, flag);
}
@PutMapping
public Result update(@RequestBody Book book) {
boolean flag = bookService.update(book);
return new Result(flag ? Code.UPDATE_OK:Code.UPDATE_ERR,flag);
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
boolean flag = bookService.delete(id);
return new Result(flag ? Code.DELETE_OK:Code.DELETE_ERR,flag);
}
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
Book book = bookService.getById(id);
//对象不为空表示成功,否则表示失败
Integer code = book != null ? Code.GET_OK : Code.GET_ERR;
//封装消息:对象不为空返回空串,否则返回查询失败
String msg = book != null ? "" : "数据查询失败,请重试!";
//状态码,数据,信息
return new Result(code,book,msg);
}
@GetMapping
public Result getAll() {
List<Book> bookList = bookService.getAll();
//集合不为空则返回成功
Integer code = bookList != null ? Code.GET_OK : Code.GET_ERR;
//集合不为空返回空串,否则返回查询失败
String msg = bookList != null ? "" : "数据查询失败,请重试!";
//状态码,数据,信息
return new Result(code,bookList,msg);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 项目异常处理方案
出现异常现象的常见位置与常见诱因如下:
- 框架内部抛出的异常:因使用不合规导致
- 数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时)
- 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
- 表现层抛出的异常:因数据收集、校验等规则导致(例如:不匹配的数据类型间导致异常)
- 工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)
异常的处理位置:所有的异常均抛出至表现层进行处理
处理思路:每个方法都需要捕获处理异常→共性代码→AOP思想,异常通知
于是Spring提供了两个注解@RestControllerAdvice、@ExceptionHandler
# 异常处理器
# @RestControllerAdvice
- 名称:@RestControllerAdvice
- 类型:类注解
- 位置:Rest风格开发的控制器增强类上定义
- 作用:为Rest风格开发的控制器类做增强,目标切点是所有的Controller方法,相当于异常通知
- 说明:此注解自带@ResponseBody 注解与@Component 注解,具备对应的功能
# @ExceptionHandler
- 名称:@ExceptionHandler
- 类型:方法注解
- 位置:专用于异常处理的控制器方法上方
- 作用:设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行
- 说明:此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常
@RestControllerAdvice是对Controller进行增强的,可以全局捕获spring mvc抛的异常。并匹配相应的@ExceptionHandler中指定的异常类型,重新封装异常信息,将统一格式返回给前端。
这个类所在的包要能够被Spring MVC扫描到
示例
@RestControllerAdvice //用于标识当前类为REST风格对应的异常处理器
public class ProjectExceptionAdvice {
//统一处理所有的Exception异常
@ExceptionHandler(Exception.class)
public Result doOtherException(Exception ex) {
// 给前端响应状态码,数据,信息
return new Result(666, null, ex.getMessage());
}
}
2
3
4
5
6
7
8
9
10
# 异常处理方案
- 业务异常(BusinessException)
- 规范的用户行为产生的异常
- 不规范的用户行为操作产生的异常
- 处理:
- 发送对应消息传递给用户,提醒规范操作
- 系统异常(SystemException)
- 项目运行过程中可预计且无法避免的异常
- 处理:
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给运维人员,提醒维护
- 记录日志
- 其他异常(Exception)
- 编程人员未预期到的异常
- 处理:
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给编程人员,提醒维护(纳入预期范围内)
- 记录日志
定义业务异常类
//自定义异常处理器,用于封装异常信息,对异常进行分类
public class BusinessException extends RuntimeException{
@Getter @Setter
private Integer code;
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
}
public BusinessException(Integer code,String message,Throwable cause) {
super(message, cause);
this.code = code;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
定义系统异常类
//自定义异常处理器,用于封装异常信息,对异常进行分类
public class SystemException extends RuntimeException{
//异常的编码
@Getter @Setter
private Integer code;
//两个参数的构造方法:状态码,信息
public SystemException(Integer code, String message) {
super(message);
this.code = code;
}
//三个参数的构造方法:状态码,信息,异常
public SystemException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
定义异常通知类
@RestControllerAdvice //用于标识当前类为REST风格对应的异常处理器
public class ProjectExceptionAdvice {
//@ExceptionHandler用于设置当前处理器类对应的异常类型
@ExceptionHandler(SystemException.class)
public Result doSystemException(SystemException ex){
//记录日志
System.out.println("出现系统级异常");
//发送消息给运维
//发送邮件给开发人员,ex对象发送给开发人员
return new Result(ex.getCode(),null,ex.getMessage());
}
@ExceptionHandler(BusinessException.class)
public Result doBusinessException(BusinessException ex){
System.out.println("出现业务级异常");
return new Result(ex.getCode(),null,ex.getMessage());
}
//除了自定义的异常处理器,保留对Exception类型的异常处理,用于处理非预期的异常
@ExceptionHandler(Exception.class)
public Result doOtherException(Exception ex){
//记录日志
System.out.println("出现其它未知异常");
//发送消息给运维
//发送邮件给开发人员,ex对象发送给开发人员
return new Result(Code.SYSTEM_UNKNOW_ERR,null,"系统繁忙,请稍后再试!");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
需要在SpringMVC的注解扫描范围内