Skip to content

[!quote] Spring Spring 是一个开发生态圈,它提供了若干个子项目,用于完成特定功能 600

  • Spring:也就是 Spring Framework,包含了核心功能【IOCAOP事务 ……】
  • SpringBoot:基于 Spring,简化了开发
  • Spring MVC:是 Spring 中的一个模块,提供了一种基于 MVC【模型 - 视图 - 控制器】设计模式的框架
  • SSM:Spring , Spring MVC , MyBatis

Spring 依赖介绍

  • spring-boot-starter-web Web 开发相关依赖
    • spring-boot-starter Spring Boot 核心功能
      • spring-boot-autoconfigure 提供自动配置机制,依据 spring.factories 加载配置
  • spring-boot-configuration-processor 仅用于编译时处理配置元数据,作用是 IDE 提示和自动补全
xml
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>3.2.0</version>
	<relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>
	<dependency>  
	    <groupId>org.springframework.boot</groupId>  
	    <artifactId>spring-boot-starter-web</artifactId>  
	</dependency>
</dependencies>

❤️ 请求

  • RequestMapping 的子集:
    • @GetMapping 限定路径的请求方式只能是 HTTP GET
    • @PostMapping 限定路径的请求方式只能是 HTTP POST
    • @PutMapping 限定路径的请求方式只能是 HTTP PUT
    • @DeleteMapping 限定路径的请求方式只能是 HTTP DELETE
    • ……

路径参数

GET 请求:http://localhost:8080/users/1/posts/2

java
@RequestMapping("/users/{userId}/posts/{postId}")  
public String PathParam(@PathVariable Integer userId, @PathVariable Integer postId) {
	System.out.println("User ID: " + userId);
	System.out.println("Post ID: " + postId);
    return "OK";  
}

---
123

简单参数 / 表单参数

GET 请求:http://localhost:8080/simpleParam?name=Tom&age=10

java
@RestController
public class RequestController {  
	// 注释RequestMapping,定义请求路径
    @RequestMapping("/simpleParam")    
    public String simpleParam(String name, Integer age) {  
        System.out.println(name + ":" + age);  
        return "OK";  
    }  
}


Tom:10

@RestController = @Controller + @ResponseBody【方法的返回值将直接作为请求体返回】

复杂参数

GET 请求:http://localhost:8080/complexPojo?name=Tom&age=20&address.province=广东&address.city=广州

java
@RequestMapping("/complexPojo")  
public String complexPojo(User user) {  
    System.out.println(user);  
    return "OK";  
}


User{name='Tom', age=20, address=Address{province='广东', city='广州'}}
java
public class User {  
    private String name;  
    Integer age;  
    Address address;  
}

public class Address {  
    String province;  
    String city;  
}

数组参数

GET 请求:http://localhost:8080/arrayParam?hobby=dance&hobby=game&hobby=sing

java
@RequestMapping("/arrayParam")  
public String ArrayParam(String[] hobby) {   // 数组名与请求参数名相同
    System.out.println(Arrays.toString(hobby));  
    return "OK";  
}


[dance, game, sing]

集合参数

GET 请求:http://localhost:8080/listParam?hobby=dance&hobby=game&hobby=sing

java
@RequestMapping("/listParam")  
public String ListParam(@RequestParam List<String> hobby) {
    System.out.println(hobby);  
    return "OK";  
}


[dance, game, sing]

动态参数

前端传的参数数量可能是不固定的,这时我们需要使用 Map 集合来动态接收

java
@PatchMapping("/update")
public Response updateCostItem(@RequestParam Map<String, String> allParams) {
    Response response = new Response();
    Service.update(allParams);
    return response;
}

日期参数

GET 请求:http://localhost:8080/dateParam?updateTime=2023-10-09 15:50:20

java
@RequestMapping("/dateParam")  
public String DateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime) {  
    System.out.println(updateTime);  
    return "OK";  
}


2023-10-09T15:50:20

Json 参数

POST 请求:http://localhost:8080/jsonParam

json
{
    "name": "Tom",
    "age": 20,
    "address": {
        "province": "广东",
        "city": "广州"
    }
}
java
@RequestMapping("/jsonParam")  
public String JsonParam(@RequestBody User user) {  // 注解表示将json数据封装成对象
    System.out.println(user);  
    return "OK";  
}

User{name='Tom', age=20, address=Address{province='广东', city='广州'}}

文件参数

MultipartFile 是 SpringBoot 提供的保存文件的一种格式

[!summary] 方法

  • getName() 返回前端 form 表单的名称
  • getOriginalFilename() 获取源文件的昵称
  • getContentType() 返回文件的内容类型
  • isEmpty() 判断上传的文件是否有内容
  • getSize() 返回文件大小【单位为字节】
  • getBytes() 返回一个将文件内容转化成一个以 byte 为元素的数组
  • getInputStream() 返回 InputStream 读取文件的内容
  • transferTo(File var1) 复制 file 文件到指定位置
  • 前端
