学习SSM的记录(六)-- Spring MVC

目录

[Spring MVC 简介和体验](#Spring MVC 简介和体验)

[Spring MVC原理简单解析](#Spring MVC原理简单解析)

[Spring MVC涉及的组件](#Spring MVC涉及的组件)

[Spring MVC 快速体验](#Spring MVC 快速体验)

[Spring MVC 接收数据](#Spring MVC 接收数据)

访问路径设置

接收参数(重点)

param和json参数比较

param参数接收

路径参数接收

json参数接收

@EnableWebMvc注解

接收Cookie和请求头数据

原生Api对象和共享域对象操作

[Spring MVC 响应数据](#Spring MVC 响应数据)

handler方法分析

页面跳转控制

快速返回模板视图

转发和重定向

返回JSON数据(重点)

返回静态资源处理

RestFul风格设计

RESTFul风格特点

[Spring MVC 其他拓展](#Spring MVC 其他拓展)

全局异常处理机制

异常处理的两种方式

基于注解异常声明异常处理

拦截器使用

拦截器概念

拦截器使用

参数校验


  • SSM为SpringFramework + SpringMVC + MyBatis
  • Spring的3大特性:IoC(控制反转),DI(依赖注入),AOP(面向切面编程)。
  • 框架 = jar + 配置文件

Spring MVC 简介和体验

Spring Web MVC 是一个基于Servlet API 构建的原始Web框架,一开始包含在Spring Framework下,目前普遍被选为JavaEE项目表述层开发的首选。

Spring MVC框架的两个核心功能:1.简化前端参数接收(形参列表)

2.简化后端数据响应(返回值)

Spring MVC原理简单解析

Spring MVC 框架内主要工作的有:

  • DispatcherServlet:处理所有请求,用户的所有请求都由它接收
  • HandlerMapping:缓存handler方法和地址,根据地址(如:/user/login)查找项目中的方法(如:UserController中的login方法)
  • HandlerAdapter:适配器,真正进行参数和响应简化
  • 视图解析器:查找视图页面,如果要返回的页面如:/WEB-INF/html/index.html,只需要返回"index",它可以帮我们添加前缀和后缀,并查找页面信息并返回。

执行流程:

1.用户发送请求

2.请求到达DispatcherServlet

3.DispatcherServlet根据请求地址到HandlerMapping查找方法

4.HandlerMapping查找到handler方法并返回方法信息到DispatcherServlet。

5.DispatcherServlet根据handler方法信息将参数信息和方法信息发送到HandlerAdapter

6.HandlerAdapter进行简化参数处理,并调用handler方法

7.handler方法执行并返回数据给HandlerAdapter

8.HandlerAdapter接收数据并发送给DispatcherServlet

9.DispatcherServlet接收数据,如果接收的是页面地址则进行 操作10 ,不是则直接将数据返回给用户

10.视图解析器为页面添加前后缀并查找页面信息后返回给DispatcherServlet,DispatcherServlet将页面信息返回给用户。

Spring MVC涉及的组件

1.DispatcherServlet:Spring MVC提供,需要配置web.xml(Web项目配置文件)才能生效。

2.HandlerMapping:Spring MVC提供,需要进行IOC配置,将它加入到IOC容器才能生效,它内部缓存handler(controller的方法)和handler访问路径数据。

3.HandlerAdapter:Spring MVC提供,需要进行IOC配置,将它加入到IOC容器才能生效,它可以处理请求参数和处理响应数据。

4.Handler:handler又叫处理器,它是controller层方法的简称,由我们自己定义,接收参数,调用业务方法,返回数据。

5.ViewResolver(视图解析器):Spring MVC提供,需要进行IOC配置,将它加入到IOC容器才能生效,简化视图页面查找,前后端分离后,后端只需要返回JSON数据,不再需要视图解析器。

Spring MVC 快速体验

场景:项目启动后,用户访问/springmvc/hello服务器响应"hello springmvc!!"

1.创建项目ssm-springmvc-quick并转为web项目

2.导入项目所需的依赖:

  • ioc:spring-context
  • webmvc:spring-webmvc
  • servlet:jakarta.jakartaee-web-api

3.创建一个controller类:HelloController

复制代码
@Controller
public class HelloController {

    @RequestMapping("springmvc/hello")//用户访问地址
    @ResponseBody//直接返回数据,不需要经过视图解析器
    public String hello(){
        System.out.println("Hello , Spring MVC!");
        return "hello springmvc";
    }
}

4.创建spring配置类

复制代码
//将controller配置到ioc容器
//将handlerMapping handlerAdapter加入到ioc容器
@Configuration
@ComponentScan("com.qiu.controller")
public class MvcConfig {
    @Bean
    public RequestMappingHandlerMapping handlerMapping(){
        return new RequestMappingHandlerMapping();
    }
    @Bean
    public RequestMappingHandlerAdapter handlerAdapter(){
        return new RequestMappingHandlerAdapter();
    }
}

5.初始化ioc容器,设置dispatcherServlet访问路径(就是想要访问项目应该有的地址前缀)

以前我们需要在web.xml下操作:

复制代码
<servlet>
    <servlet-name>ds</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>ds</servlet-name>
    <url-pattern>/</url-pattern><!--访问路径-->
</servlet-mapping>

但现在可以创建一个用来初始化web项目的类,让他继承

复制代码
AbstractAnnotationConfigDispatcherServletInitializer

,项目启动后会自动扫描他,该类的作用是扫描Spring配置类来初始化ioc容器和设置DispatcherServlet的访问路径

复制代码
//继承AbstractAnnotationConfigDispatcherServletInitializer类
//用来替换web.xml,会被web项目自动加载,会初始化ioc容器,会设置dispatcherServlet的访问地址
public class SpringMvcInit extends AbstractAnnotationConfigDispatcherServletInitializer {
    //service mapper 层的ioc容器
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[0];
    }
    //设置spring配置类 springmvc controller的ioc容器
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{MvcConfig.class};
    }
    //设置springmvc自带servlet的访问地址
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

6.部署项目

7.启动项目并访问项目localhost:8080/springmvc/hello

Spring MVC 接收数据

访问路径设置

之前:@WebServlet("必须以 / 开头") /user/login

现在:@RequestMapping(不必须以 / 开头) /user/login user/login /user/login/

1.精准地址 :[一个 | 多个] /user/login | {"/user/login","/user/login2"}

2.模糊地址:* 一层模糊 ** 任意层模糊

/user/* -> /user/a,/user/aaa 可以;/user/a/b 不可以。

/user/** -> /user/a,/user/a/b,/user/a/a/a/a

3.该注解可以使用在类上或方法上,最终访问路径为:类地址 + 方法地址

4.请求方式指定:

请求方式主要有:GET,POST,PUT,DELETE

方式1:@RequestMapping("login",method=RequestMethod.GET)

方式2:@GetMapping("login") ,该注解只能用在方法上

不指定时默认所有请求方法都可以

多个请求方法@RequestMapping("a",method={RequestMethod.GET,RequestMethod.POST})

如果有类注解,方法注解内可以不写路径,该方法的访问路径默认为类注解的路径

接收参数(重点)

param和json参数比较

1.参数编码

param参数会被编译为ASCII码,如name=john doe,会被编译为name=john%20doe;而JSON参数会被编译为UTF-8。

2.参数顺序

param参数没有顺序限制,但JSON参数有顺序限制。

3.数据类型

param类型仅支持字符串类型、数值类型和布尔类型;JSON参数则支持更复杂的类型:数组,对象等。

4.嵌套性

param参数不支持嵌套,JSON参数支持

param参数接收

1.直接接收

如果形参数名和传递参数名相同,即可自动接收。

2.@RequestParam注解

可以使用该注解将Servlet请求参数绑定到controller中的方法的参数上。

使用场景: 指定绑定的请求参数名,要求参数必须传递,为请求参数提供默认值。

演示

复制代码
@Controller
@RequestMapping("user")
public class UserController {
    /**
     * 直接接收
     * 请求地址:/user/data1?name=qiu&age=18
     * 1.方法参数名和请求参数名一致,2.请求参数可以不传时
     */
    @ResponseBody
    @RequestMapping("data1")
    public String test(String name, int age){
        System.out.println("name = " + name);
        System.out.println("age = " + age);
        return "name="+name+" age="+age;
    }
    /**
     * 注解传参
     * 指定任意的请求参数名,默认要求必须传递,
     *                   如果设置为不必须传递则要设置默认。
     *      /user/data2?account=qiu&age=18
     */
    @ResponseBody
    @RequestMapping("data2")
    public String test2(@RequestParam("account") String name,
                        @RequestParam(required = false,defaultValue = "1") int age){
        System.out.println("name = " + name + ", age = " + age);
        return "name=" + name +"&age="+age;
    }
    /**
     * 特殊值
     *  一个属性传多个值,直接使用集合接收
     *  必须使用@RequestParam,不然可能会把第一个ids当作集合ids直接传值,类型不同会报错
     *      /user/data3?ids=1&ids=2&ids=3
     */
    @ResponseBody
    @RequestMapping("data3")
    public String test3(@RequestParam("ids")List<String> ids){
        System.out.println("ids = " + ids);
        return ids.toString();
    }
    /**
     * 使用实体类接收值
     * 创建User类 类属性有name 和 age,必须要有set/get方法
     *      属性名必须等于参数名
     *      /user/data4?name=qiu&age=18
     */
    @ResponseBody
    @RequestMapping("data4")
    public String test4(User user){
        System.out.println("user = " + user);
        return user.toString();
    }
}

测试

路径参数接收

像/user/{name}/18,如果我们想接收name的值,则可以使用路径参数接收

步骤:1、设置动态路径

2、接收动态路径参数

复制代码
//  /user/data5/qiu/123
@ResponseBody
@RequestMapping("data5/{name}/{password}")//设置动态路径
//获取路径参数
public String test5(@PathVariable("name") String username,@PathVariable String password){
    return username+password;
}
json参数接收

前端传递JSON数据时,SpringMVC框架可以使用@RequestBody来将JSON数据转换为Java对象。

Java对象Person

复制代码
@Data
public class Person {
    private String name;
    private int age;
    private String gender;
}

Controller类PersonController

复制代码
@Controller
@RequestMapping("json")
public class PersonController {

    @RequestMapping("person")
    @ResponseBody
    public String test(@RequestBody Person person){
        return person.toString();
    }
}

发送请求

结果会报415错误,因为java原生的api只能接收路径传参和param参数,不支持json参数

解决方法:1、导入json处理的依赖 2、为handlerAdapter配置json转化器

添加依赖

复制代码
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.0</version>
</dependency>

在spring配置类上添加**@EnableWebMvc**:为handlerAdapter配置了json转化器

这样请求就发送成功了

@EnableWebMvc注解

在spring配置类上添加该注解相当于在springxml配置文件添加了<mvc:annotation-driven/>

而<mvc:annotation-driven/>在底层会自动帮我们把json处理器添加到一个HandlerAdapter上,再把这个handlerAdapter和一个handlerMapping添加到ioc容器内。

因此当我们添加该注解后就不需要在spring配置类中手动添加HandlerAdapter和HandlerMapping了。

接收Cookie和请求头数据

在参数前添加@CookieValue和@RequestHeader就行

复制代码
public String method(@CookieValue("cookie") String value,                      @RequestHeader("header")String value2){
      ...
}

原生Api对象和共享域对象操作

想要使用原生Api对象可以直接通过参数获得

复制代码
    @Autowired
    private ServletContext context;
    public String getApi(HttpServletRequest request,
                         HttpServletResponse response,
                         HttpSession session){
//       要获取ServletContext
//       ServletContext [1.最大的配置文件,2.全局最大的共享域,3.核心api getRealPath]
//        方式1:通过request,session获取
        ServletContext servletContext = request.getServletContext();
        ServletContext servletContext1 = session.getServletContext();
//        方式2:声明一个全局变量,然后再上面添加一个@Autowire
//                (ServletContext在程序启动时会自动添加到ioc容器内)
        return "";
    }

Spring MVC 响应数据

handler方法分析

handler方法其实就是我们自己创建的controller类下的各种方法

接收请求数据,我们通过handler方法的形参列表

返回数据响应,我们通过return关键字

页面跳转控制

快速返回模板视图

当使用前后端分离模式(当前主流)时,该功能不再被需要。

要把jsp等视图返回给用户,需要使用视图解析器。

因此要先把视图解析器加入ioc容器

复制代码
@Configuration
@ComponentScan("com.qiu.controller")
@EnableWebMvc
//WebMvcConfigurer 可以快速配置springmvc的组件
public class MvcConfig implements WebMvcConfigurer {

//    将视图解析器加入到ioc容器内,
//    registry.jsp("/WEB-INF/views/",".jsp") 为handler返回的视图路径添加前后缀,其底层就是字符串拼接
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/views/",".jsp");
    }
}

编写index.jsp文件

复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%--    ${data}意思是在request域中获取key=data的value,在request.setAttribute("data","hello jsp")--%>
    <font color="red">${data}</font>
</body>
</html>

编写handler方法

复制代码
@Controller
@RequestMapping("jsp")
public class HelloController {

    @GetMapping("index")
    public String hello(HttpServletRequest request){
        //在请求域中设置data:hello jsp,为index.jsp文件内的${data}赋值
        request.setAttribute("data","hello jsp");
        //此handler没有@ResponseBody,
        //  所以将把“index”与视图解析器设置的前后缀拼接并查找文件并返回给用户
        return "index";
    }

}

这样用户就可以获取到index.jsp

转发和重定向

直接通过以下代码和注释学习

复制代码
@GetMapping("index")
public String hello(HttpServletRequest request){
    //在请求域中设置data:hello jsp,为index.jsp文件内的${data}赋值
    request.setAttribute("data","hello jsp");
    //此handler没有@ResponseBody,
    //  所以将把“index”与视图解析器设置的前后缀拼接并查找文件并返回给用户
    return "index";
}

/**
 * 转发和重定向依然不能添加@ResponseBody
 * 要转发时,需要在返回的地址前添加 forward:
 * 要重定向时,需要在返回的地址前添加 redirect:
 */
@GetMapping("forward")
public String forward(){
    return "forward:/jsp/index";
}
//原本重定向的地址如果是项目内需要包含根路径,
//      即 http://localhost:8080/mvcpro/jsp/index 应该返回 redirect:/mvcpro/jsp/index
//      但springmvc内部做了优化,不需要根路径
@GetMapping("redirect")
public String redirect(){
    return "redirect:/jsp/index";
}

@GetMapping("baidu")
public String baidu(){
    return "redirect:http://www.baidu.com";
}

返回JSON数据(重点)

接收JSON和返回JSON都需要导入jackson-databind依赖

@ResponseBody:返回JSON的注解,添加到类或方法上,不走视图解析器直接把数据返回给前端。

当我们想要返回JSON数据给前端时,先导入依赖,把JSON解析器添加到ioc容器中(使用@EnableWebMvc),然后在handler方法上添加@ResponseBody注解,最后直接把pojo类返回就行了,springmvc会自动把该类转换为JSON数据再发送给前端。

pojo类

复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String name;
    private String gender;
}

handler方法

复制代码
@Controller
@ResponseBody
@RequestMapping("json")
public class JsonController {

    @GetMapping("user")
    public User json(){
        return new User("秋","男");
    }

}

返回静态资源处理

如果项目结构像这样,

我们无法通过 .../images/OIP-C.jpg访问静态资源。(jsp属于动态资源)

原因:DispatcherServlet会接收所有请求,包括对静态资源的请求,并且他只会在HandlerMapping中根据路径查找对应的handler方法,而静态资源没有对应的handler方法。

解决办法:需要我们开启静态资源查找:

还是在 WebMvcConfigurer 接口下,有个 configureDefaultServletHandling 方法

复制代码
//相当与开启了<mvc:default-servlet-handler/>
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();//开启
}

开启后dispatcherServlet在HandlerMapping中没有找到资源,会再拿着路径查找有没有对应的静态资源。

RestFul风格设计

RestFul:Http协议的标准使用方案和风格。

前端往后端发送请求时,需要考虑:

1.路径如何设计(如:/user/add?) 2.要使用哪种传递参数方案(param?json?path?)

3.要使用哪种请求方式(get?post?delete?put?)

RestFul就是解决这些问题的一种方法。

RESTFul风格特点

1.每一种URI代表一种资源(URI是名词)

设计URI时尽量不使用动词,如:要新增用户不用:POST /user/add,而是使用:POST /user; 要删除用户不用:DELETE /user/delete?id=1,而是使用:DELETE /user/1

2.客户端使用GET,POST,PUT,DELETE 4个表示操作方式的动作来对服务器资源进行操作:

GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源。

3.资源使用xml或JSON

Spring MVC 其他拓展

全局异常处理机制

异常处理的两种方式

异常处理一般分为:编程式异常处理(使用trycatch手动显示地处理) 和 声明式异常处理(将异常处理的逻辑从具体业务逻辑中脱离,通过配置等方式进行统一的管理和处理)。

基于注解异常声明异常处理

1.声明异常处理控制器类

2.声明异常处理handler方法

java 复制代码
//该注解 = @ControllerAdvice + @ResponseBody
//@ControllerAdvice 代表当前类的异常处理controller
//异常发生时,会走此类写的handler,
@RestControllerAdvice
public class GlobalExceptionHandler {

    //发送异常 -》 ControllerAdvice -》@ExceptionHandler -》根据异常类调用方法

    @ExceptionHandler(ArithmeticException.class)
    public Object ArithmeticExceptionHandler(ArithmeticException e){
        //自定义处理异常
        return null;
    }
    @ExceptionHandler(Exception.class)
    public Object ExceptionHandler(Exception e){
        return null;
    }


}

拦截器使用

拦截器概念

Filter过滤器

在javaweb项目中,可以使用Filter过滤器,当请求来到服务器时,会先经过过滤器的处理(登录保护,编码格式,权限处理),再到对应的目标类。

但SpringMVC中,Filter过滤器就不适用了,SpringMVC使用DispatcherServlet接收请求,当我们设置了Filter时,请求会在到达DispatcherServlet前经Filter处理,而SpringMVC内部细化流程Filter无法拦截。

HandlerInterceptor 拦截器(SpringMVC环境下推荐使用)

SpringMVC提供了HandlerInterceptor拦截器,它会在handlerAdapter调用handler之前和之后拦截以及整体处理之后拦截。

拦截器使用

1.创建拦截器类

java 复制代码
public class MyInterceptor implements HandlerInterceptor {

    //在handler执行前拦截:编码格式设置,登录保护,权限处理

    /**
     *
     * @param request   请求对象
     * @param response  响应对象
     * @param handler   我们要调用的方法对象
     * @return true 放行     false 拦截
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
    //在handler执行后触发,没有拦截机制(方法已执行,拦截无意义)
    // modelAndView 返回的视图和共享域对象
    // 可以进行敏感词检查
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }
    //在数据返回给客户端前拦截
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

2.修改配置类添加拦截器

复制代码
@Configuration
@ComponentScan("com.qiu.controller")
@EnableWebMvc
//WebMvcConfigurer 可以快速配置springmvc的组件
public class MvcConfig implements WebMvcConfigurer {

//    将视图解析器加入到ioc容器内,
//    registry.jsp("/WEB-INF/views/",".jsp") 为handler返回的视图路径添加前后缀,其底层就是字符串拼接
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/views/",".jsp");
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //配置方案1:拦截所有请求
        //registry.addInterceptor(new MyInterceptor());
        //配置方案2:指定地址拦截        /**:拦截路径下所有请求
        //registry.addInterceptor(new MyInterceptor())
        //                .addPathPatterns("/user/**");
        //配置方案3:排除拦截   
        registry.addInterceptor(new MyInterceptor())
                          .addPathPatterns("/user/**")
                                .excludePathPatterns("/user/data"); 
    }
}

参数校验

场景:每次请求参数都需要判断是否为空和检查格式是否正确。

java通过了jsr303系列注解,只需要准备对应的实体类并在其属性上添加注解,当该实体类对象的属性的数据为空或者格式有误,就会报错。

使用

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.应用

java 复制代码
@Data
public class User {
    //age   1 <=  age < = 150
    @Min(10)
    private int age;

    //name 3 <= name.length <= 6
    @Length(min = 3,max = 10)
    private String name;

    //email 邮箱格式
    @Email
    private String email;

}

3.handler方法标记

java 复制代码
@RestController
@RequestMapping("user")
public class UserController {

    /**
     * @Validated 代表应用校验注解! 必须添加!
     */
    @PostMapping("save")
    public Object save(@Validated @RequestBody User user,
                       //在实体类参数和 BindingResult 之间不能有任何其他参数, BindingResult可以接受错误信息,避免信息抛出!
                       BindingResult result){
       //判断是否有信息绑定错误! 有可以自行处理!
        if (result.hasErrors()){
            System.out.println("错误");
            String errorMsg = result.getFieldError().toString();
            return errorMsg;
        }
        //没有,正常处理业务即可
        System.out.println("正常");
        return user;
    }
}
相关推荐
西岸行者4 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意4 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码5 天前
嵌入式学习路线
学习
毛小茛5 天前
计算机系统概论——校验码
学习
babe小鑫5 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms5 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下5 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。5 天前
2026.2.25监控学习
学习
im_AMBER5 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J5 天前
从“Hello World“ 开始 C++
c语言·c++·学习