Spring MVC

1. Spring MVC概述

1.1 Spring MVC是什么

SpringMVC是Spring的一个模块,是一个基于MVC设计模式的web框架。

1.2 Spring MVC执行流程。

1.3 组件分析

  • 前端控制器(默认配置)Dispatcher Servlet 作用:只负责分发请求。可以很好的对其它组件进行解耦合。

  • 处理器映射器(默认配置)HandlerMapping

    作用:将前端的url和处理器方法建立映射关系

  • 处理器适配器(默认配置)HandlerAdapter

    适配并调用具体的处理器方法执行

  • 处理器 controller

    (需要程序员开发)

  • 视图解析器 ViewResolver

    根据逻辑视图查找映射物理视图

简化的MVC执行流程:

前后分离的执行流程:

2. Handler

引入依赖

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

2.1 参数

Handler的参数列表,功能非常强大,主要功能有以下两点:

  1. 接收请求参数

  2. 获取servletAPI

  3. @Controller

    @RestController

    public class Login {

    }

2.1.1接收请求参数
  • 简单类型的参数,Spring MVC可以直接帮我们封装成参数列表中声明的类型,比如String、int、double......

java 复制代码
//http://localhost:8080/login?username=&password=
@RequestMapping("/login")
public void login(String username, String password){
    System.out.println(username);
    System.out.println(password);
}
2.1.2 获取servletAPI

可以在Handler的形参中直接使用以下类型:

  • HttpServletRequest 通过request对象获取请求信息

  • HttpServletResponse 通过response处理响应信息

  • HttpSession 通过session对象得到session中存放的对象

java 复制代码
@ResponseBody 
   @RequestMapping("/login")   // 获取前端请求资源路径
    public String login(String username, Integer password, HttpServletRequest request) {
        HttpSession session = request.getSession();
        session.setAttribute("username", username);
        return "index";
    }
		@ResponseBody 
    @RequestMapping("/test")   // 获取前端请求资源路径
    public String test(HttpServletRequest request) {
        HttpSession session = request.getSession();
        Object username = session.getAttribute("username");
        String uname = (String) username;
        return uname;
    }
		@ResponseBody 
    @RequestMapping("/login")   // 获取前端请求资源路径
    public String login(String username, Integer password, HttpSession session) {
        session.setAttribute("username", username);
        return "index";
    }
		@ResponseBody 
    @RequestMapping("/test")   // 获取前端请求资源路径
    public String test(HttpSession session) {
        Object username = session.getAttribute("username");
        String uname = (String) username;
        return uname;
    }

2.2 返回值(了解)

可以为Handler指定两种种返回值类型:

  • void

    如果返回值为void的时候,可以在Handler形参上定义requestresponse,使用requestresponse指定响应结果

  • response.getWriter().println();

  • String

    逻辑视图名称

    返回的字符串就是逻辑视图。

  • return "index.jsp";

  • 请求转发与重定向

java 复制代码
@RequestMapping("/login")
    // @ResponseBody  注释,否则返回json格式
    public String login(String username, Integer password, HttpSession session) {
        session.setAttribute("username", username);
        //   return "forward:/test";  // 请求转发
        return "redirect:/test"; // 重定向
    }

    @RequestMapping("/test")
    @ResponseBody  // 这里需要返回json
    public String test(HttpSession session) {
        Object username = session.getAttribute("username");
        String uname = (String) username;
        return uname;
    }

