[!quote] Spring Spring 是一个开发生态圈,它提供了若干个子项目,用于完成特定功能
- Spring:也就是 Spring Framework,包含了核心功能【
IOC,AOP,事务……】 - 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-starter Spring Boot 核心功能
- spring-boot-configuration-processor 仅用于编译时处理配置元数据,作用是 IDE 提示和自动补全
<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
@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
@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=广州
@RequestMapping("/complexPojo")
public String complexPojo(User user) {
System.out.println(user);
return "OK";
}
User{name='Tom', age=20, address=Address{province='广东', city='广州'}}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
@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
@RequestMapping("/listParam")
public String ListParam(@RequestParam List<String> hobby) {
System.out.println(hobby);
return "OK";
}
[dance, game, sing]动态参数
前端传的参数数量可能是不固定的,这时我们需要使用 Map 集合来动态接收
@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
@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:20Json 参数
POST 请求:http://localhost:8080/jsonParam
{
"name": "Tom",
"age": 20,
"address": {
"province": "广东",
"city": "广州"
}
}@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 文件到指定位置
- 前端
<form action="/upload" method="get" enctype="multipart/form-data">
图像: <input type="file" name="image">
</form>- 后端
#在配置文件中限制上传文件的Size
#配置单个文件上传大小限制(默认值为1M)
spring.servlet.multipart.max-file-size=10MB
#配置单次请求上传文件总大小限制(默认值为10M)
spring.servlet.multipart.max-request-size=100MB@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当传参名和接收参数名不一致时,进行映射
@GetMapping("/greet")
public String greet(@RequestParam(required = false) String name) {
if (name == null) {
return "Hello, Guest!";
}
return "Hello, " + name + "!";
}@GetMapping("/greet")
public String greet(@RequestParam(defaultValue = "Guest") String name) {
return "Hello, " + name + "!";
}/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 关系
@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 响应,例如设置响应头、状态码以及返回自定义内容【文件下载、流式数据输出】
@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
package com.example.web_2.Dao;
public interface EmpDao { //定义一个接口,固定格式
public List<String> listEmp();
}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 类
package com.example.web_2.Service;
public interface EmpServie {
public List<String> listEmp();
}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 类发来的数据,响应数据给前端
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
@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注释【通过类型注入多个注解中的单个依赖】
@Service
public class EmpServiceA implements EmpServie {……}
@Primary //表示优先这个Service类
@Service
public class EmpServiceB implements EmpServie {……}- 添加
@Qualifier注释【通过类型注入多个注解中的单个依赖】
@Configuration
public class RetryConfig {
@Bean
@Qualifier("empServiceB")
public EmpServie createEmpServie() {
return EmpServie.builder()
.maxAttempts(5)
.build();
}
}@DependsOn告诉 Spring 容器,当前这个 Bean 在创建前,必须先初始化指定名称的 Bean
// 表示在初始化 MyBean 之前,要先初始化 beanName1,beanName2
@DependsOn({"beanName1", "beanName2"})
@Component
public class MyBean {
// ...
}@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);
}
}