Spring MVC基础
MVC
1. MVC概念
MVC 是Model、View、Controller的缩写,分别代表Web应用程序的三种职责
- 模型:用于存储数据以及处理用户请求的业务逻辑
- 视图:向控制器提交数据,显示模型中的数据
- 控制器:根据视图提出的请求,判断将请求和数据交给什么模型去处理,处理后将有关结果交给哪个视图更新显示
2. 基于Servlet的MVC模式
基于Servlet 的MVC 模式的具体实现如下:
- 模型:一个或多个JavaBean 对象,用于存储数据(实体模型,由JavaBean类创建)和处理业务逻辑(业务模型,由一般的Java类创建)
- 视图:一个或多个JSP页面,向控制器提交数据和模型提供数据显示,JSP页面主要使用HTML标记和JavaBean标记来显示数据
- 控制器:一个或多个Servlet对象,根据视图提交的请求控制,即将请求数据转发给处理业务逻辑的JavaBean,并将处理结果存放到实体模型JavaBean中,输出给视图显示。
一、MVC的工作原理
Spring MVC框架主要由DispatcherServlet、处理器映射、控制器、视图解析器,视图组成。
Spring MVC的工作流程如下:
-
客户端提交请求到DispatcherServlet
-
由DispatcherServlet 控制器寻找一个或多个 HandlerMapping ,找到处理请求的 Controller
-
DispatcherServlet 将请求提交到Controller
-
@RunWith(SpringJUnit4ClassRunner.class) //Spring整合JUnit专用的类加载器 @ContextConfiguration(classes = SpringConfig.class) //指定Spring上下文配置 public class AccountServiceTest { @Autowired private MyService myservice; @Test9 public void myTestOne(){ /*函数实现*/ }; }
-
DispatcherServlet 寻找一个或多个 ViewResoler 视图解析器,找到ModelAndView 指定的视图
-
视图复杂将结果显示到客户端
二、Spring MVC 接口
pring MVC 的四个接口: DispatcherServlet、HandlerMapping、Controller和ViewResoler
Spring MVC 的所有请求都经过DispatcherServlet统一分发。DispatcherServlet将请求发送给Controller之前,需要借助 Spring MVC、提供的HandlerMapping定位到具体的Controller
-
HandlerMapping 接口负责完成客户请求到Controller的映射
-
Controller 接口将处理用户请求,这和Java Servlet实现的作用是一致的。一旦Controller处理完用户请求,则返回 ModelAndView对象给DispatcherServlet 前端控制器,ModelAndView 中包含模型(Model)和视图(View)。
从宏观考虑,DispatcherServlet是整个Web应用的控制器;从微观上考虑Controller 是单个HTTP请求处理过程中的控制器,而ModelAndView是HTTP请求过程中返回的模型(Model)和视图(View)
- ViewResoler接口(视图解析器)在Web应用中负责查找View对象,从而将相应结果渲染给客户
学习目标
- 掌握基于SpringMVC获取请求参数与响应json数据操作
- 熟练应用基于REST风格的请求路径设置与参数传递
- 能够根据实际业务建立前后端开发通信协议并进行实现
- 基于SSM整合技术开发任意业务模块功能
三、SpringMVC简介
-
SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
-
优点
-
使用简单,开发编辑(相比于Servlet)
-
灵活性比较强
-
四、SpringMVC入门案例
-
使用SpringMVC技术需要先导入SpringMVC坐标和Servlet坐标
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency>
-
创建SpringMVC控制器类(等同于Servlet功能)
@Controller //使用注解声明bean交给IOC管理 public class UserController { @RequestMapping("/save") //设置当前操作的访问路径 @RequestBody //设置当前返回值类型,将返回的数据转为JSON public String save() { System.out.println("user save..."); return "{'code':200}"; } }
-
初始化SpringMVC环境(同spring环境),设定SpringMVC加载对应的bean
//创建SpringMvc的配置文件,加载controller对应的bean @Configuration @ComponentScan("com.itheima.controller") public class SpringMvcConfig {}
-
初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { //加载SpringMvc容器配置 protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return ctx; //该方法告诉tomact容器加载spring的配置 } //设置哪些请求归属springMVC处理 protected String[] getServletMappings() {s return new String[]{"/"}; //将哪些请求交给SpringMvc处理 } //加载Spring容器配置 protected WebApplicationContext createRootApplication() { return null; } }
4.1 工作流程分析
- 启动服务器初始化过程
- 服务器启动,执行
ServletContainsInitConfig
类,初始化web容器 - 执行
createServletApplicationContext
方法,创建WebApplicationContext
对象 - 加载
SpringMvcConfig
- 执行
@ComponentScan
加载对应的bean - 加载
UserController
,每个@RequestMapping
的名称对应一个具体的方法 - 执行
getServletMappings
方法,定义所有的请求都通过SpringMVC
- 服务器启动,执行
4.2 SpringMVC案例总结
- SpringMVC入门程序开发总结(1+N)
- 一次性工作
- 创建工程,设置服务器,加载工程
- 导入坐标
- 创建web容器启动类,架子啊SpringMVC配置,并设置SpringMVC请求拦截路径
- SpringMVC核心配置类(设置配置类,扫描controller包,加载Controller控制器bean)
- 多次工作
- 定义处理请求的控制器类
- 定义处理请求的控制器方法,并设置映射路径(
@RequestMapping
)与返回json数据(@ResponseBody
)
4.3 注解@Controller
-
名称:
@Controller
-
类型:类注解
-
位置:SpringMVC控制器类定义上方
-
作用:设定SpringMVC的核心控制器Bean
-
范例
@Controller public class UserController{}
4.4 注解@RequestMapping
-
名称:
@RequestMapping
-
类型:方法注解
-
位置:SpringMVC控制器方法定义上方
-
作用:设置当前控制器方法请求访问路径
-
范例
@RequestMapping("/save") public void save(){ System.out.println("user save ..."); }
-
相关属性:value(默认):请求访问路径
4.5 注解@ResponseBody
-
名称:
@ResponseBody
-
类型:方法注解
-
位置:SpringMVC控制器方法定义上方
-
作用:设置当前控制器方法响应内容为当前返回值,无需解析
-
范例:
@RequestMapping("/save") @ResponseBody public String save(){ System.out.println("user save ..."); return "{'info' : 'springMVC'}" }
五、bean的加载控制
创建的包目录一般含有如下确定的包:config
、controller
、service
、dao
- SpringMVV相关的bean(表现层bean)
- Spring控制的bean
- 业务bean(Service)
- 功能bean(DataSource等)
因为功能不同,如何避免Spring错误的加载到SpringMVC的bean?
加载Spring控制的bean的时候,排除掉SpringMVC控制的bean
- SpringMVC相关bean加载控制
- SpringMVC加载的bean对应的包均在controller包内
- Spring相关bean的加载控制
- 方式一:Spring加载的bean设定扫描范围大一些,然后排除掉controller包内的bean
- 方式二:Spring加载的bean设定扫描范围为精确范围,例如service包、dao包等
- 方式三:不区分Spring和SpringMVC的环境,加载到同一个环境中
5.1 注解@ComponentScan
-
名称:
@ComponentScan
-
类型:类注解
-
范例:
@Configuration @ComponentScan(Value = "com.itheima", excludeFilter @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ) ) public class SpringConfig {}
-
属性
excludeFilters
:排除扫描路径中加载的bean,需要指定类型(type)与具体项(classes)includeFilters
:加载指定的bean,需要指定类型(type)与具体项(classes)
5.2 Controller加载控制与业务bean加载控制
-
bean的加载配置
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return ctx; } protected WebApplicationContext createRootApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringConfig.class); return ctx } protected String[] getServletMappings() { return new String[]{"/"}; } }
-
简化开发
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{ protected Class<?>[] getServletConfigClasses(){ return new Class[]{SpringMvcConfig.class}; } protected String[] getServletMappings() { return new String[]{"/"}; } protected Class<?>[] getRootConfigClasses(){ return new Class[]{SpringConfig.class}; } }
六、请求与响应
6.1 注解@RequestMapping
-
名称:
@RequestMapping
-
类型:方法注解 类注解
-
位置:SpringMVC控制器方法定义上方
-
作用:设置当前控制器方法请求访问路径,如果设置在类上统一设置当前控制器请求访问路径前缀
-
范例
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/save") @ResponseBody public String save(){ System.out.println("user save ..."); return "{'module' : 'user save'}"; } }
6.2 普通参数的传递
使用get请求并携带参数的时候
-
携带参数的url路径:
http://localhost/commonParam?name=itcast&age=15
-
普通参数:url地址传参,地址参数名与形参名相同,定义形参即可接收参数
@RequestMapping("/commonParam") @ResponseBody public String commonParam(String name,int age){ System.out.println("参数name ==> " + name); System.out.println("参数age ==> " + age); return "{'module' : 'common param'}" } //直接定义参数接收即可
-
普通参数:请求参数名与形参变量名不同,使用
@RequestParam
绑定参数关系@RequestMapping("/commonParam") @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'}" } //直接定义参数接收即可
使用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'}" } //直接定义参数接收即可
![image-20230805145601246](https://i-blog.csdnimg.cn/blog_migrate/b5dd7b0e3e8812a9e0f455ec9a0e4d79.png)
6.3 注解@RequestParam
-
名称:
@RequestParam
-
类型:形参注解
-
位置:SpringMVC控制器方法形参定义前面
-
作用:绑定请求参数与处理器方法形参间的关系
-
范例:
@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'}" }
-
参数:
- required:是否为必传参数
- defaultValue:参数默认值
6.4 Post请求中文乱码处理
-
为Web容器添加过滤器并指定字符集,Spring-web包中提供了专用的字符过滤器
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{ //配字符编码过滤器 protected Filter[] getServletFilters(){ CharacterEncodingFilter filter = new CharacterEncodingFilter(); filter.setEncoding("utf-8"); return new Filter[]{filter}; } }
6.5 POJO参数传递
- POJO参数:请求参数名与形参对象属性名相同,定义POJO类型形参即可接收参数
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
System.out.println("pojo参数传递 user ==> "+ user);
return "{'module':'pojo param'}";
}
6.6 嵌套的POJO参数传递
- 嵌套POJO:POJO对象中包含POJO对象
- 参数接收:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数
public class User{
private String name;
private int age;
private Address address;
}
public class Address{
private String province;
private String city;
}
@ResquestMapping("/pojoContainPojoParam")
@ResponseBody
public String pojoContainPojoParam(User user){
System.out.println("pojo嵌套pojo参数传递 user ==> " + user);
return "{'module' : 'pojo contain pojo param'}";
}
6.7 数组参数传递
- 数组参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型形参即可接收参数
@RequestMapping("/arrayParam")
@ResponseBody
public String arrayParam(String[] likes){
System.out.println("数组参数传递 likes ==>" + Arrays.toString(likes));
return "{'module' : 'array Param'}"
}
6.8 集合参数传递
- 集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes){
System.out.println("集合参数传递 likes ==> " + likes);
return "{'module' : 'list param'}";
}
6.9 JSON数据传递参数
- JSON数据的转换需要使用到一些jar包,此处使用【jackson】
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
- 开启自动转换JSON数据的功能【注解
@EnableWebMvc
】
@Configuration
@ComponentScan("com.itheima.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
@EnableWebMvc
注解功能强大,该注解整合多个功能,此处仅使用其中一部分功能,即json数据进行自动类型转换
6.10 @EnableWebMvc
-
名称:
@EnableWebMvc
-
类型:配置类注解
-
位置:SpringMvc配置类定义上方
-
作用:开启SpringMVC多项辅助功能
-
范例
@Configuration @ComponentScan("com.itheima.controller") @EnableWebMvc public class SpringMvcConfig {}
6.11 @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'}"; }
6.12 @RequestBody和@RequestParam区别
- 区别
@RequestParam
用于接收url地址传参,表单传参【application/x-www-form-urlencoded】@RequestBody
用于接收json数据【application/json】
- 应用
- 后期开发中,发送json格式数据为主,@RequestBody应用较广
- 如果发送非json格式数据,选用@RequestParam接收请求参数
6.13 日期参数传递
-
日期类型数据基于系统不同格式也不同
- 2088-08-18
- 2088/08/18
- 08/18/2088
-
接收形参时,根据不同的日期格式设置不同的接收方式
@RequestMapping("dataParam") @ResponseBody public String dataParam(Data data, @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("参数传递 date(yyyy-MM-dd) ==> " + date1); System.out.println("参数传递 date(yyyy/MM/dd HH:mm:ss) ==> " + date2); return "{'module':'data param'}"; }
6.14 @DateTimeFormat
-
名称:
@DateTimeFormat
-
类型:形参注解
-
位置:SpringMVC控制器方法形参前面
-
作用:设定日期时间型数据格式
-
范例
@RequestMapping("/dataParam") @ResponseBody public String dataParam(Date date){ System.out.println("参数传递 date ==> " + date); return "{'module' : 'data param'}"; }
-
属性:pattern:日期时间格式字符串
七、异常处理器
- 异常处理器
- 集中的、统一的处理项目中出现的异常
@RestControllerAdvice
public class ProjectExceptionAdvice {
@ExceptionHandler(Exception.class)
public Result doException(Exception ex){
return new Result(666,null);
}
}
7.1 @RestControllerAdvice
-
名称:
@RestControllerAdvice
-
类型:类注解
-
位置:Rest风格开发的控制器增强类定义上方
-
作用:为Rest风格开发的控制器类做增强
-
范例:
@RestControllerAdvice public class ProjectExceptionAdvice { }
-
说明:
- 此注解自带
@ResponseBody
注解与@Component
注解,具备对应的功能
- 此注解自带
7.2 @ExceptionHandler
-
名称:
@ExceptionHandler
-
类型:方法注解
-
位置:专用于异常处理的控制器方法上面
-
作用:设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行
-
范例:
@RestControllerAdvice public class ProjectExceptionAdvice { @ExceptionHandler(Exception.class) public Result doException(Exception ex){ return new Result(404,null); } }
说明:此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常
八、拦截器
拦截器概念
- 拦截器是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行
- 作用:
- 在指定的方法调用前后执行预先设定的代码
- 组织原始方法的执行
拦截器和过滤器区别
- 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
- 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强
8.1 拦截器入门案例
-
声明拦截器的bean,并实现HandlerInterceptor接口(注意:扫描加载bean)
@Component public class ProjectInterceptor implements HandlerInterceptor { public boolean preHandle(..) throws Exception { System.out.println("preHanle..."); return true; } public void postHanle(..) throws Exception { System.out.println("postHandle.."); } public void afterCompletion(..) throws Exception { System.out.println("afterCompletion..."); } }
-
定义配置类,继承WebMVCConfigurationSupport,实现addInterceptor方法(注意:扫描加载配置)
@Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { @Override public void addInterceptors(InterceptorRegistry registry) { ... } }
-
添加拦截器并设定拦截的访问路径,路径可以通过可变参数设置多个
@Conxfuguration public class SpringMvcSupport extends WebMvcConfigurationSupport { @Autowired private ProjectInterceptor projectInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(projectInterceptor).addPathPatterns("/books"); } }
-
上述操作可以在SpringMVC配置类上实现【使用标准接口WebMvcConfigurer简化开发(注:侵入式较强)】
@Configuration @CompoentScan("com.itheima.controller") @EnableWebMvc public class SpringMvcConfig implements WebMvcConfigurer{ @Autowired private ProjectInterceptor projectInterceptor; public void addInterceptor(InterceptorRegistry registry) { registry.addInterceptor(projectInterceptor).addPathPatterns("/books",'/books/*'); } }
8.2 拦截器执行流程
8.3 拦截器参数配置
前置处理
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler)throws Exception {
System.out.println("preHandle...");
return true;
}
- 参数
- request:请求对象
- response:响应对象
- handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装
- 返回值
- 返回值为false,被拦截的处理器将不执行
完成后处理
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
System.out.println("afterCompletion...");
}
- 参数
- ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
后置处理
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...");
}
- 参数
- modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整