2.3 注解

  • @RequestMapping

    • 声明在方法上:

    java 复制代码
    //@RequestMapping("/login")
    //@RequestMapping(value = "/login")
    //@RequestMapping(path = "/login")
    //@RequestMapping(value = {"/login1", "/login2"})
    @Controller
    public class TestController {
        @ResponseBody  
        @RequestMapping(value = {"/login1", "/login2"}, method = RequestMethod.GET)
      	// 只有get请求才可以 post请求进不来
        public String login(String username) {
            System.out.println(username);
            return username;
        }
    }
    • 通过value属性配置该方法的访问路径

    • 通过method属性指定该方法允许的访问方式,默认情况get,post都支持

    java 复制代码
    // 静态文件目录,resources/static/login.html  访问方式 http://localhost:8080/login.html
    <form action="/login1" method="get">
    
        <input type="text" name="username">
        <button>提交</button>
    
    </form>

    声明在类上:窄化请求

    ⚠️:我们的controller中是不允许有相同的资源路径的

    假设我们的EmpController和DeptController都需要将资源路径成定义"/findAll"必然是不合法的

    我们可以将资源路径分别定义成"emp/findAll"和"dept/findAll",这种方式叫做窄化请求

    窄化请求,可以对请求URL进行分类管理,例如:/person/add/person/list......

    • @RequestParam

      该注解用来标注一个请求参数:

      在方法的形参前,可以加可以不加,加了就有特殊含义了

    java 复制代码
    @RequestMapping(value = "/login1")
        public String login1(String name) {
            System.out.println(name);
            return "index";
        }
    //上述方式在请求 login1 可以不强制传递请求参数,那打印name结果是null
    
    
        @RequestMapping(value = "/login2")
        public String login2(@RequestParam String name) {
            System.out.println(name);
            return "index";
        }
    //上述方式在请求 login2 强制必须传递请求参数,那打印name结果就是请求参数传的值
    
    
    //http://localhost:8080/login3?name=tom
     @RequestMapping(value = "/login3")
        public String login3(@RequestParam("name") String username) {
            System.out.println(username);
            return "index";
        }
        //上述方式在请求 login3 的请求参数是name 指定请求参数的名字,用于处理前后端参数不一致
        //@RequestParam(value = "name") 和 @RequestParam(name = "name") 等同于 @RequestParam("name")
    
    
    
    //http://localhost:8080/login4    
    @RequestMapping(value = "/login4")
        public String login4(@RequestParam(name = "name", required = false) String username) {
            System.out.println(username);
            return "index";
        }
    //  required = false,默认是true 代表着必须传递参数,如果设置false代表可以不传递参数,不传为null
    
    
    @RequestMapping(value = "/login5")
    public String login5(@RequestParam(name = "name",defaultValue = "zs") String username) {
      System.out.println(username);
      return "index";
    }
    //设置默认值,这样required就不需要在写了。
      • value@RequestParam(value="username")等同于@RequestParam("username"),对应请求参数的键

      • required:参数是否必填

      • defaultValue:设置默认值

    • @PathVariable

      将路径的一部分作为请求参数,RESTful的基础。

    java 复制代码
    //http://localhost:8080/findById/1
    @RequestMapping("/findById/{id}")
    public String findById(@PathVariable Integer id) {
       System.out.println(id);
       return "index";
    }
    
    //请求参数就不可以是?号。参数必须是目录形式。
    
    @RestController
    @RequestMapping("/person")
    public class PersonController {
        @GetMapping("/findById/{id}")
        public String findById(@PathVariable Integer id) {
            System.out.println(id);
            return null;
        }
    
        @PutMapping("/updateById/{id}")
        public String updateById(@PathVariable Integer id) {
            System.out.println(id);
            return null;
        }
    
        @DeleteMapping("/deleteById/{id}")
        public String deleteById(@PathVariable Integer id) {
            System.out.println(id);
            return null;
        }
    
    
        @GetMapping("/findByIdOrName/{id}/{name}")
        public String findByIdOrName(@PathVariable Integer id, @PathVariable String name) {
            System.out.println(id);
            System.out.println(name);
            return null;
        }
    }

    RESTful

    RESTful就是一个url的编写风格。使用RESTful就不需要用❓问号分割请求参数了。

    一个严格的RESTful中是不可能存在❓的。

    一个URl对应一个资源,请求方式确定一个动作。

    ps:有一张person表id有1 2 3 ,name有zs,lisi,wangwu。

    POST请求:就是向数据库中添加数据。/person,代表把person对象添加到表中

    DELETE请求:就是向数据库中删除一条数据。 /person/1 ,代表把person表中id为1的 删除

    PUT请求:就是向数据库修改一条数据。/person/1,代表把person表中id为1的 修改

    GET请求:就是向数据库表查询数据。/person/3, 代表把person表中id为3的 查询出来

