SpringAI从入门到熟练

学习SpringAI的记录情况

文章目录


前言

因公司需要故而学习SpringAI文档,故将自己所见所想写成文章,供大佬们参考

主要是为什么这么写呢,为何不抽出来呢,还是希望可以用的时候更加方便一点,如果大家有需求可以自行去优化。

SrpingAI入门

这里我用到的是智普AI,大家可以根据自己喜欢用的AI自由进行切换

 至于API-KEY可自行去智谱AI开放平台可自行去找,这里新用户注册目前是还送2000万TOKEN的

引包

        <!--智普AI--><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-zhipuai-spring-boot-starter</artifactId></dependency>

 配置文件

 至于API-KEY可自行去智谱AI开放平台可自行去找,这里新用户注册目前是还送2000万TOKEN的

配置类

获取API-KEY

@Component
public class ChatConfig {//获取配置文件中的API-KEY@Value("${spring.ai.zhipuai.api-key}")private String apiKey;public String getApiKey() {return apiKey;}
}

 测试用例代码

    //为啥用这个模型呢,因为免费private static final String default_model = "GLM-4-Flash";//temperature 是一个超参数,用于控制生成文本的多样性和随机性。//低温度(例如:0.1 到 0.3):模型会生成更确定、常规和一致的输出。低温度通常会使模型产生更 加保守的回答,重复性较高,生成的内容更加准确、接近训练数据中的常见模式。//高温度(例如:0.7 到 1.0):模型的输出会更加随机、多样、创意性强。高温度会导致生成的内容 更加多样化,可能包括一些不太常见或创新性的回答,但也可能带来不太准确或不太连贯的结果。private static final double default_temperature = 0.7;   @GetMapping("/AIchat")public BaseResponse<String> AIGeneration(@RequestParam(value = "message", defaultValue = "你是谁呢") String message) {// 创建ZhiPuAiChatOptions对象,设置模型和温度ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().withModel(default_model).withTemperature(default_temperature).build();// 创建ZhiPuAiChatModel对象,传入ZhiPuAiApi和optionsChatModel chatModel = new ZhiPuAiChatModel(new                 ZhiPuAiApi(chatConfig.getApiKey()), options);// 创建ChatClient对象,传入chatModelChatClient build = ChatClient.builder(chatModel).build();String substring = "";String content = null;try {// 调用build.prompt()方法,传入用户消息,调用call()方法,获取返回内容content = build.prompt().user(message).call().content();} catch (Exception e) {// 获取异常信息String errorMessage = e.getMessage();// 获取异常信息中冒号后面的内容int i = errorMessage.lastIndexOf(":");String newString = errorMessage.substring(i + 2);// 去掉异常信息中的最后一个字符substring = newString.substring(0, newString.length() - 3);}// 如果异常信息不为空,抛出BusinessException异常if(!substring.isEmpty()){throw new BusinessException(ErrorCode.PARAMS_ERROR,substring);}// 返回成功结果return ResultUtils.success(content);}

 测试AI返回结果

 全参数响应结果

这里是对token的消耗做了一个统计,查看用户的所剩token情况的一个返回结果展示

其实也就只是把String类型的返回结果转成了ChatResponse类型仅此而已

