SpringMVC框架
第1章 SpringMVC入门
1.1 SpringMVC简介
Spring MVC 全称:Spring Web MVC是 Spring 框架的一部分,专注于实现 Web 应用程序的模型-视图-控制器(Model-View-Controller, MVC)设计模式。它为构建灵活且松耦合的 Web 应用提供了强大的功能,同时保持了与 Spring 框架其他模块的良好集成。
1.2 SpringMVC特点
- Spring 家族原生产品,与IOC容器等基础设施无缝对接
- 表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案
- 代码清新简洁,大幅度提升开发效率
- 内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可
- 性能卓著,尤其适合现代大型、超大型互联网项目要求
- 异常处理机制
- 使用
@ExceptionHandler
注解可以定义全局异常处理器,集中处理不同类型的异常,简化错误页面展示和消息反馈。 - 支持自定义错误页面和 HTTP 状态码返回。
- 使用
- 强大的 RESTful 支持
- 内置对 RESTful Web 服务的支持,使用
@RestController
和@RequestMapping
注解可以轻松创建 RESTful API。 - 支持多种数据格式(如 JSON、XML),并能够自动将对象序列化为响应体内容。
- 内置对 RESTful Web 服务的支持,使用
- AOP 集成
- 与 Spring AOP 集成良好,允许开发者添加横切关注点(如日志记录、事务管理)而不影响业务逻辑代码。
- 可以通过拦截器(Interceptor)在请求到达控制器之前或响应发送给客户端之后执行某些操作。
1.3 核心组件及调用流程
1.3.1 SpringMVC核心组件
-
DispatcherServlet
- 功能:作为前端控制器,
DispatcherServlet
是整个 Spring MVC 请求处理的核心。它接收所有的 HTTP 请求,并将它们分发给适当的处理器(Handler)。 - 作用:统一入口点,负责初始化 WebApplicationContext 和其他必要组件。
- 功能:作为前端控制器,
-
HandlerMapping (处理器映射)
- 功能:定义了一组规则来决定哪个处理器应该处理特定的请求。它可以基于 URL、HTTP 方法、参数等进行匹配。
- 作用:将请求映射到具体的处理器上,如 Controller 中的方法。
-
HandlerAdapter (处理器适配器)
- 功能:适配器接口,用于执行由
HandlerMapping
映射到的处理器。它负责调用处理器的方法并处理其结果。 - 作用:使不同类型的处理器可以被一致地调用,增加了灵活性。
- 功能:适配器接口,用于执行由
-
ViewResolver (视图解析器)
- 功能:根据逻辑视图名找到实际的视图资源(如 JSP 文件或 Thymeleaf 模板),然后由
DispatcherServlet
将模型数据传递给视图进行渲染。 - 作用:解耦视图名称和物理位置之间的关系,便于维护和扩展。
- 功能:根据逻辑视图名找到实际的视图资源(如 JSP 文件或 Thymeleaf 模板),然后由
-
Controller (控制器)
- 功能:接收来自
DispatcherServlet
的请求,并调用相应的业务逻辑。通常是一个带有@Controller
或@RestController
注解的类。 - 作用:处理用户输入,准备数据,并选择一个合适的视图来展示结果。
- 功能:接收来自
-
ModelAndView (模型和视图)
- 功能:包含模型数据和逻辑视图名,由控制器返回给
DispatcherServlet
用于渲染视图。在 RESTful API 中,通常只返回模型数据(即实体对象)。 - 作用:连接了业务逻辑层与表示层,保证数据能够正确传递给视图。
- 功能:包含模型数据和逻辑视图名,由控制器返回给
-
view
- 功能:
View
接口表示一个逻辑视图,它负责将模型(Model)中的数据转换为最终的用户界面,如 HTML 页面、JSON 响应等。 - 作用:
View
主要用于数据转换及数据渲染,根据传入的数据,使用模板引擎(如 JSP、Thymeleaf、FreeMarker 等)或直接生成响应内容,最终形成用户能够看到的页面。
- 功能:
-
interceptor(拦截器)
- 功能:
Interceptor
(拦截器)是一个非常有用的组件,它允许开发者在请求到达控制器之前或响应发送给客户端之后执行某些操作。 - 作用:拦截器可以用来实现诸如日志记录、权限检查、性能监控、国际化设置等功能,而无需修改业务逻辑代码。
- 功能:
-
ExceptionHandler (异常处理器)
- 功能:用于全局捕获和处理控制器抛出的异常。可以通过
@ExceptionHandler
注解定义。 - 作用:提供一种集中处理错误的方式,简化了代码结构,提高了可读性和维护性。
- 功能:用于全局捕获和处理控制器抛出的异常。可以通过
1.3.2 SpringMVC组件调用关系
1.4 SpringMVC之Helloworld(入门体验)
1.4.1 环境准备
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>
1.4.2 定义请求处理器Handler(Controller)
package com.atguigu.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;/*** @CreateTime: 2024/11/18*/
@Controller("helloController") //定义请求处理器
public class HelloController {/*** @Date: 2024/11/18 15:50* 为当前方法映射URL(/hello)*/@RequestMapping("/hello")//设置响应体@ResponseBody public String hello(){return "hello springMVC!!!";}}
1.4.3 启动及测试项目
-
启动SpringBoot项目
-
测试项目
-
springBoot内置Tomcat默认端口号:8080
-
springBoot项目默认上下文路径:/
-
http://localhost:8080/hello
-
1.5 @RequestMapping详解
@RequestMapping
是 Spring MVC 中用于映射 Web 请求到处理方法的注解。它可以应用于类或方法级别,用来指定 URL 模式、HTTP 方法类型等条件。
1.5.1 @RequestMapping位置
-
类级别:为当前类映射URL(不能单独使用)
@Controller @RequestMapping("/users") public class UserController {// ... }
-
方法级别:为当前方法映射URL(可以单独使用)
@RequestMapping(value = "/getUser", method = RequestMethod.GET) @ResponseBody public String getUser() {// 返回字符串return "success"; }
1.5.2 @RequestMapping路径匹配
-
精确匹配
@RequestMapping("/hello") @RequestMapping("/helloController/hello")
-
模糊匹配
?:任意单个字符
*:任意数量任意字符(单层)
**:代表任意层(目录)的任意数量的任意字符
@RequestMapping("/hello/?") @RequestMapping("/helloController/*") @RequestMapping("/helloController/**")
1.5.3 @RequestMapping常用属性
- value:指定请求的 URL 模式。可以是绝对路径(如
/users
)或者相对路径(相对于父级路径)。如果在类上使用,则该路径会作为子路径添加到所有方法级别的路径前。 - method:指定允许的 HTTP 方法(GET, POST, PUT, DELETE 等)。默认情况下,不设置此属性意味着该方法可以响应任何 HTTP 方法。注意:如违背请求方式,会报错405
- params:限定只有当特定的请求参数存在时,才匹配该请求。例如
params = "id"
表示 URL 中必须包含名为id
的参数。 - headers:限定只有当特定的请求头存在时,才匹配该请求。例如
headers = "Referer=http://example.com/"
。 - consumes:限定只有当请求的内容类型与给定值匹配时,才匹配该请求。例如
consumes="application/json"
。 - produces:限定只有当响应的内容类型与给定值匹配时,才匹配该请求。例如
produces="application/json"
表示该方法将生成 JSON 格式的响应。
1.5.4 @RequestMapping四大组合注解
Spring 还提供了一些更具体的注解,它们是
@RequestMapping
的简化版本,用于直接映射特定类型的 HTTP 请求:
@GetMapping
:组合了@RequestMapping(method = RequestMethod.GET)
。@PostMapping
:组合了@RequestMapping(method = RequestMethod.POST)
。@PutMapping
:组合了@RequestMapping(method = RequestMethod.PUT)
。@DeleteMapping
:组合了@RequestMapping(method = RequestMethod.DELETE)
。@PatchMapping
:组合了@RequestMapping(method = RequestMethod.PATCH)
。
第2章 SpringMVC处理请求数据
2.1 接收查询&请求体参数
2.1.1 默认情况:参数名与形参名一致
springmvc中形参列表中可直接接收请求参数
- 参数名与形参列表中参数名必须一致
- 如不一致:默认值null
-
Apipost工具发送请求
-
查询参数也叫query参数
-
请求体参数:必须使用POST方式
-
-
Controller接收数据
/** * @Date: 2024/11/19 9:24* springmvc处理请求参数(默认) */ @ResponseBody @GetMapping("/doRequestParameter") public String doRequestParameter(String stuName,Integer stuAge){System.out.println("stuName = " + stuName);System.out.println("stuAge = " + stuAge);return "success"; }
2.1.2 @RequestParam注解
参数名与形参名不一致时,可以使用@RequestParam注解获取请求参数
@RequestParam
是 Spring MVC 中用于将 HTTP 请求中的查询参数或表单字段绑定到控制器方法的参数上的注解。它使得从请求中提取参数并传递给处理方法变得非常简单和直观。下面是对@RequestParam
注解的常用属性
- value:
- 指定请求参数的名称,必须与 URL 或表单中的参数名相匹配。
- 如果省略此属性,则默认使用方法参数的名字作为参数名。
- required(默认为
true
):
- 表示该参数是否必需。如果设置为
true
并且请求中没有提供相应的参数,Spring 将抛出MissingServletRequestParameterException
异常。- 设置为
false
时,即使请求中缺少该参数,也不会报错,参数值将被设为null
或者是基本类型的默认值(如0
对于整数类型)。- defaultValue:
- 当请求中未包含指定参数时使用的默认值。这可以避免因缺少参数而导致的异常,并为参数提供一个备用值。
- 注意:当设置了
defaultValue
时,required
属性自动视为false
,因为有了默认值,参数就不再是必需的了。- name:
- 这是一个别名属性,等价于
value
,用于指定参数名称。
-
ApiPost发送请求
-
Controller接收数据
/** * @Date: 2024/11/19 9:24* springmvc处理请求参数(默认) */ @ResponseBody @GetMapping("/doRequestParameter") public String doRequestParameter(String stuName,@RequestParam(value = "stuAge2",required = false) Integer stuAge){System.out.println("stuName = " + stuName);System.out.println("stuAge = " + stuAge);return "success"; }
2.1.3 POJO入参
SpringMVC中支持POJO入参,将POJO对象设置为形参即可
使用POJO入参,要求参数名与POJO中的属性名必须一致
如不一致会注入null值
-
ApiPost发送请求
-
Controller接收数据
package com.atguigu.pojo;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;/*** @CreateTime: 2024/11/19*/ @Data @NoArgsConstructor @AllArgsConstructor public class Student {private Integer stuId;private String stuName;private Integer stuAge;private String[] hobbys;}/*** 测试pojo入参 */ @ResponseBody @PostMapping("/testPojoParam") public String testPojoParam(Student student){System.out.println("student = " + student);return "success"; }
2.2 接收路径参数(@PathVariable)
SpringMVC中支持在URL中使用占位符入参,一般在后续RESTFul风格CRUD中使用,语法如下:
- 占位符语法:{参数名}
- @PathVariable(“参数名”)
- ApiPost发送请求
- Controller接收数据
/**
* @Date: 2024/11/19 10:40* 测试URL参数
*/
@ResponseBody
@GetMapping("/testURLParam/{stuId}")
public String testURLParam(@PathVariable("stuId") Integer stuId){System.out.println("stuId = " + stuId);return "success";
}
2.3 接收JSON参数
SpringMVC中使用@RequestBody注解接收Json数据
- 使用String类型接收:获取的是Json类型的字符串
- 使用POJO类型接收:获取的是POJO对象
-
ApiPost发送请求
-
Controller接收数据
/*** @Date: 2024/11/19 10:16* 测试pojo入参 */ @ResponseBody @PostMapping("/testJSONParam") public String testJSONParam(@RequestBody Student student /*String jsonStr*/){// System.out.println("jsonStr = " + jsonStr); //json字符串System.out.println("student = " + student);return "success"; }
2.4 接收Cookie数据
SpringMVC中使用@CookieValue注解获取Cookie数据
-
ApiPost发送请求
-
Controller接收数据
/** * @Date: 2024/11/19 10:40* 测试Cookie信息 */ @ResponseBody @GetMapping("/testCookieInfo") public String testCookieInfo(@CookieValue("JSESSIONID") String jsessionId){System.out.println("jsessionId = " + jsessionId);return "success"; }
2.5 接收请求头数据
SpringMVC中使用@RequestHeader注解接收请求头信息
-
ApiPost发送请求
-
Controller接收数据
/** * @Date: 2024/11/19 10:40* 测试Cookie信息 */ @ResponseBody @GetMapping("/testRequestHeaderInfo") public String testRequestHeaderInfo(@RequestHeader("User-Agent") String userAgent){System.out.println("User-Agent = " + userAgent);return "success"; }
2.6 接收文件数据(文件上传)
SpringBoot环境中,自带文件上传处理器,也可以在application.properties配置文件中,设置文件上传大小和数量等问题
spring.application.name=spring09-mvc # 总文件大小限定 spring.servlet.multipart.max-request-size=200MB # 单文件大小限定 spring.servlet.multipart.max-file-size=20MB # 文件数量限定 spring.webflux.multipart.max-parts=10
-
HTML代码
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <!-- 文件上传表单要求 1 method必须是Post 2 enctype必须是multipart/form-data 3 springmvc默认限定,单个文件大小为1M以内,总文件大小是10M以内 --> <form method="post" enctype="multipart/form-data" action="file/upload">用户名 <input type="text" name="username"> <br>头像 <input type="file" name="headImg"> <br> <!--单文件输入框-->生活照 <input type="file" name="lifeImg" multiple="multiple"> <br> <!--多文件输入框--><input type="submit"> </form></body> </html>
-
或ApiPost工具测试
-
Controller处理文件上传
package com.atguigu.spring09mvc.controller;import org.springframework.util.ResourceUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile;import java.io.File; import java.util.UUID;@RequestMapping("/file") @RestController public class FileController {@RequestMapping("/upload")public String upload(@RequestParam("username") String username ,@RequestParam("headImg") MultipartFile headImg,@RequestParam("lifeImg") MultipartFile[] lifeImg)throws Exception{// 获取文件信息/*System.out.println(headImg.getName()); // 文件参数名System.out.println(headImg.getOriginalFilename()); // 文件原始名System.out.println(headImg.getContentType()); // 文件媒体类型System.out.println(headImg.getSize()); // 文件大小System.out.println(headImg.getBytes().length); // 文件字节数组System.out.println(headImg.isEmpty()); // 文件是否为空*/// 文件名处理String originalFilename = headImg.getOriginalFilename();String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));String newFileName = UUID.randomUUID().toString() + suffix;// 保存到指定目录File classPath = ResourceUtils.getFile("classpath:static/");// 获取静态资源目录File dir = new File(classPath, "upload");if (!dir.exists()) {dir.mkdirs();}headImg.transferTo(new File(dir, newFileName));return "success";}}
2.7 原生ServletAPI入参
SpringMVC中支持原生ServletAPI,直接作为形参入参即可
/**
* @Date: 2024/11/19 10:40* 测试Cookie信息
*/
@ResponseBody
@PostMapping("/testServletAPIInfo")
public String testServletAPIInfo(HttpServletRequest request, HttpServletResponse response){HttpSession session = request.getSession();String stuId = request.getParameter("stuId");System.out.println("stuId = " + stuId);String stuName = request.getParameter("stuName");System.out.println("stuName = " + stuName);return "success";
}
2.8 接收HttpEntity 获取所有请求信息(了解)
SpringMVC中可以使用HttpEntity获取请求信息,包括请求头,请求体等
package com.atguigu.spring09mvc.controller;import com.atguigu.spring09mvc.pojo.Product;
import org.springframework.http.HttpEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/httpEntity")
@RestController
public class HttpEntityController {@RequestMapping("/test1")public String test1(HttpEntity<Product> entity) {System.out.println(entity.getHeaders());System.out.println(entity.getBody());return "test1";}
}
第3章 SpringMVC处理响应数据
3.1 响应数据
SpringMVC中使用@ResponseBody注解响应数据,包括响应普通文本或Json数据,如响应过程中未添加@ResponseBody注解,SpringMVC会寻找视图解析器.@ResponseBody具体源码如下:
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ResponseBody {}
- @ResponseBody可以书写的位置
- 方法上:表示当前方法响应数据
- 类上:表示当前类中所有方法响应数据
- 注意:当前类中所有方法都响应数据时,使用@RestController
- @RestController = @Controller + @ResponseBody
3.1.1 普通文本
/*** @Date: 2024/11/19 11:15* 处理json数据*/
@ResponseBody
@RequestMapping("/testJsonData")
public String doJsonDataString(){return "success";
}
3.1.2 Json数据
/*** @Date: 2024/11/19 11:15* 处理json数据*/
@ResponseBody
@RequestMapping("/testJsonData")
public Student /*String*/ doJsonDataString(){Student student = new Student();student.setStuId(1005);student.setStuName("wangwu");student.setStuAge(18);student.setHobbys(new String[]{"打游戏","看电影"});return student;// 响应json数据(json字符串)//String jsonStr = "{\"name\":\"zhangsan\",\"age\":18}";//return jsonStr;
}
3.2 响应静态资源
3.2.1 转发与重定向(html)
-
转发
/*** @Date: 2024/11/19 11:24* 响应静态资源*/@RequestMapping("/testStaticResource")public String doStaticResource(){System.out.println(" 转发======= " );return "/html/index.html"; //默认跳转路径方式:转发//return "forward:/html/index.html" }
-
重定向
/*** @Date: 2024/11/19 11:24* 响应静态资源*/@RequestMapping("/testStaticResource")public String doStaticResource(){System.out.println(" 重定向======= " );return "redirect:/html/index.html"; //重定向}
3.2.2 其他资源(了解)
/*** @Date: 2024/11/19 11:24* 响应静态资源*/@RequestMapping("/testStaticResource")public String doStaticResource(){System.out.println(" 其他静态资源 ======= " );return "/imgs/a.jpg";}
3.3 响应文件(文件下载)
-
html代码
<a href="fileController/filedownload?fileName=3.jpg">3.jpg下载</a>
-
Controller下载
/*** @Date: 2024/11/20 16:16* 实现文件下载,返回ResponseEntity*/@RequestMapping("/filedownload")public ResponseEntity<byte[]> filedownload(String fileName) throws Exception {File staticDir = ResourceUtils.getFile("classpath:static/path/"+fileName);InputStream inputStream = new FileInputStream(staticDir);byte[] bytes = new byte[inputStream.available()];inputStream.read(bytes);//响应头[设置响应头类,通知浏览器该文件需要下载,不要打开]// ①创建MultiValueMap接口类型的对象,实现类是HttpHeadersMultiValueMap responseHeaderMap = new HttpHeaders();// ②存入下载文件所需要的响应消息头responseHeaderMap.add("Content-Disposition", "attachment; filename="+fileName);ResponseEntity responseEntity = new ResponseEntity(bytes,responseHeaderMap, HttpStatus.OK);return responseEntity;}
第4章 RESTFul风格设计及练习
4.1 RESTFul风格概述
4.1.1 RESTFul简介
RESTful(Representational State Transfer)是一种软件架构风格,用于设计网络应用程序和服务之间的通信。它是一种基于标准 HTTP 方法的简单和轻量级的通信协议,广泛应用于现代的Web服务开发。
通过遵循 RESTful 架构的设计原则,可以构建出易于理解、可扩展、松耦合和可重用的 Web 服务。RESTful API 的特点是简单、清晰,并且易于使用和理解,它们使用标准的 HTTP 方法和状态码进行通信,不需要额外的协议和中间件。
RESTful 架构通常用于构建 Web API,提供数据的传输和操作。它可以用于各种应用场景,包括客户端-服务器应用、单页应用(SPA)、移动应用程序和微服务架构等。
总而言之,RESTful 是一种基于 HTTP 和标准化的设计原则的软件架构风格,用于设计和实现可靠、可扩展和易于集成的 Web 服务和应用程序!
4.1.2 RESTFul特点
- 每一个URI代表1种资源(URI全称:Uniform Resource Identifier,即统一资源标识符);
- 客户端使用GET、POST、PUT、DELETE 4个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源;
- 资源的表现形式是XML或者JSON;
- 客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息。
4.2 RESTFul设计规范
4.2.1 HTTP协议请求方式规范
操作 | 请求方式 |
---|---|
查询操作 | GET |
保存操作 | POST |
删除操作 | DELETE |
更新操作 | PUT |
4.2.2 RESTFul与传统风格对比
传统风格URL | 传统方式请求方式 | RESTFul风格URL | RESTFul请求方式 | |
---|---|---|---|---|
增加 | /saveEmp | POST | /emp | POST |
删除 | /deleteEmp?id=1001 | GET | /emp/101 | DELETE |
修改 | /updateEmp | POST | /emp | PUT |
查询 | /getEmpById?id=1001 | GET | /emp/101 | GET |
4.3 RESTFul实战案例
4.3.1 准备环境
-
新建工程
-
数据库环境
CREATE TABLE t_emp(eid INT PRIMARY KEY AUTO_INCREMENT,ename VARCHAR(20) NOT NULL,salary DOUBLE(10,2) NOT NULL,address VARCHAR(20) NOT NULL,did INT NOT NULL );INSERT INTO t_emp(ename,salary,address,did) VALUES('zhangsan',15000,'北京昌平',1); INSERT INTO t_emp(ename,salary,address,did) VALUES('lisi',15000,'北京朝阳',1); INSERT INTO t_emp(ename,salary,address,did) VALUES('王五',16000,'北京昌平',2); INSERT INTO t_emp(ename,salary,address,did) VALUES('zhaoliu',20000,'北京昌平',1); INSERT INTO t_emp(ename,salary,address,did) VALUES('qianqi',25000,'北京昌平',2);
4.3.2 参考案例代码
-
pom.xml
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jdbc</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency> </dependencies>
-
application.properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/0923_demo spring.datasource.username=root spring.datasource.password=root
-
pojo实体层
package com.atguigu.pojo;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;/*** @Author zhangchunsheng* @CreateTime: 2024/11/19*/ @Data @NoArgsConstructor @AllArgsConstructor public class Employee {private Integer eid;private String ename;private Double salary;private String address;private Integer did;private Dept dept;}
-
dao持久化层
package com.atguigu.dao;import com.atguigu.pojo.Employee;import java.util.List;/*** @CreateTime: 2024/11/19*/ public interface EmployeeDao {/*** @Date: 2024/11/19 15:13* 添加员工信息*/void insertEmployee(Employee employee);/*** @Date: 2024/11/19 15:20* 删除员工信息通过id*/void deleteEmployeeById(Integer id);/*** @Date: 2024/11/19 15:20* 修改员工信息*/void updateEmployee(Employee employee);/*** @Date: 2024/11/19 15:21* 通过id获取员工信息*/Employee getEmployeeById(Integer id);/*** @Date: 2024/11/19 15:21* 获取所有员工信息*/List<Employee> getAllEmps();/*** @Date: 2024/11/19 17:10* 获取所有员工及员工对应的部门信息*/List<Employee> getAllEmpAndDept();}
package com.atguigu.dao.impl;import com.atguigu.dao.EmployeeDao; import com.atguigu.pojo.Employee; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository;import java.util.List;/*** @CreateTime: 2024/11/19*/ @Repository("employeeDao") public class EmployeeDaoImpl implements EmployeeDao {@Autowired@Qualifier("jdbcTemplate")private JdbcTemplate jdbcTemplate;@Overridepublic void insertEmployee(Employee employee) {String sql = "INSERT INTO t_emp(ename,salary,address,did) VALUES(?,?,?,?)";jdbcTemplate.update(sql, employee.getEname(), employee.getSalary(), employee.getAddress(), employee.getDid());}@Overridepublic void deleteEmployeeById(Integer id) {String sql = "delete from t_emp where eid = ?";jdbcTemplate.update(sql,id);}@Overridepublic void updateEmployee(Employee employee) {String sql = "update t_emp set ename=?,salary=?,address=?,did=? where eid=?";jdbcTemplate.update(sql, employee.getEname(), employee.getSalary(), employee.getAddress(), employee.getDid(), employee.getEid());}@Overridepublic Employee getEmployeeById(Integer id) {String sql = "select eid,ename,salary,address,did from t_emp where eid = ?";return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Employee.class), id);}@Overridepublic List<Employee> getAllEmps() {String sql = "select eid,ename,salary,address,did from t_emp";return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Employee.class));}@Overridepublic List<Employee> getAllEmpAndDept() {String sql = "SELECT e.eid,e.ename,e.salary,e.address,d.did,d.dname" +" FROM t_emp e LEFT JOIN t_dept d ON e.did=d.did";return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Employee.class));}}
-
service业务逻辑层
package com.atguigu.service;import com.atguigu.pojo.Employee;import java.util.List;/*** @CreateTime: 2024/11/19*/ public interface EmployeeService {/*** @Date: 2024/11/19 15:13* 添加员工信息*/void saveEmployee(Employee employee);/*** @Date: 2024/11/19 15:20* 删除员工信息通过id*/void deleteEmployeeById(Integer id);/*** @Date: 2024/11/19 15:20* 修改员工信息*/void updateEmployee(Employee employee);/*** @Date: 2024/11/19 15:21* 通过id获取员工信息*/Employee findEmployeeById(Integer id);/*** @Date: 2024/11/19 15:21* 获取所有员工信息*/List<Employee> findAllEmps();/*** @Date: 2024/11/19 17:10* 获取所有员工及员工对应的部门信息*/List<Employee> getAllEmpAndDept();}
package com.atguigu.service.impl;import com.atguigu.dao.EmployeeDao; import com.atguigu.pojo.Employee; import com.atguigu.service.EmployeeService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service;import java.util.List;/*** @CreateTime: 2024/11/19*/ @Service("employeeService") public class EmployeeServiceImpl implements EmployeeService {@Autowired@Qualifier("employeeDao")private EmployeeDao employeeDao;@Overridepublic void saveEmployee(Employee employee) {employeeDao.insertEmployee(employee);}@Overridepublic void deleteEmployeeById(Integer id) {employeeDao.deleteEmployeeById(id);}@Overridepublic void updateEmployee(Employee employee) {employeeDao.updateEmployee(employee);}@Overridepublic Employee findEmployeeById(Integer id) {return employeeDao.getEmployeeById(id);}@Overridepublic List<Employee> findAllEmps() {return employeeDao.getAllEmps();}@Overridepublic List<Employee> getAllEmpAndDept() {return employeeDao.getAllEmpAndDept();}}
-
controller控制层
package com.atguigu.controller;import com.atguigu.pojo.Employee; import com.atguigu.service.EmployeeService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.web.bind.annotation.*;import java.util.List;/*** @CreateTime: 2024/11/19*/ @RestController public class EmployeeController {@Autowired@Qualifier("employeeService")private EmployeeService employeeService;/*** @Date: 2024/11/19 15:34* 添加员工信息*/@PostMapping("/employees")//@ResponseBody //可省略,因为使用@RestControllerpublic String addEmployee(@RequestBody Employee employee) {employeeService.saveEmployee(employee);return "{'status':'ok'}";}/*** @Date: 2024/11/19 15:35* 根据id删除员工信息*/@DeleteMapping("/employee/{eid}")public String deleteEmployeeById(@PathVariable("eid") Integer id) {employeeService.deleteEmployeeById(id);return "{'status':'ok'}";}/*** @Date: 2024/11/19 15:47* 修改员工信息*/@PutMapping("/employee/all")public String updateEmployee(@RequestBody Employee employee) {employeeService.updateEmployee(employee);return "{'status':'ok'}";}/*** @Date: 2024/11/19 15:48* 根据id查询员工信息*/@GetMapping("/employee/{eid}")//@ResponseBody //可省略,因为使用@RestControllerpublic Employee getEmpById(@PathVariable("eid") Integer eid){return employeeService.findEmployeeById(eid);}/*** @Date: 2024/11/19 15:52* 查询所有员工信息*/@GetMapping("/employee")//@ResponseBody //可省略,因为使用@RestControllerpublic List<Employee> getAllEmp(){return employeeService.findAllEmps();}@GetMapping("/empAndDept")public List<Employee> getAllEmpAndDept(){return employeeService.getAllEmpAndDept();}}
-
ApiPost测试
-
增
-
删
-
改
-
查
-
通过eid查询员工信息
-
查询所有员工信息
-
-
4.4 跨域问题
4.4.1 跨域问题概述
-
什么是跨域
跨域指的是客户端脚本(如 JavaScript)尝试访问与当前页面不在同一个域下的资源。这里的“域”指的是协议、主机名和端口号的组合。例如:
http://example.com
和https://example.com
是不同域(因为协议不同)。http://example.com:8080
和http://example.com
是不同域(因为端口不同)。http://example.com
和http://sub.example.com
是不同域(因为主机名不同)。
-
跨域的原因
跨域问题的存在主要是为了防止 CSRF(跨站请求伪造)攻击和其他潜在的安全风险。浏览器实施了同源策略(Same-Origin Policy),这是一种安全机制,它阻止了一个源加载的文档或脚本对另一个源加载的资源进行某些类型的访问。
4.4.2 跨域问题解决方案
跨域问题有很多解决方案,具体如下,本课程中以
CORS
解决方案为主
- CORS (Cross-Origin Resource Sharing)
CORS 是一种基于 HTTP 头的机制,它允许服务器声明哪些源可以访问它的资源。通过设置适当的响应头,服务器可以指示浏览器是否允许跨域请求。
- JSONP (JSON with Padding)
JSONP 是一种早期用于绕过跨域限制的技术,适用于只读操作(GET 请求)。它的工作原理是在客户端动态创建
<script>
标签,并通过 URL 参数传递回调函数名称。服务器返回的数据会包裹在这个回调函数中,从而执行客户端定义的代码。然而,由于 JSONP 只支持 GET 请求且存在安全风险,现代应用更倾向于使用 CORS 或其他解决方案。
- 代理服务器
通过在同一个域下设置一个代理服务器来转发请求到目标服务器,这样客户端就可以直接与代理服务器通信而不会违反同源策略。这种方法适用于无法修改目标服务器配置的情况。
- WebSocket
对于实时通信的需求,WebSocket 提供了一种全双工通信协议,它不受同源策略的限制。因此,即使在不同域之间也可以建立 WebSocket 连接。
- Nginx 配置
如果你使用 Nginx 作为反向代理,可以在 Nginx 配置文件中添加 CORS 相关的头信息来处理跨域请求。
-
配置类方式(了解)
import org.springframework.context.annotation.Bean; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration public class WebConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("https://example.com") // 允许的来源.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的方法.allowedHeaders("*") // 允许的头部.allowCredentials(true); // 是否允许发送凭证} }
-
注解方式:@CrossOrigin
@CrossOrigin
是 Spring 框架提供的一个注解,专门用于简化跨域资源共享(CORS, Cross-Origin Resource Sharing)的配置。它可以在类级别或方法级别使用,以允许来自不同源的请求访问受保护的资源。以下是@CrossOrigin
注解的常用属性:- origins:
- 指定允许访问的来源域名,默认值是
"*"
, 表示允许所有来源。 - 可以是一个字符串数组,例如
{"https://example.com", "https://anotherdomain.com"}
。
- 指定允许访问的来源域名,默认值是
- methods:
- 指定允许的 HTTP 方法,默认是所有方法 (
RequestMethod.GET
,RequestMethod.POST
, 等)。 - 可以指定具体的 HTTP 方法,如
RequestMethod.GET
或RequestMethod.POST
。
- 指定允许的 HTTP 方法,默认是所有方法 (
- allowedHeaders:
- 指定允许的请求头字段,默认是所有头字段。
- 例如,
{"X-Requested-With", "Content-Type"}
。
- exposedHeaders:
- 指定哪些响应头可以被客户端访问,默认为空列表。
- 这对于某些需要暴露给前端的自定义响应头非常有用。
- allowCredentials:
- 是否允许发送凭证(如 Cookies),默认是
false
。 - 如果设置为
true
,则origins
不能为"*"
,必须指定具体来源。
- 是否允许发送凭证(如 Cookies),默认是
- maxAge:
- 设置预检请求(preflight request)的结果缓存时间(秒),默认是
1800
秒(30 分钟)。 - 预检请求是指浏览器在发送实际请求之前发出的 OPTIONS 请求,用以确认服务器是否允许跨域请求。
- 设置预检请求(preflight request)的结果缓存时间(秒),默认是
-
类级别应用
@CrossOrigin(origins = "https://example.com") @RestController @RequestMapping("/api") public class MyController {// 所有该类中的方法都将允许来自 https://example.com 的请求 }
-
方法级别应用
@RestController @RequestMapping("/api") public class MyController {@CrossOrigin(origins = "https://example.com", methods = {RequestMethod.GET, RequestMethod.POST})@GetMapping("/data")public ResponseEntity<String> getData() {return new ResponseEntity<>("Some data", HttpStatus.OK);}// 其他方法可能有不同的 CORS 设置或没有 CORS 设置 }
- origins:
第5章 SpringMVC异常处理器
5.1 异常处理基本概念
在 Spring MVC 中,异常处理是确保应用程序健壮性和用户体验的关键部分。Spring 提供了多种机制来捕获和处理异常,从而可以优雅地响应错误情况,并向用户展示友好的消息或采取适当的行动。
- 为什么需要处理异常?
- **提高用户体验,友好错误提示:**如果异常没有被正确捕获和处理,它们可能会沿着调用栈向上抛出,最终导致程序崩溃或行为异常。良好的异常处理机制能够及时拦截并妥善处理这些意外情况,确保系统继续稳定运行。
- **增强系统的可靠性,防止未处理异常传播:**某些操作(如文件读写、网络连接)可能涉及外部资源的使用,在发生异常的情况下,必须确保这些资源得到正确的释放,避免内存泄漏或其他资源浪费的问题。
- **简化调试与维护,减少重复代码:**通过集中化异常处理逻辑(例如使用全局异常处理器),可以减少在业务逻辑中散布大量 try-catch 块的情况,使得代码更加简洁易读,同时也降低了维护成本。
- 如何处理异常?
- 编程式异常处理:将异常处理代码与核心业务代码书写在同一代码块中,耦合度高(不推荐)
- 声明式异常处理:将异常处理代码,先横向提取到切面类中,再动态织入到核心业务代码(推荐使用)
5.2 声明式异常处理
声明式异常处理是 Spring MVC 中一种强大且灵活的技术,它允许开发者通过注解和配置轻松地定义异常处理规则,而无需在业务逻辑中嵌入复杂的 try-catch 结构。SpringMVC提供很多注解实现异常处理器,其中常用注解如下:
@ControllerAdvice
注解用于定义一个类作为全局异常处理器,它可以应用于整个应用程序中的所有控制器。
@ExceptionHandler
注解,可以在该类中定义方法来处理特定类型的异常。
5.2.1 全局异常处理
这是最常用的一种声明式异常处理方法,它允许你定义一个或多个类来集中处理所有控制器抛出的异常。
package com.atguigu.myexceptionhandler;import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;/*** @CreateTime: 2024/11/20*/
//@ResponseBody
//@ControllerAdvice //定义异常处理器
//@RestControllerAdvice // = @ControllerAdvice+@ResponseBody
@ControllerAdvice
public class MyExceptionHandler {/*** @Date: 2024/11/20 9:26* 处理空指针异常*/@ResponseBody@ExceptionHandler(NullPointerException.class)public String doNullPointerException(Exception ex) {System.out.println("ex = " + ex);return "{'status:'501'}"; //响应文本提示:错误信息}/*** @Date: 2024/11/20 9:26* 处理算术异常*/@ExceptionHandler(ArithmeticException.class)public String doArithmeticException(Exception ex) {System.out.println("ex = " + ex);return "/error/error_502.html"; //响应页面提示:错误信息}}
5.2.2 局部异常处理(了解)
除了全局异常处理器外,你还可以在每个控制器内部使用
@ExceptionHandler
来处理特定于该控制器的异常。这种方式提供了更细粒度的控制,但不如全局处理器灵活。
@Controller
public class MyController {@ExceptionHandler(ResourceNotFoundException.class)public String handleResourceNotFoundException(ResourceNotFoundException ex, Model model) {model.addAttribute("errorMessage", ex.getMessage());return "error/resource-not-found"; // 返回视图名称}// 控制器其他方法...
}
第6章 SpringMVC拦截器
6.1 拦截器(Interceptor)简介
在 Spring MVC 中,
Interceptor
(拦截器)是一种用于在请求处理的不同阶段插入自定义逻辑的机制。拦截器可以用来实现诸如日志记录、权限检查、性能监控、国际化设置等功能,而无需修改业务逻辑代码。下面是对Interceptor
的详细解析:拦截器允许开发者在以下三个关键点上执行额外的逻辑:
- 预处理请求(Pre-Handling)
- 在请求被传递给控制器之前执行。
- 可以用于验证用户身份、解析请求参数、设置线程局部变量等。
- 后处理响应(Post-Handling)
- 在控制器方法执行完毕但视图渲染之前执行。
- 可以用于添加额外的数据到模型中,或者修改返回的
ModelAndView
对象。- 完成处理(After Completion)
- 在整个请求处理完成后执行,无论是否发生异常。
- 通常用于资源清理、记录日志等。
6.2 拦截器基本实现
6.2.1 定义拦截器
package com.atguigu.interceptor;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;/*** @CreateTime: 2024/11/20*/
@Component("myInterceptor1")
public class MyInterceptor1 implements HandlerInterceptor {/*** @Date: 2024/11/20 10:21* 在执行Controller之前执行*/public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("==>1.Myinterceprot111->preHandle()!!!");return true; //true:放行 false:不放行}/*** @Date: 2024/11/20 10:21* 在执行Controller之后执行*/public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("==>3.Myinterceprot111->postHandle()!!!");}/*** @Date: 2024/11/20 10:21* 在最后执行*/public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("==>4.Myinterceprot111->afterCompletion()!!!");}}
6.2.2 注册拦截器
package com.atguigu.config;import com.atguigu.interceptor.MyInterceptor1;
import com.atguigu.interceptor.MyInterceptor2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** @CreateTime: 2024/11/20*/
@Configuration
public class SpringMVCConfig implements WebMvcConfigurer {@Autowiredprivate MyInterceptor1 myInterceptor1;/*** @Date: 2024/11/20 10:26* 注册拦截器*/public void addInterceptors(InterceptorRegistry registry) {//注册拦截器(myInterceptor1)registry.addInterceptor(myInterceptor1).addPathPatterns("/interceptorController/testInterceptor");}}
6.3 拦截器工作原理
6.3.1 单个拦截器工作原理
-
客户端向服务器发送请求
-
执行拦截器第一个方法preHandle(),放行请求(return true)
-
执行Controller中相应方法
-
执行拦截器第二个方法postHandle()
-
执行拦截器第三个方法afterCompletion()
6.3.2 多个拦截器工作原理
假设我们有三个拦截器 A、B 和 C,并且它们按此顺序被添加到拦截器链中。那么,在处理请求的过程中,各个拦截器的方法将按照以下顺序执行:
- Pre-Handle 阶段
- A.preHandle()
- B.preHandle()
- C.preHandle()
在这个阶段,拦截器的
preHandle()
方法会按照它们被注册的顺序依次调用。每个拦截器都有机会决定是否继续处理请求。如果任何一个拦截器的preHandle()
返回false
,则整个请求处理过程将会终止,后续的拦截器和控制器方法都不会被执行。
- Controller 处理
如果所有拦截器的
preHandle()
方法都返回true
,请求将继续传递给控制器进行处理。控制器执行完毕后,会生成一个ModelAndView
对象(或类似的响应结果)。
- Post-Handle 阶段
- C.postHandle()
- B.postHandle()
- A.postHandle()
一旦控制器完成了业务逻辑处理,但视图还未渲染之前,拦截器链中的
postHandle()
方法将以逆序执行。也就是说,最后一个注册的拦截器 C 的postHandle()
方法最先被调用,然后是 B 和 A。这个阶段可以用来修改ModelAndView
或者添加额外的数据。
- After-Completion 阶段
- C.afterCompletion()
- B.afterCompletion()
- A.afterCompletion()
最后,在整个请求处理完成后(包括视图渲染),无论是否发生异常,拦截器链中的
afterCompletion()
方法也会以逆序执行。这一步骤通常用于清理资源、记录日志等操作。总结:假设这三个拦截器按 A -> B -> C 的顺序注册,当一个请求到达时,控制台输出如下:
A - Pre Handle B - Pre Handle C - Pre Handle // Controller 执行... C - Post Handle B - Post Handle A - Post Handle // 视图渲染完成... C - After Completion B - After Completion A - After Completion
注意事项
- 短路行为:如果某个拦截器的
preHandle()
方法返回false
,则该请求将不会继续传递给下一个拦截器或控制器,而是直接进入afterCompletion()
阶段。- 异常处理:如果在
preHandle()
或控制器执行期间抛出了异常,postHandle()
不会被调用,但是afterCompletion()
仍然会被调用,并且异常信息会作为参数传递给它。- 线程安全性:确保拦截器中的共享资源是线程安全的,尤其是在多线程环境下使用时。
6.3.3 源码解析拦截器工作原理
-
SpringMVC断点入口
-
preHandle()正序执行
-
postHandle()倒序执行
-
afterCompletion()倒序执行
6.4 拦截器与过滤器异同
特性 | 拦截器(Interceptor) | 过滤器(Filter) |
---|---|---|
作用范围 | 仅限于 Spring MVC 控制器请求,不处理静态资源或其他非 Spring MVC 请求。 | 可应用于整个 Web 应用程序中的所有请求/响应,包括静态资源。 |
生命周期 | 生命周期依赖于 Spring 容器,在应用启动时被创建并注册到 Spring MVC 配置中。 | 在整个应用程序的生命周期中始终存在,随应用启动而初始化。 |
配置方式 | 通过 Java 配置类(实现 WebMvcConfigurer 接口)或 XML 配置文件来定义。 | 通过 web.xml 文件或者使用注解(如 @WebFilter 和 ServletContainerInitializer )来定义。 |
执行顺序 | 在请求已经被 DispatcherServlet 接收后但在传递给具体处理器之前执行;preHandle() 按注册顺序,postHandle() 和 afterCompletion() 逆序。 | 在请求到达 Spring MVC 的 DispatcherServlet 之前执行;过滤器链按配置顺序依次执行。 |
功能特性 | 提供了更细粒度的控制,可以在请求处理的不同阶段插入自定义逻辑,如认证授权、性能监控等。 | 支持字符编码设置、日志记录、权限检查等功能,可以直接操作 HttpServletRequest 和 HttpServletResponse 对象。 |
性能考虑 | 由于只针对 Spring MVC 请求,可能具有更好的性能表现,因为它减少了不必要的处理步骤。 | 因为会拦截所有请求,处理大量请求时可能会引入额外开销,特别是对静态资源的处理。 |
适用场景 | 适合处理由 Spring MVC 控制器管理的请求,并利用框架提供的丰富特性。 | 适合需要对所有 HTTP 请求(包括静态资源)进行统一处理的情况。 |
第7章 SpringMVC数据校验
7.1 SpringMVC数据校验概念
Spring MVC 提供了强大的数据校验功能,以确保从客户端接收到的数据符合预期的格式和规则。通过使用 Bean Validation API(如 Hibernate Validator),你可以轻松地为输入数据添加校验逻辑,并在控制器层中处理验证结果。
当然,数据校验不只是在Controller层可以处理,在前端JS中也可以验证数据是否合法,本章节主要学习Controller中的处理方式。
常用校验规则如下:
校验注解 作用 @AssertFalse 验证Boolean类型字段是否为false @AssertTrue 验证Boolean类型字段是否为true @DecimalMax 验证字符串表示的数字是否小于等于指定的最大值 @DecimalMin 验证字符串表示的数字是否大于等于指定的最小值 @Digits(integer, fraction) 验证数值是否符合指定的格式,integer指定整数精度,fraction指定小数精度 验证字符串是否为邮箱地址格式 @Future 验证日期是否在当前时间之后 @Past 验证日期是否在当前时间之前 @Min(value) 验证数字是否大于等于指定的最小值 @Max(value) 验证数字是否小于等于指定的最大值 @Null 验证对象是否为null @NotNull 验证对象是否不为null, 与@Null相反**(a!=null)** @NotEmpty 验证字符串是否非空**(a!=null && a!=“”)** @NotBlank 验证字符串是否非空白字符**(a!=null && a.trim().length > 0)** @Size(max=, min=) 验证字符串、集合、Map、数组的大小是否在指定范围内 @Pattern(regexp=, flag=) 验证字符串是否符合指定的正则表达式
7.2 SpringMVC数据校验基本实现
7.2.1 导入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>
7.2.2 定义校验规则
在 DTO(Data Transfer Object)或实体类上使用注解来定义校验规则。这些注解可以应用于字段、getter 方法或类级别。
/*** @Author zhangchunsheng* @CreateTime: 2024/11/20*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentDTO {@NotNull(message = "id不能为空!!!")private Integer stuId;@Length(min = 3, max = 6, message = "长度在3-6之间!!!")private String stuName;@Min(value = 18, message = "年龄最小18岁!!!")@Max(value = 120,message = "年龄最大120岁!!!")private Integer stuAge;@Email(message = "邮箱格式不正确!!!")private String stuEmail;}
7.2.3 控制器中验证
package com.atguigu.controller;import com.atguigu.pojo.Student;
import com.atguigu.pojo.vo.StudentVO;
import jakarta.validation.Valid;
import org.springframework.beans.BeanUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @Author zhangchunsheng* @CreateTime: 2024/11/20*/
@RestController
public class TestValidateController {@GetMapping("/testValidate")public String doValidate(@RequestBody @Valid StudentDTO studentDTO,BindingResult br) {if(br.hasErrors()){Map<String,Object> errorMap = new HashMap<>();List<FieldError> fieldErrors = br.getFieldErrors();for (FieldError fieldError : fieldErrors) {errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());}return errorMap.toString();}Student student = new Student();BeanUtils.copyProperties(studentDTO, student);System.out.println("student = " + student);return "doValidate";}}
7.3 SpringMVC自定义数据校验器(了解)
7.3.1 定义校验注解
package com.atguigu.annotation;import jakarta.validation.Constraint;
import jakarta.validation.Payload;import java.lang.annotation.*;@Documented
@Constraint(validatedBy = {GenderValidate.class}
)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Gender {String message() default "性别只能是男或女!";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};}
7.3.2 定义校验注解规则
package com.atguigu.annotation;import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;/*** @Author zhangchunsheng* @CreateTime: 2024/11/20*/
public class GenderValidate implements ConstraintValidator<Gender,String> {@Overridepublic void initialize(Gender constraintAnnotation) {ConstraintValidator.super.initialize(constraintAnnotation);}@Overridepublic boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {return value.equals("男") || value.equals("女");}
}
/*** @Author zhangchunsheng* @CreateTime: 2024/11/20*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentDTO {@NotNull(message = "id不能为空!!!")private Integer stuId;@Length(min = 3, max = 6, message = "长度在3-6之间!!!")private String stuName;@Min(value = 18, message = "年龄最小18岁!!!")@Max(value = 120,message = "年龄最大120岁!!!")private Integer stuAge;@Email(message = "邮箱格式不正确!!!")private String stuEmail;//自定义验证器@Gender(message = "请求输入正确的性别!")private String stuGender;}
7.4 VO与DTO
7.4.1 各种O的概念
在 Spring MVC 中,
VO
(Value Object)、DTO
(Data Transfer Object)等对象类型用于不同场景下的数据处理和传输。理解这些概念及其用途对于设计良好的分层架构非常重要。以下是关于各种O
介绍:
- Value Object (VO):强调不可变性和基于内容的相等性,适用于表示具体值或属性组合。
- Data Transfer Object (DTO):用于简化不同层次间的数据传输,避免直接暴露实体类。
- Entity:表示持久化的业务实体,通常与数据库表相对应。
- Form Object:专为表单提交设计,便于收集和验证用户输入。
- Command Object:封装了执行某项操作所需的所有信息,常用于命令模式。
- Transfer Object (TO):专注于远程调用或跨进程通信中的高效数据传输。
7.4.2 DTO介绍
DTO全称:Data Transfer Object (DTO)
定义
DTO 主要用于在不同的应用程序层之间传递数据,特别是从服务层到表现层(如控制器)。它通常是一个简单的 POJO(Plain Old Java Object),包含一组 getter 和 setter 方法。
使用场景
- 简化复杂业务逻辑与视图之间的数据交换。
- 避免直接暴露实体类给前端,从而保护内部数据结构并减少耦合。
- 支持多样的展示需求,例如将多个实体的信息整合到一个 DTO 中进行返回。
案例代码
/*** @Author zhangchunsheng* @CreateTime: 2024/11/20*/ @Data @NoArgsConstructor @AllArgsConstructor public class StudentDTO {@NotNull(message = "id不能为空!!!")private Integer stuId;@Length(min = 3, max = 6, message = "长度在3-6之间!!!")private String stuName;@Min(value = 18, message = "年龄最小18岁!!!")@Max(value = 120,message = "年龄最大120岁!!!")private Integer stuAge;@Email(message = "邮箱格式不正确!!!")private String stuEmail;//自定义验证器@Gender(message = "请求输入正确的性别!")private String stuGender;}
7.4.3 VO介绍(了解)
VO全称:Value Object
定义
- 不可变性:Value Object 是一个表示值的对象,它的状态是不可改变的。一旦创建,其属性就不能被修改。
- 相等性基于内容:两个 Value Object 如果它们的内容相同,则认为它们相等,而不是根据对象的引用地址。
使用场景
- 当你需要确保某个对象的状态不会发生变化时,比如货币、日期时间等。
- 在领域驱动设计(DDD)中,Value Object 用来封装具有特定含义的值或属性组合。
案例代码
public final class Money implements ValueObject {private final BigDecimal amount;private final Currency currency;public Money(BigDecimal amount, Currency currency) {this.amount = amount;this.currency = currency;}// Getters but no setters... }
第8章 Swagger接口文档
8.1 Swagger介绍
Swagger 是一个用于设计、构建、记录和使用 RESTful Web 服务的开源框架。它不仅简化了 API 的开发过程,还提供了强大的工具来生成交互式的 API 文档,使得开发者和消费者能够更好地理解和使用这些 API。
Swagger 可以快速生成实时接口文档,方便前后开发人员进行协调沟通。遵循 OpenAPI 规范。
Knife4j 是基于 Swagger之上的增强套件什么是 Swagger?
Swagger 实际上是 OpenAPI 规范的一部分,OpenAPI 规范定义了一套规则来描述 RESTful API。而 Swagger 则提供了一系列工具和服务来支持这一规范,包括但不限于:
- Swagger Editor:一个基于浏览器的编辑器,允许你以 YAML 或 JSON 格式编写 OpenAPI 规范文件,并实时预览文档。
- Swagger UI:一个动态生成的 HTML 页面,根据 OpenAPI 规范文件展示 API 的详细信息,并允许用户直接在浏览器中测试 API 调用。
- Swagger Codegen:可以从 OpenAPI 规范自动生成客户端 SDK 和服务器存根代码。
8.2 Swagger基本应用
8.2.1 常用API
Knife4j 使用,参考:https://doc.xiaominfo.com/docs/quick-start
swagger标准常用注解;
访问 http://ip:port/doc.html 即可查看接口文档
注解 | 标注位置 | 作用 |
---|---|---|
@Tag | controller 类 | 描述 controller 作用 |
@Parameter | 参数 | 标识参数作用 |
@Parameters | 参数 | 参数多重说明 |
@Schema | model 层的 JavaBean | 描述模型作用及每个属性 |
@Operation | 方法 | 描述方法作用 |
@ApiResponse | 方法 | 描述响应状态码等 |
8.2.2 实现步骤
-
导入依赖
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>4.4.0</version> </dependency>
-
编写配置文件:application.properties或application.yml
# springdoc-openapi项目配置 springdoc:swagger-ui:path: /swagger-ui.htmltags-sorter: alphaoperations-sorter: alphaapi-docs:path: /v3/api-docsgroup-configs:- group: 'default'paths-to-match: '/**'packages-to-scan: com.atguigu.controller # knife4j的增强配置,不需要增强可以不配 knife4j:enable: truesetting:language: zh_cn
-
使用注解
package com.atguigu.pojo;import com.atguigu.springmvc05restful.validation.annotations.Gender; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.*; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.web.bind.annotation.PathVariable;import java.io.Serializable; import java.math.BigDecimal;@AllArgsConstructor @NoArgsConstructor @Data // 单一职责原则 public class Employee implements Serializable {@Schema(description = "修改员工信息时,该属性必须有值.增加员工信息时则不需要赋值")private Integer id;@Schema(description = "员工姓名")private String name;private Integer age;private String email;private String address;private BigDecimal salary;private String gender;}
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag;@RestController @RequestMapping("/api/v1") @Tag(name = "员工管理", description = "员工信息管理的控制器") public class UserController {@Operation(summary = "查询全部员工信息的处理器")@GetMapping("/employees")public String getEmployees() {List<Employee> employees =employeeService.findAllEmployee();return employees.toString();}@Operation(summary = "根据id修改员工信息的业务接口实现")@PutMapping("/employee")public String updateEmployee(@RequestBodyEmployee employee){employeeService.updateEmnployee(employee);return "success";}@Operation(summary = "添加员工信息的处理器")@Parameters({@Parameter(name = "eid", description = "员工ID",in = ParameterIn.PATH),@Parameter(name = "salary", description = "员工薪资",in = ParameterIn.PATH),@Parameter(name = "gender", description = "员工性别",in = ParameterIn.PATH),@Parameter(name = "address", description = "员工住址")})@PostMapping("/emp")public String insertEmployee(@RequestBody Employee employee) {employeeService.insertEmployee(employee);return "{'status':'ok'}";} }
-
访问Swagger UI
- 最后,访问Knife4j的文档地址:
http://ip:port/doc.html
即可查看文档
- 最后,访问Knife4j的文档地址:
第9章 SpringMVC工作原理
9.1 SpringMVC九大组件
9.1.1 DispatcherServlet
- 作用:
- 前端控制器,是整个 Spring MVC 的控制中心。用户请求到达前端控制器后,由前端控制器分发请求至后端控制器。
- DispatchServlet中核心调度方法:doDispatch()
- 配置:通常在 web.xml 中配置 DispatcherServlet。
9.1.2 HandlerMapping
- 作用:
- 处理器映射器,负责根据用户请求找到 Handler(处理器),即 Controller。
- 通过HandlerMapping获取HandlerAdapter对象
- 类型:包括 BeanNameUrlHandlerMapping、DefaultAnnotationHandlerMapping、RequestMappingHandlerMapping 等。
9.1.3 HandlerAdapter
- 作用:
- 处理器适配器,按照特定规则(HandlerAdapter 要求的规则)去执行 Handler。
- 通过HandlerAdapter调用Controller中相应方法
- 类型:包括 SimpleControllerHandlerAdapter、HttpRequestHandlerAdapter、AnnotationMethodHandlerAdapter 等。
9.1.4 Handler(Controller)
- 作用:处理器,是后端控制器,在 MVC 模型中负责处理具体的业务逻辑。
- 注解:通常使用
@Controller
注解标记一个类为 Controller。
9.1.5 ModelAndView
- 作用:封装了 Model 和 View 的信息。Controller 处理完用户请求后返回一个 ModelAndView 对象,其中包含了模型数据和视图信息。
- 结构:包含一个 Map 对象(用于存放模型数据)和一个 View 或视图名称(用于指定视图)。
9.1.6 ViewResolver
- 作用:视图解析器,根据逻辑视图名解析成真正的视图 View(如 JSP、Thymeleaf 等)。
- 类型:包括 InternalResourceViewResolver、FreeMarkerViewResolver、ThymeleafViewResolver 等。
9.1.7 View
- 作用:视图,负责将结果显示给用户。视图可以是 JSP、HTML、PDF 等任何形式的页面。
- 实现:通常由 ViewResolver 解析得到。
9.1.8 ExceptionHandler
- 作用:异常处理器,用于处理 Controller 中抛出的异常。
- 注解:使用
@ExceptionHandler
注解标记一个方法为异常处理器。
9.1.9 Interceptor
- 作用:拦截器,类似于 Servlet 中的 Filter,用于在请求到达 Controller 之前或之后执行一些预处理或后处理操作。
- 配置:在 Spring MVC 配置文件中配置拦截器链。
9.2 SpringMVC工作原理
9.2.1 图解SpringMVC工作原理
9.2.2 SpringMVC工作原理源码简述
客户端发送请求:用户通过浏览器或其他客户端工具向服务器发起 HTTP 请求。
DispatcherServlet 接收请求:所有进入应用程序的请求首先由 DispatcherServlet 接收。
查找 HandlerMapping:根据请求的 URL,DispatcherServlet 使用 HandlerMapping 来确定哪个控制器应该处理该请求。
选择 HandlerAdapter:找到合适的 HandlerAdapter 来执行选定的控制器。
执行控制器方法:HandlerAdapter 调用控制器中的相应方法,并传递必要的参数(如路径变量、请求参数等)。
处理业务逻辑:控制器方法内部可能涉及到调用服务层、访问数据库等操作,完成相应的业务逻辑。
如控制器中未使用@ResponseBody
- 返回 ModelAndView:控制器方法完成后,通常会返回一个包含模型数据和视图名称的 ModelAndView 对象。
- 解析视图:DispatcherServlet 使用 ViewResolver 将视图名称转换为实际的视图对象。
- 渲染视图:使用解析后的视图对象,结合模型数据生成最终的响应内容(如 HTML 页面)。
- 发送响应:将生成的内容发送回客户端展示给用户。
如控制器中使用@ResponseBody:返回数据即可
配置拦截器
- 控制器中存在异常
- 执行异常处理器
- 拦截器中preHandle()及afterCompletion()会执行,postHandle()不会执行
- 控制器中不存在异常
- 不执行异常处理器
ndlerMapping、RequestMappingHandlerMapping 等。
9.1.3 HandlerAdapter
- 作用:
- 处理器适配器,按照特定规则(HandlerAdapter 要求的规则)去执行 Handler。
- 通过HandlerAdapter调用Controller中相应方法
- 类型:包括 SimpleControllerHandlerAdapter、HttpRequestHandlerAdapter、AnnotationMethodHandlerAdapter 等。
9.1.4 Handler(Controller)
- 作用:处理器,是后端控制器,在 MVC 模型中负责处理具体的业务逻辑。
- 注解:通常使用
@Controller
注解标记一个类为 Controller。
9.1.5 ModelAndView
- 作用:封装了 Model 和 View 的信息。Controller 处理完用户请求后返回一个 ModelAndView 对象,其中包含了模型数据和视图信息。
- 结构:包含一个 Map 对象(用于存放模型数据)和一个 View 或视图名称(用于指定视图)。
9.1.6 ViewResolver
- 作用:视图解析器,根据逻辑视图名解析成真正的视图 View(如 JSP、Thymeleaf 等)。
- 类型:包括 InternalResourceViewResolver、FreeMarkerViewResolver、ThymeleafViewResolver 等。
9.1.7 View
- 作用:视图,负责将结果显示给用户。视图可以是 JSP、HTML、PDF 等任何形式的页面。
- 实现:通常由 ViewResolver 解析得到。
9.1.8 ExceptionHandler
- 作用:异常处理器,用于处理 Controller 中抛出的异常。
- 注解:使用
@ExceptionHandler
注解标记一个方法为异常处理器。
9.1.9 Interceptor
- 作用:拦截器,类似于 Servlet 中的 Filter,用于在请求到达 Controller 之前或之后执行一些预处理或后处理操作。
- 配置:在 Spring MVC 配置文件中配置拦截器链。
9.2 SpringMVC工作原理
9.2.1 图解SpringMVC工作原理
[外链图片转存中…(img-uwf0ADPc-1737341419220)]
9.2.2 SpringMVC工作原理源码简述
客户端发送请求:用户通过浏览器或其他客户端工具向服务器发起 HTTP 请求。
DispatcherServlet 接收请求:所有进入应用程序的请求首先由 DispatcherServlet 接收。
查找 HandlerMapping:根据请求的 URL,DispatcherServlet 使用 HandlerMapping 来确定哪个控制器应该处理该请求。
选择 HandlerAdapter:找到合适的 HandlerAdapter 来执行选定的控制器。
执行控制器方法:HandlerAdapter 调用控制器中的相应方法,并传递必要的参数(如路径变量、请求参数等)。
处理业务逻辑:控制器方法内部可能涉及到调用服务层、访问数据库等操作,完成相应的业务逻辑。
如控制器中未使用@ResponseBody
- 返回 ModelAndView:控制器方法完成后,通常会返回一个包含模型数据和视图名称的 ModelAndView 对象。
- 解析视图:DispatcherServlet 使用 ViewResolver 将视图名称转换为实际的视图对象。
- 渲染视图:使用解析后的视图对象,结合模型数据生成最终的响应内容(如 HTML 页面)。
- 发送响应:将生成的内容发送回客户端展示给用户。
如控制器中使用@ResponseBody:返回数据即可
配置拦截器
- 控制器中存在异常
- 执行异常处理器
- 拦截器中preHandle()及afterCompletion()会执行,postHandle()不会执行
- 控制器中不存在异常
- 不执行异常处理器
- 拦截器中所有方法正常执行
相关文章:
SpringMVC框架
第1章 SpringMVC入门 1.1 SpringMVC简介 Spring MVC 全称:Spring Web MVC是 Spring 框架的一部分,专注于实现 Web 应用程序的模型-视图-控制器(Model-View-Controller, MVC)设计模式。它为构建灵活且松耦合的 Web 应用提供了强大的功能&…...
将 OneLake 数据索引到 Elasticsearch - 第 1 部分
作者:来自 Elastic Gustavo Llermaly 学习配置 OneLake,使用 Python 消费数据并在 Elasticsearch 中索引文档,然后运行语义搜索。 OneLake 是一款工具,可让你连接到不同的 Microsoft 数据源,例如 Power BI、Data Activ…...
flutter跨端UI框架简介
flutter跨端UI框架简介 简介 Flutter是由Google开发的开源应用开发框架,主要用于构建高性能、跨平台的移动、Web和桌面应用程序。Flutter使用Dart语言,提供了一套丰富的Widgets,使开发者能够快速创建美观的用户界面。其最大特点是热重载功能…...
Centos 修改历史读录( HISTSIZE)
history命令 -c #清空命令历史 -r #读历史文件附加到历史列表 -w #保存历史列表到指定的历史文件 命令历史相关环境变量 HISTSIZE #命令历史记录的条数 HISTFILE #指定历史文件,默认为~/.bash_history HISTFILESIZE #命令历史文件记录历史的条数 以上变量可以 exp…...
利用ML.NET精准提取人名
在当今信息爆炸的时代,文本处理任务层出不穷,其中人名提取作为基础且重要的工作,广泛应用于信息检索、社交网络分析、客户关系管理等领域。随着人工智能不断进步,ML.NET作为微软推出的开源机器学习框架,为开发者提供了…...
关于deepin上运行Qt开发的程序
国产化替代是将来各单位的主流趋势,探索自行开发应用程序在国产操作系统上正常运行是将来的主要工作之一。本文浅尝gui程序在统信社区版——deepin上遇到的小问题。 使用Qt在deepin上做了一个类似gif的帧动画弹窗,在编译运行时,程序可以正常…...
3.3 Go函数可变参数
可变参数(variadic parameters)是一种允许函数接受任意数量参数的机制。它在函数定义中使用 ...type 来声明参数类型,所有传递的参数会被收集为一个切片,函数内部可以像操作普通切片一样处理这些参数。 package mainimport "…...
Queries Acceleration -Tuning- Tuning Execution 学习笔记
1 Adjustment of RuntimeFilter Wait Time 1.1 Case: Too Short RuntimeFilter Wait Time 1.1.1 没有看懂,好像是等待时间过小也会导致性能下降 1.1.2 set runtime_filter_wait_time_ms = 3000; 2 Data Skew Handling 2.1 Case 1: Bucket Data Skew Leading to Suboptimal …...
VS C++ 配置OPENCV环境
VS C 配置OPENCV环境 1.下载opencv2.安装环境3.opencv环境4.VS配置opencv环境5.EXE执行文件路径的环境lib和dll需要根据是debug还是release环境来区分使用哪个 6.Windows环境 1.下载opencv 链接: link 2.安装环境 双击运行即可 3.opencv环境 include文件路径:opencv\build\…...
Midscene.js:重新定义UI自动化的新时代工具
前言 Midscene.js 是一个创新的、面向开发者的 UI 自动化解决方案,并通过人工智能技术简化自动化脚本的编写与维护。 它提供了三种核心方法——交互(.ai, .aiAction)、提取(.aiQuery)和断言(.aiAssert&am…...
记录让cursor帮我给ruoyi-vue后台管理项目整合mybatis-plus
自己整合过程中会出现 work.web.exception.GlobalExceptionHandler :100 | 请求地址/admin/device/install/detail/1,发生未知异常. org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.fire.mapper.DeviceInstallMapper.selectById at o…...
uniapp中h5的微应用解决办法
考虑过用wujie,参考官网Vue组件封装 | 无界的教程,虽然没报错,但是子应用的vue节点根本没挂载上,不知道什么原因,如下图所示 后面采用iframe方式将子应用导入进来: 父应用: <template>&…...
再尝Semantic Kernel,planning特性很香
背景 书接上回《浅尝semantic kernel》; 上次只试了试SK框架的插件特性,这次试了下编排的特性,也就是把多个本地任务按顺序按逻辑执行,没想到效果也是出奇的好! 关键步骤 架构层面的代码本篇不在赘述,这…...
华为OD机试真题---战场索敌
华为OD机试真题“战场索敌”是一道考察算法和数据结构应用能力的题目。以下是对该题目的详细解析: 一、题目描述 有一个大小是NM的战场地图,被墙壁’#‘分隔成大小不同的区域。上下左右四个方向相邻的空地’.‘属于同一个区域,只有空地上可…...
Case逢无意难休——深度解析JAVA中case穿透问题
Case逢无意难休——深度解析JAVA中case穿透问题~ 不作溢美之词,不作浮夸文章,此文与功名进取毫不相关也!与大家共勉!! 更多文章:个人主页 系列文章:JAVA专栏 欢迎各位大佬来访哦~互三必回&#…...
HTML入门知识
目录 一、什么是HTML 二、HTML文档的语法规则 三、HTML基本标签 四、HTML注释与特殊字符 1、注释 2、特殊字符 五、HTML属性 六、常见误区与建议 一、什么是HTML HTML,全称Hyper Text Markup Language,即超文本标记语言,是一种用于创…...
idea maven本地有jar包,但还要从远程下载
idea 中,java 工程执行 maven reimport,报jar报无法下载。 我奇了个怪,我明明在本地仓库有啊,你非得从远程下载? 我从供应商那里拿来的,远程当然没有了。 这太奇葩了吧,折腾好久不行。 后来…...
利用大语言模型(LLM)增强软件测试自动化的最佳实践
在当今迅速变化的科技时代,软件测试行业面临着越来越高的效率和质量要求。作为测试专家,我们都知道,传统的测试方法往往无法满足快速迭代和高效交付的需求。在这种背景下,自动化测试逐渐成为解决方案的一部分。而大语言模型&#…...
联想电脑怎么设置u盘启动_联想电脑设置u盘启动方法(支持新旧机型)
有很多网友问联想电脑怎么设置u盘启动,联想电脑设置u盘启动的方法有两种,一是通过bios进行设置。二是通过快捷方式启动进入u盘启动。但需要注意有两种引导模式是,一种是uefi引导,一种是传统的leacy引导,所以需要注意制…...
Browser-Use WebUI项目启动指南
摘要 此前发布《Browser - Use WebUI 使用体验》博文后,鉴于部分朋友运行时出现问题,重新运行并整理相关内容。本文详细记录 Web UI 项目启动全过程,涵盖 Python 3.11、Chrome 浏览器及 API Keys 等环境要求,Python 环境检查、依赖…...
期权帮|如何利用股指期货进行对冲套利?
锦鲤三三每日分享期权知识,帮助期权新手及时有效地掌握即市趋势与新资讯! 如何利用股指期货进行对冲套利? 对冲就是通过股指期货来平衡投资组合的风险。它分为正向与反向两种策略: (1)正向对冲ÿ…...
基于springboot的酒店客房管理系统----数据库课程设计
目录 1.需求分析 1.1用例模型 1.1.1用例图 1.1.2用例描述 1.2过程模型 1.2.1数据流 1.2.2数据字典 1.3 安全性和完整性需求 1.4?数据字典 1.4.1数据项 1.4.2数据结构 1.4.3数据流 1.4.4数据存储 1.4.5数据过程 2概念结构设计 2.1 ER模型 2.1.1 实体及属性 2…...
SPDK vhost介绍
目录 1. vhost技术的背景与动机Virtio 介绍virtio-blk数据路径为例 2. vhost技术的核心原理2.1 vhost-kernel2.2 vhost-user举例 2.3 SPDK vhostvhost的优势IO请求处理数据传输控制链路调整 3. SPDK vhost的实现与配置3.1 环境准备3.2 启动SPDK vhost服务3.3 创建虚拟块设备3.4…...
理解C++运行时类型识别符: typeid
1. 格式 typeid (type-id) typeid (expression) typeid 运算符允许在运行时确定对象的类型。 typeid 的结果是一个 const type_info&。该值是对 type_info 对象的引用,该对象表示 type-id 或表达式的类型,具体取决于使用哪种形式的 typeid。有关…...
Fullcalendar @fullcalendar/react 样式错乱丢失问题和导致页面卡顿崩溃问题
问题描述: 我使用 fullcalendar的react版本时,出现了一个诡异的问题,当我切换到 一个iframe页面时(整个页面是一个iframe嵌入的),再切换回来日历的样式丢失了!不仅丢失了样式还导致页面崩溃了&…...
C++从入门到实战(二)C++命名空间
C从入门到实战(二)C命名空间 前言一、C的第一个程序二、命名空间(一)为什么需要命名空间(二)定义命名空间(三)使用命名空间1.通过命名空间限定符:2.使用 using 声明&…...
Spring 定时任务:@Scheduled 注解四大参数解析
本文主要介绍了在 Spring 框架中使用Scheduled注解实现定时任务的方法,重点讲解了fixedRate、fixedDelay、cron和initialDelay这四个参数的用法,并通过实例代码进行了详细说明。 1. fixedRate 参数 参数含义 fixedRate指定任务固定时间间隔执行。如设…...
计算机网络之物理层
本文章目录结构出自于《王道计算机考研 计算机网络_哔哩哔哩_bilibili》 01 物理层 在网上看到其他人做了详细的笔记,就不再多余写了,直接参考着学习吧。 1 详解物理层-通信基础【王道计算机网络笔记】_wx63088f6683f8f的技术博客_51CTO博客 2 详解物…...
Oracle 数据库常见字段类型大全及详细解析
在工作期间会遇到数据库建表的业务,经常会使用复制粘帖等操作,而不清楚数据库的字段类型。本文记录了 Oracle 数据库常见字段类型,根据不同的数据需求,可以选择不同的字段类型来存储数据。 文章目录 一、字符类型(Char…...
总线、UART、IIC、SPI
一图流 总线 概念 连接多个部件的信息传输线,是各部件共享的传输介质 类型 片内总线:连接处理器内核和外设的总线,在芯片内部 片外总线:连接芯片和其他芯片或者模块的总线 总线的通信 总线通信的方式 串行通信 数据按位顺序传…...
纯css实现div宽度可调整
<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>纯css实现div尺寸可调整</title><style…...
Arduino大师练成手册 -- 读取DHT11
要在 Arduino 上控制 DHT11 温湿度传感器,你可以按照以下步骤进行: 硬件连接: 将 DHT11 的 VCC 引脚连接到 Arduino 的 5V 引脚。 将 DHT11 的 GND 引脚连接到 Arduino 的 GND 引脚。 将 DHT11 的 DATA 引脚连接到 Arduino 的数字引脚&am…...
GS论文阅读--GeoTexDensifier
前言 本文是一个关于高斯致密化策略对高斯地图进行优化,他主要关注了几何结构和纹理信息。我最近对于高斯点的分布比较感兴趣,因为高斯点的分布决定了之后重建质量的好坏,初始化高斯很重要,但之后的维护需要致密化与修建策略&…...
Android实战经验篇-玩转Selinux(详解版)
列文章转如下链接: Android Display Graphics系列文章-汇总 Android实战经验篇-系列文章汇总 本文主要包括部分: 一、Selinux概述 1.1 SELinux是什么? 1.2 自主访问控制(DAC) 1.3 强制访问控制(MAC&…...
【langgraph】ubuntu安装:langgraph:未找到命令
langgraph 在ubuntu24.04 参考:langgraph运行:报错: (05_ep_dev) root@k8s-master-pfsrv:/home/zhangbin/perfwork/01_ai/05_ep_dev/expert# langgraph dev langgraph:未找到命令查看langraph的安装情况 pip show langgraph...
深入探究分布式日志系统 Graylog:架构、部署与优化
文章目录 一、Graylog简介二、Graylog原理架构三、日志系统对比四、Graylog部署传统部署MongoDB部署OS或者ES部署Garylog部署容器化部署 五、配置详情六、优化网络和 REST APIMongoDB 七、升级八、监控九、常见问题及处理 一、Graylog简介 Graylog是一个简单易用、功能较全面的…...
HTML新春烟花
系列文章 序号目录1HTML满屏跳动的爱心(可写字)2HTML五彩缤纷的爱心3HTML满屏漂浮爱心4HTML情人节快乐5HTML蓝色爱心射线6HTML跳动的爱心(简易版)7HTML粒子爱心8HTML蓝色动态爱心9HTML跳动的爱心(双心版)1…...
CentOS 7 安装fail2ban hostdeny方式封禁ip —— 筑梦之路
centos 7 换源参考CentOS 7.9 停止维护(2024-6-30)后可用在线yum源 —— 筑梦之路_centos停止维护-CSDN博客 安装fail2ban yum install fail2ban 新增配置文件 cat > /etc/fail2ban/action.d/hostsdeny.conf << EOF [Definition] actionstart actionstop action…...
java入门基础笔记语法篇(3)
一、 注释 什么是注释 注释定义:注释是写在程序中对代码进行解释说明的文字,方便自己和他人查看以理解程序。 Java注释的三种写法: 单行注释:以“//”开头,注释内容只能写一行。多行注释:以“/*”开头&…...
VUE对接deepseekAPI调用
1.先去开放平台注册账号申请api key。开放平台:https://platform.deepseek.com/api_keys 2.你的项目需要有发送请求的axios或者自己写。 npm install axios # 或 yarn add axios 3.创建 API 调用函数 在 Vue 项目中,通常会将 API 调用的逻辑封装到一个…...
【C++高并发服务器WebServer】-1:Linux中父子进程fork创建及关系、GDB多进程调试
本文目录 一、进程创建二、GDB多进程调试 一、进程创建 在Linux中输入man 2 fork可以查看man文档中的fork的相关函数信息。 fork的作用就是创建一个子进程。 通过fork我们可以知道,创建子进程的时候,复制父进程的信息。 我们看看翻译的man文档信息&am…...
leetcode——相交链表(java)
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。 图示两个链表在节点 c1 开始相交: 题目数据 保证 整个链式结构中不存在环。 注意,函数返回结果后&…...
Spring 框架:配置缓存管理器、注解参数与过期时间
在 Spring 框架中,可通过多种方式配置缓存具体行为,常见配置方法如下。 1. 缓存管理器(CacheManager)配置 基于内存的缓存管理器配置(以SimpleCacheManager为例) SimpleCacheManager 是 Spring 提供的简单…...
UDP協議與代理IP介紹
UDP,全稱是用戶數據報協議(User Datagram Protocol),是Internet協議套組的一部分,與TCP協議一道工作。與TCP相比,UDP可以理解為一個更“羽量級”的協議。它不需要像TCP那樣在數據傳輸開始之前建立連接&…...
24.日常算法
1. 数组中两元素的最大乘积 题目来源 给你一个整数数组 nums,请你选择数组的两个不同下标 i 和 j,使 (nums[i]-1)*(nums[j]-1) 取得最大值。请你计算并返回该式的最大值。 示例 1: 输入:nums [3,4,5,2] 输出:12 解释…...
【Python】FastAPI框架快速实现后端(一)
FastAPI框架快速实现后端-SQLModel的使用 介绍正文基础模型模型与表定义数据表模型关系定义 介绍 最近1个多月,用FastAPI做了几个日常工作用的小功能,感觉FastAPI确实很适合这种场景,功能要求简单,交付要求比较急,这个…...
西门子【Library of General Functions (LGF) for SIMATIC S7-1200 / S7-1500】
文章目录 概要整体架构流程技术名词解释技术细节小结 概要 通用函数库 (LGF) 扩展了 TIA Portal 中用于 PLC 编程的 STEP 7 指令(数学函数、时间、计数器 等)。该库可以不受限制地使用,并包含 FIFO 、搜索功能、矩阵计算、 astro 计…...
年度总结和寒假总结
年度总结 加入Hope实验室 年初,我加入了Hope实验室,在实验室里,我接触到了更加前沿的技术和项目。刚开始时,我主要学习了Java语言和MySQL数据库。这是我第一次系统地接触这些技术,相比之前的课堂学习,实验…...
STM32 GPIO配置 点亮LED灯
本次是基于STM32F407ZET6做一个GPIO配置,实现点灯实验。 新建文件 LED.c、LED.h文件,将其封装到Driver文件中。 双击Driver文件将LED.c添加进来 编写头文件,这里注意需要将Driver头文件声明一下。 在LED.c、main.c里面引入头文件LED.h LED初…...
C#标准Mes接口框架(持续更新)
前言 由于近期我做了好几个客户的接入工厂Mes系统的需求。但是每个客户的Mes都有不同程度的定制需求,原有的代码复用难度其实很大。所以打算将整个接入Mes系统的框架单独拿出来作为一个项目使用,同时因为不同的设备接入同一个Mes系统,所以代…...