html
<form action="/upload" method="get" enctype="multipart/form-data">
	图像: <input type="file" name="image">
</form>
  • 后端
Properties
#在配置文件中限制上传文件的Size

#配置单个文件上传大小限制(默认值为1M)  
spring.servlet.multipart.max-file-size=10MB  
#配置单次请求上传文件总大小限制(默认值为10M)  
spring.servlet.multipart.max-request-size=100MB
java
@RestController  
public class UploadController {  
    @PostMapping("/upload")  
    public Result UploadFile(MultipartFile image) throws IOException {  
	    //获取到image对象的文件名
        String originalFilename = image.getOriginalFilename();  
        //拿到文件名的最后一个.的索引
        int indexOf = originalFilename.lastIndexOf(".");  
        //获取到该文件的后缀名
        String suffixName = originalFilename.substring(indexOf);  

		//获取uuid
        String uuid = UUID.randomUUID().toString();  
        //拼接uuid和后缀名
        String newFileName = uuid + suffixName;  

		//将文件保存到指定路径
        image.transferTo(new File("E:/抖音/" + newFileName));  
  
        return Result.buildResult(Result.Status.OK);  
    }  
}

文件通过 MultipartFile 传递到服务器后,会产生一个临时文件,如果这时不对文件做任何操作。只要请求响应完毕之后,**这个文件就会被自动删除,不会保存**

💛 注解

@RequestParam

  • required 设置该参数是否必传
    • true 参数必须要传,不传抛异常
    • false 参数可以不传,不传为 null
  • defaultValue 给参数设置默认值,只支持单个参数,不支持给集合设置
  • name 当传参名和接收参数名不一致时,进行映射
java
@GetMapping("/greet")
public String greet(@RequestParam(required = false) String name) {
    if (name == null) {
        return "Hello, Guest!";
    }
    return "Hello, " + name + "!";
}
java
@GetMapping("/greet")
public String greet(@RequestParam(defaultValue = "Guest") String name) {
    return "Hello, " + name + "!";
}
java
/greet?username=John

@GetMapping("/greet")
public String greet(@RequestParam(name = "username") String name) {
    return "Hello, " + name + "!";
}

使用技巧

  • 传入简单参数 / 表单参数时,一般不需要使用 @RequestParam ,但是前端请求时不传参数不会报明显错误,这样不好
    • 当你的传参名,和接收名不同时,需要使用该注解来协调
    • 当你需要设置该参数是否必传时,需要使用该注解
    • 当需要给参数设置默认值时
  • 传入集合参数 / 动态参数时,需要使用该注解

❤️ 响应

💛 通过方法返回值响应

这是 SpringBoot 提供的一种简洁的方式来处理响应,SpringBoot 会根据方法的返回值自动选择合适的消息转换器来处理响应

💙 应用级别响应

💙 非应用级别响应

非应用级别响应 直接会响应到 HTTP 状态码里,设置为 4xx 时,请求就会失败 :

  • ok(String) 响应信息
  • ok(对象) 可以放一个 Map 对象展示 key - value 关系
java
@GetMapping("/verify")
public ResponseEntity<String> verify(String token) {
	if (token.equals("success")) {
		return ResponseEntity.ok("success");
	} else {
		return ResponseEntity.status(HttpStatus.OK).body("forbidden");
	}
}

💛 HttpServletResponse 直接响应

可以通过 HttpServletResponse 完全控制 HTTP 响应,例如设置响应头、状态码以及返回自定义内容文件下载、流式数据输出

java
@GetMapping("/resp")
public void resp(HttpServletResponse response) throws IOException {
    response.setContentType("text/plain");
    response.setStatus(HttpServletResponse.SC_OK);
    response.getWriter().write("direct response");
}

❤️ 分层

Dao

Dao 层的作用是获取数据【文件数据,xml 数据,json 数据等】,在 MyBatis 中叫 Mapper

java
package com.example.web_2.Dao;  
  
public interface EmpDao {  //定义一个接口,固定格式
    public List<String> listEmp();  
}
java
package com.example.web_2.Dao;  

//加载数据【文件数据,xml数据,json数据等】
public class EmpDaoA implements EmpDao {    
    @Override  
    public List<String> listEmp() {  
        List<String> list1 = new ArrayList(List.of("吴彦祖", "陈冠希", "金城武"));  
        return list1;  
    }  
}

Service

Sevice 层的作用对数据进行处理,然后返回给 Controller 类

java
package com.example.web_2.Service;  

public interface EmpServie {  
    public List<String> listEmp();  
}
java
package com.example.web_2.Service;  
  
//对数据进行逻辑处理,返回给Controller类  
public class EmpServiceA implements EmpServie {  
    private EmpDao empDao = new EmpDaoA();  //需要从EmpDao获取数据,所以创建接口对象