测试用例代码

 @GetMapping("/AIchat")public BaseResponse<ChatResponse> AIGeneration(@RequestParam(value = "message", defaultValue = "你是谁呢") String message) {// 创建ZhiPuAiChatOptions对象,设置模型和温度ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().withModel(default_model).withTemperature(default_temperature).build();// 创建ZhiPuAiChatModel对象,传入ZhiPuAiApi和optionsChatModel chatModel = new ZhiPuAiChatModel(new ZhiPuAiApi(chatConfig.getApiKey()), options);// 创建ChatClient对象,传入chatModelChatClient build = ChatClient.builder(chatModel).build();String substring = "";ChatResponse chatResponse = null;try {chatResponse = build.prompt().user(message).call().chatResponse();} catch (Exception e) {// 获取异常信息String errorMessage = e.getMessage();// 获取异常信息中冒号后面的内容int i = errorMessage.lastIndexOf(":");String newString = errorMessage.substring(i + 2);// 去掉异常信息中的最后一个字符substring = newString.substring(0, newString.length() - 3);}// 如果异常信息不为空,抛出BusinessException异常if(!substring.isEmpty()){throw new BusinessException(ErrorCode.PARAMS_ERROR,substring);}// 返回成功结果return ResultUtils.success(chatResponse);}

返回结果

 前端传递不同的模型测试方法

测试用例代码

    @GetMapping("/chat")public BaseResponse<String> generation(@RequestParam(value = "message", defaultValue = "你是谁呢") String message,@RequestParam(value = "model", defaultValue = "GLM-4-Flash") String model,@RequestParam(value = "temperature", defaultValue = "0.7") double temperature) {// 创建新的 ZhiPuAiChatOptions 实例,根据请求的参数动态设置ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().withModel(model).withTemperature(temperature).build();// 获取 ChatModel 实例,使用新的配置ChatModel chatModel = new ZhiPuAiChatModel(new ZhiPuAiApi(chatConfig.getApiKey()), options);ChatClient build = ChatClient.builder(chatModel).build();String substring = "";// 使用新的 ChatModel 进行对话生成String content = null;try {content = build.prompt().user(message).call().content();} catch (Exception e) {// 获取错误消息String errorMessage = e.getMessage();int i = errorMessage.lastIndexOf(":");String newString = errorMessage.substring(i + 2);substring = newString.substring(0, newString.length() - 3);}if(!substring.isEmpty()){throw new BusinessException(ErrorCode.PARAMS_ERROR,substring);}return ResultUtils.success(content);}

 其实跟上面的代码差不多,只不过是从前端传递过来一个模型,当然这里我并没有对于上传上来的模型做校验,如果感兴趣的话,可自行查看,并解决

 测试结果

 这里可以看到我们的异常返回起到效果了,原谅我是一个穷鬼,不想花钱在测试这个上面

 测试到这里,相信你已经知道了点什么,就是如果你问的问题较长的话,实际使用GET传输是有问题的,因为默认GET请求的长度是有限制的,建议自行换成POST请求,然后再继续往下

流式响应

测试用例代码

@GetMapping(value = "/AIStream",produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<String> AIStream(@RequestParam(value = "message", defaultValue = "你是谁呢") String message) {// 创建ZhiPuAiChatOptions对象,设置模型和温度ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().withModel(default_model).withTemperature(default_temperature).build();// 创建ZhiPuAiChatModel对象,传入ZhiPuAiApi和optionsChatModel chatModel = new ZhiPuAiChatModel(new ZhiPuAiApi(chatConfig.getApiKey()), options);// 创建ChatClient对象,传入chatModelChatClient chatClient = ChatClient.builder(chatModel).build();Flux<String> content = chatClient.prompt().user(message).stream().content();// 返回成功结果return content;}

响应结果

这里使用的是SSE,SSE是一种服务端向客户端推送的一种技术,需要的可自行去浏览这个技术

 流式响应全参数

测试用例代码

// 使用GetMapping注解,指定请求路径为/AIChatResponseStream,返回类型为TEXT_EVENT_STREAM_VALUE@GetMapping(value = "/AIChatResponseStream",produces = MediaType.TEXT_EVENT_STREAM_VALUE)// 定义一个方法,返回类型为Flux<ChatResponse>,参数为String类型的message,默认值为"你是谁呢"public Flux<ChatResponse> AIChatResponseStream(@RequestParam(value = "message", defaultValue = "你是谁呢") String message) {// 创建ZhiPuAiChatOptions对象,设置模型和温度ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().withModel(default_model).withTemperature(default_temperature).build();// 创建ChatModel对象,传入ZhiPuAiApi和optionsChatModel chatModel = new ZhiPuAiChatModel(new ZhiPuAiApi(chatConfig.getApiKey()), options);// 创建ChatClient对象,传入chatModelChatClient chatClient = ChatClient.builder(chatModel).build();// 调用chatClient的prompt方法,传入message,返回一个Flux<ChatResponse>对象Flux<ChatResponse> chatResponseFlux = chatClient.prompt().user(message).stream().chatResponse();// 返回Flux<ChatResponse>对象return chatResponseFlux;}

响应结果

 至于这里如何查看内容流式响应全参数输出完毕,有一个STOP的停止参数可以使用

位置贴到下面

//全参数流失响应地址接口停止标志--------------当然这里如果你要配合前端可以通过这个STOP来观察结果是否输出完毕,可以通过这个标志来停掉SSE连接
parsedData.results[0].metadata.finishReason === "STOP"

 默认角色回复实现

测试用例代码

// 使用GetMapping注解,指定请求路径为/AIChatDefaultStream,返回类型为文本流@GetMapping(value = "/AIChatDefaultStream",produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<String> AIChatDefaultStream(// 使用@RequestParam注解,指定请求参数名为message,默认值为"你是谁呢"@RequestParam(value = "message", defaultValue = "你是谁呢") String message) {// 创建ZhiPuAiChatOptions对象,设置模型和温度ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().withModel(default_model).withTemperature(default_temperature).build();// 创建ChatModel对象,使用ZhiPuAiChatModel和ZhiPuAiApiChatModel chatModel = new ZhiPuAiChatModel(new ZhiPuAiApi(chatConfig.getApiKey()), options);// 创建ChatClient对象,设置默认系统ChatClient chatClient = ChatClient.builder(chatModel).defaultSystem("你现在的身份是一个星轨会议系统的客服助手,请以友好、乐于助人且愉快的方式来回复。").build();// 调用chatClient的prompt方法,设置用户消息,并返回内容流Flux<String> content = chatClient.prompt().user(message).stream().content();// 返回内容流return content;}

 响应结果

 这里就可以看到响应结果他已经换成了星轨会议系统的客服助手,如果你希望做某一个角色的内容,你可以通过预训练这个参数,使得这个AI客服助手的回复更加友好。

AI知晓日期

测试用例代码

// 使用GetMapping注解,指定请求路径为/AIChatDefaultStream,返回类型为文本流@GetMapping(value = "/AIChatDefaultStream",produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<String> AIChatDefaultStream(// 使用@RequestParam注解,指定请求参数名为message,默认值为"你是谁呢"@RequestParam(value = "message", defaultValue = "你是谁呢") String message) {// 创建ZhiPuAiChatOptions对象,设置模型和温度ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().withModel(default_model).withTemperature(default_temperature).build();// 创建ChatModel对象,使用ZhiPuAiChatModel和ZhiPuAiApiChatModel chatModel = new ZhiPuAiChatModel(new ZhiPuAiApi(chatConfig.getApiKey()), options);// 创建ChatClient对象,设置默认系统ChatClient chatClient = ChatClient.builder(chatModel).defaultSystem("你现在的身份是一个星轨会议系统的客服助手,请以友好、乐于助人且愉快的方式来回复。我希望你可以以中文的方式给我回答。今天的日期是 {current_date}.").build();// 调用chatClient的prompt方法,设置用户消息,并返回内容流Flux<String> content = chatClient.prompt().user(message).system(s->s.param("current_date", LocalDate.now().toString())).stream().content();// 返回内容流return content;}

响应结果

 这样的话AI就知道当前的日期

 记忆对话

测试用例代码

创建一个配置类引入ChatMemory

@Configuration
public class ZhiPuChatMemoryConfig {@Bean@Qualifier("customMemory")public ChatMemory chatMemory() {return new InMemoryChatMemory();}}

 引入ChatMemory

// 使用GetMapping注解,指定请求路径为/AIChatDefaultStream,返回类型为文本流@GetMapping(value = "/AIChatDefaultStream",produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<String> AIChatDefaultStream(// 使用RequestParam注解,指定请求参数名为message,类型为String@RequestParam(value = "message") String message) {// 创建ZhiPuAiChatOptions对象,设置模型和温度ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().withModel(default_model).withTemperature(default_temperature).build();// 创建ChatModel对象,传入ZhiPuAiApi和optionsChatModel chatModel = new ZhiPuAiChatModel(new ZhiPuAiApi(chatConfig.getApiKey()), options);// 创建ChatClient对象,传入chatModel,设置默认系统消息和默认顾问ChatClient chatClient = ChatClient.builder(chatModel).defaultSystem("你现在的身份是一个星轨会议系统的客服助手,请以友好、乐于助人且愉快的方式来回复。我希望你可以以中文的方式给我回答。这里我问你日期的话再回答,否则的话你不要回答,今天的日期是 {current_date}.")//.defaultAdvisors(new PromptChatMemoryAdvisor(chatMemory)).defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory)).build();// 调用chatClient的prompt方法,设置用户消息、系统消息参数、顾问参数,返回内容流Flux<String> content = chatClient.prompt().user(message).system(s->s.param("current_date", LocalDate.now().toString())).advisors(a -> a.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)).stream().content();// 返回内容流return content;}

 响应结果

这样的话就会记住你最近的100条对话,当然这里你也可以自行实现ChatMemory,使用Redis去进行获取

 上面两种记忆是不同的,具体详情,请看下表

特性MessageChatMemoryAdvisorPromptChatMemoryAdvisor
记忆管理粒度基于消息(Message)级别的记忆基于提示(Prompt)级别的记忆
记忆的存储方式将每一条消息存储到记忆中生成合适的提示,将历史对话作为提示提供给模型
适用场景适用于需要精细管理每一条消息的场景适用于需要生成合适提示来为模型提供上下文的场景
如何影响对话直接影响聊天记录,确保每一条消息都被记住影响提示构造,确保生成的提示与上下文一致
内存管理的灵活性依赖于每个消息的存储提示的构造和上下文的动态管理

打印日志功能

 测试用例代码

其实也就是再次添加一个Advisors,再控制台打印一下就可以

public class LoggingAdvisors implements RequestResponseAdvisor {@Overridepublic AdvisedRequest adviseRequest(AdvisedRequest request, Map<String, Object> adviseContext) {System.out.println("Request"+request);return request;}@Overridepublic int getOrder() {return 0;}
}
// 使用GetMapping注解,指定请求路径为/AIChatDefaultStream,返回类型为文本流@GetMapping(value = "/AIChatDefaultLoggingStream",produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<String> AIChatDefaultLoggingStream(// 使用RequestParam注解,指定请求参数名为message,类型为String@RequestParam(value = "message") String message) {// 创建ZhiPuAiChatOptions对象,设置模型和温度ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().withModel(default_model).withTemperature(default_temperature).build();// 创建ChatModel对象,传入ZhiPuAiApi和optionsChatModel chatModel = new ZhiPuAiChatModel(new ZhiPuAiApi(chatConfig.getApiKey()), options);// 创建ChatClient对象,传入chatModel,设置默认系统消息和默认顾问ChatClient chatClient = ChatClient.builder(chatModel).defaultSystem("你现在的身份是一个星轨会议系统的客服助手,请以友好、乐于助人且愉快的方式来回复。我希望你可以以中文的方式给我回答。这里我问你日期的话再回答,否则的话你不要回答,今天的日期是 {current_date}.")//.defaultAdvisors(new PromptChatMemoryAdvisor(chatMemory)).defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory),new LoggingAdvisors()).build();// 调用chatClient的prompt方法,设置用户消息、系统消息参数、顾问参数,返回内容流Flux<String> content = chatClient.prompt().user(message).system(s->s.param("current_date", LocalDate.now().toString())).advisors(a -> a.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)).stream().content();// 返回内容流return content;}

 响应结果

 

 FunctionCall回调

测试用例代码

package com.hhh.springai_test.Client;import com.hhh.springai_test.config.ChatConfig;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.zhipuai.ZhiPuAiChatModel;
import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;@Component
public class ZhipuChatClient {@Resourceprivate ChatConfig chatConfig;@Autowired@Qualifier("customMemory")  // 明确指定使用你自定义的 ChatMemory 实现private ChatMemory chatMemory;public ChatClient getZhipuChatClient() {ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().withModel("GLM-4-Flash").withTemperature(0.95F).build();// 创建ChatModel对象,传入ZhiPuAiApi和optionsChatModel chatModel = new ZhiPuAiChatModel(new ZhiPuAiApi(chatConfig.getApiKey()), options);// 创建ChatClient对象,传入chatModel,设置默认系统消息和默认顾问ChatClient chatClient = ChatClient.builder(chatModel).defaultSystem("你现在的身份是一个星轨会议系统的客服助手,请以友好、乐于助人且愉快的方式来回复。我希望你可以以中文的方式给我回答。这里我问你日期的话再回答,否则的话你不要回答").defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory)).build();return chatClient;}
}
 @GetMapping(value = "/AIChatDefaultLoggingStream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<String> AIChatDefaultLoggingStream(@RequestParam(value = "message") String message) {Flux<String> content = zhipuChatClient.getZhipuChatClient().prompt().user(message).function("report", "举报", new Function<MyController.Request, Flux<String>>() {@Overridepublic Flux<String> apply(MyController.Request request) {System.out.println("举报名字是" + request.name);return Flux.just("星轨");}}).stream().content();return content;}

 测试用例结果

 

 可以看到日志也已经解决了,在这里遇到了整整两天的bug,一直在尝试使用functions,通过传递functionBean来进行解决,但是一直是有问题的,但是一直报错,只能使用这种方式来解决,当然,如果大佬们有好的文章,记得可以推荐我使用一下,谢谢各位大佬们

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/498959.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何使用大语言模型进行事件抽取与关系抽取

诸神缄默不语-个人CSDN博文目录 文章目录 1. 什么是事件抽取与关系抽取&#xff1f;2. 示例&#xff1a;使用大语言模型进行事件抽取与关系抽取 1. 什么是事件抽取与关系抽取&#xff1f; 事件抽取是指从文本中识别出与某些“事件”相关的信息。这些事件通常包括动作、参与者、…

GoldenDB组件及对应的用户和进程

1. GoldenDB组件及对应的用户和进程 GoldenDB数据库由管理节点、全局事务节点GTM、计算节点CN、数据节点DN等组成。 1.1. 管理节点 管理节点分为集群管理、Insight运维管理平台&#xff08;InsightServer、RDB、ZK&#xff09;。 1.1.1. 集群管理 1. 集群管理包括Metadatas…

认识编程-从思维方面看编程

如果说艺术通过色彩、线条和旋律将人类内心深处的梦想具象化&#xff0c;使之成为可以感知的视觉或听觉作品&#xff0c;那么计算机程序则是在数字世界中以逻辑和算法为画笔&#xff0c;描绘着创作者的构思与愿望。 编程是实现这一过程的语言和工具&#xff0c;它让那些抽象的…

小程序分包优化实践:解决主包过大和vendor.js体积问题

随着 uniapp 开发的小程序功能越来越复杂&#xff0c;主包的大小也逐渐增长&#xff0c;导致上传代码时遇到了2MB的限制。同时&#xff0c;由于微信小程序tabbar页面必须放在主包中&#xff0c;这进一步增加了主包的负担。为了提高用户体验并遵守平台规则&#xff0c;我们有必要…

Java Day1回顾简介-----变量命名规则

Java 简介、开发环境配置 菜鸟教程 编译运行 javac HelloWorld.java java HelloWorld Hello World public class HelloWorld {public static void main(String[] args) {System.out.println("Hello World");} }JAVA基础语法 类、对象、方法、实例、变量 对象&a…

vue3 + element-ui + vue router的使用教程 基于HBuilderX

文章目录 前言1.安装vue router2.创建一个router.js文件 封装router3.在main.js中导入router.js4.使用el-menu5.在el-menu使用vue router6.运行项目查看效果如下 前言 开发环境基于 HBuilderX 本文主要介绍 element-ui的el-menu组件结合vue router的使用教程 el-menu组件 &am…

Dockerfile运行指令

1.RUN 在build构建时执行命令。 举例&#xff1a;安装vim Shell命令格式 RUN yum install -y vim Exec命令格式 RUN ["yum","install","-y","vim"] 2.CMD 用于设置容器启动时默认执行的命令或参数。 如果Dockerfile中有多个CMD&a…

【经管】上市公司供应链风险数据测算数据集+dofile(2008-2023年)

A股上市公司企业供应链风险是指在企业运营过程中&#xff0c;由于供应链各环节的波动和不稳定&#xff0c;导致企业面临的生产、销售和财务风险。随着市场环境的变化&#xff0c;A股上市公司在全球化竞争中暴露出越来越多的供应链风险问题。 一、A股上市公司企业供应链风险的介…

记录一个我在idea启动时的报错

这几天我的idea突然就不能用了我就想着下一个新的&#xff0c;但是却一直报错报错内容如下 这个是我在网上截的pycharm的。 我在网上查了很多方法都不能用&#xff0c;今天重写安装发现我点了关联.java 和.pom和创建环境变量 这几个只需要创建一个快捷方式就行。我重新安装之…

HTML5新特性|01 音频视频

音频 1、Audio (音频) HTML5提供了播放音频文件的标准 2、control(控制器) control 属性供添加播放、暂停和音量控件 3、标签: <audio> 定义声音 <source> 规定多媒体资源,可以是多个<!DOCTYPE html> <html lang"en"> <head><…

138.WEB渗透测试-信息收集-小程序、app(9)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;137.WEB渗透测试-信息收集-小程序、app&#xff08;8&#xff09; 小程序的信息收集&…

TSN:工业通信的未来

一.传统以太网 1.竞争传输 传统以太网是基于竞争的传统以太网通信机制&#xff0c;当多个PC需要同一链路传输数据时&#xff0c;此时多个PC会竞争链路的使用权&#xff08;CSMA/CA&#xff09;。 如上图所示&#xff0c;当ES1向ES3传输大量非关键流量&#xff08;BE&#xff0…

如何将联系人从Android转移到 OPPO? [解决了]

概括 OPPO Reno4系列预计将于2020年10月1日上午9点30分举行线上发布会。从其官方预告片中我们不难发现&#xff0c;OPPO Reno4旗舰手机试图诠释梦想、挑战、勇气、自信和可能性。 3D曲面屏&#xff0c;图形流畅&#xff0c;机身更轻薄&#xff0c;色彩真实。听起来棒极了&…

[羊城杯 2024]不一样的数据库_2

题目描述&#xff1a; 压缩包6 (1).zip需要解压密码&#xff1a; 尝试用ARCHPR工具爆破一下&#xff1a; &#xff08;字典可自行在github上查找&#xff09; 解压密码为&#xff1a;753951 解压得到13.png和Kee.kdbx文件&#xff1a; 二维码图片看上去只缺了正常的三个角&…

MIT实验笔记冲刺3:页表操作(理论部分)

目录 分页硬件 内核地址空间 代码&#xff1a;创建地址空间 物理内存分配 代码&#xff1a;物理内存分配器 进程地址空间 代码&#xff1a;sbrk 代码&#xff1a;exec 实际的操作系统 这个实验将重点放到了我们的页表上&#xff0c;实际上&#xff0c;页表在我们上一个…

二、SQL语言,《数据库系统概念》,原书第7版

文章目录 一、概览SQL语言1.1 SQL 语言概述1.1.1 SQL语言的提出和发展1.1.2 SQL 语言的功能概述 1.2 利用SQL语言建立数据库1.2.1 示例1.2.2 SQL-DDL1.2.2.1 CREATE DATABASE1.2.2.2 CREATE TABLE 1.2.3 SQL-DML1.2.3.1 INSERT INTO 1.3 用SQL 语言进行简单查询1.3.1 单表查询 …

纯前端实现将pdf转为图片(插件pdfjs)

需求来源 预览简历功能在移动端&#xff0c;由于用了一层iframe把这个功能嵌套在了app端&#xff0c;再用一个iframe来预览&#xff0c;只有ios能看到&#xff0c;安卓就不支持&#xff0c;查了很多资料和插件&#xff0c;原理基本上都是用iframe实现的。最终转换思路&#xf…

【容器化技术 Docker 与微服务部署】详解

容器化技术 Docker 与微服务部署 一、容器化技术概述 &#xff08;一&#xff09;概念 容器化技术是一种操作系统级别的虚拟化方法&#xff0c;它允许将应用程序及其依赖项&#xff08;如运行时环境、系统工具、库等&#xff09;打包成一个独立的、可移植的单元&#xff0c;这…

【从零开始入门unity游戏开发之——C#篇43】C#补充知识——值类型和引用类型汇总补充、变量的生命周期与性能优化、值类型和引用类型组合使用

文章目录 一、值类型和引用类型汇总补充1、值类型和引用类型汇总2、值类型和引用类型的区别3、简单的判断值类型和引用类型 二、变量的生命周期与性能优化1、**栈和堆的区别**2、**变量生命周期**3、**垃圾回收&#xff08;GC&#xff09;机制**4、**代码示例与优化**4.1. 临时…

CSS2笔记

一、CSS基础 1.CSS简介 2.CSS的编写位置 2.1 行内样式 2.2 内部样式 2.3 外部样式 3.样式表的优先级 4.CSS语法规范 5.CSS代码风格 二、CSS选择器 1.CSS基本选择器 通配选择器元素选择器类选择器id选择器 1.1 通配选择器 1.2 元素选择器 1.3 类选择器 1.4 ID选择器 1.5 基…