3. 静态资源访问

springboot中放置静态资源的目录,常用有static和templates目录,只要把静态资源放到这几个目录下,就能直接访问到

static目录下静态资源默认是可以被访问到的,但是templates目录下资源默认是访问不到的,需要做配置:

pring.web.resources.static-locations=classpath:/templates/,classpath:static/

也可以直接给所有静态资源添加一个前缀,既可统一拦截,又可统一放开

spring.mvc.static-path-pattern=/res/**

4. 文件上传

4.1 同步表单上传

1.添加fileupload依赖

<dependency>

<groupId>commons-fileupload</groupId>

<artifactId>commons-fileupload</artifactId>

<version>1.4</version>

</dependency>

2.配置

在配置文件中添加上传文件最大值

spring.servlet.multipart.max-file-size=20MB

3.form表单
html 复制代码
file.html
<html>
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>
</head>
<body>
<form action="/fileUpload" method="post" enctype="multipart/form-data">
    文件上传 : <input type="file" name="file">
    <br>
    <button>上传</button>
</form>
</body>
</html>

4.Controller编写

java 复制代码
@Controller
public class FileController {
    @RequestMapping("/fileUpload")
    public String fileUpload(MultipartFile file) {
        System.out.println(file.getOriginalFilename());
        System.out.println(file.getSize());
        return "index";
    }
}

4.2 高级上传

需求:跨服务器上传图⽚,⻚⾯不刷新,图⽚即时回显。

跨服务器上传图⽚:Jersey框架

⻚⾯不刷新:AJAX

图⽚即时回显:

先导入依赖

<dependency>

<groupId>commons-fileupload</groupId>

<artifactId>commons-fileupload</artifactId>

<version>1.4</version>

</dependency>

<dependency>

<groupId>io.minio</groupId>

<artifactId>minio</artifactId>

<version>8.2.0</version>

</dependency>

1.图片服务器
2.编写form表单
3.application.properties&Controller
java 复制代码
package com.codingfuture.demo1;

import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.UUID;

/**
 * @author 
 */
@RestController
public class FileController {
    @Value("${minio.endpointUrl}")
    public String endpointUrl;

    @Value("${minio.accessKey}")
    public String accessKey;

    @Value("${minio.secreKey}")
    public String secreKey;

    @Value("${minio.bucket}")
    public String bucket;