    @Override  
    public List<String> listEmp() {  
        List<String> emplist = empDao.listEmp();  
        emplist.add("黎明");  //对数据进行处理【添加数据】
        return emplist;  
    }  
}

/*
使用private EmpDao empDao = new EmpDaoA();的方式是为了遵循编程中的"面向接口编程"原则,这样可以在需要时轻松替换具体的实现类,而无需修改EmpServiceA类的其他部分。这样,代码在处理empDao时只关注EmpDao接口定义的方法,而不依赖于具体的实现细节。

另外,通过使用依赖注入的设计模式,可以将EmpDao对象的创建和管理交给外部的代码(例如使用依赖注入容器或手动注入)。这样可以更好地解耦和组织代码,提高代码的可测试性和可维护性。
*/

Controller

Controller 是获取来自 Service 类发来的数据,响应数据给前端

java
package com.example.web_2.Controller;  

//获取Service发来的数据,并响应数据给前端  
@RestController  
public class EmpController {  
    private EmpServie empServie = new EmpServiceA();  //创建EmpService对象
  
    @RequestMapping("/emp")  
    public Result listEmp() {  
        List<String> list = empServie.listEmp();  //获取Service处理过后的数据
        return Result.buildResult(Result.Status.OK, list);  //返回数据
    }  
}


---
GET请求:`http://localhost:8080/emp`
json:
{
    "status": "200",
    "message": "正确",
    "data": [
        "吴彦祖",
        "陈冠希",
        "金城武",
        "黎明"
    ]
}

公共路径 :在类上指定 @RequestMapping

java
@RestController  
@RequestMapping("/user")             //指定公共路径
public class UserController {  
	……
}

❤️ 解耦

以上的分层方式,实现了高内聚,但是依然没有实现低耦合【Controller 中还是有依赖 Service,Service 还是有依赖 Dao】 ![[JavaWeb Draw#^group=g1pvEhriTd5poW0zM1k4o|500]] EmpController 需要 EmpService,那我们可以把 EmpService 放到 IOC 容器里,然后 EmpController 需要时就到容器中取

💛 控制反转 IOC

对象的创建控制权由程序自身转移到容器【本身由 EmpController 自身创建 EmpService 对象,变为由容器创建对象

对象在 IOC 容器中叫 Bean 对象,Bean 对象的名称默认是类名首字母小写

  • @Component 如果某个类不属于以下三类,但是也想交给 IOC 处理时
    • 如果是控制器类上就用 @Controller
    • 如果是逻辑处理 Service 实现类就用 @Service
    • 如果是访问 Dao 类上就用 @Repository【如果 Dao 层中需要使用 MyBatis,那要将 @Repository 注解改为 @Mapper

💛 依赖注入 DI

容器为应用程序提供运行时所依赖的资源【例如,容器为 EmpController 提供运行时所需要的 EmpService 对象

  • 添加 @Autowired 注释【通过类型注入单个注解中的单个依赖
  • 添加 @Primary 注释【通过类型注入多个注解中的单个依赖
java
@Service
public class EmpServiceA implements EmpServie {……}

@Primary  //表示优先这个Service类
@Service  
public class EmpServiceB implements EmpServie {……}
  • 添加 @Qualifier 注释【通过类型注入多个注解中的单个依赖
java
@Configuration
public class RetryConfig {
    @Bean
    @Qualifier("empServiceB")
    public EmpServie createEmpServie() {
        return EmpServie.builder()
                .maxAttempts(5)
                .build();
    }
}
  • @DependsOn 告诉 Spring 容器,当前这个 Bean 在创建前,必须先初始化指定名称的 Bean
java
// 表示在初始化 MyBean 之前,要先初始化 beanName1,beanName2
@DependsOn({"beanName1", "beanName2"})
@Component
public class MyBean {
    // ...
}

java
@Repository  //将这个类交给IOC容器处理,成为IOC容器中的Bean
public class EmpDaoA implements EmpDao {  
    //加载数据【文件数据,xml数据,json数据等】  
    @Override  
    public List<String> listEmp() {  
        List<String> list1 = new ArrayList(List.of("吴彦祖", "陈冠希", "金城武"));  
        return list1;  
    }  
}

@Service //将这个类交给IOC容器处理,成为IOC容器中的Bean  
public class EmpServiceA implements EmpServie {  
    @Autowired  //程序运行时,IOC容器会为这个变量提供Bean对象  
    private EmpDao empDao;  
  
    @Override  
    public List<String> listEmp() {  
        List<String> emplist = empDao.listEmp();  
        emplist.add("黎明");  
        return emplist;  
    }  
}

@RestController  
public class EmpController {  
    @Autowired  //程序运行时,IOC容器会为这个变量提供Bean对象
    private EmpServie empServie;  
  
    @RequestMapping("/emp")  
    public Result listEmp() {  
        List<String> list = empServie.listEmp();  
        return Result.buildResult(Result.Status.OK, list);  
    }  
}