一.SpringMVC简介和体验
(一)介绍
(二)创建SpringMVC项目
1.创建Maven项目
2.导入依赖
XML
<properties>
<spring.version>6.0.6</spring.version>
<servlet.api>9.1.0</servlet.api>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- springioc相关依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- web相关依赖 -->
<!-- 在 pom.xml 中引入 Jakarta EE Web API 的依赖 -->
<!--
在 Spring Web MVC 6 中,Servlet API 迁移到了 Jakarta EE API,因此在配置 DispatcherServlet 时需要使用
Jakarta EE 提供的相应类库和命名空间。错误信息 "'org.springframework.web.servlet.DispatcherServlet'
is not assignable to 'javax.servlet.Servlet,jakarta.servlet.Servlet'" 表明你使用了旧版本的
Servlet API,没有更新到 Jakarta EE 规范。
-->
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>${servlet.api}</version>
<scope>provided</scope>
</dependency>
<!-- springwebmvc相关依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
3.转成maven/web程序
用jbljavatoweb插件将Java项目转换为Web项目
webapp出现蓝点代表成功转成maven/web程序
4.controller声明
handler就是controller内部的具体方法
java
@Controller
public class HelloController {
@RequestMapping("springmvc/hello")//对外访问的地址 到handlerMapping注册的注解
@ResponseBody//直接返回字符串给前端,不去找视图解析器
public String hello() {
System.out.println("hello,springmvc");
return "hello springmvc!";
}
}
5.Spring MVC核心组件配置类
声明springmvc涉及组件信息的配置类
(1)controller配置ioc容器
(2).handlerMapping handlerAdapter加入到ioc容器
java
@Configuration
@ComponentScan("com.yan.controller")
public class MvcConfig {
@Bean
public RequestMappingHandlerMapping handlerMapping() {
return new RequestMappingHandlerMapping();
}
@Bean
public RequestMappingHandlerAdapter handlerAdapter(){
return new RequestMappingHandlerAdapter();
}
}
6.SpringMVC环境搭建
本类可以被web项目加载,会初始化ioc容器,会设置dispatcherServlet的地址
java
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 指定service / mapper层的配置类
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
/**
* 指定springmvc的配置类
*
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{MvcConfig.class};
}
/**
* 设置dispatcherServlet的处理路径!
* 一般情况下为 / 代表处理所有请求!
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
7将攻击部署在tomcat上,启动测试
(三)WebApplicationInitializer接口
每当web项目启动,就会自动调用WebApplicationInitializer接口的onStartup方法AbstractAnnotationConfigDispatcherServletInitializer, 用于 DispatcherServlet 初始化 (实现了WebApplicationInitializer接口),该基类既要完成 WebApplicationInitializer 接口中配置servlet容器的功能,又完成了配置MVC的功能,即同时配置了 DispatcherServlet 和 ContextLoaderListener 。
二.SpringMVC接收数据
(一)访问路径设置
@RequestMapping注解的作用就是将请求的 URL 地址和处理请求的方式(handler方法)关联起来,建立映射关系。
@WebServlet:必须用/开头
@RequestMapping:不必使用/开头
1.精准路径
精准地址可以有一个或多个
@RequestMapping(value = {"/user/register"})
2.模糊路径
/* 为单层任意字符串,只能匹配URL地址中的一层,如果想准确匹配两层,那么就写"/*/*"以此类推
/** 为任意层任意字符串,可以匹配URL地址中的多层。
3.类和方法级别区别
(1)设置到类级别
类上提取通用的访问地址
(2)设置到方法级别
方法上是具体的handler地址
访问:类地址+方法地址即可
4.请求方式指定
java
public enum RequestMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}
(1)默认情况下:
@RequestMapping("/logout")
只要地址正确,任何请求方式都可以访问!
(2)指定请求方式下:
@RequestMapping(value = "regist", method = {RequestMethod.GET})
不符合请求方式会出现405异常
5.进阶注解
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
进阶注解只能添加到handler方法上,无法添加到类上!
(二)接收参数
1.param和json参数比较
(1)参数编码
param类型:ASCII码
JSON类型:UTF-8
(2)参数顺序
param参数没有顺序限制,但是JSON类型的参数是有序的,JSON采用键值对形式
(3)数据类型
param类型的参数支持字符串类型,数组类型和布尔类型等简单类型
JSON适合更复杂的格式
(4)可读性
param类型参数更简单,易读但是JSON格式在传递嵌套数据结构更勤洗易懂
2.param参数接收
(1)直接接值
-形参列表填写对应名称的参数即可 请求参数名=形参参数名
-可以不给形参传递值
java
@RequestMapping("data")
@ResponseBody
public String data(String name, int age) {
System.out.println("name=" + name + ",age" + age);
return "name = " + name + "age = " + age;
}
(2)@RequestParam注解
可以使用 @RequestParam 注释将 Servlet 请求参数(即查询参数或表单数据)绑定到控制器中的方法参数。
@RequestParam(
value="指定请求参数,如果形参名和参数名一致,可以省略",
required=false 前端是否必须传递,默认为必须,不传报错400异常,
defaultValue="1" 当非必须传递时,可以设置默认值)
使用场景
-指定绑定的请求参数名
-要求请求参数必须传递
-为请求参数提供默认值
java
@RequestMapping("data1")
@ResponseBody
public String data1(@RequestParam(value = "account") String username, @RequestParam(required = false, defaultValue = "1") int page) {
return "username = " + username + " , page= " + page;
}
(3)特殊场景接值
-一名多值
多选框,提交的数据的时候一个key对应多个值,我们可以使用集合进行接收!
不加@RequestParam将hbs对应的一个字符赋值给集合,类型异常
加了注解,HandlerAdapter用集合的add方法加入字符串
java
//hbs=吃&hbs=玩&hbs=打
@GetMapping("data2")
@ResponseBody
public String data2(@RequestParam List<String> hbs) {
System.out.println("hbs=" + hbs);
return "ok";
}
-实体接收
可以在方法内部直接使用对象的属性来访问请求参数,要求声明实体类属性名等于请求参数名
java
@Data
public class User {
private String name;
private int age;
}
java
@RequestMapping("data3")
@ResponseBody
public String data3(User user) {
System.out.println(user);
return user.toString();
}
3.路径参数接收
Spring MVC 框架提供了 @PathVariable 注解来处理路径传递参数。
@PathVariable 注解允许将 URL 中的占位符映射到控制器方法中的参数
-动态路径设计: /user/{动态部分}/{动态部分} 动态部分使用{}包含即可! {}内部动态标识!
-形参列表取值:
@PathVariable Long id 如果形参名 = {动态标识} 自动赋值!
@PathVariable("动态标识") Long id 如果形参名 != {动态标识} 可以通过指定动态标识赋值!
java
@RequestMapping("{account}/{password}")
public String login(@PathVariable(value = "account", required = true) String username, @PathVariable String password) {
return username+password;
}
4.JSON参数接收
定义一个用于接收 JSON 数据的 Java 类
java
@Data
public class Person {
private String name;
private int age;
private String gender;
}
在控制器中,使用 @RequestBody 注解来接收 JSON 数据,并将其转换为 Java 对象,例如:
java
@RequestMapping("json")
@Controller
@ResponseBody
public class JsonController {
@PostMapping("data")
public String data(@RequestBody Person person) {
System.out.println("person:" + person);
return person.toString();
}
}
测试:
原因:
Java原生的api只支持路径参数和param参数,不支持json
json是前端格式
解决:导入json处理的依赖 ,handlerAdopter配置json转换器
使用此注解@EnableWebMvc加入json处理器
XML
@EnableWebMvc //handlerAdopter 配置json转换器
@Configuration
@ComponentScan("com.yan")
public class MvcConfig {
@Bean
public RequestMappingHandlerMapping handlerMapping() {
return new RequestMappingHandlerMapping();
}
@Bean
public RequestMappingHandlerAdapter handlerAdapter(){
return new RequestMappingHandlerAdapter();
}
}
pom.xml 加入jackson依赖
XML
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>
@EnableWebMvc注解说明
添加 HandlerMapping,HandlerAdapter,给HandlerAdapter添加jacksonJson处理器
(三)接收Cookie数据
java
@Controller
@RequestMapping("cookie")
@ResponseBody
public class CookieController {
@RequestMapping("data")
public String data(@CookieValue(value = "cookieName") String value) {
System.out.println("value=" + value);
return value;
}
@GetMapping("save")
public String save(HttpServletResponse response){
Cookie cookie = new Cookie("cookieName", "root");
response.addCookie(cookie);
return "ok";
}
}
(四)接收请求头数据
java
@Controller
@RequestMapping("header")
@ResponseBody
public class HeaderController {
@GetMapping("data")
public String data(@RequestHeader("Host") String host) {
System.out.println("host = " + host);
return "host = " + host;
}
}
(五)原生Api对象获取
想要获取请求或者响应对象,或者会话等,可以直接在形参列表传入,并且不分先后顺序!
举例:
获取ServletContext和HttpSession对象
java
@GetMapping("api")
public String getApi(HttpServletRequest req, HttpServletResponse resp, HttpSession httpSession){
ServletContext servletContext = req.getServletContext();
return null;
}
springmvc会在初始化容器的时候,将servletContext对象存储到ioc容器中,也可以用@Autowired注解自动装配servletContext对象
三.SpringMVC响应数据
(一)转发和重定向
1.方法的返回值都是String
2.不加responseBody注解
3.返回字符串前面 redirect:/重定向地址 forward:/请求转发地址
转发和重定向都一样,都是项目下路径!转到项目内的资源都不需要添加项目根路径!
转发只能向项目下的资源跳转
重定向可以是项目下的资源也可以是项目外的资源,重定向到项目外的资源如百度网址
redirect:http://www.baidu.com
java
@GetMapping("forward")
public String forward() {
return "forward:/index";
}
@GetMapping("redirect")
public String redirect() {
return "redirect:/jsp/index";
}
(二)返回JSON数据
1.前置准备
(1)导入json依赖
(2)@EnableWebMvc
2.@ResponseBody注解
@RestController =@Controller + @ResponseBody
所以使用了 @RestController 注解就相当于给类中的每个方法都加了 @ResponseBody 注解。
如果类中每个方法上都标记了 @ResponseBody 注解,那么这些注解就可以提取到类上。
java
@RequestMapping("json")
@Controller
@ResponseBody
public class JsonController {
@GetMapping("data")
public User data() {
User user = new User();
user.setName("two dogs");
user.setAge(2);
return user;
}
@GetMapping("data2")
public List<User> data1() {
User user = new User();
user.setName("two dogs");
user.setAge(2);
List<User> users = new ArrayList<>();
users.add(user);
return users;
}
}
(三)返回静态资源
1.原因
对 SpringMVC 来说,必须有对应的 @RequestMapping 才能找到处理请求的方法
现在 images/photo.jpg 请求没有对应的 @RequestMapping 所以返回 404
2.问题解决
在 SpringMVC 配置配置类:
java
@EnableWebMvc // 配置json转换器
@Configuration
@ComponentScan("com.yan")
public class MvcConfig implements WebMvcConfigurer {
//开启静态资源处理
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
四.RESTful风格设计和实践
(一)请求方式要求
GET用来获取资源
POST用来新建资源(也可以用于更新资源)
PUT用来更新资源
DELETE用来删除资源
(二)路径要求
使用URL+请求方式确定具体的动作,handler的地址+请求方式都相同才是重复
通用原则:
-路径参数 应该用于指定资源的唯一标识或者 ID,而请求参数 应该用于指定查询条件或者操作参数。
-
请求参数应该限制在 10 个以内,过多的请求参数可能导致接口难以维护和使用。
-
对于敏感信息,最好使用 POST 和请求体来传递参数。
(三)接口设计
为什么查询用户详情,就使用路径传递参数,多条件模糊查询,就使用请求参数传递?
-对于查询用户详情,使用路径传递参数是因为这是一个单一资源的查询,即查询一条用户记录。使用路径参数可以明确指定所请求的资源,便于服务器定位并返回对应的资源,也符合 RESTful 风格的要求。
-而对于多条件模糊查询,使用请求参数传递参数是因为这是一个资源集合的查询,即查询多条用户记录。使用请求参数可以通过组合不同参数来限制查询结果,路径参数的组合和排列可能会很多,不如使用请求参数更加灵活和简洁。
(四)代码实现
java
public class User {
private Integer id;
private String name;
private Integer age;
}
java
@RestController
@RequestMapping("user")
public class UserController {
@GetMapping
public List<User> page(@RequestParam(required = false, defaultValue = "1") int page,
@RequestParam(required = false, defaultValue = "10") int size) {
System.out.println("page=" + page + "size" + size);
return null;
}
@PostMapping
public User save(@RequestBody User user) {
return user;
}
@GetMapping("{id}")
public User detail(@PathVariable Integer id) {
return null;
}
@PutMapping
public User update(@RequestBody User user) {
return user;
}
@DeleteMapping("{id}")
public User delete(@PathVariable Integer id) {
return null;
}
@GetMapping("search")
public List<User> search(String keywork,
@RequestParam(required = false, defaultValue = "1") int page,
@RequestParam(required = false, defaultValue = "10") int size) {
return null;
}
}
五.SpringMVC扩展
(一)全局异常处理机制
声明式异常处理:则是将异常处理的逻辑从具体的业务逻辑中分离出来,通过配置等方式进行统一的管理和处理。
1.声明异常处理控制器类
@ControllerAdvice 代表当前类的异常处理controller!
@RestControllerAdvice=@ControllerAdvice+@ResponseBody
2.声明异常处理hander方法
@ExceptionHandler(HttpMessageNotReadableException.class)
该注解标记异常处理Handler,并且指定发生异常调用该方法!指定的异常,可以精准查找,找不到才查找父异常
3.配置文件扫描控制器类配置
确保异常处理控制类被扫描
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ArithmeticException.class)
public Object ArithmeticExceptionHandler(ArithmeticException e) {
//自定义处理异常即可
String message = e.getMessage();
return message;
}
@ExceptionHandler(Exception.class)
public Object ExceptionHandler(Exception e) {
String message = e.getMessage();
return message;
}
}
(二)拦截器的使用
1.创建拦截器类
java
public class MyInterceptor implements HandlerInterceptor {
/**
* 执行handeler之前,调用的拦截方法
*
* @param request
* @param response
* @param handler 我们要调用的方法的对象
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
/**
* 当handler执行完毕后触发的方法,没有拦截机制
*
* @param request
* @param response
* @param handler
* @param modelAndView 返回的视图和共享域数据对象
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* 整体处理完毕
*
* @param request
* @param response
* @param handler
* @param ex 报错了异常对象
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
2.修改配置类添加拦截器
java
@Configuration
@ComponentScan("com.yan")
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
//添加拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
}
}
3.配置详解
a.默认拦截全部
java
@Configuration
@ComponentScan("com.yan")
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
}
}
b.精准配置
设置拦截器处理指定请求 路径可以设置一个或者多个,为项目下路径即可
也支持 /* 和 /** 模糊路径。 * 任意一层字符串 ** 任意层 任意字符串
java
@Configuration
@ComponentScan("com.yan")
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/user/data");
}
}
c.排除设置
排除匹配,排除应该在匹配的范围内排除
java
@Configuration
@ComponentScan("com.yan")
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/user/**").excludePathPatterns("/user/data");
}
}
4.多个拦截器的执行顺序
preHandle() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置顺序调用各个 preHandle() 方法。
postHandle() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置相反的顺序调用各个 postHandle() 方法。
afterCompletion() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置相反的顺序调用各个 afterCompletion() 方法。
(三)参数校验
1.导入依赖
XML
<!-- 校验注解 -->
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>9.1.0</version>
<scope>provided</scope>
</dependency>
<!-- 校验注解实现-->
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.0.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator-annotation-processor -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>8.0.0.Final</version>
</dependency>
2.创建实体类,为其添加校验对象
要求:
(1)name 不为null且不为空 字符串
@NotBlank 集合 @NotEmpty 包装 @NotNull
(2)password长度大于6
(3)age必须>=1
(4)email 邮箱格式字符串
(5)birthday 过去时间
java
public class User {
@NotBlank
private String name;
@Length(min = 6)//标注值字符串大小必须在指定的范围内
private String password;
@Min(1) //标注值必须大于或等于 value
private int age;
@Email //标注值必须是格式正确的 Email 地址
private String email;
@Past //标注值只能用于日期型,且必须是过去的日期
private Date birthday;
}
3.handler(@Validated 实体类 对象)
要接收json,还要加上@RequestBody 注解
java
@RequestMapping("user")
@RestController
public class UserController {
@GetMapping
public User regist(@Validated @RequestBody User user){
return user;
}
}
测试:
4.handler标记和绑定错误收集
捕捉绑定错误信息
在实体类参数和 BindingResult 之间不能有任何其他参数
BindingResult可以接受错误信息,避免信息抛出
java
@GetMapping
public Object regist(@Validated @RequestBody User user, BindingResult result) {
if (result.hasErrors()) {
Map data = new HashMap();
data.put("code", 400);
data.put("msg", "参数校验异常");
return data;
}
return user;
}
测试: