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的参数列表,功能非常强大,主要功能有以下两点:
-  接收请求参数 
-  获取servletAPI 
-  @Controller 
 @RestController
 public class Login {
 
 }
2.1.1接收请求参数
-  简单类型的参数,Spring MVC可以直接帮我们封装成参数列表中声明的类型,比如String、int、double…… 
-  //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中存放的对象
-  @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形参上定义request和response,使用request或response指定响应结果
-  response.getWriter().println(); 
-  String逻辑视图名称 返回的字符串就是逻辑视图。 
-  return "index.jsp"; 
-  请求转发与重定向 
-  @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-  声明在方法上: 
-  //@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都支持 
-  // 静态文件目录,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该注解用来标注一个请求参数: 在方法的形参前,可以加可以不加,加了就有特殊含义了 
-  @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的基础。 
-  //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表单
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编写
@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
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
public class User {
    private Integer id;
    private String username;
    private String password;
    getter&& setter
}<!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部分
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部分
<script>
       $("#btn2").click(function () {
        $.ajax({
            type: "get",
            url: "http://localhost:8080/receiveJson",
            // url: "http://localhost:8080/receiveJsonData",
            success(res) {
                console.log(res);
            }
        })
</script>Handler部分
// 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.
跨域配置
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);
    }
}