    //  获取文件上传对应的地址
    @PostMapping("/file-upload")
    public String uploadFile(MultipartFile file) {
        System.out.println(file.getOriginalFilename());
        System.out.println(file.getName());
        try {
            System.out.println(endpointUrl);
            // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象
            MinioClient minioClient = MinioClient.builder()
                    .endpoint(endpointUrl)
                    .credentials(accessKey, secreKey)
                    .build();

            InputStream inputStream = file.getInputStream();

            // 文件名字
            String fileName = System.currentTimeMillis() + UUID.randomUUID().toString();
            // 上传文件 已经文件大小
            // file.getContentType() 文件类型
            System.out.println(file.getContentType());
            minioClient.putObject(
                    PutObjectArgs.builder().bucket(bucket).object(fileName).stream(
                                    inputStream, file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build());
            inputStream.close();

            // 返回文件访问路径
            String url = endpointUrl + "/" + bucket + "/" + fileName;
            System.out.println(url);
            return url;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

5. Spring MVC对JSON的支持

Spring MVC对JSON的⽀持⾮常友好,主要⽤到两个注解:@RequestBody,@ResponseBody

也可以配置为其它的JSON⼯具,⽐如fastjson,⾃学如何配置。

为什么学习Jackson,因为它是SpringMVC默认的,方便配置使用。

@RequestBody

java 复制代码
public class User {
    private Integer id;
    private String username;
    private String password;
    getter&& setter
}
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<button id="btn1">前端发送json到后端</button>
<button id="btn2">接收后端数据</button>

<script>

    $("#btn1").click(function () {
        $.ajax({
            type: "post",
            url: "http://localhost:8080/sendJson",
            data: '{"username":"zs","password":"123456","id":20}',
            contentType: 'application/json' //告诉服务器 前端发的数据是json格式
        })
    })

    $("#btn2").click(function () {
        $.ajax({
            type: "get",
            url: "http://localhost:8080/receiveJson",
            // url: "http://localhost:8080/receiveJsonData",
            success(res) {
                console.log(res);
            }
        })
    })
</script>

</body>
</html>

Handler部分

java 复制代码
package com.codingfuture.springboot01.controller;

import com.codingfuture.springboot01.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 
 * @create
 */
@Controller
public class JsonController {
    @PostMapping("/sendJson")
    public String jsonTest1(@RequestBody User user) {
        //@RequestBody 前端来的json字符串在请求体中
        System.out.println(user);
        return "index";
    }
}

测试:查看控制台是否接受到前端发送过来的json数据。

@ResponseBody

JavaScript部分

javascript 复制代码
<script>
       $("#btn2").click(function () {
        $.ajax({
            type: "get",
            url: "http://localhost:8080/receiveJson",
            // url: "http://localhost:8080/receiveJsonData",
            success(res) {
                console.log(res);
            }
        })
</script>

Handler部分

java 复制代码
// login.html
    @GetMapping("/receiveJson")
    @ResponseBody //代表这得到的返回值要作为响应体 返回给前端,就不会走试图解析器
    public List<User> jsonTest2() {
        User user = new User();
        user.setUsername("zs");
        user.setPassword("123456");
        user.setId(20);
        List<User> list = new ArrayList<>();
        list.add(user);
        return list;
    }

    @GetMapping("/receiveJsonData")
    @ResponseBody
    public Map<String, Object> jsonTest3() {

        User user = new User();
        user.setUsername("zs");
        user.setPassword("123456");
        user.setId(20);
        List<User> list = new ArrayList<>();
        list.add(user);
        Map<String, Object> map = new HashMap<>();
        map.put("code", 200);
        map.put("message", "数据请求成功!");
        map.put("data", list);
        return map;
    }

测试:查看前端页面console页面,是否拿到后端响应过来的数据。

注意:@ResponseBody 注解一定要放在方法上面,标注,不要放到方法返回值处标注。

@ResponseBody 注解也可以放到类上,这样该类内部每个方法都会隐式被标注为@ResponseBody。

Fastjson

<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>fastjson</artifactId>

<version>2.0.10</version>

</dependency>

6. 拦截器

登录需求

创建拦截器

实现HandlerInterceptor接口

配置拦截器

Access to XMLHttpRequest at 'http://localhost:8080/login' from origin 'null' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

跨域配置

java 复制代码
package com.codingfuture.uploaddemo.controller.config;

import com.codingfuture.uploaddemo.controller.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author Petrel
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/*.html")
                .excludePathPatterns("/login");
    }
    

    /**
     * 允许跨域调用的过滤器
     */
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        //允许所有域名进行跨域调用
        config.addAllowedOriginPattern("*");
        //允许跨越发送cookie
        config.setAllowCredentials(true);
        //放行全部原始头信息
        config.addAllowedHeader("*");
        //允许所有请求方法跨域调用
        config.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }

}
相关推荐
考虑考虑5 分钟前
JDK9中的dropWhile
java·后端·java ee
想躺平的咸鱼干13 分钟前
Volatile解决指令重排和单例模式
java·开发语言·单例模式·线程·并发编程
hqxstudying40 分钟前
java依赖注入方法
java·spring·log4j·ioc·依赖
·云扬·1 小时前
【Java源码阅读系列37】深度解读Java BufferedReader 源码
java·开发语言
春生野草1 小时前
关于SpringMVC的整理
spring
Bug退退退1232 小时前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq
小皮侠2 小时前
nginx的使用
java·运维·服务器·前端·git·nginx·github
Zz_waiting.2 小时前
Javaweb - 10.4 ServletConfig 和 ServletContext
java·开发语言·前端·servlet·servletconfig·servletcontext·域对象
全栈凯哥2 小时前
02.SpringBoot常用Utils工具类详解
java·spring boot·后端
兮动人2 小时前
获取终端外网IP地址
java·网络·网络协议·tcp/ip·获取终端外网ip地址