• Spring MVC


    什么是 MVC

    Spring MVC 其实是 Spring Web MVC,是基于 Servlet API,所以 Servlet 是 Spring MVC 的 “父亲”。因此,Servlet 那一套编程方法,在 Spring MVC 中,也是可以使用的。但是 Spring MVC 比 servlet 要简单很多。

    Spring MVC :一开始就在 Spring 框架当中。只是属于 Spring 中的一个 web 模块。

    一个 web 项目,只做三件事

    1. 实现用户请求 到 程序 的 链接,就是用户请求可以被 程序接收到。
    2. 在前后端建立联系的情况下,拿到用户请求的参数。
    3. 拿到参数之后,进行业务处理,并将其结果返回给前端。
    4. 使用 Spring MVC 的话,只需要前后端的名称一样就行了。

    MVC 定义

    MVC 是 Model View Controller (模型视图控制器)的缩写,它是软件⼯程中的⼀种软件架构模式,它把软件系统分为模型、视图和控制器三个基本部分。
    在这里插入图片描述

    1. Model(模型) 是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
    2. View(视图) 是应用程序中处理数据显示的部分,通常视图是依据模型数据创建的。
    3. Controller(控制器) 是应⽤程序中处理⽤户交互的部分。通常控制器负责从视图读取数据,控制⽤户输⼊,并向模型发送数据。

    四者之间的关系如下
    在这里插入图片描述

    1. 可以把 View,Controller,Model 看作是一个软件(项目)。
    2. 然后用户通过浏览器发送一个 HTTP 请求到 Controller ,然后 Controller 完成对数据的校验,校验数据的正确性和合法性。
    3. Controller 校验数据通过之后,就会将请求数据发送到 model,然后 model 对数据库进行处理之后,再把结果发给 Controller
    4. 然后 COntroller 在把得到的数据交给 View
    5. View 是服务器的视图层,然后和前端的模板相结合,再加上渲染,然后发给客户,客户就能看懂了。

    MVC 和 Spring MVC 的关系

    两者的关系,就像 IoC 和 DI 的关系。前者是思想,后者是实现。

    Spring MVC 是⼀个实现了 MVC 模式,并继承了 Servlet API 的 Web 框架。因为是 Web 框架,所以⽤户在浏览器中输⼊了 url 之后,我们的 Spring MVC 项 ⽬就可以感知到⽤户的请求。

    Spring MVC 的影响

    现在绝大部分的 Java 项目都是基于 Spring(或 Spring Boot)的,而 Spring 的核心就是 Spring MVC。现在大部分的 Java 项目都是 Spring MVC 项目。

    创建 Spring MVC 项目

    创建项目和我们之前创建 Spring Boot 项目一样,只是在添加依赖的时候,选择 Spring web 就好了:
    在这里插入图片描述
    这样就完成了 Spring MVC 项目的创建了。

    Spring MVC 作用

    1. 实现 用户 和 程序 的映射(在浏览器输入 URL 之后,能够在程序中匹配到相应的方法)
    2. 服务器端要得到用户的请求参数
    3. 服务器端要将结果返回给用户(前端)

    实现用户 和 程序的映射

    1. 使用 @RequestMapping(“/xxx”) 映射,可以加在 类上面,就是一级目录,然后方法上面再加的话,就是二级目录:

      @Controller
      @ResponseBody
      @RequestMapping("/user")
      public class UserController {
      
          @RequestMapping("/sayhi")
          public String sayHi() {
              return "hello world";
          }
      }
      

      访问方法如下:
      在这里插入图片描述

      user 是一级目录,sayhi 是二级目录。默认是 GET 请求:
      在这里插入图片描述

    2. 使用 POST 请求来访问资源,通过 method 参数来设置:

      @Controller
      @ResponseBody
      @RequestMapping("/user")
      public class UserController {
      
          @RequestMapping(method = RequestMethod.POST, value = "/sayhi")
          public String sayHi() {
              return "hello world";
          }
      }
      

      访问结果如下:
      在这里插入图片描述
      就支持 POST 访问了。

    3. 使用 @PostMapping 来指定 POST 访问:

      @Controller
      @ResponseBody
      @RequestMapping("/user")
      public class UserController {
      
          @PostMapping("/sayhi")
          public String sayHi() {
              return "hello world";
          }
      }
      

      访问结果如下:
      在这里插入图片描述

    4. 通过 GetMapping 来进行 GET 访问:

      @Controller
      @ResponseBody
      @RequestMapping("/user")
      public class UserController {
      
          @GetMapping("/sayhi")
          public String sayHi() {
              return "hello world";
          }
      }
      

      访问结果如下:
      在这里插入图片描述

    获取用户请求参数

    获取单个参数

    通过传入参数,然后返回对象:

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @GetMapping("/getuserbyid")
        public UserInfo getUserById(Integer id) {
            UserInfo userInfo = new UserInfo();
            userInfo.setId(id);
            userInfo.setUsername("zhangsan");
            userInfo.setAge(18);
            return userInfo;
        }
    }
    

    传入 id = 1,只要保证传入的参数和接收的参数一样就好了,运行结果如下:
    在这里插入图片描述
    使用 Integer 是可以接收 null 的。

    获取多个参数

    通过传多个参数来获取:

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @RequestMapping("/login")
        public String login(String username, String password) {
            return "用户名:" + username + " 密码:" + password;
        }
    }
    

    运行结果如下:
    在这里插入图片描述
    也就是获取多个参数和获取一个参数的方法是一样的,只需要传递多个参数就好了。

    获取对象

    如果有多个参数的时候,通过传对象可以让结果看的更简洁:

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @RequestMapping("/reg")
        public String login(UserInfo userInfo) {
            return "用户信息:" + userInfo;
        }
    }
    

    前端传这样的参数:

    http://127.0.0.1:8080/user/reg?username=zhangsan&password=123&age=18
    

    运行结果如下:
    在这里插入图片描述

    后端参数重命名

    某些特殊情况下,前端传递的参数 key 和我们后端接收的 key 可能不一致比如前端传递了 name 之后,后端是用 username 来接收的,那么就会报错。通过 @RequestParam 来对存在念书重命名:

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @RequestMapping("/reg")
        public String login(@RequestParam("name") String username) {
            return "用户名:" + username;
        }
    }
    

    前端参数:

    http://127.0.0.1:8080/user/reg?name=zhangsan
    

    运行结果如下:

    在这里插入图片描述
    也就是当使用了这个注解之后,前端必须要传一个 name 参数。否则就报错。如果加了 required 之后,设置 required 的参数为 false,因为默认是 true,就不会报错了,会显示用户名是 null:

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @RequestMapping("/reg")
        public String login(@RequestParam(value = "name", required = false) String username) {
            return "用户名:" + username;
        }
    }
    

    运行结果如下:
    在这里插入图片描述

    接收 JSON 对象

    使用 RequestBody 注解来完成对象的接收,后端代码如下:

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @RequestMapping("/reg")
        public String login(@RequestBody UserInfo userInfo) {
            return "用户信息:" + userInfo;
        }
    }
    

    然后通过 Postman 来模拟:
    在这里插入图片描述

    从 URL 地址当中获取参数

    这里并不是从 URL 地址中的参数部分获取参数,而是从 / 来作为分隔符获取参数,通过 @PathVariable 注解来实现

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @RequestMapping("/info/{id}/{name}")
        public String getURLInfo(@PathVariable Integer id, @PathVariable String name) {
            return "ID:" + id + " name:" + name;
        }
    }
    

    前端 URL:

    http://127.0.0.1:8080/user/info/2/zhangsan
    

    运行结果如下:
    在这里插入图片描述
    在这里插入图片描述

    上传文件

    通过 @RequestPart 注解来实现对文件的上传,只需要对参数加上注解就好了。然后通过 Spring 的 MultipartFile 来接收文件就好了:

    @Slf4j
    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @RequestMapping("/upload")
        public boolean upImg(Integer id, @RequestPart("image") MultipartFile file) {
            boolean result = false;
            //保存图片到本地目录
            try {
                file.transferTo(new File("D:/img.png"));
            } catch (IOException e) {
                log.error("上传图片失败: " + e.getMessage());
            }
            return result;
        }
    }
    

    Postman 模拟如下:
    在这里插入图片描述
    D 盘的文件如下:
    在这里插入图片描述
    但是如果这样每次的名字都一样的话,就会导致文件覆盖。

    配置文件保存路径

    直接把路径放在配置文件当中,这样的话,如果项目上线也很容易修改,直接修改为线上环境就好了,application 是必不可少的,不过可以通过 - 在 application 后面 加上名称。通过 application.yml 来配置 配置文件的运行环境:

    # 设置配置文件的运行平台
    spring:
      profiles:
        active: dev
    

    在这里插入图片描述
    开发环境配置
    在这里插入图片描述
    生产环境如下
    在这里插入图片描述
    文件路径

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        //从配置文件中读取图片的保存路径
        @Value("${img.path}")
        private String imgPath;
    
        @RequestMapping("/sayHi")
        public String sayHi() {
    
            return "文件路径:" + imgPath;
        }
    }
    

    访问结果如下:
    在这里插入图片描述
    这样就完成了对图片路径的访问。换成生产环境也一样可以。

    图片名不能重复

    让图片名不能重复,通过 UUID 来让图片永远不重复。UUID 是自动生成一个全球不同的一串数字。如果用时间戳的话,总会有两个人同时传数据,所以就会导致内容覆盖。

    获取图片的格式

    就是获取原图片的格式,不能向我们上面这样,改变图片的格式。从最后一个点,向后截取,就能获取到文件的后缀名了:

    @Slf4j
    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        //从配置文件中读取图片的保存路径
        @Value("${img.path}")
        private String imgPath;
    
        @RequestMapping("/upload")
        public boolean upImg(Integer id, @RequestPart("image") MultipartFile file) {
            boolean result = false;
            //保存图片到本地目录
    
            String fileName = file.getOriginalFilename();
            fileName = fileName.substring(fileName.lastIndexOf("."));
            fileName = UUID.randomUUID().toString() + fileName;
            try {
                file.transferTo(new File(imgPath + fileName));
                result = true;
            } catch (IOException e) {
                log.error("上传图片失败: " + e.getMessage());
            }
            return result;
        }
    }
    

    Postman 模拟如下:
    在这里插入图片描述
    保存路径内容如下:
    在这里插入图片描述

    获取 Cookie/Session/header

    获取 Request 和 Response 对象

    web 程序里面都会有 Request 和 Response 参数,只需要在需要的时候,直接使用就好了:

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @RequestMapping("/sayhi")
        public String sayHi(HttpServletRequest request) {
            return "hello world " + request.getParameter("name");
        }
    }
    

    运行结果如下:
    在这里插入图片描述

    获取 Cookie

    直接使用 @CookieValue 就好了,先设置好 Cookie:
    在这里插入图片描述
    然后输出:

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @RequestMapping("/cookie")
        public String getCookie(@CookieValue("lisi") String cookie) {
            return "Cookie Value" + cookie;
        }
    }
    

    运行结果如下:
    在这里插入图片描述
    如果要读取多个 cookie 的话,就设置多个 Cookie 参数。

    获取 Header

    直接通过 @RequestHeader 的注解来实现获取 Header,我们获取 Header 里面的 user-agent:

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @RequestMapping("/header")
        public String getCookie(@RequestHeader("User-Agent") String userAgent) {
            return "user-Agent: " + userAgent;
        }
    }
    

    运行结果如下:
    在这里插入图片描述

    存储和获取 Session

    设置 Session

    听过 HttpSession 来设置 session:

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @RequestMapping("/header")
        public boolean setSession(HttpServletRequest request) {
            boolean result = false;
            //得到 Session 
            HttpSession session = request.getSession(true);//true 是创建会话
            //使用 setAtt 设置值
            session.setAttribute("userinfo", "userinfo");
            return true;
        }
    }
    

    运行结果如下,设置 Session 就是多了一个 JSESSIONID :
    在这里插入图片描述

    获取 Session

    Spring Boot 通过 @SessionAttribute 注解就可以获取到 Session 了:

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @RequestMapping("/getsession")
        public String getSession(@SessionAttribute(value = "userinfo",required = false) String userinfo) {
            return "会话:" + userinfo;
        }
    }
    

    访问结构如下:
    在这里插入图片描述
    加上 required 的时候,如果 session 当中没有 此属性 的时候,就不会报错了。

    返回数据

    如下代码:

    @Controller
    @RequestMapping("/user")
    public class UserController {
    
        @RequestMapping("/sayhi")
        public String sayHi() {
            return "hello";
        }
    }
    

    这样返回的其实是一个 静态页面 的结果:
    在这里插入图片描述
    把代码当中换成 hello.html ,然后写一个页面,就可以了:

    @Controller
    public class UserController {
    
        @RequestMapping("/sayhi")
        public String sayHi() {
            return "hello.html";
        }
    }
    

    运行结果如下:
    在这里插入图片描述
    @ResponseBody,就表示返回非静态页面的数据。

    使用 @RestController

    通过 @RestController 注解,就可以直接返回非静态页面的数据了:

    @RestController
    public class UserController {
    
        @RequestMapping("/sayhi")
        public String sayHi() {
            return "hello.html";
        }
    }
    

    运行结果如下:
    在这里插入图片描述

    练习

    form 表单

    通过 MVC 和 form 表单来实现。前端代码:

    DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>addtitle>
        head>
        <body>
            <form action="/calc">
                <h1>计算器h1>
                数字1:<input name="num1" type="text"><br>
                数字2:<input name="num2" type="text"><br>
                <input type="submit" value=" 点击相加 ">
            form>
        body>
    html>
    

    后端接受的时候,路由也是 /calc 来接收,然后返回页面数据就好了:

    @RestController
    public class CalcController {
    
         @RequestMapping("/calc")
        public String calc(Integer num1, Integer num2) {
             if (num1 == null || num2 == null) {
                 return "

    参数错误!

    返回"
    ; } return "

    结果:" + (num1 + num2) + "

    返回"
    ; } }

    后端返回的结果,这里写成了 h1 标签。运行结果如下:
    在这里插入图片描述
    点击之后效果如下:
    在这里插入图片描述

    Ajax

    通过 Ajax 来完成用户的登录,通过模拟登录来实现用户交互,前端代码如下:

    doctype html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <script src="js/jquery-1.9.1.min.js">script>
        <title>Documenttitle>
        <script>
          //ajax 提交
          function mysub(){
            //1.判空   也可以把 jQuery 符号换成 $
            var username = jQuery("#username");
            var password = jQuery("#password");
            //使用 trim 方法来去掉空格
            if(jQuery.trim(username.val())==""){
              alert("请先输入用户名!");
              //把光标重新放到元素上面
              username.focus();
              return;
            }
            if(jQuery.trim(password.val())==""){
              alert("请先输入密码!");
              password.focus(); //光标重制到此元素
              return;
            }
            jQuery.ajax({
              url:"/login",
              type:"POST",
              data:{"username":username.val(),
                "password":password.val()},
              success:function(result){
                alert(JSON.stringify(result));
              }
            });
          }
        script>
      head>
      <body>
        <div style="text-align: center;">
          <h1>登录h1>
          用户:<input id="username">
          <br>
          密码:<input id="password" type="password">
          <br>
          <input type="button" value=" 提交 " onclick="mysub()" style="margin-top: 20px;margin-left: 50px;">
        div>
      body>
    html>
    

    后端代码如下:

    @RestController
    public class UserController {
    
        @RequestMapping("/login")
        public HashMap<String, Object> login(String username, String password) {
            HashMap<String, Object> result = new HashMap<>();
            int state = 200;
            int data = -1;//等于 1 的话,就表述登陆成功
            String msg = "";
            if (StringUtils.hasLength(username) && StringUtils.hasLength(password)) {
                if (username.equals("admin") && password.equals("admin")) {
                    data = 1;
                    msg = "";
                }
            } else {
                msg = "非法参数";
            }
            result.put("state", state);
            result.put("data", data);
            result.put("msg", msg);
            return result;
        }
    }
    

    运行结果如下:
    在这里插入图片描述
    故意填错密码:
    在这里插入图片描述
    填对密码的时候,就和我们约定的结果一样了:
    在这里插入图片描述

    通过 JSON 来传输

    通过 JSON 来传输数据的时候,后端就要通过 @RequeestBody 来接收了。然后前端也要指定为 JSON 格式发送。就是转化为 JSON 字符串就好了。前端代码如下:

    doctype html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <script src="js/jquery-1.9.1.min.js">script>
        <title>Documenttitle>
        <script>
          //ajax 提交
          function mysub(){
            //1.判空   也可以把 jQuery 符号换成 $
            var username = jQuery("#username");
            var password = jQuery("#password");
            //使用 trim 方法来去掉空格
            if(jQuery.trim(username.val())==""){
              alert("请先输入用户名!");
              //把光标重新放到元素上面
              username.focus();
              return;
            }
            if(jQuery.trim(password.val())==""){
              alert("请先输入密码!");
              password.focus(); //光标重制到此元素
              return;
            }
            jQuery.ajax({
              url:"login",
              type:"POST",
              contentType:"application/json",
              data:JSON.stringify({"username":username.val(),
                "password":password.val()}),
              success:function(result){
                alert(JSON.stringify(result));
              }
            });
          }
        script>
      head>
      <body>
        <div style="text-align: center;">
          <h1>登录h1>
          用户:<input id="username">
          <br>
          密码:<input id="password" type="password">
          <br>
          <input type="button" value=" 提交 " onclick="mysub()" style="margin-top: 20px;margin-left: 50px;">
        div>
      body>
    html>
    

    后端代码如下:

    @RestController
    public class UserController {
    
        @RequestMapping("/login")
        public HashMap<String, Object> login(@RequestBody UserInfo userInfo) {
            HashMap<String, Object> result = new HashMap<>();
            int state = 200;
            int data = -1;//等于 1 的话,就表述登陆成功
            String msg = "";
            if (StringUtils.hasLength(userInfo.getUsername()) && StringUtils.hasLength(userInfo.getPassword())) {
                if (userInfo.getUsername().equals("admin") && userInfo.getPassword().equals("admin")) {
                    data = 1;
                    msg = "";
                } else {
                    msg = "用户名或密码不正确";
                }
            } else {
                msg = "用户名或密码不正确";
            }
            result.put("state", state);
            result.put("data", data);
            result.put("msg", msg);
            return result;
        }
    }
    

    运行结果如下:
    在这里插入图片描述

    请求转发和请求重定向

    请求转发是 forward,请求重定向是 redirect。

    请求转发

    通过 forward 来实现请求转发:

    @Controller
    public class UserController {
    
        @RequestMapping("/myforward")
        public String myForward() {
            return "forward:/hello.html";
        }
    }
    

    forward 后面加的是请求转发路径。运行结果如下:
    在这里插入图片描述
    直接到了 hello.html 页面了。

    请求重定向

    请求重定向是 redirect :

    @Controller
    public class UserController {
    
        @RequestMapping("/myredirect")
        public String myRedirect() {
            return "redirect:/hello.html";
        }
    }
    

    运行结果如下:
    在这里插入图片描述
    这里就是直接访问 hello.html 页面了。重定向是发生在客户端的行为,不会经过服务器端。

  • 相关阅读:
    Java多线程使用HttpClient技术爬取多个网站的资源
    走进人工智能|自主无人系统 从概念到现实的飞跃
    Matplotlib傻瓜指南
    实现表格表头自定义编辑、一键导入、增加列
    使用MySQL如何查询一年中每月的记录数
    springboot+大学生就业规划系统 毕业设计-附源码191451
    C++:map和set容器
    java面试题-微服务面试题
    面试系列之Python基础
    Mixin从理论到实践
  • 原文地址:https://blog.csdn.net/sjp151/article/details/126865958