SpringMVC总结

一.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;
    }

测试:

相关推荐
m0_571957582 小时前
Java | Leetcode Java题解之第543题二叉树的直径
java·leetcode·题解
魔道不误砍柴功4 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_2344 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
闲晨4 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
Chrikk5 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*5 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue5 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man5 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
测开小菜鸟5 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity6 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq