SpringMVC
-
基于
Servlet API构建的原始Web控制层框架- 简化参数的接收和响应
-
在没有SpringMVC框架时,用户请求直达Controller,由我们自己处理参数,并处理数据格式响应
-
有了SpringMVC不再直接访问Controller,访问流程如下(SpringMVC内部工作体系)
- 所有请求先到达DispatcherServlet(CEO)
- DispatcherServlet 在 HanlerMapping(缓存handler方法和地址,秘书) 查找是否存在对应handler(Controller里的每个方法,打工人)
- 接着将请求发送给 HandlerAdaptor(简化请求参数和响应结果(例如对象转JSON),经理) ,HandlerAdaptor处理后调用对应的 Handler 处理,并获取结果处理后返回给DispatcherServlet
- 最后若是不响应JSON 要响应视图页面,DispatcherServlet就去找 ViewResolver(视图解析器,简化视图查找,财务) ,然后响应给 CEO。 (前后端分离项目,后端只返回JSON数据,无需视图解析器)
- 最后 CEO 将结果响应给用户
一、入门案例
记住:
@ResponseBody、@RequestBody、@RestController的区别
0、下载插件:jblJavatoWeb,创建maven-web app项目
1、引入依赖
- spring-webmvc(包含spirng的core、aoc、aop、context)
- Jarkata.servlet-api(用于DispatcherServlet)
- Tomcat10.0.26
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.0.9</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
2、创建配置类
package com.elias.config;
/**
* 1、配置IOC容器,将handlerMapping(秘书)、handlerAdapter(经理) 加入到IOC容器
*/
@Configuration
@ComponentScan("com.elias")//开启注解声明bean
public class MvcConfig {
//添加秘书、经理bean到IOC容器
@Bean
public RequestMappingHandlerMapping handlerMapping(){
return new RequestMappingHandlerMapping();
}
@Bean
public RequestMappingHandlerAdapter handlerAdapter(){
return new RequestMappingHandlerAdapter();
}
}
3、创建SpringMVC环境搭建类,创建SpringMvcInit类并继承AbstractAnnotationConfigDispatcherServletInitializer接口
- 该接口也是层层继承,其实是实现了一个WebApplicationInitializer接口
- 重写了它的onStartUP()方法:每当web项目启动,就会自动调用该方法
- 我们就可以在里面初始化我们的IOC容器并配置servlet
package com.elias.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
//该类的作用:继承接口后
// 可以被web项目自动加载,初始化IOC容器
// 并帮我们设置dispatcherServlet的地址("/"),所有的请求都会到达CEO了
public class SpringMvcInit extends AbstractAnnotationConfigDispatcherServletInitializer {
//service、mapper层的ioc容器配置(后期会用到)
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
//设置我们项目的配置类(IOC容器配置):springmvc、controller
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcInit.class};
}
//配置SpringMVC内部自带servlet(DS)的访问地址
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
4、定义Controller 和 Handler(打工人)
package com.elias;
@Controller//添加到IOC容器
@RequestMapping("/test/hello") //作用:注册地址,将Handler注册到HandlerMapping
@ResponseBody //设置直接响应JSON字符串,不要找视图解析器
public class TestController {
//定义Handler
public String hello(){
System.out.println("Hello");
//返回给前端
return "hello Spring";
}
}
二、SpringMVC接收数据
1、路径注解设置
**
* @RequestMapping:用于将Handler注册到HanlderMapping
* @WebSerlet("/test/hello):必须以 / 开头
* @RequestMapping("test/hello")、("/test/hello"):都可以
* @RequestMapping可放在类上作为公共路径,放在方法上就是Handler私有路径,访问Handler地址即公共+私有
* 1、精准地址:可以为Handler设置多个访问地址,用{}包起来
* 例如:RequestMapping({"/test/hello1","/test/hello2"})
* 2、模糊匹配:* 表示匹配 任意一层字符串,** 表示匹配 任意层任意字符串
* /test/* : /test/aa、/test/aaaa(ok);/test/aa/aa(not ok)
* /test/** :/test/aa、/test/aa/aa、/test/a/a/a/a(都ok)
* 3、请求方式指定:默认情况,任意方式都可以请求
* 指定请求方式:@RequestMapping("test/hello",method = RequestMethod.GET)
* 不符合请求方式:405
* 4、注解进阶
* @GetMapping、@DeleteMapping、@UpdateMapping、@PutMapping,只能使用在方法上
* =@RequestMapping(xxx,method=RequestMethod.xxxMapping)
*/
2、Param参数和Json参数
- Get方法传参数用Param
- Post方法穿参数用Json
3、Param参数接收
1)行参名和类型 与 传递参数相同,自动接收
- 可以不传递、不报错
//直接接收:行参名和类型 与 传递参数相同,自动接收
//可以不传递、不报错
@GetMapping("/1")
public void test1(int age,String name){
System.out.println(age+name);
}
2)@RequestParam
-
使用场景
- 接收指定参数
- 要求该参数必须传递
- 为请求参数提供默认值
-
基本用法
- 接收指定参数,必须穿参数
- 非必需传值,要设置默认值
//接收指定参数,必须穿参数
//非必需传值,要设置默认值
@GetMapping("/2")
//http://localhost:8080/test?age=10
public void test2(@RequestParam(value = "name") String myName,
@RequestParam(required = false,defaultValue = "10") int age){
System.out.println(myName+age);
}
3)集合参数
请求:...?hobby=1&hobby=2&hobby=3
//接收集合参数:加了注解,经理才会用add方法将值加入集合
//...?hb=1&hb=2
@GetMapping("/3")
public void test3(@RequestParam List<String> hb){
System.out.println(hb);
}
4)实体类参数
- 直接用实体类接收即可,不强求传
//实体对象接收值,例如用户注册的信息
@GetMapping("/3")
//...?name=1&age=2
public void test3(User user){
System.out.println(user);
}
4、路径参数
-
必须使用
@PathVarible -
请求:/test/{age}
- 要接受的路径参数要用{}包裹起来
//接收路径参数
@GetMapping("/5/{age}/{name}")
//...?name=1&age=2
public void test4(@PathVariable(value="age") int myAge
@PathVariable String name){
System.out.println(age);
}
5、Json参数
- 接收json格式参数,用post请求方式,用java对象接收,属性名与json的key值一致
@PostMapping
public User test1(@RequestBody User user){
System.out.println(user);
return user;
}
1)报错:前端报错415,不支持数据类型
2)原因:Java原生的api 不支持json格式(前端的格式)、而且没有json类型处理的工具(jakson)
3)解决:1、导入json处理的依赖 2、给handlerAdaptor配置Json转化器
- 导入json处理的依赖:jackson-databind
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.3</version>
</dependency>
- 给handlerAdaptor配置Json转化器:在MVCconfig配置文件加上注解
@EnableWebMvc
package com.elias.config;
@Configuration
@ComponentScan("com.elias")
@EnableWebMvc//作用:添加秘书、添加经理(下面的两个bean就不用写了)、给经理添加jackson json处理器
public class MvcConfig {
//@Bean
//public RequestMappingHandlerMapping handlerMapping(){
// return new RequestMappingHandlerMapping();
//}
//@Bean
//public RequestMappingHandlerAdapter handlerAdapter(){
// return new RequestMappingHandlerAdapter();
//}
}
6、获取Cookie和请求头
//cookie的存储
@GetMapping("save")
public String save(HttpServletResponse response){
Cookie cookie=new Cookie("cookieName","root");
response.addCookie(cookie);
return "ok";
}
//接收cookie
@RequestMapping("data")
public String cookie(@CookieValue(value = "cookieName") String value){
System.out.println("value:"+value);
return "ok";
}
//获取请求头:@RequestHeader("header名"),要获取什么header用对应的name
@RequestMapping("header")
public String header(@RequestHeader("Host") String host){
System.out.println(host);
return "ok";
}
7、获取原生对象
package com.elias;
@RestController
@RequestMapping("/test")
public class TestController {
//注入ServletContext对象
@Autowired
private ServletContext servletContext;
//获取原生对象可以直接在Handler里声明参数即可,然后直接食用即可
@GetMapping
public void data(HttpServletResponse response,
HttpServletRequest request,
HttpSession session){
//ServletContext [1、最大的配置文件 2、全剧最大共享域 3、核心api getRealPath()]
//获取方式1:request获取 or session获取
ServletContext servletContext1 = request.getServletContext();
ServletContext servletContext2 = session.getServletContext();
//获取方式2:ServletContext 会自动装入IOC容器,直接注入即可
servletContext1.setAttribute("name","zhj");
}
}
8、共享域对象操作
-
共享域:方便同一web'应用程序的多个组件之间传递数据
-
三大共享域
- request:一次请求或者转发
- session:一次会话期间(一个浏览器的多次请求)
- servletContext:整个项目
使用
//获取原生对象可以直接在Handler里声明参数即可,然后直接食用即可
@GetMapping
public void data(HttpServletResponse response,
HttpServletRequest request,
HttpSession session){
//三大共享域:request、session、servletContext
// 都用:setAttribute()方法存数据,getAttribute()获取数据
ServletContext servletContext = request.getServletContext();
servletContext.setAttribute("name","zhj");
String name = (String) servletContext.getAttribute("name");
}
三、SpringMVC响应数据
1、开发模式介绍
- 混合开发、前后端分离开发
2、响应视图、转发、重定向
- 前后端分离一般不涉及
3、响应Json数据
@ResponseBody
@RestController
@RestController//@Controller+@ResponseBody(返回json的注解,可以放到类上或方法上)
@RequestMapping("/json")
public class TestController {
@GetMapping
//返回类型是什么,HandlerAdaptor(经理)都会帮我们转成json格式
//@ResponseBody:数据直接走响应体返回,不走视图解析器(快速查找视图,转发和重定向都不生效了)
public User getUser(){
User user=new User();
user.setName("zhj");
return user;
}
}
4、静态资源处理
4.1、WebMvcConfigure 接口
- 该接口有一系列方法 用于简化配置 组件(不用写@Bean,重写方法即可)
4.2、静态资源访问
webapp下的images静态文件里的图片 访问不到(访问路径当作寻找handler了)
解决方案
在mvc配置类文件里重写方法方法 configureDefaultServletHandling()
package com.elias.config;
@Configuration
@ComponentScan("com.elias")
@EnableWebMvc//作用:添加秘书、添加经理(下面的两个bean就不用写了)、给经理添加jackson json处理器
public class MvcConfig implements WebMvcConfigurer {
//开启静态资源查找<mvc:default-servlet-handler/>,底层做了个转发
//dispatcherServlet -> handlerMapping找有没有对应的handler -> 没有 -> 找有没有静态资源
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
//调用参数的enable()方法开启静态资源查找(给老板配了个二秘书)
configurer.enable();
}
}
四、RESTFUL风格设计和实战
1、Restful开发风格介绍
-
HTTP协议的标准使用方案
- 设计路径
- 设计参数传递
- 设计请求方式
2、接口设计
| 功能 | 接口和请求方式 | 请求参数 | 返回值 |
|---|---|---|---|
| 分页查询 | GET /users | Page1&size=10 param | {响应数据} |
| 用户添加 | POST /users | {user数据} | {响应数据} |
| 单个用户详情 | GET /users/1 | 路径参数 | {响应数据} |
| 用户更新 | PUT /users | {user更新数据} | {响应数据} |
| 用户删除 | DELETE /users/1 | 路径参数 | {响应数据} |
| 条件模糊查询 | GET /users/search | Page1&size=10&keyword=关键字 param | {响应数据} |
-
路径参数:用于指定资源的唯一标识或者ID
-
请求参数:用于指定查询条件或操作参数
-
敏感信息:用POST和请求体来传递(登陆、注册的账号密码)
-
GET、DELETE无请求体
- 用路径传参数(参数是id,用路径)
- param传参数(参数不是id,是范围参数,用param)
-
POST、PUT有请求体
- 路径、param
- 请求体(json) ,一般都用这个
-
五、SpringMVC其他扩展
1、全局异常处理
1.1异常处理的两种方式
- 编程式:在代码内部对异常编写处理逻辑
- 声明式:将异常处理逻辑放到外部编写,通过配置统一管理
1.2、全局异常处理机制
- error.GlobalExceptionHandler
package com.elias.erroe;
//全局异常处理器,异常发生就会走此类的handler
@ControllerAdvice//可以返回逻辑视图,转发和重定向的
@RestControllerAdvice//@ResponseBody 直接返回JSON字符串
public class GlobalExceptionHandler {
//@@ExceptionHandler(要处理的异常的字节码对象),可以直接用Exception.class接受所有异常
//指定的异常未找到,会查找其父异常
@ExceptionHandler(ArithmeticException.class)
//参数是捕获的异常
public String arithmeticExceptionHandler(ArithmeticException e){
//自定义处理异常即可
return e.getMessage();
}
@ExceptionHandler(Exception.class)
public String exceptionHandler(Exception e){
//自定义处理异常即可
return e.getMessage();
}
}
2、拦截器
2.1、拦截器概念
-
对请求进行拦截,统一处理后选择放行或拦截的一种组件
-
执行handler之前!调用的拦截方法!
- 编码格式设置
- 登录保护
- 权限设置
拦截器
- 由SpringMVC提供,拦截SpringMVC负责的请求,就在IOC容器中,只需代码装配一下
过滤器
- 工作在 Servlet 容器中,拦截的最大范围是 整个Web应用,IOC容器不支持,需调用专门的工具方法
二者作用范围不同
- Servlet(DispatcherServlet)
选择
- 功能需要如果MVC能实现,就不用过滤器
2.2、拦截器使用
1)创建interceptor.MyInterceptor 拦截器类,实现 HandlerInterceptor 接口
2)根据需求重写三个拦截方法
package com.elias.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class MyInterceptor implements HandlerInterceptor {
/**
* 拦截方法
* @param request 请求对象
* @param response 响应对象
* @param handler 我们要调用的方法对象
* @return true:放行 false:拦截住
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器preHandle()方法");
return true;//放行
}
/**
* handler执行完毕后调用的方法,无拦截机制,只有PreHandler return true才执行
* @param @param request 请求对象
* @param response 响应对象
* @param handler 我们要调用的方法对象
* @param modelAndView 返回的视图和共享域数据对象
* @throws Exception
对结果处理,一般用于敏感词汇检查
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器postHandle()方法");
}
/**
* 整体执行完后调用的方法,无拦截机制
* @param @param request 请求对象
* @param response 响应对象
* @param handler 我们要调用的方法对象
* @param ex handler报错,异常的对象
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throwsException {
System.out.println("拦截器afterCompletion()方法");
}
}
- 三个方法的执行位置
3)修改MVC配置类,添加拦截器
package com.elias.config;
@Configuration
@ComponentScan("com.elias")
@EnableWebMvc//作用:添加秘书、添加经理(下面的两个bean就不用写了)、给经理添加jackson json处理器
public class MvcConfig implements WebMvcConfigurer {
//在IOC容器注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//配置一、默认拦截所有请求
registry.addInterceptor(new MyInterceptor());
//配置二、设置拦截指定地址请求
//支持模糊匹配 * 任意一层、** 任意多层
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/user/*");
//配置三、排除拦截,排除的地址必须在拦截地址内部
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/user/*")
.excludePathPatterns("/user/a");
//多个拦截器,按添加先后顺序拦截(pre以次,post、after反向以次)
registry.addInterceptor(new MyInterceptor1());
registry.addInterceptor(new MyInterceptor2());
}
}
3、参数校验注解jsr303
- java提供注解,hibernate实现,但springmvc支持这套实现
- Java为Bean数据合法性校验提供的标准框架,通过注解,标准的验证接口进行验证
1)注解
2)使用步骤
- 引入依赖
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>8.0.0.Final</version>
</dependency>
- 在实体类添加限制注解
package com.elias;
@Data
public class User {
@NotBlank
private String name;
@Min(1)
private int age;
@Length(min=6)
private String password;
@Past
private Date birthday;
@Email
private String email;
}
-
在handler方法中添加处理
@Validated- BindingResult result
package com.elias;
@RestController//@Controller+@ResponseBody(返回json的注解,可以放到类上或方法上)
@RequestMapping("/user")
public class TestController {
//接受用户数据,校验
/*
1、实体类添加校验注解
2、handler(@Validated 实体类 对象)
3、细节: param|json 校验注解都有效果
json参数 - 加上@RequestBody即可
如果,不符合校验规则,直接会向前端跑出异常
接收错误绑定!自定义返回结果
捕获错误绑定错误信息:
1、handler(校验对象,BindingResult result) 要求:bindingResult必须紧挨 校验对象
2、bindingresult获取绑定错误
*/
@PostMapping("/test")
public Object test(@Validated @RequestBody User user, BindingResult result){
//判断是否有绑定错误,不进行直接返回,自己决定
if(result.hasErrors()){
Map data=new HashMap();
data.put("code",400);
data.put("msg","参数校验异常,请检查");
return data;
}
return user;
}
}