首页
东邻西舍
本站信息
前来吐槽
统计
Search
1
openwrt系统上安装第三方插件
28,883 阅读
2
ubuntu下zerotier的基本使用教程
15,988 阅读
3
给小米R3G更换系统:从padavan刷成openwrt
14,993 阅读
4
openwrt使用第一步:设置上网拨号
12,964 阅读
5
openwrt无线中继功能:实现不插网线就能上网
11,531 阅读
学习点滴
后端
前端
Linux
踩坑实录
AI
折腾搞机
关于建站
只言片语
登录
Search
标签搜索
Linux
Java
建站
踩坑实录
折腾搞机
Ubuntu
MySQL
MyBatis
CSS
HTML
Spring
SQL
Nginx
OpenWrt
树莓派
路由器
Windows
Maven
只言片语
Win10
知识分子没文化
累计撰写
87
篇文章
累计收到
155
条评论
首页
栏目
学习点滴
后端
前端
Linux
踩坑实录
AI
折腾搞机
关于建站
只言片语
页面
东邻西舍
本站信息
前来吐槽
统计
搜索到
53
篇与
的结果
2025-12-16
折腾AI画图小记(一):ComfyUI环境相关
目录: 一、环境相关 1.1、启动器 1.2、插件下载 1.3、主要目录 二、模型下载 2.1、模型文件格式 2.2、根据显卡选择恰当精度的模型 2.3、寻找适合ComfyUI的模型 运行环境: Win11 专业版、ComfyUI 0.12.3 Python 版本 : 3.12.12 PyTorch 版本: 2.9.0 CUDA 版本 : 12.8 显卡:RTX 2080Ti 22G 推荐几个良心 B 站 UP:Work-Fisher、黑鹤001、鳌拜拜丿AllBuy、飞天闪客 关于 ComfyUI 相关的一些基础概念和教程示例,强烈推荐阅读其官方文档,从模型下载到工作流都有:ComfyUI 官方文档 - ComfyUI 一、环境相关 1.1、启动器 我目前用的启动器是由 B 站 UP 主 Mr陌客 开发的 NB启动器,UI 界面美观,功能比较丰富,看日志、管理插件、环境检测、管理依赖等这些功能都有,个人感觉很好用。 1.2、插件下载 现在基本百分之九十九的插件基本都能在 Github 上能找到,手动安装的方法是从 Github 上把项目 clone 下来放在目录的 custom_nodes 文件夹下,或者使用启动器的安装插件功能,当然启动器上面的安装插件功能也都是选定 Github 上的插件之后自动在 custom_nodes 文件夹下执行 clone 命令。 安装完成之后重启 ComfyUI 即可。 部分推荐安装的插件: Comfy-Org/ComfyUI-Manager:ComfyUI 的自定义节点管理器,支持安装、卸载、禁用和启用各种自定义节点。 Dontdrunk/ComfyUI-DD-Translation:基于 AIGODLIKE-ComfyUI-Translation 的衍生分支,为 ComfyUI 提供全面的简体中文翻译支持,支持界面语言切换和自定义节点汉化。 nunchaku-ai/ComfyUI-nunchaku:集成 Nunchaku 量化推理引擎,支持 4-bit 量化模型的高效推理,兼容 FLUX.1 等模型。 rgthree/rgthree-comfy:提供一系列自定义节点和优化工具,让 ComfyUI 工作流更简洁、快速和易用。 crystian/ComfyUI-Crystools:提供资源监控(CPU/GPU/内存)、进度条、元数据提取、图像保存及调试等实用工具集。 pythongosssss/ComfyUI-Custom-Scripts:增强 ComfyUI 的界面功能,包括嵌入式自动补全、节点自动排列、颜色拾取器和工作流快捷操作。 1038lab/ComfyUI-QwenVL:集成 Qwen-VL 系列多模态模型(含 Qwen2.5-VL/Qwen3-VL),支持图像理解、视频分析和文本生成。 yawiii/ComfyUI-Prompt-Assistant:提示词助手,支持调用多种大模型进行提示词翻译、润色扩写、图片反推,并提供预设插入和历史记录功能。 kijai/ComfyUI-KJNodes:提供多种实用自定义节点,包括跨子图的 Set/Get 连接管理及其他工作流优化工具。 yolain/ComfyUI-Easy-Use:对常用节点进行优化和集成,简化 ComfyUI 操作流程,提升图像生成的使用便利性。 ltdrdata/ComfyUI-Impact-Pack:提供图像检测、面部修复、分割、预览桥接及多种实用检测与修复节点,支持复杂工作流优化。 jags111/efficiency-nodes-comfyui:高效加载器和工作流简化节点集合,减少节点数量并优化 SDXL/SD1.5 等生成流程。 Suzie1/ComfyUI_Comfyroll_CustomNodes:包含多 ControlNet、LoRA、宽高比切换、流程控制开关等多种实用自定义节点。 WASasquatch/was-node-suite-comfyui:大型节点套件,提供图像处理、文本处理、数学运算、视频/GIF 处理等 100+ 高级节点。 BlenderNeko/ComfyUI_ADV_CLIP_emb:高级 CLIP 文本编码节点,支持更精细的提示词权重控制和嵌入混合。 shadowcz007/comfyui-mixlab-nodes:提供图像混合、抠图、蒙版处理、布局优化及多种创意编辑节点。 1.3、主要目录 一般从官方仓库下载的 ComfyUI 便携包目录下,python_embeded 是自带的 python 环境,ComfyUI 文件夹是程序本体,主要介绍一下 ComfyUI 文件夹下的几个常用文件夹: models:存放各类模型文件,所有 AI 模型权重文件都按类型分类存放在其子目录中。节点加载器(如 Checkpoint 加载器、LoRA 加载器、uNet 加载器)会自动扫描对应的路径。 checkpoints:存放主模型(如 SDXL、Flux、Z-Image、Wan 等)文件,文件格式多为 .safetensors 或 .ckpt(很少)。使用“checkpoint 加载器”节点加载该目录下的模型。 clip / text_encoders:都存放 CLIP 文本编码模型文件,使用任意一个文件夹都可以。使用“CLIP 加载器”节点可加载该目录下的模型。 controlnet:存放 ControlNet 模型(.safetensors 文件)。这类模型用于对图像生成过程进行精确控制,例如根据线稿上色、模仿特定姿势、依据深度图生成等。使用“controlnet 加载器”节点可加载该目录下的模型。 unet / diffusion_models:存放扩散模型的 UNet 权重(.safetensors 文件)。使用“unet 加载器”节点可加载该目录下的模型。 vae:存放 VAE(变分自编码器)模型(.safetensors 文件),用于图像编码/解码,提升生成质量或修复颜色问题。使用“vae 加载器”节点可加载该目录下的模型。 loras:存放 LoRA 模型(.safetensors 文件),这是一种轻量级的微调模型,用于在基础模型上增加特定的人物、风格、服饰等特征,文件体积较小。使用“lora 加载器”节点可加载该目录下的模型。 hypernetworks:存放超网络模型,是另一种微调技术,功能与 LoRA 类似,但目前使用不如 LoRA 广泛。 embeddings:存放文本嵌入模型(也称为 Textual Inversion)。它可以将一个特定的概念(如自定义的角色、画风)编码成一个词,方便在提示词中调用。 LLM:专门用于存放大语言模型(Large Language Model)权重文件,特别是 GGUF 格式(llama.cpp / GGML 量化模型)、transformers 格式的完整模型文件夹、VLM(Vision-Language Model)多模态模型等。它不是 ComfyUI 核心自带的文件夹,而是由各种 LLM 相关自定义节点自动创建路径。 configs:存放模型的配置文件(.yaml 或 .json),定义了模型的网络结构和采样参数。 其他可能出现的文件夹(如 ipadapter、insightface、ultralytics 等)由自定义节点或特定功能创建。 custom_nodes:安装第三方插件的主要位置,每个子文件夹对应一个插件,插件内通常包含 .py 文件定义节点。 input:用于存放上传的输入文件,当使用“加载图片”、“加载视频”等节点上传文件时,文件默认保存到这里,上传或手动复制文件进来,节点也可以直接从此目录读取。 output:生成结果的默认保存目录,使用“保存图片”、“保存视频”等节点时,生成的图片(.png/.jpg)、视频、GIF 等会保存在这里。 temp:存放临时文件、缓存、中间结果(如上传的临时图像、预览图、某些节点生成的中间数据)。 user\default\workflows:存放工作流的目录。 注意:模型文件不能乱放,必须放在对应文件夹,否则节点无法识别。为了管理方便,可以在这些文件夹里再创建子文件夹分类存放不同版本的模型,ComfyUI 也能正常识别。 二、模型下载 2.1、模型文件格式 所谓模型文件格式(.safetensors、.gguf、.ckpt、.pt)本质上都是“模型权重的存储方式”,主要有这么几种格式: .safetensors:由 Hugging Face 推出的一种安全的模型权重格式,只存“纯数据”(tensor),加载速度快,不包含任何可执行代码,因此安全性高,没有执行潜在恶意代码的风险。 .gguf:由 llama.cpp 团队开发的二进制格式,专门为大语言模型(LLM) 设计,支持 Q2、Q3、Q4、Q5、Q8 等不同比特量化(quantization),可大幅缩小模型体积,并且支持 CPU 运行。 .ckpt:“Checkpoint”(检查点)的缩写,通常指代训练过程中的模型快照。一个 .ckpt 文件(或一组文件)不仅包含模型权重,还通常包含优化器状态、当前训练轮数、学习率调度器状态等信息。 .pt / .pth:PyTorch 框架原生的模型文件格式,两者可以互换使用。它们是使用最广泛的模型格式之一,兼容性最好,但文件较大,加载较慢,且存在安全风险。 其中,ComfyUI 中这几种模型文件格式都有涉及,不过主要重点关注 “.safetensors”、“.gguf”,checkpoint / vae / clip / lora / unet 模型文件大部分是 “.safetensors” 格式,也会有部分经过量化的 checkpoint 模型文件为 “.gguf” 格式,如果涉及大语言模型的节点大部分用到的是经过量化的 “.gguf” 格式。 其他自定义节点或特定功能也会用到“.pt / .pth”文件,不过一般也无需专门下载,即便缺少该文件,在运行工作流时也会自动下载。 完整的基础模型文件在 HuggingFace 平台(需科学上网)或者国内的镜像站 HF-Mirror 上下载,还有阿里旗下的 魔搭社区 也是类似于 HuggingFace 的平台,上面的模型文件虽然不如 HuggingFace 丰富,但也涵盖了目前大部分的开源模型及其量化版本,除此之外,Civitai | Discover and Create AI Art(C站)也提供了丰富的模型。 2.2、根据显卡选择恰当精度的模型 HuggingFace 或魔搭社区常有同一模型的不同精度变体,也就是经常听到的 FP16、BF16、FP8 等,文件名里会标,尽量根据自己的显卡来选择对应精度的模型。目前近几代 Nvidia 显卡对于浮点运算的硬件加速支持情况也各不相同: 50系列(Blackwell):FP16、BF16、FP8、FP4 40系列(Ada):FP16、BF16、FP8 30系列(Ampere):FP16,BF16 20系列(Turing):FP16 10系列(Pascal)及以下:只有较慢的全精度 FP32 当然,这并不代表在 RTX2080Ti 这种不支持 FP8 / BF16 硬件加速的显卡上使用不了 FP8 / BF16 精度的模型,相反,其加载新精度的模型文件通常不会报错,依然还是能跑起来。原因在于,ComfyUI(以及背后的PyTorch和CUDA)在加载模型时检测到显卡如果不支持原生 FP8 / BF16 硬件加速,系统会将低精度权重自动转换到硬件支持的格式(通常是 FP16 或 FP32)后再进行计算。 缺点在于每次都需要进行数据类型转换,中间多了数据转换的开销,实际速度有时不如直接用原生支持的FP16模型快。 FPxx / BFxx 就是模型权重的数值精度(Precision),简单来说,精度越高,模型计算时的误差越小,生成的图像细节理论上越还原,但速度和显存占用会相应增加,反之亦然。除此之外,混合精度(mixed precision)技术在 AI 推理中也有应用:用低精度格式存储/计算权重和激活值,同时在关键部分保留较高精度来维持生成质量,从而追求资源占用与生成质量的平衡。 目前主流的精度格式主要有以下几种: FP32 (Float32):全精度,32 位浮点数。这是传统深度学习训练的标准格式,精度最高,但体积最大,对算力和显存要求极高。老款显卡(如10系及更早)通常只能跑这个精度,速度最慢。 FP16 (Float16):半精度,16 位浮点数。这是目前最通用的格式。它将模型体积缩小了一半,在支持它的显卡上能获得显著的速度提升。几乎所有现代显卡(20系及以上)都原生支持 FP16。 BF16 (Brain Float16):也是一种 16 位浮点数,但它的动态范围比 FP16 更大。它主要用于训练和推理,对显存要求低,且在支持它的硬件上效率极高。需要 30系(Ampere)及以上的显卡才能原生支持。 FP8 (Float8):8位浮点数,它将模型体积进一步压缩,推理速度极快,显存占用极低。这是 40系(Ada)和 50系(Blackwell)显卡的“独享”特性,能带来巨大的性能飞跃。 更低精度(如GGUF量化、NVFP4等):GGUF 常用于 LLM 相关节点,支持 Q4/Q5/Q8 等比特量化,体积更小,可 CPU/GPU 混合运行。NVFP4 是 从 50系(Blackwell)显卡引入的新特性,目前主要利好新卡,且需匹配特定 PyTorch/CUDA 版本。 要更详细了解数据精度的话,推荐 B 站 UP 主飞天闪客的视频:【闪客】你管这破玩意叫 UE8M0?_哔哩哔哩_bilibili 2.3、寻找适合ComfyUI的模型 最省事的办法是在 HuggingFace 或者 魔搭社区 下载 ComfyUI 官方(Comfy-Org - HuggingFace / Comfy Org - 魔搭社区)提供的 safetensors 模型文件,基本包含了大部分社区主流开源模型的 safetensors 文件,除此之外,ComfyUI 官方文档 里面有很多模型的工作流模板,也提供了其对应模型的下载链接。 对于新发布的、在 ComfyUI 官方仓库中找不到 safetensors 文件的模型来说,可以下载由社区大佬根据官方开源模型适配、微调、量化的 safetensors 模型文件。 以在 HuggingFace 下载 Z-Image-Turbo 模型的 safetensors 文件为例,先找到其官方仓库 Tongyi-MAI/Z-Image-Turbo · Hugging Face,在右侧的模型系谱中会显示社区在官方模型基础上进行 Adapters(适配)、Finetunes(微调)、Quantizations(量化)的版本(魔搭社区界面也是类似的)。 个人的经验是先在Finetunes(微调)找提供了 safetensors 文件、且下载人数比较多的仓库,没有合适的再从Adapters(适配)、Quantizations(量化)找。 仓库一般都会包含扩散模型(diffusion_model)、VAE 模型(vae)、CLIP 模型(text_encoder),下载下来放在对应的本地模型目录(unet/diffusion_models、vae、clip/text_encoder)即可。 如果只有一个 safetensors 文件,且命名中包含了 “AIO” 字样,那就是将扩散模型、VAE 模型和 CLIP 模型打包在一起的一体化模型,放在 checkpoint 文件夹中,使用 checkpoint 加载器加载模型;如果命名中不包含 “AIO” 字样,说明该版本模型的扩散模型、VAE 模型和 CLIP 模型是分开的,甚至有可能只提供了扩散模型,而 VAE 模型和 CLIP 模型需要单独下载,不过很多生图模型用到的 VAE 模型和 CLIP 模型就 ae、Qwen等几个,比较容易下载。 如果 ComfyUI 在运行时往模型目录添加了新模型,不用重启,在界面上按 R就能刷新模型与节点缓存。
2025年12月16日
5,899 阅读
0 评论
3 点赞
2024-12-03
不使用 MySQL Installer 安装 MySQL 的方法
目录: 1、下载安装MySQL 2、在安装目录新建配置 3、添加环境变量 4、安装 5、启动服务 6、卸载 现在 Windows 下安装 MySQL 的时候需要首先下载 MySQL Installer,通过这个软件来联网安装管理不同版本的 MySQL,但是这个软件默认是将 MySQL 安装在 C 盘的,修改安装路径的地方也藏的很隐蔽,并且由于安装时 MySQL Installer 需要联网下载安装包,这对于内网里的机器无疑就是很不友好的。但是从 Oracle 官网下载到的 MSI Installer 程序只是解压出了 MySQL 程序,并没有进行任何的环境变量配置、服务配置,那么就需要通过手动配置程序信息。 1、下载安装MySQL 最新版本下载链接:MySQL :: Download MySQL Community Server 旧版本下载链接:MySQL :: Download MySQL Community Server (Archived Versions) 选择下载某一个版本的 MSI 安装程序或者 ZIP 压缩包,下载完成之后进行安装或解压。 2、在安装目录新建配置 下文中的 MySQL 安装目录以 D:\Program\Environment\MySQL\MySQL Server 5.7 为例: 安装目录下创建一个 “data” 文件夹 安装目录下创建一个 “my.ini” 文本文件,向其中写入以下内容(文件路径根据实际修改): [mysqld] # mysql 安装目录 basedir="D:\Program\Environment\MySQL\MySQL Server 5.7" # mysql 数据库的数据存放目录 datadir="D:\Program\Environment\MySQL\MySQL Server 5.7\data" sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES # MySQL服务器监听的端口 port=3306 # 服务端编码方式 character-set-server=utf8mb4 [clinet] # 客户端编码方式 loose-default-character-set=utf8mb4 # 端口号设置 port=3306 [WinMySQLadmin] Server="D:\Program\Environment\MySQL\MySQL Server 5.7\bin\mysqld.exe" 3、添加环境变量 右键 “开始菜单” -> “系统” -> “高级系统设置” -> “环境变量” 向 Path 中添加环境变量,值为 MySQL 安装目录下的 bin 文件夹: D:\Program\Environment\MySQL\MySQL Server 5.7\bin 4、安装 上面环境变量配置没有问题的话,就可以在 cmd 窗口中使用 mysqld 命令了。 此时以管理员身份运行 cmd 窗口,执行命令来初始化相关 data 文件: # 在前面配置好的 data 文件夹下会生成初始化的文件 mysqld --initialize-insecure --user=mysql 安装 MySQL 服务: mysqld -install 5、启动服务 可以通过以下命令启动 MySQL 服务: net start mysql 或者在 ”服务“ 窗口中找到 ”MySQL“ 这项服务,右键启动即可: 服务窗口可通过在“运行”窗口输入 “services.msc” 来启动,或者右键 开始菜单 -> 计算机管理 -> 服务和应用程序 -> 服务 找到。 在启动过程中有可能会出现报错: 在 MySQL 的服务属性中可以看到这项服务可执行文件的程序路径是不正确的: 这个窗口里面没有提供可以改这个路径的地方,查了一下,得需要到注册表中改,在注册表中(Win + R 运行窗口中输入“regedit”回车打开)找到 “HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\MySQL” 这个路径,修改其中 ImagePath 的值,将引号里的路径替换为本地 MySQL 安装文件的实际路径(就只替换前面的路径,其他的不用改): 保存退出之后,再到 “服务” 窗口中刷新一下,可执行文件的路径变成了新的路径之后再次启动即可成功启动 MySQL 服务。 目前本地运行的 MySQL 服务用 root 用户就可以登录,密码为空 MySQL 修改密码: 连接到数据库之后用 SQL 语句为 root 用户更改密码: alter user '用户名'@'localhost' identified by '新密码'; 修改完之后,刷新权限信息: flush privileges; 也可以在 MySQL 控制台中使用 mysqladmin 命令修改用户密码: # 新密码不需要加引号,否则会报错 mysqladmin -u [用户名] -p password [新密码]; 6、卸载 以管理员身份执行命令: mysqld -remove 删除服务: sc delete mysql 删除注册表文件: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog\Application\MySQL HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\services\MySQL HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\services\eventlog\Application\MySQL 参考资料: MySQL8.0绿色版本——最纯净的安装体验(免去卸载不掉只能重做系统的‘囧‘境)_mysql8.0 绿色安装-CSDN博客 mysql 启动提示:错误2系统找不到指定文件;(本地计算机上的mysql服务启动后停止,某些服务在未由其他服务或程序使用时将自动停止)_无法编辑imagepath-CSDN博客
2024年12月03日
1,947 阅读
1 评论
1 点赞
2024-03-07
Redis进阶学习笔记
目录: 一、Redis 的基础知识 1.1、什么是 Redis 1.2、为什么要有 Redis 二、内存管理与淘汰机制 2.1、内存过期策略 2.2、内存淘汰机制 三、持久化 3.1、RDB 持久化 3.2、AOF 持久化 3.3、RDB-AOF 混合持久化(Redis 4.0) 四、Redis集群方案 4.1、单机模式(Standalone) 4.2、主从复制(Replication) 4.3、哨兵模式(Redis Sentinel) 4.4、Redis Cluster 五、应用场景 5.1 数据库 5.2 系统缓存 5.3 消息队列 5.4 计数器和排行榜 5.5 分布式锁 六、缓存相关问题 6.1、缓存穿透 6.2、缓存击穿 6.3、缓存雪崩 Redis 官网:Redis - The Real-time Data Platform 一、Redis 的基础知识 1.1、什么是 Redis Redis(REmote DIctionary Server)是一个开源的、基于内存的、键值对(Key-Value)数据库,它支持多种数据结构,并提供原子操作、事务、Lua脚本、发布订阅等高级特性。 Redis 支持的基础数据类型有五种: 数据类型 介绍 底层实现 特点 String(字符串) 最基础、最常用的数据类型 SDS(简单动态字符串) 可以存储:字符串、整数、浮点数、二进制数据单个数据最大支持 512MB支持原子操作(如自增、自减) List(列表) 本质是一个双向链表 压缩列表 / 双向链表 有序、可重复支持从两端插入/弹出操作时间复杂度:头尾 O(1) Hash(哈希) 类似于 Map<String, String> 压缩列表 / 哈希表 存储键值对集合(field-value)适合存储对象支持单字段读写 Set(集合) 无序、不 哈希表 / 整数集合 自动去重支持集合运算(交集、并集、差集) ZSet(Sorted Set,有序集合) 在 Set 基础上增加了一个 score(分数) 用来排序 跳跃表 + 哈希表 元素唯一,但 score 可重复按 score 自动排序(底层是跳跃表 + 哈希表)查询效率高(范围查询) 还有几种基于基础数据类型的扩展类型: 数据类型 介绍 特点 Bitmap(位图) 基于 String 的按位操作 每一位是 0 或 1非常节省空间(适合大规模布尔状态) HyperLogLog 用于基数统计(去重计数)的概率算法 占用极小内存(≈12KB)有一定误差(约 0.81%) Geospatial(地理位置) 用于存储地理位置信息 基于 Sorted Set 实现支持距离计算、范围搜索 Stream(流,Redis 5+) 类似消息队列,但功能更强 支持消费组(Consumer Group)支持消息持久化可回溯消息 1.2、为什么要有 Redis 传统关系型数据库(如 MySQL)擅长复杂查询和事务,但磁盘 I/O 导致高并发下存在延迟高、吞吐低的问题,因此数据库的读写性能往往会成为瓶颈。 而加入 Redis 作为高性能缓存,将使用频率高的数据(热点数据)放在内存中,需要时可以直接通过请求 Redis 获取,只有当 Redis 获取不到时才会查询数据库,这种方式可以降低数据库的压力,同时还显著提升系统的响应速度和吞吐量。 Redis 的主要优势包括: 基于内存:所有数据存储在内存中,读写也基于内存操作,避免了磁盘 I/O,因此速度极快,这是最根本的原因; 单线程模型(核心I/O线程): 准确来讲,Redis 不是完全单线程,Redis 6.0 之后,网络I/O、持久化会使用多线程,但命令执行依然是单线程; 在进行读写操作时,单线程可以避免多线程的上下文切换和竞争(锁竞争、死锁)带来的额外开销,反而提升了处理速度,此时的瓶颈不在CPU,而在内存和网络; 并且由于单线程没有了复杂的同步机制,因此也不易出错; I/O多路复用机制:让单线程能同时监听多个 socket 连接,当某个连接有数据请求时,才去处理。类似于一个服务员同时服务很多桌客人,谁举手就去谁那。使用了epoll等高效机制; 高效的数据结构:底层针对不同场景设计了极致优化的数据结构。 二、内存管理与淘汰机制 Redis 是内存数据库,随着应用的运行,如果不清理,Redis 中的 Key 会不断增加,最终内存耗尽导致 OOM(Out Of Memery)。 针对于内存溢出问题,Redis 通过内存过期策略预防内存溢出,而当内存使用达到 maxmemory 上限导致新 Key 无法写入时,Redis 使用内存淘汰机制淘汰旧数据为新数据腾出空间。 2.1、内存过期策略 Redis 通过为 Key 设置过期时间(TTL)来管理内存中的数据生命周期。当 Key 的过期时间到达时,Redis 会自动删除该键及其对应的值,以避免旧数据的无限累计。 内存过期策略主要包括: 定时删除:为每个设置过期时间的 Key 维护一个定时器,当过期时间到达时立即删除。但这种方式会消耗大量的CPU资源,比较影响性能。 惰性删除:在访问 Key 时才检查其是否过期,如果过期则删除。这种方式不会额外消耗CPU资源,但可能导致内存中存在大量过期的 Key,占用内存空间。 需要说明的是,Redis 并不是在 Key 到期的一瞬间就立刻将其从内存中删除,而是采用了定时删除与惰性删除两种策略的组合: Redis 内部开了一个定时任务,默认每段时间(比如 100ms)运行一次; 从设置了过期时间(TTL)的 Key 中随机抽取 20 个数据进行检查; 如果这 20 个 Key 中过期的 Key 占比超过 25%(即超过 5 个),Redis 会认为当前内存中过期 Key 较多,不会等待下一个 100ms,而是立即重复上一个步骤继续抽查、继续清理,直到过期的 Key 占比低于 25%。 内存过期策略也有可能会导致一个问题,如果同一时刻大量数据过期或者 Redis 无法提供服务,客户端请求从 Redis 中获取不到已过期的数据就会查询数据库,从而使数据库请求激增,压力过大造成数据库服务器宕机,即缓存雪崩。 所以要避免大量数据在同一时刻过期,解决办法是在固定过期时间上增加一个随机偏移量,同时对于热点数据,不设置过期时间,使其达到“物理”上的永不过期,可以避免缓存击穿问题。 2.2、内存淘汰机制 当内存使用达到 maxmemory 上限,且新数据写入时,触发淘汰策略,根据配置的内存淘汰策略删除部分键值对以释放内存。 那么应该选择哪些数据淘汰,大致的思想有两种: LRU(Least Recently Used)是按照最近最少使用原则来筛选数据,即最不常用的数据会被筛选出来; LFU(Least Frequently Used)是 Redis4 新增的淘汰策略,它根据 key 的最近访问频率进行淘汰,即访问次数最少的数据会被筛选出来。 再结合淘汰数据的范围(全部 Key 或设置了过期时间的 Key),那么综合考虑下来内存淘汰策略就有八种: 策略 描述 适用场景 noeviction 默认策略。不淘汰,写请求直接返回错误(如SET失败) 数据绝不能丢的场景,如作为数据库 allkeys-random 从所有key中随机淘汰 缓存数据访问频率相近 allkeys-lru 从所有数据中淘汰最近最少使用(LRU)的key 通用缓存,希望保留最可能被再次访问的数据 allkeys-lfu(Redis4.0) 从所有数据中淘汰使用频率最低(LFU)的key 通用缓存,希望保留经常被访问的数据 volatile-ttl 从设置了过期时间的key中,淘汰将要过期的key 通用缓存,希望优先淘汰快过期的数据 volatile-random 从设置了过期时间的key中,随机淘汰key 缓存数据访问频率相近 volatile-lru 从设置了过期时间的数据中淘汰最近最少使用(LRU)的key 通用缓存,希望保留最可能被再次访问的数据 volatile-lfu(Redis4.0) 从设置了过期时间的数据中淘汰使用频率最低(LFU)的key 通用缓存,希望保留经常被访问的数据,比LRU更精准 结合内存过期策略来说,如果此时设置的淘汰策略是 allkeys-lru 或 allkeys-random,那么即使某个 Key 还没到过期时间,也可能被提前淘汰,使用 volatile-lru 或 volatile-ttl 策略,即只针对有过期时间的 Key 进行淘汰,就可以解决这个问题。 三、持久化 对于 Redis 来说,由于数据都在内存中,如果发生断电或进程崩溃时就必然面临数据丢失的风险,因此有了数据的持久化,以便在 Redis 重启后能恢复数据。 Redis 支持的持久化方式有三种:RDB 持久化、AOF 持久化和 RDB-AOF 混合持久化。 3.1、RDB 持久化 RDB(Redis Database)是 Redis 默认采用的持久化方式,它的原理是 Redis 在后台 fork(复制)一个子进程,子进程将该时间的内存数据生成快照文件写入到磁盘中,创建一个经过压缩的 “.rdb” 二进制文件,存储各个数据库的键值对数据等信息。 RDB 持久化的触发方式有两种: 手动触发:通过 SAVE 或 BGSAVE 命令触发 RDB 持久化操作,创建“.rdb”文件; 自动触发:通过配置选项,让服务器在满足指定条件时自动执行 BGSAVE 命令。 RDB 持久化的优缺点: 优点:RDB生成紧凑压缩的二进制文件,体积小,恢复数据的速度非常快; 缺点:RDB 持久化没办法做到实时,BGSAVE 每次运行都要执行 fork 操作创建子进程,属于重量级操作,不宜频繁执行。 3.2、AOF 持久化 AOF 持久化的原理是记录了每次写入命令到 “.aof” 格式的文本文件,类似MySQL的binlog,当 Redis 重启时再重新执行 AOF 文件中的命令来恢复数据到内存中。 AOF的工作流程包括:命令写入(append)、文件同步(sync)、文件重写(rewrite)、重启加载(load)。 需要控制命令同步到硬盘的频率,即多久将命令写入到 AOF 文件中一次,就是同步策略 (appendfsync): appendfsync always:每次写操作后立即同步到磁盘。最安全,但性能最差; appendfsync everysec:每秒同步一次。折中方案,默认推荐,最多丢1秒数据,性能和安全平衡最好; appendfsync no:由操作系统决定同步时机。最快,但最不安全,但可能丢大量数据。 AOF 重写(Rewrite)机制:为了解决文件越来越大的问题,Redis会自动(或手动 BGREWRITEAOF)在后台重写 AOF 文件。只保留“当前数据集的最小命令集合”,去掉冗余操作(比如多次 SET 同一个 Key,只留最后一次)。重写操作由子进程完成,不会阻塞主进程。 AOF 持久化的优缺点: 优点: 数据安全性高,最多丢失一秒的数据; 生成的 “.aof” 格式的日志文件可读,便于调试和修复。 缺点: 文件通常比RDB大,恢复数据慢(因为需要逐条重新执行命令); 对性能有一定影响(尤其是 always 策略)。 3.3、RDB-AOF 混合持久化(Redis 4.0) RDB 持久化和 AOF 持久化都有各自鲜明的优缺点,因此从 Redis4.0 开始引入集成二者优点的持久化方式:RDB-AOF 混合持久化,这种方式的原理是将内存数据先以 RDB 格式写入一个快照到 AOF 文件头部,后续再把写命令追加到快照后面,恢复时先恢复数据的快照,再执行后面的追加命令。 与 AOF 持久化类似,RDB-AOF 混合持久化也具备同步策略和重写机制。 优势:兼顾了 RDB 持久化的加载速度和 AOF 持久化的数据安全,是 Redis 生产环境的首选方案。 四、Redis集群方案 4.1、单机模式(Standalone) 一台服务器上运行一个Redis实例(redis-server)。数据全部放在一台机器的内存里,所有读写操作都经过这个实例,没有任何备份或扩展。 优点:部署简单,适合开发、测试、小型项目;且性能最好,因为没有网络复制开销 缺点:机器宕机、Redis进程崩溃,都会导致服务完全不可用;数据量超过单机内存或 QPS 过高,就扛不住。 4.2、主从复制(Replication) 一个主节点(Master)负责处理写请求,多个从节点(Replica / Slave)实时复制主节点(Master)的数据,只负责处理读请求。整个集群的数据流向是单向的:Master -> Slave。 优点:结构简单,配置容易;从节点分担主节点读压力,极大提升 QPS 缺点:所有写操作都依赖主节点,写性能上限受限于主节点,无法水平扩展写能力;主节点故障需要人工干预切换,无法自动故障转移;异步复制可能导致轻微数据不一致 4.3、哨兵模式(Redis Sentinel) 哨兵模式基于主从模式扩展,主要解决主从模式中主节点(Master)宕机无法自动恢复的问题,以实现高可用。 在主从架构的基础上,引入一组独立的 Sentinel(哨兵)节点。Sentinel 不处理业务读写,只负责心跳检测主从节点,当主节点故障(客观下线),哨兵通过 Raft 协议投票选出一个新的主节点,并通知客户端和其他从节点。 主观下线与客观下线:一个 Sentinel 认为节点挂了是主观下线,半数以上 Sentinel 认为挂了才是客观下线,避免网络误判。 哨兵节点的数量至少为 3 个,且为奇数个,以避免脑裂问题,总体架构推荐至少 3 Sentinel + 1 Master + 2+ Replica。 优点:保留了主从模式的优点,依然支持读写分离,从节点分担读压力;实现了故障自动检测和切换 缺点:本质上还是主从模式,写性能上限受限于主节点,依然无法水平扩展写能力;且主从切换过程中,Redis 会短暂不可用 4.4、Redis Cluster Redis Cluster 是 Redis 官方提供的分布式解决方案,自 Redis 3.0 引入,旨在通过分片(Sharding)技术将数据分布在多个主节点上,解决单机写瓶颈和内存容量瓶颈。 Redis Cluster 将整个数据集分成 16384 个哈希槽(Hash Slot)。每个键通过 CRC16(key) % 16384 计算后,决定落在哪个槽位。集群中存在多个主节点(Master),每个主节点负责一段连续的槽位区间(如 0-5000、5001-10000、10001-16383),并且可挂载一个或多个从节点(Replica)作为数据备份实现高可用。节点之间通过 Gossip 协议相互通信,自动发现新节点、传播故障信息、协调槽位迁移。客户端连接任意一个节点,若请求的数据不在当前节点,节点会返回正确的目标节点地址(MOVED 重定向),智能客户端根据新的目标节点地址自动跳转到正确的节点。 优点: 数据容量水平扩展:总数据量不再受限于单机内存,集群总容量随节点增加而线性增长 写性能水平扩展:写操作分散到多个主节点,增加节点即可线性提升写吞吐量,彻底突破单机写瓶颈 内置高可用:主节点故障自动转移,无需人工干预,比哨兵 Sentinel 更快 故障隔离更好:单个节点故障只影响部分数据,只影响它负责的槽位,其他数据仍可正常读写 缺点: 复杂度高:搭建、监控、运维更麻烦(需要处理 slot 迁移、热点 slot 等) 客户端要求高:必须用支持Cluster的客户端(Redisson、go-redis等),否则需要处理MOVED重定向 批量操作受限:不支持跨节点的事务、和多 Key 操作(除非 Key 带有相同的 {hashtag},强制将其分配到同一槽位) 资源消耗更高:至少6个节点起步 数据倾斜风险:热点key可能集中在某个节点,需要优化key设计 五、应用场景 5.1 数据库 Redis 可以作为主数据库使用,适用于对性能要求高、数据量相对较小的场景,如会话存储、实时排行榜等。 5.2 系统缓存 Redis 最常见的用途是作为缓存层,通过缓存热点数据,可以减少对数据库的查询次数,减轻数据库负担,显著提高应用访问速度。 5.3 消息队列 Redis 的列表和发布/订阅模式可以用于实现消息队列: 通过列表(List)的 LPUSH 和 RPOP 命令可以实现简单的消息队列功能 通过发布/订阅模式可以实现更复杂的消息分发和订阅功能 5.4 计数器和排行榜 Redis 的原子操作和有序集合(ZSet)非常适合实现计数器和排行榜功能,通过为每个元素关联一个分数,可以方便地实现排序和统计功能,如网站访问统计、游戏积分榜等。 5.5 分布式锁 Redis 的原子性操作可以用于实现分布式锁。通过 SETNX 命令可以尝试获取锁,如果获取成功则执行临界区代码;执行完毕后通过 DEL 命令释放锁,保证在分布式环境下的互斥访问。为了避免死锁,还可以为锁设置过期时间。 六、缓存相关问题 在高并发系统中,使用 Redis 做缓存时,最容易引发系统故障的三大“杀手”就是缓存穿透、缓存击穿和缓存雪崩。 6.1、缓存穿透 客户端请求的数据在 Redis 中不存在(缓存未命中),同时在数据库中也不存在。每次请求都会直接打到数据库。如果是恶意构造的大量无效请求(比如 userId = -1、随机长字符串、已删除的商品 ID),数据库瞬间就被打崩。 核心特点: 查询的是“根本不存在”的数据。 缓存和数据库都无法命中,请求每次都穿透。 极易被恶意攻击利用(也叫恶意穿透)。 常见场景: 接口未做参数校验,攻击者用不存在的 ID 疯狂刷接口。 查询已下架或从未存在过的商品 / 用户详情。 解决方式: 接口层参数校验(第一道防线):对请求参数做基础合法性检查(格式、长度等),提前过滤无效请求 缓存空对象(空值缓存)(最常用、简单有效):当数据库返回 null/空时,也在 Redis 中缓存一个空结果,并设置较短的过期时间(建议 30 秒 ~ 5 分钟,避免长期占用内存) 布隆过滤器(Bloom Filter):在 Redis 前加一层布隆过滤器,提前判断该 Key 是否可能存在,如果肯定不存在,直接返回 null,不查 Redis 和数据库,如果可能存在,再走正常缓存流程。 6.2、缓存击穿 某个高并发热点 Key 的缓存突然过期(或被删除),而此时有大量并发请求同时到达。所有请求发现缓存 miss,同时去查数据库,导致数据库瞬时压力暴增,甚至宕机。 核心特点: 问题发生在单个热点 Key 过期瞬间。 查询的是真实存在且非常热门的数据。 影响虽然是单个 Key,但并发量极大,破坏力强。 常见场景: 秒杀商品的库存/详情 Key 刚好过期。 热门新闻、明星话题、爆款商品的缓存到期。 大促期间某个热卖品的缓存失效。 解决方式(核心:只让一个请求去查数据库,其他等待或用旧数据): 缓存未命中时,先尝试获取分布式锁。只有拿到锁的线程才允许查询数据库,其他线程等待或短暂重试。 不依赖 Redis 的原生 expire,而是把“过期时间”字段存到 Value 里(例如 JSON 中加一个 expireTime)。用户请求时,如果发现逻辑已过期,后台异步线程去刷新数据,用户先返回旧数据 热点数据永不过期 + 后台刷新 对极重要热点 Key 不设过期时间,或设很长 TTL,由定时任务/消息队列后台定期刷新 6.3、缓存雪崩 大量缓存 Key 在同一时间段内集体过期(或 Redis 实例宕机),导致海量请求同时穿透到数据库,数据库压力骤增,甚至引发连锁反应导致系统雪崩。 核心特点: 影响的是大量 Key(而非单个)。 通常发生在同一时刻大量 Key 同时失效。 后果最严重,可能导致整个服务不可用。 常见场景: 所有 Key 统一设置相同过期时间(如全设 30 分钟)。 大促活动结束,大量活动缓存同时到期。 Redis 重启或故障(无持久化时,所有缓存瞬间丢失)。 解决方式: 最简单、最有效:设置过期时间时加上随机偏移,避免大量 Key 同时到期 缓存预热 + 永不过期策略:提前把热点数据加载到缓存;核心数据可永不过期,由后台异步更新 多级缓存:第一层用 Caffeine/Guava 本地缓存挡住大部分请求,Redis 作为二级,数据库作为最后防线 熔断降级 + 限流 + 快速失败:使用 Sentinel / Hystrix / Resilience4j 等框架:当数据库压力过大时,快速熔断,返回兜底数据(“系统繁忙,请稍后重试”或缓存的旧数据)
2024年03月07日
3,209 阅读
0 评论
3 点赞
2023-11-03
Docker学习笔记
目录: 一、Docker 1.1、什么是 Docker 1.2、安装 Docker 1.3、更改镜像源 二、基本概念 2.1、镜像(Image) 2.2、容器(Container) 2.3、标签(Tag) 2.4、仓库(Repository)与注册中心(Registry) 二、命令 2.1、镜像管理 从 Dockerfile 构建镜像: 从远程仓库拉取某个镜像: 将本地镜像推送到远程仓库 列出本地所有镜像 列出与关键词匹配的本地镜像 查看镜像的详细信息(以 JSON 格式输出) 查看镜像的分层历史(各层的创建指令和大小) 删除本地镜像文件 删除所有悬空镜像 删除所有未被任何容器使用的镜像 将镜像保存为 tar 文件(一般用于离线迁移) 从 tar 文件加载镜像到本地 给镜像打标签: 2.2、容器启停 创建并启动容器 只创建容器,不启动 启动已经创建的容器: 在运行的容器中执行命令 停止运行中的容器: 强制停止容器: 重启容器: 2.3、容器管理 查看运行中的容器 删除已停止的容器 删除所有已停止容器 查看容器的标准输出和错误日志: 查看容器内运行的进程 试试监控容器的 CPU、内存、网络等资源使用情况 在主机与容器之间复制文件 导出容器为 tar 文件: 从 tar 文件导入为镜像: 2.4、标签管理 环境:ubuntu 24.04 一、Docker 1.1、什么是 Docker Docker是一个开源平台,用于将应用程序及其所有依赖项打包成标准的容器,从而实现“一次构建,到处运行”。 Docker 与传统虚拟机(Virtual Machine,VM)都是实现环境隔离和应用封装的技术,但它们在架构、性能、资源利用和使用场景上有本质区别。 Docker 容器除了运行其中的应用外,基本不消耗额外的系统资源,保证应用性能的同时,尽量减少系统开销。 传统虚拟机方式运行 N 个不同的应用就要起 N 个不同的虚拟机(每个虚拟机都要单独分配独占的内存、磁盘等资源),而 Docker 只需要启动 N 个隔离的“很薄的”容器,并将应用放进容器内即可。应用获得的是接近原生的运行性能。 对比维度 Docker(容器) 虚拟机(VM) 操作系统 共享宿主机内核 每个 VM 一个完整 OS 启动速度 秒级(0.1~2s) 分钟级 镜像大小 MB ~ 几百 MB GB 级 资源开销 极低 高 性能 接近裸机 有一定损耗 隔离性 进程级隔离 硬件级隔离 安全性 较弱(共享内核) 更强 可移植性 非常强 一般 运维成本 低 高 密度 单机可跑数百容器 单机只能跑少量 VM 虚拟机 = 在硬件上虚拟出一整台“电脑”,解决的是“硬件虚拟化”问题 Docker = 在操作系统上隔离出一个“进程运行环境”,解决的是“应用交付与环境一致性”问题 可以粗略类比为: 虚拟机:每个租户 独立一套房子(含水电) Docker:每个租户 独立一个房间,共用整栋楼的水电 Docker 官网:Docker: Accelerated Container Application Development Docker 官方文档:Manuals | Docker Docs 国内中文翻译文档:Docker中文文档(Docker官方文档,Docker官方教程) 1.2、安装 Docker 使用官方安装脚本: # 下载并执行Docker官方安装脚本 curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh 或者通过添加 Docker 软件源安装: sudo apt-get update sudo apt-get install ca-certificates curl -y sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://mirrors.cloud.tencent.com/docker-ce/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://mirrors.cloud.tencent.com/docker-ce/linux/ubuntu/ \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 用上面任何一种方法安装完成 Docker 之后启动: # 启动Docker服务 sudo systemctl start docker # 设置开机自启 sudo systemctl enable docker 执行以下命令,检查安装结果: sudo docker info 输出版本信息则说明安装成功。 1.3、更改镜像源 需要说明的是,以往在国内使用很普遍的腾讯云、阿里云和各个大学的 Docker 镜像站点自从 2024 年之后都陆续不可用,现在可使用的镜像源已经很少,也不确定能用多久, 开发者镜像 - 免费手机工具 站长工具 源代码查看器 Docker镜像极速下载服务 - 毫秒镜像 Docker 镜像免费公共测试访问入口 | 轩辕镜像免费版 而在国内买了云服务商的服务器的话,可以使用其提供的内网 Docker 镜像,不过只能在其服务器上使用而无法在外网机器上使用。 腾讯云:腾讯云 - 安装 Docker 并配置镜像加速源,阿里云:阿里云 - 容器镜像服务,文档中明确写 https://mirror.ccs.tencentyun.com 只支持内网访问,不再支持外网域名访问加速。 找到配置镜像源的文件(如果文件不存在请新建) /etc/docker/daemon.json ,写入如下内容: { "registry-mirrors" : [ "https://docker.m.daocloud.io", "https://docker.xuanyuan.me", "https://docker.1ms.run" ] } 之后重新启动服务: sudo systemctl daemon-reload sudo systemctl restart docker 执行 docker info 命令检查配置是否成功,如果输出内容中 Registry Mirrors 一项变成了配置的镜像源,则说明更改镜像源成功。 二、基本概念 2.1、镜像(Image) 镜像是一个用于创建容器(Container)只读模板,可以粗略理解为面向对象编程中的“类”(Class),其包含了运行一个软件所需的代码、运行环境、库、环境变量和配置文件等。 镜像具有如下特点: 不可变:镜像一旦构建完成,内容便默认不可修改 分层结构:镜像采用联合文件系统(UnionFS),以分层方式组织文件,使得多个镜像可以共享相同的基础层,从而节省空间 可分发:镜像可以被推送到远程注册中心(如 Docker Hub)或从注册中心拉取,实现跨平台使用 2.2、容器(Container) 容器时镜像的运行实例,类似于“对象”,是镜像这个“类”的一个具体实例。当启动一个镜像时,Docker 会基于该镜像创建一个容器,并在其中运行应用程序(例如Nginx、MySQL等)。 容器的特点: 可读写:虽然镜像不可修改,但容器在镜像的基础上增加了一个可写层,所有对系统的修改都发生在这个层中。 具有生命周期:可以启动、停止、暂停、删除。 资源隔离:容器之间默认隔离无法交互的,通过 Linux 的 Namespace 和 Cgroups 技术实现进程、网络、文件系统的资源的隔离与限制。 2.3、标签(Tag) 标签用于标识镜像的特定版本或变体。一个镜像可以有多个标签,但它们可能指向同一个镜像 ID(即内容相同)。 如果不指定标签,默认使用 latest 注意:latest 并不一定代表“最新稳定版”,它只是一个约定俗成的标签,实际含义由镜像维护者决定。 2.4、仓库(Repository)与注册中心(Registry) 仓库(Repository):存储同一软件不同标签(版本)镜像的集合。例如 library/nginx 是 Docker Hub 上 nginx 的官方仓库。 注册中心(Registry):存放多个仓库的服务。最常见的是 Docker Hub,也可以搭建私有 Registry(如 Harbor、GitLab Container Registry)。 示例完整镜像名: docker.io/library/nginx:1.25 其中: docker.io:Registry 地址(默认可省略) library:命名空间(官方镜像通常省略为 nginx) nginx:仓库名 1.25:标签 二、命令 2.1、镜像管理 Dockerhub 地址:Docker Hub Container Image Library | App Containerization 国内镜像: 从 Dockerfile 构建镜像: 参数: -t 指定镜像标签 --file 指定 Dockerfile 路径,不存在此参数时默认从当前路径下构建 docker build -t <tag> ./ 从远程仓库拉取某个镜像: 镜像标签 <tag> 可省略,默认使用 latest docker pull <image><:tag> 如果没有配置镜像源只是想要临时通过某个(docker.xuanyuan.me)镜像源拉去镜像: docker pull docker.xuanyuan.me/<image><:tag> 将本地镜像推送到远程仓库 docker push <image> 列出本地所有镜像 参数: -a 查看所有镜像 -q 只显示镜像 ID docker image list # 等价于: docker image ls # 等价于: docker images 列出与关键词匹配的本地镜像 docker image list <关键词> # 等价于: docker image ls <关键词> # 等价于: docker images <关键词> 查看镜像的详细信息(以 JSON 格式输出) docker image inspect <image> 查看镜像的分层历史(各层的创建指令和大小) docker image history <image> 删除本地镜像文件 参数: -f 强制删除 -a 删除所有未使用镜像 <image> 可以是文件名,也可以是镜像 ID docker image rm <image> # 简写: docker rmi <image> 注意:当存在依赖于该镜像创建的容器时,镜像文件默认是无法被删除的。尽管可以使用 -f 参数来强制删除一个存在容器依赖的镜像,但并不推荐。正确的做法是,先删除依赖该镜像的所有容器之后再删除镜像。 删除所有悬空镜像 悬空镜像:Dangling Images,即没有标签且未被容器引用的镜像,通常出现在重新构建同名镜像后,旧镜像失去标签但仍存在) docker image prune 删除所有未被任何容器使用的镜像 docker image prune -a 将镜像保存为 tar 文件(一般用于离线迁移) docker save -o <file.tar> <image> 从 tar 文件加载镜像到本地 docker load -i <file.tar> 给镜像打标签: docker tag <image> <tag>qq 2.2、容器启停 Docker 运行容器前需要本地存在对应的镜像(Image), 如果本地不存在该镜像,Docker 会尝试从配置文件(/etc/docker/daemon.json)的注册中心中下载。 创建并启动容器 参数: -d 后台运行 -it 交互式终端 --name 指定容器名 -p host:container,端口映射 -v /host:/container,卷挂载 --restart 重启策略 docker run [OPTIONS] <image> 例如: docker run -d --name nginx-app-test -p 8080:80 nginx 只创建容器,不启动 参数: --name 容器名 -v 卷挂载 docker create -i <container> --name <name> 启动已经创建的容器: 参数:-i 交互式启动 docker start -i <container> 在运行的容器中执行命令 参数: -it 交互式终端 docker exec -it <container> <command> 停止运行中的容器: 优雅停止容器,会向容器发送 SIGTERM,等待退出 参数:-t 停止等待时间(单位为秒) docker stop -t <time> 强制停止容器: 会向容器发送 SIGKILL。同Linux的kill -9 docker kill <container> 重启容器: 参数:-t 停止等待时间(单位为秒) docker restart <container> 2.3、容器管理 查看运行中的容器 参数: -a 查看所有容器 -q 只显示容器ID docker ps # 等价于: docker container ls 删除已停止的容器 docker rm <container> 若要删除的容器仍在运行,需加上参数 -f: docker rm -f <container> 删除所有已停止容器 docker container prune 查看容器的标准输出和错误日志: 参数: -f 跟踪日志 --tail <n> 显示最后 n 行 docker logs <container> 查看容器内运行的进程 docker top <container> 试试监控容器的 CPU、内存、网络等资源使用情况 docker stats <container> 在主机与容器之间复制文件 docker cp ... 从主机复制文件到容器: docker cp <主机路径> <容器名或ID>:<容器内路径> # 例如: docker cp ./app.conf myapp:/etc/app.conf 从容器复制文件到主机: docker cp <容器名或ID>:<容器内路径> <主机路径> # 例如: docker cp myapp:/var/log/app.log ./ 导出容器为 tar 文件: 参数:-o 指定输出文件名 docker export <container> -o <file.tar> 从 tar 文件导入为镜像: docker import <file.tar> 2.4、标签管理 给镜像打标签: docker tag <image> <tag> 删除特定标签: docker rmi <image>:<tag> 参考文档: Docker - 马燕龙个人博客 云服务器 搭建 Docker_腾讯云 Docker 教程 | 菜鸟教程
2023年11月03日
4,400 阅读
0 评论
2 点赞
2022-06-24
关于华为悦盒EC6108V9的一种比较另类的救砖方法
目录: 一、前期准备 1.1、USB转TTL模块 1.2、主板上焊接针脚 二、HiTool 线刷 2.1、跑码 2.2、开始刷机 2.3、系统启动 三、破解 四、后续 五、碰到的坑 5.1、关于在刷机前配置板端ip的问题 5.2、发送数据帧失败,请检查串口连接是否正常 我手上的这个电视盒子,是2016 年家里办宽带的时候电信送的,在 2020 年疫情期间被我刷机给变砖了 :@(狂汗) 。盒子接上电源开机的时候电视完全没有反应,左边绿灯与中间红灯常亮,Recovery 进不去,使用遥控器也没有任何反应。当时还抱着卡刷救砖的希望,尝试了网上说的短接 J15 针脚强制刷机,结果一点用也没有,然后淘宝又买了 TTL 转 USB 模块准备线刷救砖,买来还没操作就被网上那些教程吓住了,再加上那段时间打游戏也打的火热,这个事也就被无限期搁置了(拖延症晚期患者 ::(酸爽) )。后来直接就买了斐讯 N1,转 N1 的阵营了,一直用到了现在。直到前两天在 B 站刷到这个将华为悦盒刷成 Ubuntu 服务器的视频: {bilibili bvid="BV1Y3411V77b" page=""/} 这个视频让我又有了天晴了雨停了我又觉得我行了的感觉,正好这几天毕业答辩的事也忙完闲下来了,所以花了一(亿)点点时间做一下最后救砖的尝试,当然最后算是成功了,所以写这篇博客来记录一下,为同样变砖的同志提供一个救砖的参考方法。 事先在此说明:这是在华为悦盒使用卡刷、强制卡刷都没有办法救砖时,并且还找不到更适配的固件的情况下,我摸索出来的一个比较另类的救砖方法,目前这个方法只在我的这个盒子上成功了,其他的盒子还没有试过(也没有条件尝试),因此并不能保证这个方法的通用性。在 ZNDS 论坛上有个类似的方式是这个(不过我并没有尝试成功)。 如果你是老司机了,那么本文救砖的方式可以试一试,救砖成功那么恭喜你,如果尝试多次还不成功的话那建议再去找找别的方式,相反如果你此前没有过刷机的经验或者经验不足,且无法独立解决报错问题,那么强烈建议慎用本文所提供的方法!! 在刷机过程中造成的损坏恕博主不能负责!!! 在刷机过程中造成的损坏恕博主不能负责!!! 在刷机过程中造成的损坏恕博主不能负责!!! 首先说明一下我手上这个盒子的型号规格:型号为华为悦盒 EC6108V9,用的 CPU 型号是 Hi3798MV100,存储规格为 1G + 4G。 型号信息: 主板布局: 一、前期准备 1.1、USB转TTL模块 线刷需要 USB 转 TTL 模块,淘宝几块钱就能买到还送杜邦线,就是这玩意: 无脑买型号是 CH340 的就行,看网上说大部分 CH340 免驱动,但是我手上这个不是,所以建议到时候跟卖家问清楚再买。甚至只要你愿意多花几块钱,还可以买到土豪金配色的(刷机时有神秘 buff 加成 :@(吐舌) )。 刷机之前检验一下 TTL 模块是否需要驱动,模块插到电脑上之后,在设备管理器 -> 端口选项下面会显示硬件名称跟端口: 如果不显示的话应该是没有驱动,打开 Win10 更新 -> 检查更新,有更新的话就点击安装就行: 1.2、主板上焊接针脚 此外还需要在电视盒子主板的 GND、RX、TX 这三个脚位上焊接三个排针,方便用杜邦线连接 TTL 模块,最后效果是这样: 需要注意的是盒子主板与 TTL 模块针脚连接的对应关系:模块的 GND 脚连接主板的 GND 脚,模块的 RX 与 TX 与主板要反着连,即模块的 RX 连接主板的 TX,TX 连接主板的 RX。 二、HiTool 线刷 线刷工具用到 HiTool,下载地址放在文末。这个软件需要 Java 环境,如果打开报错的话就配置一下 Java 环境,配置方法参考这篇博客:Win10系统配置Java开发环境 - Roookie博客 | 记录 · 收纳 · 分享 (wlplove.com)。 软件启动之后,首先要选择芯片平台,我选择了“Hi3798MV100”,然后进入软件,打开 “HiBurn”模块,这就是线刷需要用到的功能。 2.1、跑码 开始刷机前,简单介绍一下“跑码”。 这个操作在刷机失败之后查找错误原因是非常有用的。我刚开始查资料时看到网上很多人都提到了“跑码”这个词,弄得我一脸懵逼,不过跟着操作了以后就大概理解什么意思了,简单来说,跑码会在硬件启动过程中输出硬件信息、一些提示信息,也会在出现错误的时候输出错误代码,对定位硬件启动的错误有很大的帮助。 在软件菜单栏上点击这个图标打开终端窗口: 在打开的终端窗口上前三个按钮的功能分别是连接、断开连接、设置: 连接前先点击设置按钮配置一下连接端口,端口就是前面设备管理器里显示的 TTL 模块的端口: 设置完成之后,点击连接按钮,然后给盒子通电,首先会在终端输出当前设备的硬件信息,然后盒子会尝试启动系统,同时在终端输出启动过程中的提示信息,比如刚开始我的盒子就是一直循环输出同一段报错信息: 看报错信息应该是在尝试启动的过程中卡在了 fastboot 上。 2.2、开始刷机 相关文件链接已放在文末。 给盒子断电并连接网线,在软件里配置本机与盒子的 IP 地址: 串口:设备管理器里显示的 TTL 模块的连接端口 服务器 IP:本机的 IP 地址 IP 地址:网络盒子的 IP,可以不用改 网关:路由器的 IP 然后找到下方的“烧写eMMC”: 选择刷机文件目录下面的 “xml” 分区表文件,下方表格中就会自动导入相关的分区镜像,选择要刷入的分区文件,点击烧写,当下面控制台出来提示以后给盒子通电,然后等着刷机完成就行了。 这就是整个刷机步骤,很简单吧? ::(呵呵) 但是吧,实操就不是这样了,因为适配的线刷固件是个大问题。 我找了不少的搜索引擎、论坛、网盘,只找到了几个 CA 版四川电信的盒子线刷固件,并且都是适配 8G 版本的华为悦盒,对比了一下文件结构和大小,最后发现有好几个是同一份文件 ::(挖鼻) ,其他还找到了很多诸如“update.zip”的这类卡刷包以及线刷的 fastboot、recovery 等文件,然而卡刷包对于线刷并没有什么卵用。 鉴于找不到完全适配的线刷固件,所以只能凑活用这个四川电信的 CA 固件了。刚开始我还没有什么思路,我就抱着死马当活马医的心态想着给盒子刷这个固件试试,看能不能刷进去。 点击“刷写”不到一分钟,不出意外地控制台就报错了: 第一点提示说是“烧写的 Fastboot 镜像与当前单板型号或 Flash 不匹配导致”,那就可能是 fastboot 文件的问题?此时我灵机一动,正好有之前下载到的适配我这个盒子的 fastboot 文件啊,于是我就用下载到的 fastboot 文件替换了刷机固件下的 fastboot 文件,然后继续刷机。 果然,这下子没有报错,显示开始刷机了,刷的时间比较长,四十几分钟后提示烧写完成: 2.3、系统启动 我一下来了精神,没想到瞎猫碰到死耗子,竟然真就给刷进去了。我连忙给盒子接到电视上,找出吃灰了两年的遥控器,塞两节电池之后开始开机,果然就见到了那个熟悉的界面: 接下来系统开始进行初始化走进度条,就在我以为救砖成功的时候,进度条卡在了 50% 不动弹了,提示“接入网络故障”: 可问题是我已经给盒子插了网线了啊,莫非是要连接光猫上的“iTV”接口?试了连接到光猫上的网口还是卡在这里,我想着要不然恢复一遍出厂设置吧,结果恢复完之后提示信息又变成“通路故障或AAA认证失败”了: 行吧,虽然依然没进去系统,至少比变砖强点。 这时候想起网上不少的刷机教程,都提到了卡刷这个方式,于是我也下了好几个卡刷包想着试一试卡刷,将刷机文件在硬盘根目录与“upgrade”文件夹各放了一份,开机通过电源键进入 Recovery,选择 “Apply update from external storage(从外部存储更新)”选项卡刷,但是要么直接提示“更新失败”,要么就是好不容易开始更新了,结果一会之后卡进度条还是提示更新失败。 起初我还怀疑是不是刷机包给下错了,但我试了好几个之后都不行之后就大概确定不是了,再尝试了一下短接 J15 针脚强刷,依然没有任何反应,卡在这了。 后面看到这一篇帖子华为悦盒通用刷机救砖破解教程_华为悦盒/华为盒子 ROM固件_小白刷机网 (xiaobaishuaji.com)中提到的更新了签名会导致卡刷失败,按这里面提到的方式刷机前进行一次“三清”,这不清不重要,清完之后启动的时候结果直接变砖无法启动 :@(喷血) ,.......无奈。 三、破解 在这个界面卡了好长时间,结果发现其实按遥控器上的设置建是可以进入设置菜单的,但需要操作码,网上查了一下,我用“6321”进去了。 操作码大概可能有这么多个:一般是运营商客服电话(10000、10086、10010),除此之外还有:8288、6321、2878、3008、8005、1301、123456等,都没有用的话,可以试试专家模式密码189189 来自:华为悦盒ec6108v9高级设置操作码常用汇总 到设置界面看了一下设备信息,我大概知道为什么卡刷失败了,因为这个盒子从 pub 版本被刷成 CA 版的了: 到这里的话,我对卡刷是不报什么希望了。但是好在盒子已经能启动了,虽然还没办法通过系统自带的 IPTV 是进入桌面,但是能进设置界面啊,那就存在破解安装当贝桌面的可能性,然后绕过 IPTV 进入系统当贝桌面。 关于破解华为悦盒安装第三方桌面的步骤,在 ZNDS 论坛的这篇帖子 最新华为悦盒V9_V9U_V9E_V9A_V8通用傻瓜式刷机包!_华为悦盒_ZNDS 里已经写挺详细的,我就懒得写了,直接参考这篇帖子就可以。破解工具在文末。 接下来就比较顺了,破解果然是可以的,破解重启之后就有选择桌面的弹窗了: 四、后续 破解完成之后,安装了几个软件用了一会,体验吧只能说很一般,目前主流的视频软件操作起来卡卡的,盒子发热还比较严重(虽然不摸是感觉不出来的),并且遥控器上的首页按键是没有响应的(估计可能是固件问题)。当然还是不能用太高的要求来评价它,毕竟盒子是七八年前了,实在太老了,就连 CPU 还是 32 位,性能放在今天来看实在是有点捉急。综合来说,安装一个直播软件来看电视直播是比较好的选择。 到这我就准备要收拾家伙事儿不搞了,然后我就在网上又发现了一个线刷固件,适用于 8G 版本盒子,我想着最后尝试一下吧,下载替换 fastboot 文件一气呵成,刷固件依然花了四十多分钟。启动之后还是卡在了运营商自带的 IPTV 界面,进入设置界面查看设备信息之后,发现这个固件是浙江的移动百盒固件,不过是 PUB 版本的,还行,比之前的那个 CA 固件强,用卡刷的方式尝试了一下刷第三方系统,是可以正常刷第三方系统的。因此把这个固件下载链接也放在了文末,有需要的话用这个也行。 最后用卡刷的方式刷入电信的原厂固件,再破解就行了,本来想着还能再认证一下使用 IPTV 来看电视的,但是我尝试之后发现这边的 IPTV 认证地址竟然都挂了,所以就干脆放弃了。 五、碰到的坑 这里记录几个我在刷机过程中碰到的问题,以供参考。碰到问题与报错不要怕,仔细查看分析控制台输出与报错,多猜多尝试。 5.1、关于在刷机前配置板端ip的问题 刚开始我并没有注意到这个问题,所以开始刷机时都是报错: 第一次我还不信邪,多尝试了几次之后我就意识到了问题:我的电视盒子都开不了机了,连接到网线之后路由器后台根本识别不到这个设备,那么现在设置的 IP 地址肯定是无效的呀。 这就是我碰到的第一个大坑,研究了一会我的解决办法是,给家里吃灰的一个旧路由器插上电,用这个路由器来组网,这个局域网里面就只有盒子和笔记本两台设备,修改 HiToll 设置里的 IP 地址,板端 IP 同样随便设置(不能跟笔记本 IP 重复),这下刷机就可以了。 所以关于这个问题我的猜测是这样的(计算机网络学的不好,猜错了大佬轻喷 ::(小红脸) ):当局域网中设备比较多的时候,笔记本将刷机数据通过路由器给下一个结点传输的时候,设备太多就导致无法确定目标传输设备,所以就会连接超时报错,相反如果只有两个设备的话,路由器传输数据时不是传输给盒子就是传输给笔记本,传输给笔记本应该是不太可能,所以就只能将数据传输给另一设备,这么阴差阳错地就完成了传输。 怎么莫名有种玄学的感觉?? ::(笑尿) 5.2、发送数据帧失败,请检查串口连接是否正常 刚点击刷写以后,控制这样报错: 以我的情况来看,是 fastboot 文件没有选对,换了适配设备的 fastboot 文件就不报错了。 文末再放上几个刷机查资料过程中发现的几个有用的帖子吧,供有需要的参考: 海思hi3798mv100机型通用twrp_华为悦盒_ZNDS 华为悦盒怎么进入REC模式升级双清恢复出厂设置最全方法_华为悦盒_ZNDS 河南移动cmcc-2 ch电视盒子Hi3798MV310线刷-卡刷-刷机-救砖 - 知乎 (zhihu.com) ec6108v9c刷回安卓看电视 - 海思机顶盒NAS社区 (histb.com) 关于hitool刷写... - 海思机顶盒NAS社区 (histb.com) 华为悦盒EC6108V9 CA高安版1+4G电信4K机顶盒拆机救砖文件TTL线刷 | 西安艾金达贸易有限公司 (aijinda.cn) 华为 悦盒EC6108V9_PUB-8G内存版线刷救砖软件下载_怎么安装第三方软件_应用APP下载_沙发管家 (shafa.com) 【已解决】 EC6108V9 等待boot启动超时,单板上无fastboot或当..._华为悦盒_ZNDS 文件下载链接: https://pan.baidu.com/s/1OZdZQRhWsE49pto_oAzbwQ?pwd=xy6m 提取码: xy6m
2022年06月24日
10,330 阅读
0 评论
6 点赞
2022-06-03
记录一下写论文过程中新学会的一些Word使用技巧
目录: 一、设置子标题 二、在不同的页设置不同的页眉/页码/页脚 三、添加的图片只占一行位置,无法完全显示出来 四、将文章中的英文、数字的字体格式一键设置为Times New Roman 五、在文档任意一个位置上开始之前的编号 六、快捷键: 七、绘制三线表 一、设置子标题 选中要设置为标题的行 开始 -> 段落上面找到多级标题的图标: 选择一个设置的多级标题样式: 就出现了标题,然后更改标题级别使其成为子标题: 此时标题编号可能不是我们想要的,所以还需要调整一下标题编号: 标题更改成功: 二、在不同的页设置不同的页眉/页码/页脚 在需要页眉/页码/页脚页码的页上第一行文字前(或是上一页的最后一行)添加分页符(布局-> 分隔符 -> 下一页): 插入页眉/页码/页脚,此时插入的页眉/页码/页脚将以这个分页符为分界被分成了两部分,选中前面不用的页眉/页码/页脚,取消这个选项(引用 -> 链接到前一条页眉/页脚): 接下来删除不用的页眉/页码/页脚即可,还有多出来的分页符 三、添加的图片只占一行位置,无法完全显示出来 就像这样: 方框为图片实际占的位置,但显示效果上只占了一行,图片不能全部显示出来 原因:图片的段落格式中设置了行距为“固定值” 解决办法:选中图片,在段落格式设置中把行距设置为单倍行距就可以解决问题 四、将文章中的英文、数字的字体格式一键设置为Times New Roman ctrl + A 选中全文,在字体设置中分别设置中文字体与英文字体的格式即可 还有个简单粗暴的方法,选中全文将全部字体设置为Times New Roman即可,中文不会受到影响。 五、在文档任意一个位置上开始之前的编号 先将这个编号的样式设置成与上一个编号相同的样式与级别 右键 -> 设置编号值(光标要放在标题一行或者选中标题),选择“继续上一列表”,填写需要开始的编号 六、快捷键: 格式刷: ctrl + Shift + C 复制格式 ctrl + Shift + V 粘贴格式 重复上一次操作:F4 七、绘制三线表 选中整个表格,右键 -> 表格属性 -> 边框和底纹 只保留表格的上下框线,并设置其宽度为 0.75 磅 再选中表格的第一行,右键设置其下框线即可:
2022年06月03日
2,898 阅读
1 评论
0 点赞
2021-12-20
Spring Boot学习笔记
目录 一、Spring Boot简介 二、Spring Boot的主要特性 2.1、自动配置 2.2、起步依赖(Starters) 2.3、简化配置 2.4、内置服务器 2.5、无代码生成与 XML 配置 三、Spring Boot的配置 3.1、配置文件类型 3.1.1、application.properties 3.1.2、application.yaml(或.yml) 3.2、配置优先级 3.2.1、配置文件位置的优先级 3.2.2、配置文件格式的优先级 3.3、读取配置文件的方式 3.3.1、使用@Value注解 3.3.2、使用Environment对象 3.3.3、将部分配置封装成实体类 3.4、多环境开发时配置文件的切换 3.5、yml配置文件示例 四、整合MyBatis 4.1、添加起步依赖 4.2、添加配置 4.3、扫描Mapper接口 五、使用Maven打包Spring Boot项目 一、Spring Boot简介 Spring Boot 是 Spring 框架的一个扩展,旨在简化 Spring 应用的初始搭建和开发过程。它通过自动配置和约定优于配置的原则,提供一个开箱即用的应用程序架构,能够快速构建独立运行的 Spring 应用,开发者基于 Spring Boot 的预置结构继续开发,省时省力。 Spring Boot 官方文档:https://docs.spring.io/spring-boot/index.html Spring 中文网翻译的 Spring Boot 中文文档:https://springdoc.cn/spring-boot 通过 IDEA 创建 Spring Boot 项目: 添加核心依赖 Spring Web: 除了用 IDEA 创建 Spring Boot 项目,也可以直接访问 Spring Boot 项目生成网站,设定项目的元数据(如项目类型、语言、Spring Boot 版本等)之后,创建好的项目就会以压缩包的格式下载到本地,解压导入到开发工具即可。 Spring Boot 项目生成网站: https://start.spring.io/:Spring Boot 官方的项目生成工具,Java 版本和 Spring Boot 版本都比较新,国内有可能访问不了或访问速度比较慢 https://start.springboot.io/:官方项目生成工具的国内镜像,Java 版本和 Spring Boot 版本与官网一致,国内都能访问 https://start.aliyun.com/:从域名能看出来是阿里云镜像,国内都能访问,而且能用旧的 Java 版本和 Spring Boot 版本生成项目 一个典型的 Spring Boot 项目结构如下: SpringBoot-project/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ └── MyApplication.java │ │ └── resources/ │ │ ├── application.properties │ │ └── static/ │ │ └── templates/ │ └── test/ │ └── java/ │ └── com/ │ └── example/ │ └── MyApplicationTests.java ├── pom.xml └── README.md MyApplication.java:Spring Boot 应用的主类,使用 @SpringBootApplication 注解标记。 application.properties:Spring Boot 应用的配置文件,可以配置各种属性。 static/:存放静态资源文件,例如 HTML、CSS、JavaScript 文件等。 templates/:存放模板文件,例如 Thymeleaf、Freemarker 等。 二、Spring Boot的主要特性 2.1、自动配置 开发者无需手动配置大量 XML 或注解,Spring Boot 能够根据项目中添加的依赖,自动为应用程序生成合理的默认配置,减少手动配置。例如,在项目中添加了 spring-boot-starter-web 依赖,Spring Boot 会自动配置嵌入式的 Tomcat 服务器,并为 Spring MVC 设置好必要的组件,如 DispatcherServlet 等,无需开发者手动编写大量的配置代码。 2.2、起步依赖(Starters) 起步依赖是指对常用库和框架的依赖声明的集合,使用起步依赖可以一次性地添加相关的依赖和配置到项目中,大大简化了项目的依赖管理,减少因依赖冲突而导致的问题。 比如,spring-boot-starter-data-jpa 包含了使用 Spring Data JPA 进行数据库操作所需的所有依赖,包括 Hibernate、Spring Data JPA 和数据库连接池等。 2.3、简化配置 Spring Boot 使用 application.properties 或 application.yml 作为统一的配置文件,将应用程序的各种配置集中管理,并且提供默认配置,尽量减少开发者的手动配置。这些配置文件放置在项目的类路径下,Spring Boot 会自动加载并应用其中的配置。 并且 Spring Boot 支持多环境配置,允许开发者根据不同的运行环境(如开发、测试、生产)使用不同的配置文件。通过在配置文件名中使用 {profile} 占位符,如 application-dev.yml、application-prod.yml,并在 application.yml 中指定 spring.profiles.active 属性来激活相应的环境配置。 2.4、内置服务器 Spring Boot 支持嵌入式的服务器,如 Tomcat、Jetty 和 Undertow 等,开发者可以将应用程序打包成一个可执行的 JAR 文件,使用 java -jar 命令可启动服务,无需单独部署到外部的应用服务器上。 例如,在开发 Web 应用时,只需在 pom.xml 文件中添加 spring-boot-starter-web 依赖,Spring Boot 会自动配置并启动一个嵌入式的 Tomcat 服务器,让开发者可以快速进行开发和测试。 2.5、无代码生成与 XML 配置 Spring Boot 无需代码生成和 XML 配置,完全基于注解和 Java 配置。 三、Spring Boot的配置 3.1、配置文件类型 Spring Boot 支持两种主要的配置文件类型:application.properties 和 application.yaml(.yml)。 3.1.1、application.properties 这是传统的 Java 配置文件格式,每行定义一个属性,采用 key=value 的形式。例如: server.port=8080 spring.datasource.url=jdbc:mysql://localhost:3306/spring_db spring.datasource.username=root spring.datasource.password=password 3.1.2、application.yaml(或.yml) YAML(YAML Ain't Markup Language)是一种人类可读的数据序列化格式,它的特点包括: 使用缩进(空格数不限)表示层级关系(必须用空格缩进,不能使用 Tab) 使用 key: value 的形式表示键值对 支持列表、嵌套结构等复杂数据类型 例如: server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mydb username: root password: password 关于 .yaml 和 .yml 的区别: 在 Spring Boot 项目中,YAML 文件通常以 .yaml 或 .yml 作为文件扩展名。 这两种扩展名本质上是相同的,它们都表示 YAML 格式的文件。.yaml 是 YAML 文件的官方推荐扩展名,而 .yml 是 .yaml 的简写形式。在功能上两者完全等价,Spring Boot 对它们的解析方式没有任何区别,无论使用 .yaml 还是 .yml,Spring Boot 都能正确加载和解析配置文件。 如果同时存在 application.yaml 和 application.yml,Spring Boot 会优先加载 application.yml。 3.2、配置优先级 3.2.1、配置文件位置的优先级 优先级最高的配置是命令行参数,通过命令行传递的这个参数将会覆盖原配置: java -jar app.jar --server.port=9999 不同位置配置文件的优先级分别为: 项目根目录下的 /config 子目录中的配置文件 项目根目录中的配置文件 类路径下的 /config 包中的配置文件 类路径根目录中的配置文件 3.2.2、配置文件格式的优先级 如果在同一个目录下同时存在 application.properties、application.yml、application.yaml 这几个文件,那么 Spring Boot 对配置文件的加载优先级为: application.properties > application.yml > application.yaml 3.3、读取配置文件的方式 假设在 YAML 配置文件中存在如下配置: server: port: 8081 spring: datasource: url: jdbc:mysql://localhost:3306/spring_db?useSSL=false&serverTimezone=UTC username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver userInfo: name: 张三 age: 30 likes: - 游戏 - 健身 - 读书 3.3.1、使用@Value注解 @Value 注解可以直接将配置文件中的单个属性值注入到类的字段中。 @Component public class ConfiurationBean { // 简单属性 @Value("${spring.datasource.url}") private String url; // 数组 @Value("${userInfo.likes}") private String[] likes; // 数组里的其中一个属性 @Value("${userInfo.likes[1]}") private String like; // ... } 3.3.2、使用Environment对象 在 Spring Boot 中,Environment(org.springframework.core.env.Environment)是一个用于管理应用程序运行时环境配置信息的接口,它可以使开发者方便地获取和使用项目的配置参数,无论是来自 application.properties、application.yml 文件,还是系统环境变量、命令行参数等。 在需要使用 Environment 对象的类中,可以使用 @Autowired 注解将 Environment 对象自动注入进来,然后在类的方法中使用 getProperty() 方法获取配置信息。 @Component public class ConfiurationBean { @Autowired private Environment env; public void printProperty() { // 获取并使用属性 String port = env.getProperty("server.port"); System.out.println(port); } } 3.3.3、将部分配置封装成实体类 创建一个 Java 实体类,属性与配置项一一对应,使用 @ConfigurationProperties 注解将配置文件中的属性绑定到该 Java 对象上: @Component @ConfigurationProperties(prefix = "userInfo") public class UserInfo() { private String name; private int age; private String[] likes; // get方法、set方法、构造方法 } 3.4、多环境开发时配置文件的切换 在项目的开发中,常常需要在多个环境(开发环境 dev、生产环境 prod 等)部署项目,并且每个环境的配置可能都不太一样(如日志等级、数据库属性等),因此每个环境都有单独的一个配置文件。 Spring Boot 支持根据不同的环境加载不同的配置文件,常用的做法是建一个公共配置文件 application.properties 或 application.yml 来存放每个环境中相同的配置,再给每一个环境建一个单独的配置文件存放个性化配置,该配置文件的命名规则为 application-{profile}.properties 或 application-{profile}.yml。 例如开发环境的配置文件为 application-dev.yml,生产环境的配置文件为 application-prod.yml,则在公共配置文件 application.yml 中激活指定配置文件的方式为: spring: profiles: active: dev 也可以在启动项目 JAR 文件时通过命令行参数 spring.profiles.active 指定: java -jar app.jar --spring.profiles.active=dev 如果既在公共配置文件 application.yml/application.properties 中指定了激活哪个配置文件,又通过命令行参数指定,则最后生效的是命令行参数,因为命令行参数的优先级最高。 在 IDEA 中也可以直接指定命令行参数: 新版本 IDEA: 3.5、yml配置文件示例 server: port: 8081 spring: datasource: url: jdbc:mysql://127.0.0.1:3306/spring_db?useSSL=false&setUnicode=true&characterEncoding=utf8&serverTimezone=UTC username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver # 数据源用druid type: com.alibaba.druid.pool.DruidDataSource # MyBatis配置 mybatis: # 指定Mapper XML文件的位置 mapper-locations: classpath:mapper/*.xml # 实体类的包路径 type-aliases-package: com.example.demos.web configuration: # 开启驼峰命名映射 map-underscore-to-camel-case: true # 项目日志等级 logging: level: # 全局日志等级 root: info # 设置MyBatis的日志级别为DEBUG com.example.mapper: debug org.mybatis: debug org.apache.ibatis: debug # 自定义配置 四、整合MyBatis 4.1、添加起步依赖 新版 IDEA 中可以直接在 pom.xml 文件的 <dependencies> 标签旁边点击 “Add Starters” 添加 MyBatis 与 MySql 的起步依赖: 或者手动在 pom.xml 文件中添加 MyBatis 的起步依赖与 MySQL、Druid 的依赖坐标: <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.22</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> 4.2、添加配置 然后在配置文件中添加数据源与 MyBatis 配置: spring: datasource: url: jdbc:mysql://127.0.0.1:3306/spring_db?useSSL=false&setUnicode=true&characterEncoding=utf8&serverTimezone=UTC username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver # 使用druid数据源 type: com.alibaba.druid.pool.DruidDataSource # MyBatis配置 mybatis: # 指定Mapper XML文件的位置 mapper-locations: classpath:mapper/*.xml # 实体类的包路径 type-aliases-package: com.example.demos.web # 指定MyBatis的全局配置文件位置,可以进行更细颗粒度的MyBatis配置,不需要就不写 # config-location: classpath:mybatis-config.xml configuration: # 开启驼峰命名映射 map-underscore-to-camel-case: true # 项目日志等级 logging: level: # 全局日志等级 root: info # 设置MyBatis的日志级别为DEBUG com.example.mapper: debug org.mybatis: debug org.apache.ibatis: debug 4.3、扫描Mapper接口 最后在写接口的时候在上面用 @Mapper 注解标记一下这是 MyBatis 的 Mapper 接口: @Mapper public interface UserMapper { User select(); // ... } 或者直接在 Spring Boot 启动类上面用注解 @MapperScan() 进行全局配置扫描,指定 Mapper 接口类的包路径,Spring 会自动扫描该包下的所有接口,并将其注册为 MyBatis 的 Mapper 接口,无需在每个接口上都添加 @Mapper 注解。 @SpringBootApplication @MapperScan("com.example.mapper") public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } 至此,Spring Boot 框架整合 MyBatis 框架完成。 五、使用Maven打包Spring Boot项目 在 pom.xml 文件中,确保配置了 Spring Boot Maven 插件: <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.6.3</version> <configuration> <!-- 指定启动程序主类 --> <mainClass>com.example.demo.DemoApplication</mainClass> <skip>false</skip> </configuration> </plugin> </plugins> </build> 在项目根目录下运行打包的命令: # 其实是两条命令,mvn clean是清除项目target目录下的文件,mvn package打包命令,两个命令一起执行。 # -Dmaven.test.skip=true表示排除测试代码之后打包 mvn clean package -Dmaven.test.skip=true 或者在 IDEA 中依次操作 clean 与 package: 打包时,一般都要先进行 clean 操作清理项目之前构建过程中生成的临时文件和输出文件,使项目恢复到初始状态,目的是确保项目不会受到之前构建结果的影响,避免旧的文件干扰新的构建过程。 打包完成后,会生成两个 JAR 文件,其中一个后缀为 .original,只包含工程中的 Class,不包含依赖;另一个后缀为 .jar 的文件是 SpringBoot 打包插件创建的,包含了应用依赖和 Spring Boot 相关的 Class,可以直接运行。生成的 JAR 文件保存在 target 目录下,文件名为 {项目名称}-{版本号}.jar。例如:demo-0.0.1-SNAPSHOT.jar 使用以下命令运行打包好的 JAR 文件: java -jar demo-0.0.1-SNAPSHOT.jar 并且可以通过命令来指定部分参数: java -jar demo-0.0.1-SNAPSHOT.jar --server.port=9999 --spring.profiles.active=prod
2021年12月20日
2,916 阅读
0 评论
2 点赞
2021-12-11
常用的一些Nginx配置
目录: 1、监听端口 2、日志功能 2.1、访问日志 2.2、错误日志 3、配置域名 4、静态资源 5、重定向 5.1、网站根目录重定向 5.2、特定目录重定向 6、反向代理 7、负载均衡 8、SSL配置 1、监听端口 server { # 用于HTTP协议 listen 80; # 用于HTTPS协议 listen 443 ssl; # 用于HTTP2 listen 443 ssl http2; # 使用IPv6监听80端口 listen [::]:80; # 仅使用IPv6 listen [::]:80 ipv6only=on; } 2、日志功能 Nginx的日志分为两种:access_log (访问日志)和 error_log(错误日志)。 对于日志的格式,Nginx提供了一种名为 combine 的格式,如果没有明确指定日志格式则默认使用该格式。 如果不使用 combine 格式的话,可以使用 log_format 指令来自定义,其具体用法参考Nginx日志配置详解或者官方文档:HttpLog模块-Nginx中文文档。 2.1、访问日志 访问日志主要记录客户端的请求,客户端向Nginx服务器发起的每一次请求都记录在这里。客户端IP,浏览器信息,referer,请求处理时间,请求URL等都可以在访问日志中得到。 server { # 访问日志文件的写入位置,相对或绝对路径 access_log /path/to/file.log; # 选择访问日志功能的开启(on)或关闭(off) access_log on; } 2.2、错误日志 错误日志记录服务器和请求处理过程中的错误信息。 server{ # 错误日志的写入位置 error_log logs/error.log error; # 错误日志的级别,分为 debug, info, notice, warn, error, crit, alert,emerg,其取值范围是按紧急程度从低到高排列 # 只有日志的错误级别等于或高于level指定的值才会写入错误日志中。默认值是error error_log file [level]; } 3、配置域名 server { # 监听domain.com server_name domain.com; # 监听多个域名 server_name domain.com www.domain.com; # 监听所有域名 server_name *.domain.com; # 监听所有顶级域名 server_name domain.*; # 监听未指定的主机名(比如侦听IP地址本身) server_name ""; } 4、静态资源 server { listen 80; server_name domain.com; location / { # 设置网站的根目录 root /path/to/website; } } 5、重定向 5.1、网站根目录重定向 server { listen 80; server_name www.domain.com; # 将 www.domain.com 进行301重定向,跳转至 http://domain.com$request_uri return 301 http://domain.com$request_uri; } 5.2、特定目录重定向 server { listen 80; server_name www.domain.com; # 将链接 http://www.domain.com/redirect-url 进行301重定向,跳转到http://otherdomain.com location /redirect-url { return 301 http://otherdomain.com; } } 如将 domain.com 永久重定向到 www.domain.com: server { listen 80; server_name domain.com; return 301 http://www.$host$request_uri; } 6、反向代理 反向代理 - 维基百科,自由的百科全书 (wikipedia.org) Reverse proxy - Wikipedia server { listen 80; server_name domain.com; # 对特定主机的3000端口进行反向代理 location / { proxy_pass http://0.0.0.0:3000; } } 7、负载均衡 负载均衡 - 维基百科,自由的百科全书 (wikipedia.org) Load balancing (computing) - Wikipedia upstream node_js { server 0.0.0.0:3000; server 0.0.0.0:4000; server 123.131.121.122; } server { listen 80; server_name domain.com; location / { proxy_pass http://node_js; } } 8、SSL配置 server { listen 443 ssl; server_name domain.com; ssl on; # 证书文件,格式不一定都是pem,还可能是其他格式 ssl_certificate /path/to/cert.pem; # 秘钥文件,格式不一定都是pem,还可能是其他格式 ssl_certificate_key /path/to/privatekey.pem; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_session_timeout 1h; ssl_session_cache shared:SSL:50m; add_header Strict-Transport-Security max-age=15768000; } # 将HTTP永久重定向到HTTPS server { listen 80; server_name domain.com; return 301 https://$host$request_uri; } 部分内容转载修改自: Nginx Cheatsheet (hashnode.dev) Nginx日志配置详解_biubiuli的博客-CSDN博客_nginx 日志配置
2021年12月11日
2,787 阅读
0 评论
0 点赞
2021-12-08
【踩坑实录】Github提交时出现错误:remote:Permission to xxx/test.git denied to xxx
目录: 问题再现 错误原因 解决方法 问题再现 Github 提交时出现错误,报错信息如下: remote: Permission to xxx/test.git denied to xxx. fatal: unable to access 'https://github.com/xxx/test.git/': The requested URL returned error: 403 如果再试一次,可能还会有如下报错: fatal: unable to access 'https://github.com/xxx/xxx.git/': OpenSSL SSL_read: Connection was reset, errno 10054 或者: fatal: unable to access 'https://github.com/xxx/xxx.git/': Failed to connect to github.com port 443: Timed out 错误原因 这个是由于在同一台计算机上先后使用了两个Github账号。 第一个账号进行了提交之后,这个账号的凭据信息就被保存了下来,然后切换到第二个账号进行提交时,系统依然会使用第一个账号的凭据信息进行提交,因此便会报错。 解决方法 进入控制面板,找到凭据管理器选项,选择其中的 Windows 凭据,删除已经保存的 Github 凭据: 然后再重新进行提交操作,此时会弹出Github登录窗口,重新登录之后就能成功提交了。
2021年12月08日
3,259 阅读
0 评论
0 点赞
2021-11-26
SpringMVC学习笔记
目录 一、SpringMVC介绍 二、映射请求路径 2.1、使用@ResquestMapping进行路径映射 2.2、@ResquestMapping的属性 2.2.1、value或path 2.2.2、method 2.2.3、params 2.2.4、headers 2.2.5、consumes 2.2.6、produces 2.3、@ResquestMapping的衍生注解 三、请求参数传递 3.1、GET/POST请求传递基础参数 3.2、传递复杂参数 3.2.1、日期类型 3.2.2、引用类型 3.2.3、数组 3.2.4、集合(List/Set/Map) 3.3、传递Json数据 四、响应 4.1、返回Json数据 4.2、封装返回结果 五、REST风格 5.1、简介 5.2、规则 5.3、相关注解 5.3.1、@RestController 5.3.2、@PathVariable 5.3.3、@RequestParam 5.3.4、@RequestBody 5.4、Rest风格开发示例 5.3.1、查询 5.3.2、新增 5.3.3、修改(更新) 5.3.4、删除 六、异常处理 6.1、自定义异常 6.2、处理异常的方式 6.2.1、使用@ControllerAdvice和@ExceptionHandler 6.2.2、实现HandlerExceptionResolver接口 七、拦截器 7.1、创建拦截器 7.2、注册和配置拦截器 7.3、执行流程和顺序 一、SpringMVC介绍 Spring MVC 是 Spring 框架中的一个模块,专门用于构建基于模型-视图-控制器(Model-View-Controller,MVC)设计模式的 Web 应用程序。 相关依赖坐标: <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.0.RELEASE</version> </dependency> 在 IDEA 中根据 “maven-archetype-webapp” 模板创建 Webapp 项目: 创建基本项目结构: 在 config 包里面新建初始化 Servlet 的配置类: public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer { protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class}; } protected Class<?>[] getServletConfigClasses() { return new Class[]{SpringMvcConfig.class}; } protected String[] getServletMappings() { return new String[]{"/"}; } } 再新建 SpringMVC 配置类: @Configuration @ComponentScan("com.wlplove.controller") public class SpringMvcConfig { } 二、映射请求路径 2.1、使用@ResquestMapping进行路径映射 @RequestMapping 是 Spring MVC 中将 HTTP 请求映射到控制器方法的注解,它用于类级别或方法级别,可以根据请求的 URL、HTTP 请求方法(GET、POST 等)、请求参数、请求头等信息来匹配对应的处理逻辑。 @RequestMapping 注解应用在方法上,可以为该方法映射 URL 请求路径: public class UserController { @RequestMapping("/save") public String save() { // ... } } 也可以应用在类上,为该类中的所有方法设置基础的 URL 请求路径: @RequestMapping("/user") public class UserController { @RequestMapping("/save") public void save() { // ... } @RequestMapping("/delete") public void delete() { // ... } } 2.2、@ResquestMapping的属性 2.2.1、value或path 这两个属性作用相同,用于指定 URL 请求路径。用一个字符串映射一个路径,也可以用一个字符串数组映射多个路径。 @RequestMapping(value = "/save") @RequestMapping(path = "/save") // 映射多个路径 @RequestMapping(value = {"/save", "/saveUser"}) @RequestMapping(path = {"/save", "/saveUser"}) 一般也可以简写: @RequestMapping("/save") // 映射多个路径 @RequestMapping({"/save", "/saveUser"}) 2.2.2、method method 属性用于指定处理的 HTTP 方法。可以是一个 RequestMethod 枚举值或枚举值数组,常见的请求方法有 GET、POST、PUT、DELETE 等。 public class UserController { // save方法只处理POST和GET请求,路径为/save @RequestMapping(value = "/save", method = {RequestMethod.POST, RequestMethod.GET}) @ResponseBody public String save() { // ... } } 2.2.3、params 用于指定请求中必须包含的请求参数和值。可以使用一些特殊的表达式,如 paramName 表示请求中必须包含 paramName 参数;!paramName 表示请求中不能包含 paramName 参数,paramName=value 表示请求中 paramName 参数的值必须为 value。 // 请求中必须包含name参数 @RequestMapping(value = "/save", params = "name") // 请求中不能包含name参数 @RequestMapping(value = "/save", params = "!name") // 请求中name参数的值必须是123 @RequestMapping(value = "/save", params = "name=123") // 请求中name参数的值不能是123 @RequestMapping(value = "/save", params = "name!=123") 2.2.4、headers 用于指定请求中必须包含的请求头及其值,用法与 params 类似。 // 只有当请求/download路径的Accept头的值为application/pdf时,downloadPdf方法才会被调用 @RequestMapping(value = "/download", headers = "Accept=application/pdf") @ResponseBody public String downloadPdf() { // ... } 2.2.5、consumes 用于指定请求的内容类型(Content-Type),即客户端发送给服务器的数据的格式。 // uploadJson方法只处理Content-Type为application/json的请求 @RequestMapping(value = "/upload", consumes = "application/json") @ResponseBody public String uploadJson() { // ... } 2.2.6、produces 用于指定响应的内容类型(Accept),即服务器返回给客户端的数据的格式。 // getHtml方法返回的响应内容类型为text/html @RequestMapping(value = "/get", produces = "text/html") @ResponseBody public String getHtml() { return "<html><body>Hello</body></html>"; } 2.3、@ResquestMapping的衍生注解 为了简化代码,Spring 还提供了一些 @RequestMapping 的衍生注解,它们实际上是 @RequestMapping(method = xxx) 的特定形式: @GetMapping:等同于 @RequestMapping(method = RequestMethod.GET),用于处理 HTTP Get 请求 @PostMapping:等同于 @RequestMapping(method = RequestMethod.POST),用于处理 HTTP Post 请求 @PutMapping:等同于 @RequestMapping(method = RequestMethod.PUT),用于处理 HTTP Put 请求 @DeleteMapping:等同于 @RequestMapping(method = RequestMethod.DELETE),用于处理 HTTP Delet 请求 @PatchMapping:等同于 @RequestMapping(method = RequestMethod.PATCH),用于处理 HTTP Patch 请求 三、请求参数传递 3.1、GET/POST请求传递基础参数 GET 请求的参数通过 URL 传递,附加在 URL 后面,形式为查询字符串(Query String),例如: http://localhost:8080/save?name=Jerry&age=13 POST 请求数据通过请求体(Request Body)传递,不会显示在 URL 中,在 Postman 中使用传递参数: 在 Java 的控制器方法中使用相同名称的形参,SpringMVC 可以自动将请求参数绑定到控制器方法中同名的形参上: @Controller public class UserController { // 如果要限制使用特定请求方法(POST/GET),将RequestMapping换成PostMapping或者GetMapping即可 @RequestMapping("/save") @ResponseBody public String save(String name, int age) { // ... } } 除此之外,SpringMVC 也支持将请求参数映射到不同名称的形参上,需要用 @RequestParam 注解指定请求参数绑定到控制器方法的哪个形参上: @Controller public class UserController { @RequestMapping("/save") @ResponseBody public String save(@RequestParam("name")String userName, @RequestParam("age")int userAge) { // ... } } @RquestParam 注解的属性: value 或 name:指定请求参数的名称。如果省略,默认使用控制器方法的形参名作为参数名称。 required:指定参数是否为必填项。默认值为 true,表示请求中必须包含该参数,如果请求中缺少该参数,Spring 会抛出 MissingServletRequestParameterException。如果设置为 false,则参数可以为空。 defaultValue:指定参数的默认值。如果请求中未提供该参数,则使用该默认值。 3.2、传递复杂参数 3.2.1、日期类型 在请求中传递特定格式的日期: http://localhost:8080/save?date=2023-10-12 在控制层方法的形参前面加上 @DateTimeFormat 注解并指定日期格式,SpringMVC 就可以将同名请求参数转换为日期格式: @RequestMapping("/dateParse") @ResponseBody public String dateParse(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date) { // ... } 3.2.2、引用类型 例如存在一个 User 对象,其中包含一个引用类型的成员 address: public class Address { // 省 private String province; // 市 private String city; } public class User { // 姓名 private String name; // 年龄 private String age; // 地址——引用对象 private Address address; } 控制器方法中使用 User 对象接收参数: @Controller public class UserController { @RequestMapping("/save") @ResponseBody public String save(User user) { // ... } } 那么在请求中传递参数时保持参数名与控制器方法形参名相同即可(不相同时使用 @RequestParam 绑定): GET: http://localhost:8080/save?name=Jerry&age=13&address.province=河南&address.city=开封 POST: 3.2.3、数组 控制器方法中使用数组接收参数: @Controller public class UserController { @RequestMapping("/save") @ResponseBody public String save(String[] nameArray) { // ... } } 那么在请求中传递参数时都使用相同名称的参数即可: GET: http://localhost:8080/save?nameArray=Jerry&nameArray=Tom&nameArray=Mike POST: 3.2.4、集合(List/Set/Map) 控制器方法中使用集合接收参数(形参前需要加 @RequestParam 注解): @Controller public class UserController { @RequestMapping("/save") @ResponseBody public String save(@RequestParam List<String> nameList) { // ... } } 在请求中传递参数的方式与数组相同: GET: http://localhost:8080/save?nameArray=Jerry&nameArray=Tom&nameArray=Mike POST: 3.3、传递Json数据 添加处理 Java 对象与 JSON 数据相互转换的依赖坐标: <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.18.2</version> </dependency> 在 SpringMVC 配置上添加 @EnableWebMvc 注解开启 Spring MVC 的默认配置(其中包括 Java 对象与 JSON 数据的相互转换): @Configuration @ComponentScan("com.wlplove.controller") @EnableWebMvc public class SpringMvcConfig { // ... } 发送的 JSON 数据: { "name": "Tom", "age": 12 } 控制器方法中用集合或者对象接收时形参前加上 @RequestBody 注解将请求体中的数据绑定到控制器方法的参数上: @RequestMapping("/json") @ResponseBody public String json2Dto(@RequestBody User user) { // ... } 四、响应 4.1、返回Json数据 需要添加 Jackson 的依赖: <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.18.2</version> </dependency> 在控制器方法上使用 @ResponseBody 注解,告知 SpringMVC 将控制层方法的返回值直接写进 HTTP 响应体,而不是解析为视图。最后 SpringMVC 通过 HttpMessageConverter 接口将返回值转换为适当的格式(如 JSON、XML等)。 @RequestMapping("/returnJson") @ResponseBody public User returnJson() { User user = new User(); user.setAge(20); user.setName("QWERT"); return user; } @ResponseBody 支持的返回值类型: 基本类型(String、int、boolean 等)。 自定义对象(User、Product 等)。 集合或数组(List<User>、String[] 等)。 4.2、封装返回结果 新建一个返回结果对象: public class Result { /** 状态码 */ private Integer code; /** 返回内容 */ private String msg; /** 数据对象 */ private Object data; public Result(Integer code, Object data) { this.code = code; this.data = data; } public Result(Integer code, String msg, Object data) { this.code = code; this.msg = msg; this.data = data; } // get和set方法 } 自定义返回编码: public class Code { public static final Integer OK = 000000; public static final Integer ERR =999999; // 其他编码 } 对 controller 方法的返回值都统一成这个返回对象: @Controller @RequestMapping("/users") public class UserController { @Autowired private UserMapper userMapper; @RequestMapping("/{id}") @ResponseBody public Result findById(@PathVariable Integer id) { User user = userMapper.selectById(id); Integer code = user != null ? Code.OK : Code.ERR; String msg = user != null ? "数据查询成功" : "数据查询失败"; return new Result(code, msg, user); } // ... } 五、REST风格 5.1、简介 REST(Representational State Transfer)风格是一种软件架构风格,用于设计网络应用程序,其核心概念旨在通过 HTTP 协议实现客户端与服务器之间简洁、可扩展且高效的交互。 传统风格资源描述形式: http://lcoalhost/user/getById?id=1 http://localhost/user/saveUser REST风格描述形式: http://localhost/user/1 http://localhost/user REST 风格是一种设计接口的约定方式,并非规范。 5.2、规则 REST 使用统一的接口与服务器进行交互,基于 HTTP 协议的标准方法: GET 用于获取资源 POST 用于创建资源 PUT 用于更新资源 DELETE 用于删除资源 5.3、相关注解 5.3.1、@RestController @RestController 是 Spring 4.0 引入的一个组合注解,它是 @Controller 和 @ResponseBody 的结合体。其主要作用是将一个控制器类标记为使用 REST 风格,使用 @RestController 注解的类中的所有处理方法默认都会将返回值直接写入 HTTP 响应体,以 JSON、XML 等数据格式返回,而不是返回视图进行渲染。 5.3.2、@PathVariable @PathVariable 是 Spring MVC 中用于将 URL 中的模板变量绑定到控制器方法形参上的注解。它通常用于 RESTful API 中,从 URL 路径中提取参数值。 如根据 ID 查询用户的控制器方法: @GetMapping("/users/{id}") public String findById(@PathVariable Long id) { // ... } 那么对于 GET 请求链接 http://localhost/users/123456,该控制器可以通过 @PathVariable 将 “123456” 绑定到形参 id 上。 也支持在路径中定义多个路径变量: http://localhost/users/123456/orders/987654321 在控制器方法中接收多个参数: @GetMapping("/users/{userId}/orders/{orderId}") public Order getOrder(@PathVariable Long userId, @PathVariable Long orderId) { // ... } Spring MVC 5.3 及以上版本支持可选路径变量。如果路径变量是可选的,可以设置属性 required = false: @GetMapping("/users/{id}") public User getUser(@PathVariable(required = false) Long id) { if (id == null) { // 返回所有用户 return userService.getAllUsers(); } // 返回指定id的用户 return userService.getUserById(id); } 那么: http://localhost/users id为null,返回所有用户 http://lcoalhost/users/123 返回id为123的用户 5.3.3、@RequestParam 详见 3.1。 5.3.4、@RequestBody 详见 3.3。 5.4、Rest风格开发示例 5.3.1、查询 // 查询全部 @GetMapping("/users") public xxxResponse findAll() { // ... } // 根据主键查询 @GetMapping("/users/{id}") public xxxResponse findById(@PathVariable Long id) { // ... } 5.3.2、新增 @PostMapping("/users") public xxxResponse add(@RequestBody User user) { // ... } 5.3.3、修改(更新) @PutMapping("/users") public xxxResponse update(@RequestBody User user) { //... } 5.3.4、删除 @DeleteMapping("/delete/{id}") public xxxResponse delete(@PathVariable Long id) { // ... } 六、异常处理 6.1、自定义异常 新建一个自定义异常类 BusinessException: public class BusinessException extends RuntimeException { private Integer code; public BusinessException(Integer code, String message) { super(message); this.code = code; } // get和set方法 } 自定义异常编码: public class ErrorCode { public static final Integer TIMEOUT_ERROR = 10001; // 其他异常编码 } 在业务层代码中抛出异常: throw new BusinessException(ErrorCode.TIMEOUT_ERROR, "请求超时,请稍后再试"); 6.2、处理异常的方式 6.2.1、使用@ControllerAdvice和@ExceptionHandler @ControllerAdvice 是 Spring 框架提供的一个用于定义全局异常处理、数据绑定和数据预处理等逻辑的注解。在配合标注了 @ExceptionHandler 注解的方法使用时,可以在拦截到对应的异常之后终止原 controller 方法的执行,并转入该方法执行设定好的异常处理逻辑。 在 controller 包中新建一个全局异常处理类 GlobalExceptionHandler,在类中的方法上面使用 @ExceptionHandler 注解指定这个方法要处理哪种异常: @ControllerAdvice public class GlobalExceptionHandler { // Exception是所有异常的父类,在此表示拦截到所有的异常都会进行处理 @ExceptionHandler(Exception.class) // 方法参数中加入这种异常类型的形参,就可以获取到这个异常的信息 public ResponseEntity<String> doException(Exception ex) { return new ResponseEntity<>("异常信息: " + ex.getMessage()); } // 只处理RuntimeException异常 @ExceptionHandler(RuntimeException.class) public Response<T> doRuntimeException(RuntimeException ex){ // RuntimeException异常处理逻辑 } // 只处理IOException异常 @ExceptionHandler(IOException.class) public Response<T> doIOException(IOException ex){ // IOException异常处理逻辑 } // ... } 如果在控制器中使用 Rest 风格的开发,可以使用 @RestControllerAdvice 注解,@RestControllerAdvice 是 @ControllerAdvice 和 @ResponseBody 的组合注解,类中的方法返回值会自动进行 JSON 或 XML 等格式的序列化,直接作为响应体返回给客户端。 6.2.2、实现HandlerExceptionResolver接口 HandlerExceptionResolver 是 Spring Web 框架中的一个接口,它提供了一种统一处理控制器(@Controller)中抛出的异常的机制。通过实现这个接口,开发者可以自定义异常处理逻辑,以满足不同的业务需求,比如返回特定格式的错误响应、记录异常日志等。 处理异常主要通过该接口中的 resolveException 方法实现 方法参数: HttpServletRequest request:当前请求对象,通过它可以获取请求的各种信息,如请求头、请求参数等。 HttpServletResponse response:当前响应对象,可用于设置响应头、响应状态码等。 Object handler:处理当前请求的处理器对象,通常是一个控制器方法。 Exception ex:在处理请求过程中抛出的异常对象。 方法返回值: ModelAndView 是 Spring MVC 中用于封装视图和模型数据的类。如果返回 null,表示该异常解析器没有处理该异常,Spring 将继续尝试其他的异常解析器。如果返回一个非空的 ModelAndView 对象,Spring 会根据其中的视图信息渲染相应的视图,并将模型数据传递给视图。 新建一个异常处理类 CustomExceptionResolver,用 @Component 注解声明这是一个 Spring Bean,实现 HandlerExceptionResolver 接口中的 resolveException 方法: @Component public class CustomExceptionResolver implements HandlerExceptionResolver { public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ModelAndView modelAndView = new ModelAndView(); if (ex instanceof IllegalArgumentException) { modelAndView.setViewName("error/400"); // 跳转到 400 错误页面 modelAndView.addObject("errorMessage", "非法参数: " + ex.getMessage()); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); } else { modelAndView.setViewName("error/500"); // 跳转到 500 错误页面 modelAndView.addObject("errorMessage", "服务器内部错误: " + ex.getMessage()); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } return modelAndView; } } 七、拦截器 在 Spring 中,拦截器是利用 AOP(面向切面编程)的思想实现的一种机制,用在请求处理的不同阶段(如请求到达 controller 方法之前、controller 方法执行之后、视图渲染之前等)执行自定义逻辑,在以下的场景中广泛使用: 权限验证:在请求到达控制器之前,检查用户是否具有访问该资源的权限,如验证用户登录状态。 日志记录:记录请求的相关信息,如请求的 URL、参数、处理时间等,方便后续的调试和监控。 性能监控:统计请求的处理时间,找出性能瓶颈。 请求参数处理:对请求参数进行统一的预处理,如编码转换、参数验证等。 7.1、创建拦截器 在 controller 包中新建一个包 interceptor,创建拦截器类 ProjectInterceptor,实现 HandlerInterceptor 接口,并用 @Component 声明为一个 bean: @Component public class ProjectInterceptor implements HandlerInterceptor { // 在控制器处理请求之前调用 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 通过handler对象经过反射可以获取到原controller方法 System.out.println("preHandle: 请求到达控制器之前"); // 可以在此处进行权限验证、日志记录等操作 // 如果返回 false,请求将被中断,不再执行postHandle与afterCompletion return true; } // 在控制器处理请求之后、视图渲染之前调用 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle: 控制器处理请求之后"); // 可以在此处修改模型数据或视图 } // 在视图渲染完成之后调用(无论请求是否成功) @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion: 视图渲染完成之后"); // 可以在此处进行资源清理、日志记录等操作 } } 7.2、注册和配置拦截器 在 config 包下新建一个配置类 SpringMvcSupport,继承 WebMvcConfigurationSupport 类,并重写 addInterceptors 方法,在这个方法中注册和配置拦截器: @Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { // 注入之前定义好的拦截器类 @Autowired private ProjectInterceptor projectInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(projectInterceptor) // 指定要拦截的路径,可以传入多个路径 .addPathPatterns("/**") // 指定要排除的路径,可以传入多个路径 .excludePathPatterns("/login", "/register"); } } addPathPatterns 和 excludePathPatterns 方法均支持 Ant 风格的路径匹配规则,具体语法规则如下: ?:匹配单个字符。 示例:/user? 可以匹配 /user1、/userA,但不能匹配 /user12。 \*:匹配任意数量的字符(不包括路径分隔符 /)。 示例:/user/* 可以匹配 /user/123、/user/profile,但不能匹配 /user/123/address。 `\**:匹配任意数量的字符(包括路径分隔符/`)。 示例:/user/** 可以匹配 /user/123、/user/123/address。 {variable}:匹配路径变量。 示例:/user/{id} 可以匹配 /user/123,并将 123 绑定到路径变量 id 除了新建一个 SpringMvcSupport 配置类来配置拦截器的方法外,还可以让原来的 SpringMvc 配置类来实现 WebMvcConfigurer 接口中的 addInterceptors 方法,也能达到相同的效果: @Configuration @ComponentScan("com.xxx.controller") @EnableWebMvc public class SpringMvcSupport implements WebMvcConfigurer { // 注入之前定义好的拦截器类 @Autowired private ProjectInterceptor projectInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(projectInterceptor) // 指定要拦截的路径,可以传入多个路径 .addPathPatterns("/**") // 指定要排除的路径,可以传入多个路径 .excludePathPatterns("/login", "/register"); } } 7.3、执行流程和顺序 拦截器执行流程: 如果有多个拦截器,形成拦截器链,它们的执行顺序如下: preHandle:按照注册顺序依次执行,必定运行。 postHandle:按照注册顺序的逆序执行,可能不运行。 afterCompletion:按照注册顺序的逆序执行,可能不运行。 例如拦截器 A、B、C 按顺序注册: preHandle 执行顺序:A → B。 postHandle 执行顺序:B → A。 afterCompletion 执行顺序:B → A。
2021年11月26日
2,996 阅读
0 评论
3 点赞
2021-11-13
Spring学习笔记(四) - SSM框架整合
目录 一、创建Webapp项目 二、Spring配置类 三、数据源对象(以Druid为例) 四、整合MyBatis 4.1、数据库配置 4.2、MyBatis配置 五、整合SpringMVC 六、整合Log4j 七、整合Junit 环境说明: 数据库:MySQL 8.0 Java版本:JDK8 通过注解整合 SSM 框架流程: 创建 Web 工程 SSM 整合 Spring 框架:SpringConfig MyBatis 框架:JdbcConfig / jdbc.properties / MybatisConfig SpringMVC 框架:ServletConfig / SpringMvcConfig 一、创建Webapp项目 在 IDEA 中根据 “maven-archetype-webapp” 模板创建 Webapp 项目: 创建基本项目结构: 二、Spring配置类 引入 spring-conext 的依赖坐标: <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.25.RELEASE</version> </dependency> 创建一个 Java 类 SpringConfig 作为 Spring 配置类,类上面添加 @Configuration 注解标识这是一个配置类,添加 @ComponentScan 注解来设置扫描注解的路径 @Configuration @ComponentScan("com.xxx") /** * 如果有多个要扫描的包路径,可以用数组格式: * @ComponentScan({"com.xxx.dao","com.xxx.service"}) */ public class SpringConfig { } 三、数据源对象(以Druid为例) 导入 Druid 数据源的依赖坐标: <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.22</version> </dependency> 新建一个配置数据库连接信息的类 JdbcConfig,里面引入配置文件中的数据库信息,然后用一个方法构造并返回一个 DataSource 对象,最后用 @Bean 注解设置这个方法的返回值成为 Spring IoC 容器的一个 Bean: public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; /** * 数据源Bean * @return */ @Bean public DataSource dataSource() { DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(username); ds.setPassword(password); return ds; } } Spring 配置类 SpringConfig 中用 Import 注解引入 JdbcConfig 这个类: @Configuration @ComponentScan("com.xxx") +@Import({JdbcConfig.class}) public class SpringConfig { } 四、整合MyBatis 4.1、数据库配置 在 src/main/resources 目录下新建一个配置数据库连接信息的配置文件 jdbc.properties: jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db?useSSL=false&setUnicode=true&characterEncoding=utf8 jdbc.username=root jdbc.password=root 使用 @PropertySource 注解在 Spring 配置类 SpringConfig 上加载外部的 properties 文件: @Configuration @ComponentScan({"com.xxx"}) +@PropertySource("classpath:jdbc.properties") public class SpringConfig { } 4.2、MyBatis配置 导入 MyBatis 及 MySQL 的相关依赖坐标: <!-- spring-jdbc --> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.25.RELEASE</version> </dependency> <!-- mybatis --> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.16</version> </dependency> <!-- mybatis-spring --> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>3.0.3</version> </dependency> <!-- mysql --> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> 新建一个 MyBatis 的配置类 MybatisConfig,配置 SqlSessionFactoryBean 和 MapperScannerConfigurer 这两个 Bean: public class MybatisConfig { @Bean public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) { SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean(); ssfb.setDataSource(dataSource); ssfb.setTypeAliasesPackage("com.xxx.xxx"); return ssfb; } @Bean public MapperScannerConfigurer mapperScannerConfigurer() { MapperScannerConfigurer msc = new MapperScannerConfigurer(); // Mapper的XML映射文件所在路径 msc.setBasePackage("com.xxx.dao"); return msc; } } Spring 配置类 SpringConfig 中用 Import 注解引入 MybatisConfig 这个类: @Configuration @ComponentScan("com.xxx") +@Import({JdbcConfig.class, MybatisConfig.class}) @PropertySource("classpath:jdbc.properties") public class SpringConfig { } 五、整合SpringMVC 导入 SpringMVC、Servlet 与 jackson 的依赖坐标: <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.0</version> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.25.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.18.2</version> </dependency> 新建 SpringMVC 配置类 SpringMvcConfig: @Configuration @ComponentScan("com.wlplove.controller") @EnableWebMvc public class SpringMvcConfig { } 新建 Servlet 的初始化类 ServletInitializer: public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer { protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class}; } protected Class<?>[] getServletConfigClasses() { return new Class[]{SpringMvcConfig.class}; } protected String[] getServletMappings() { return new String[]{"/"}; } } 六、整合Log4j 导入 Log4j 的依赖坐标: <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.23.1</version> </dependency> 在 "src/main/java/resources" 下新建一个 Log4j 的配置文件 Log4j2.xml: <?xml version="1.0" encoding="UTF-8"?> <!-- Configuration 具有 Appenders 和 Loggers 这两种子节点,每个子节点可以定义多个 --> <configuration> <!-- Appender节点,具有 Console(控制台)、File(文件)、RoolingFile(滚动文件)这三种类型的子节点 --> <Appenders> <!-- 输出日志信息到控制台 --> <Console name="console" target="SYSTEM_OUT"> <!--指定控制日志输出的格式--> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/> </Console> <!-- File 节点用来定义输出到指定位置的文件的 Appender,会将所有内容写入到同一个文件中 --> <!-- append属性设置写入新的日志时是追加在原内容后面,还是清除所有内容之后再写入 --> <!-- <File name="allLog" fileName="logs/AlliInOne.log" append="true">--> <!-- <ThresholdFilter level="ALL" onMatch="ACCEPT" onMismatch="DENY"/>--> <!-- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5p %c{9.9.9.1}(%L) %m%n"/>--> <!-- </File>--> <!-- RollingFile 节点,将日志写入文件,但是允许日志文件根据时间或大小进行滚动,从而避免单个文件过大 --> <!-- fileName 生成的初始日志文件 --> <RollingFile name="rollingFileInfo" fileName="logs/${date:yyyy-MM}/log-info-${date:yyyy-MM-dd}.log" filePattern="logs/${date:yyyy-MM}/log-info-%d{yyyy-MM-dd}-%i.log"> <!-- ThresholdFilter 只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) --> <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/> <!-- PatternLayout 指定控制日志输出的格式,不设置默认为:%m%n --> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/> <!-- Policies 滚动策略 --> <Policies> <!-- 按时间滚动 --> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> <!-- 按大小滚动 --> <SizeBasedTriggeringPolicy size="10MB"/> </Policies> <!-- DefaultRolloverStrategy 设置一个文件下保存的日志文件数量,不设置则默认为同一文件夹下7个文件,超过这个数量后,最老的文件将被删除 --> <DefaultRolloverStrategy max="20"/> </RollingFile> </Appenders> <!-- 在 Loggers 中引入上面定义好的 Appender --> <loggers> <!-- level指定日志级别,从低到高的优先级: ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF --> <!-- 设置org.mybatis包下的日志只打印WARN及以上级别 --> <Logger name="org.mybatis" level="WARN" additivity="false"> <appender-ref ref="console"/> <appender-ref ref="rollingFileInfo"/> </Logger> <!-- 设置org.springframework包下的日志只打印WARN及以上级别 --> <Logger name="org.springframework" level="WARN" additivity="false"> <appender-ref ref="console"/> <appender-ref ref="rollingFileInfo"/> </Logger> <root level="DEBUG"> <appender-ref ref="console"/> <appender-ref ref="rollingFileInfo"/> </root> </loggers> </configuration> Log4j 配置文件详解:彻底掌握Log4j2 - 蚂蚁小哥 - 博客园 修改 MyBatis 配置类中的 SqlSessionFactoryBean,设置其 Configuration 属性: public class MybatisConfig { // ... @Bean public SqlSessionFactoryBean getSqlSessionFactory(DataSource dataSource) { SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean(); ssfb.setTypeAliasesPackage("com.xxx.xxx"); ssfb.setDataSource(dataSource); + Configuration configuration = new Configuration(); + // 修改MyBatis使用的日志框架为Log4j + configuration.setLogImpl(Log4j2Impl.class); + ssfb.setConfiguration(configuration); return ssfb; } // ... } 七、整合Junit 导入 Junit 和 Spring-test 的依赖坐标: <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.10.2</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-test --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.10.RELEASE</version> </dependency> 新建一个测试类: // 在 JUnit 5 中集成 Spring 功能(如果是Junit4则换成@RunWith(SpringJunit4ClassRunner.class)) @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = SpringConfig.class) public class SpringTest { @Autowired private UserMapper userMapper; @Test public void find() { User user = userMapper.select(); } }
2021年11月13日
2,583 阅读
0 评论
1 点赞
2021-11-10
Spring学习笔记(三) - 注解
目录: 一、通过注解定义Bean 1.1、启用注解支持 1.2、定义Bean 1.3、@Component的派生注解 1.3.1、@Repository 1.3.2、@Service 1.3.3、@Controller 1.3.4、@Configuration 1.4、用注解代替XML配置文件 1.4.1、配置类 1.4.2、初始化IoC容器 二、通过注解使用Bean 2.1、自动装配 2.1.1、@Autowired 2.1.2、@Qualifier 2.1.3、@Resource 2.1.4、@Primary 2.1.5、@Value 2.2、读取properties文件配置 2.3、作用域 2.4、自定义Bean初始化/销毁操作 2.5、总结 三、整合框架 3.1、数据源对象(以Druid为例) 3.2、整合Mybatis 3.3、整合Log4j 3.4、整合Junit 四、Spring事务 一、通过注解定义Bean 1.1、启用注解支持 <context:annotation-config /> 若要以注解方式注入 Bean,则需要开启扫描包中的组件,并用 base-package 属性指定扫描的基本包路径: <context:component-scan base-package="com.xxx" /> 使用 <context:annotation-scan> 标签时,默认将隐式启用对注解的支持。 1.2、定义Bean 在 XML 配置中如果要将一个 Bean 交给 Spring IoC 容器管理,需要进行如下配置: <bean id="userService" class="com.wlplove.service.impl.UserServiceImpl" /> 若用注解要实现相同目的,只需要在要被管理的 Java 类上添加 @Component 注解,并在后面的括号中指定 Bean 的名称: // 注解后面的括号里是 bean 的名称 @Component("userServiceImpl") public class UserServiceImpl implements UserService { // ... } @Component 注解也可以不指定 Bean 名称,Spring 会自动为该 Bean 生成一个默认的名称,通常是类名的首字母小写形式: @Component // 可以用名称“userServiceImpl”来获取这个Bean public class UserServiceImpl implements UserService { // ... } 所以只需要在 Java 类上加入 @Component 注解,用于标识这个类是 Spring 容器中的组件(即 Bean),就可以实现与 XML 配置中的 <bean> 标签相同的作用。 1.3、@Component的派生注解 此外,Spring 还提供了 @Component 的几个派生注解:@Repository、@Service、@Controller、@Configuration,这几个注解的功能和用法与 @Component 相同,但它们在使用场景上有所不同。 1.3.1、@Repository 用于数据访问层(Dao 层)中与数据库交互的类。 Spring 会对使用 @Repository 注解的类进行特定的异常处理(将数据访问层的异常,如 SQLException 等转换为 Spring 的 DataAccessException 异常,方便统一处理和异常传播)。 @Repository public interface UserMapper { // ... } 1.3.2、@Service 用于标识业务逻辑层(Service 层)中处理核心业务逻辑的类。 使用 @Service 注解可以将一个类声明为业务逻辑组件,将其对象存入 Spring 容器中,以便在其他组件(如Controller)中通过注入该 service 类的实例来使用其业务逻辑。 @Service public class UserServiceImpl implements UserService { // ... } 1.3.3、@Controller 用于标识控制层(Controller 层)中处理响应和请求的类。 被 @Controller 标记的类实际上就是一个 Spring MVC Controller 对象,它可以处理请求,并通过 @RequestMapping 等注解将不同的请求分发到对应的方法上。 @Controller public class UserController { // ... } 1.3.4、@Configuration 用于标记替换 XML 配置文件的配置类,这个注解的用法将在 1.4 中详细说明。 1.4、用注解代替XML配置文件 Spring 支持完全去除 XML 配置文件实现纯注解开发,实现的方式就是使用一个 Java 类来代替原本的 XML 配置文件。 1.4.1、配置类 创建一个 Java 类,类上面添加 @Configuration 注解标识这是一个配置类: @Configuration public class SpringConfig { // ... } 在 XML 配置文件中会用到 <context:annotation-config base-package=""> 标签来设置扫描注解的路径,在配置类上添加 @ComponentScan 注解也可以实现相同的效果: @Configuration +@ComponentScan("com.xxx") public class SpringConfig { // ... } 如果有多个包路径,可以用数组格式: @ComponentScan({"com.xxx.dao","com.xxx.service"}) 1.4.2、初始化IoC容器 当创建出一个配置类之后,初始化 Spring IoC 容器的方式也需要改变。 原来是通过 ClassPathXmlApplicationContext 类加载 XML 配置文件初始化得到 IoC 容器: ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config.xml"); 现在改用 AnnotationConfigApplicationContext 类来加载配置类得到 Spring IoC 容器: ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); 二、通过注解使用Bean 2.1、自动装配 2.1.1、@Autowired 在要引用的依赖上面使用 @Autowired 注解开启自动装配注入该依赖: @Service public class BookServiceImpl implements BookService { @Autowired private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.insert(); } } @Autowired 注解默认按照类型(byType)进行自动装配。 2.1.2、@Qualifier 如果 Spring IoC 容器中存在多个类型相同的 Bean 时,仅仅使用 @Autowired 根据类型自动装配时就导致 Spring 无法确定应该自动注入哪一个 Bean,便会抛出 NoUniqueBeanDefinitionException 异常。那么此时使用 @Qualifier 注解来指定要装配的 Bean 名称: @Repository public class BookDao1 implements BookDao { // ... } @Repository public class BookDao2 implements BookDao { // ... } @Service public class BookServiceImpl implements BookService { @Autowired // 存在两个BookDao类型的Bean,这里指定注入名称为bookDao1的Bean @Qualifier("bookDao1") private BookDao bookDao; } 需要注意的是,@Qualifier 不能单独使用,必须与 @Autowired 一起使用。因为 @Autowired 主要是根据类型进行自动装配,而 @Qualifier 则在此基础上匹配 Bean 名称,从而进行更精确的限制。 2.1.3、@Resource 还有一个与 @Autowired 类似的注解是 @Resource,默认按照名称(byName)进行自动装配,通过其 name 属性指定要注入的 Bean 名称: @Service public class BookServiceImpl implements BookService @Resource(name = "bookDao2") private BookDao bookDao; } 不像 @Autowired 是 Spring 框架提供的用于按类型(byType)自动装配的注解,@Resource 是 JavaEE 标准中(javax.annotation.Resource)提供的用于按名称(byName)自动装配的注解,并非 Spring 框架所特有的。 2.1.4、@Primary 与 @Qualifier 类似,@Primary 注解也可以用来解决当存在多个相同类型的 Bean 时应该注入哪一个 Bean 的问题。 如果 Spring IoC 容器中存在多个类型相同的 Bean,给该 Bean 上面添加 @Primary 注解,则 Spring 会优先使用标注了 @Primary 的 Bean。 2.1.5、@Value 使用 @Value 注解实现简单类型的注入: @Service public class UserServiceImpl implements UserService { @Value(30) private int age; @Value("root") private String userName; } @Value 注解除了用于给简单类型的成员注入固定的值以外,也可以用于将配置文件中的属性注入给简单类型的成员,配置文件中的属性名写在占位符 ${} 里面: @Service public class UserServiceImpl implements UserService { @Value("${user.name}") private String userName; } 2.2、读取properties文件配置 在 src/main/resources 目录下新建一个配置数据库连接信息的配置文件 jdbc.properties: jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db?useSSL=false&setUnicode=true&characterEncoding=utf8 jdbc.username=root jdbc.password=root 使用 @PropertySource 注解在 Spring 配置类上加载外部的 properties 文件,在要注入值的成员上添加 @Value 注解指定注入哪个属性: /** * Spring配置类 */ @Configuration @ComponentScan({"com.xxx"}) @PropertySource("classpath:jdbc.properties") public class SpringConfig { // ... } /** * 使用属性 */ public class DataBaseConnInfo { // 注入配置文件中的属性 @Value("${jdbc.driverName}") private String driverName; @Value("${jdbc.url}") private String url; } 若要加载多个配置文件应使用数组: @PropertySource({"classpath:jdbc.properties","classpath:appConfig.properties"}) 2.3、作用域 使用注解 @Scope 定义 Bean 的作用域是 prototype(原型/非单例)或者 singleton(单例)。 public class AppConfig { @Bean // @Scope("prototype") @Scope("singleton") public MyBean myBean() { return new MyBean(); } } 2.4、自定义Bean初始化/销毁操作 使用 @PostConstruct 注解定义初始化方法(方法名不限),将在构造方法之后执行该方法。 使用 @PreDestroy 注解定义销毁方法(方法名不限),在容器销毁前执行该方法,一般用来释放占用的资源等。 public class UserServiceImpl implements UserService { @PostConstruct publid void init() { System.out.println("init ...."); } @PreDestroy public void destory() { System.out.println("destory ..."); } } 2.5、总结 三、整合框架 3.1、数据源对象(以Druid为例) 导入 Druid 数据源的依赖坐标: <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.22</version> </dependency> 新建一个配置数据库连接的类 JdbcConfig,里面引入配置文件中的数据库信息,然后用一个方法构造并返回一个 DataSource 对象,最后用 @Bean 注解设置这个方法的返回值成为 Spring IoC 容器的一个对象: public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; /** * 数据源 * @return */ @Bean public DataSource dataSource() { DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(username); ds.setPassword(password); return ds; } } Spring 配置类中用 Import 注解引入 JdbcConfig 这个类: @Configuration @ComponentScan("com.xxx") +@Import({JdbcConfig.class}) public class SpringConfig { // ... } 3.2、整合Mybatis 导入 MyBatis 的依赖坐标: <!-- spring-jdbc --> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>6.1.4</version> </dependency> <!-- mybatis --> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.16</version> </dependency> <!-- mybatis-spring --> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>3.0.3</version> </dependency> <!-- mysql --> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> 新建一个 MyBatis 配置类 MybatisConfig,配置 SqlSessionFactoryBean 和 MapperScannerConfigurer 这两个 Bean: public class MybatisConfig { @Bean public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) { SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean(); ssfb.setDataSource(dataSource); ssfb.setTypeAliasesPackage("com.xxx.xxx"); return ssfb; } @Bean public MapperScannerConfigurer mapperScannerConfigurer() { MapperScannerConfigurer msc = new MapperScannerConfigurer(); // Mapper接口所在的包路径 msc.setBasePackage("com.xxx.mapper"); return msc; } } Spring 配置类中用 Import 注解引入 MybatisConfig 这个类: @Configuration @ComponentScan("com.xxx") +@Import({JdbcConfig.class, MybatisConfig.class}) public class SpringConfig { // ... } 3.3、整合Log4j 导入 Log4j 的依赖坐标: <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.23.1</version> </dependency> 在 "src/main/java/resources" 下新建一个 Log4j 的配置文件 Log4j2.xml: <?xml version="1.0" encoding="UTF-8"?> <!-- Configuration 具有 Appenders 和 Loggers 这两种子节点,每个子节点可以定义多个 --> <configuration> <!-- Appender节点,具有 Console(控制台)、File(文件)、RoolingFile(滚动文件)这三种类型的子节点 --> <Appenders> <!-- 输出日志信息到控制台 --> <Console name="console" target="SYSTEM_OUT"> <!--指定控制日志输出的格式--> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/> </Console> <!-- File 节点用来定义输出到指定位置的文件的 Appender,会将所有内容写入到同一个文件中 --> <!-- append属性设置写入新的日志时是追加在原内容后面,还是清除所有内容之后再写入 --> <!-- <File name="allLog" fileName="logs/AlliInOne.log" append="true">--> <!-- <ThresholdFilter level="ALL" onMatch="ACCEPT" onMismatch="DENY"/>--> <!-- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5p %c{9.9.9.1}(%L) %m%n"/>--> <!-- </File>--> <!-- RollingFile 节点,将日志写入文件,但是允许日志文件根据时间或大小进行滚动,从而避免单个文件过大 --> <!-- fileName 生成的初始日志文件 --> <RollingFile name="rollingFileInfo" fileName="logs/${date:yyyy-MM}/log-info-${date:yyyy-MM-dd}.log" filePattern="logs/${date:yyyy-MM}/log-info-%d{yyyy-MM-dd}-%i.log"> <!-- ThresholdFilter 只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) --> <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/> <!-- PatternLayout 指定控制日志输出的格式,不设置默认为:%m%n --> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/> <!-- Policies 滚动策略 --> <Policies> <!-- 按时间滚动 --> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> <!-- 按大小滚动 --> <SizeBasedTriggeringPolicy size="10MB"/> </Policies> <!-- DefaultRolloverStrategy 设置一个文件下保存的日志文件数量,不设置则默认为同一文件夹下7个文件,超过这个数量后,最老的文件将被删除 --> <DefaultRolloverStrategy max="20"/> </RollingFile> </Appenders> <!-- 在 Loggers 中引入上面定义好的 Appender --> <loggers> <!-- level指定日志级别,从低到高的优先级: ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF --> <!-- 设置org.mybatis包下的日志只打印WARN及以上级别 --> <Logger name="org.mybatis" level="WARN" additivity="false"> <appender-ref ref="console"/> <appender-ref ref="rollingFileInfo"/> </Logger> <!-- 设置org.springframework包下的日志只打印WARN及以上级别 --> <Logger name="org.springframework" level="WARN" additivity="false"> <appender-ref ref="console"/> <appender-ref ref="rollingFileInfo"/> </Logger> <root level="DEBUG"> <appender-ref ref="console"/> <appender-ref ref="rollingFileInfo"/> </root> </loggers> </configuration> Log4j 配置文件详解:彻底掌握Log4j2 - 蚂蚁小哥 - 博客园 修改 Mybatis 配置类中的 SqlSessionFactoryBean,设置其 Configuration 属性: public class MybatisConfig { // ... @Bean public SqlSessionFactoryBean getSqlSessionFactory(DataSource dataSource) { SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean(); ssfb.setTypeAliasesPackage("com.xxx.xxx"); ssfb.setDataSource(dataSource); + Configuration configuration = new Configuration(); + // 修改MyBatis使用的日志框架为Log4j2 + configuration.setLogImpl(Log4j2Impl.class); + ssfb.setConfiguration(configuration); return ssfb; } // ... } 3.4、整合Junit 导入 Junit 和 spring-test 的依赖坐标: <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.10.2</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-test --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.10.RELEASE</version> </dependency> 新建一个测试类: // 在 JUnit 5 中集成 Spring 功能,如果是Junit4则换成@RunWith(SpringJunit4ClassRunner.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = SpringConfig.class) public class SpringTest { @Autowired private UserMapper userMapper; @Test public void find() { User user = userMapper.select(); } } 四、Spring事务 导入 Spring 事务的依赖坐标: <!-- https://mvnrepository.com/artifact/org.springframework/spring-tx --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.10.RELEASE</version> </dependency> 在 Spring 的配置类上添加注解 @EnableTransactionManagement 开启 Spring 的事务管理功能: @Configuration @ComponentScan("com.xxx") @Import({JdbcConfig.class, MybatisConfig.class}) +@EnableTransactionManagement public class SpringConfig { // ... } 配置类中添加 PlatformTransactionManager 这个 Bean: @Bean public PlatformTransactionManager getTransactionManager(DataSource dataSource) { DataSourceTransactionManager dstm = new DataSourceTransactionManager(); dstm.setDataSource(dataSource); return dstm; } 最后在 service 方法或类上面用 @Transactional 注解设置添加事务管理,在执行该方法或类中的方法时便会开启事务管理,如果程序执行时发生异常就回滚之前的数据库操作。 需要注意的是,当在同一类中调用有 @Transactional 注解的方法时,可能不会触发事务管理,因为它绕过了 Spring 的代理机制,解决办法是将这个方法也标记为 @Transactional。
2021年11月10日
1,279 阅读
0 评论
1 点赞
2021-11-07
Spring学习笔记(二) - 通过XML配置整合框架
目录: 一、创建Webapp项目 二、加载properties文件 2.1、新建properties文件 2.2、指定要加载的properties文件 2.3、读取properties文件属性值的方式 三、数据源对象(以Druid为例) 3.1、导入Druid数据源依赖坐标 3.2、在XML文件中配置数据源对象为Spring管理的Bean 四、整合MyBatis 4.1、导入依赖坐标 4.2、配置SqlSessionFactoryBean 4.3、配置MapperScannerConfigurer 五、整合Junit 六、整合Log4j 在 pom.xml 文件中导入 Spring 的坐标依赖: <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency> 一、创建Webapp项目 在 IDEA 中根据 “maven-archetype-webapp” 模板创建 Webapp 项目: 创建基本项目结构: 在 src/main/resources 目录下新建一个 spring-config.xml 文件作为 Spring 的配置文件: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- Spring配置内容 --> </beans> 二、加载properties文件 2.1、新建properties文件 在 src/main/resources 目录下新建一个配置数据库连接信息的配置文件 jdbc.properties: jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db jdbc.username=root jdbc.password=root 2.2、指定要加载的properties文件 在 Spring 的 XML 配置文件中加载指定 properties 文件,写法有多种: 加载资源路径(src/main/java/resources)下指定的一个 properties 文件: <context:property-placeholder location="classpath:jdbc.properties"/> 也可以加载资源路径下的多个 properties 文件: <context:property-placeholder location="classpath:jdbc.properties,classpath:jdbc2.properties"/> 使用通配符加载 properties 文件:在资源路径或 jar 包中搜索并加载所有 properties 文件 <context:property-placeholder location="classpath*:*.properties"/> 再加上 system-properties-mode="NEVER",表示 Spring 容器在解析 properties 配置文件时,不使用系统属性来替换配置文件中的属性,从而避免系统属性对配置产生影响: <context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/> 2.3、读取properties文件属性值的方式 <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> 三、数据源对象(以Druid为例) 3.1、导入Druid数据源依赖坐标 <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.22</version> </dependency> 3.2、在XML文件中配置数据源对象为Spring管理的Bean <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <!-- 使用属性占位符 ${} 读取 properties 文件中的属性 --> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <!-- 初始连接数 --> <property name="initialSize" value="10"/> <!-- 最大连接池数量 --> <property name="maxActive" value="100"/> <!-- 超时时间 设置为-1时,如果没有可用连接,连接池会一直无限期等待,直到获取到连接为止。 如果设置为N(毫秒),则连接池会等待N毫秒,等待不到,则抛出异常 --> <property name="maxWait" value="-1"/> </bean> 四、整合MyBatis 4.1、导入依赖坐标 <!-- spring-jdbc --> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>6.1.4</version> </dependency> <!-- mybatis --> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.16</version> </dependency> <!-- mybatis-spring --> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>3.0.3</version> </dependency> <!-- mysql --> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> 4.2、配置SqlSessionFactoryBean SqlSessionFactoryBean 是 Spring 与 MyBatis 集成时使用的一个关键类,它实现了 Spring 的 FactoryBean<SqlSessionFactory> 接口,用于创建 SqlSessionFactory 对象。 <!-- 配置 SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入数据源,dataSource是在前面配置的数据库连接池 --> <property name="dataSource" ref="dataSource"/> <!-- 从类路径下加载在mybatis/mappers包和它的子包中所有的MyBatis映射器XML文件 --> <property name="mapperLocations" value="classpath*:com/wlplove/dao/*.xml"/> <!-- 为Java类型设置简短的名称,从而在MyBatis的映射器文件中直接使用这个名称,而不需要写完整的类名 --> <property name="typeAliasesPackage" value="com.wlplove.domain"/> <!-- 通过configLocation属性指定mybatis配置文件路径,不加入这个属性则使用Spring中的配置 --> <!-- <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>--> <!--MyBbatis配置 --> <property name="configuration"> <bean class="org.apache.ibatis.session.Configuration"> <property name="mapUnderscoreToCamelCase" value="true"/> <property name="cacheEna bled" value="true"/> <property name="defaultExecutorType" value="SIMPLE"/> <!-- 指定MyBatis所用日志的具体实现 --> <property name="logImpl" value="org.apache.ibatis.logging.log4j2.Log4j2Impl"/> </bean> </property> </bean> 4.3、配置MapperScannerConfigurer MapperScannerConfigurer 是 Spring 框架中用于整合 MyBatis 的一个配置类,它可以扫描指定包下的 Mapper 接口,并将它们的实现类自动注入到 Spring IoC 容器中。 <bean name="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- basePackage属性指定自动扫描Mapper接口所在的包 --> <property name="basePackage" value="com.wlplove.dao"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean> 五、整合Junit 导入 Junit 和 spring-test 的依赖坐标: <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.10.2</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-test --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.10.RELEASE</version> </dependency> 新建一个测试类: // 在 JUnit 5 中集成 Spring 功能,如果是Junit4则换成@RunWith(SpringJunit4ClassRunner.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = SpringConfig.class) public class SpringTest { @Autowired private UserMapper userMapper; @Test public void test() { User user = userMapper.select(); } } 六、整合Log4j 导入 Log4j 的依赖坐标: <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.23.1</version> </dependency> 在 "src/main/java/resources" 下新建一个 Log4j 的配置文件 Log4j2.xml: <?xml version="1.0" encoding="UTF-8"?> <!-- Configuration 具有 Appenders 和 Loggers 这两种子节点,每个子节点可以定义多个 --> <configuration> <!-- Appender节点,具有 Console(控制台)、File(文件)、RoolingFile(滚动文件)这三种类型的子节点 --> <Appenders> <!-- 输出日志信息到控制台 --> <Console name="console" target="SYSTEM_OUT"> <!--指定控制日志输出的格式--> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/> </Console> <!-- File 节点用来定义输出到指定位置的文件的 Appender,会将所有内容写入到同一个文件中 --> <!-- append属性设置写入新的日志时是追加在原内容后面,还是清除所有内容之后再写入 --> <!-- <File name="allLog" fileName="logs/AlliInOne.log" append="true">--> <!-- <ThresholdFilter level="ALL" onMatch="ACCEPT" onMismatch="DENY"/>--> <!-- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5p %c{9.9.9.1}(%L) %m%n"/>--> <!-- </File>--> <!-- RollingFile 节点,将日志写入文件,但是允许日志文件根据时间或大小进行滚动,从而避免单个文件过大 --> <!-- fileName 生成的初始日志文件 --> <RollingFile name="rollingFileInfo" fileName="logs/${date:yyyy-MM}/log-info-${date:yyyy-MM-dd}.log" filePattern="logs/${date:yyyy-MM}/log-info-%d{yyyy-MM-dd}-%i.log"> <!-- ThresholdFilter 只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) --> <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/> <!-- PatternLayout 指定控制日志输出的格式,不设置默认为:%m%n --> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/> <!-- Policies 滚动策略 --> <Policies> <!-- 按时间滚动 --> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> <!-- 按大小滚动 --> <SizeBasedTriggeringPolicy size="10MB"/> </Policies> <!-- DefaultRolloverStrategy 设置一个文件下保存的日志文件数量,不设置则默认为同一文件夹下7个文件,超过这个数量后,最老的文件将被删除 --> <DefaultRolloverStrategy max="20"/> </RollingFile> </Appenders> <!-- 在 Loggers 中引入上面定义好的 Appender --> <loggers> <!-- level指定日志级别,从低到高的优先级: ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF --> <!-- 设置org.mybatis包下的日志只打印WARN及以上级别 --> <Logger name="org.mybatis" level="WARN" additivity="false"> <appender-ref ref="console"/> <appender-ref ref="rollingFileInfo"/> </Logger> <!-- 设置org.springframework包下的日志只打印WARN及以上级别 --> <Logger name="org.springframework" level="WARN" additivity="false"> <appender-ref ref="console"/> <appender-ref ref="rollingFileInfo"/> </Logger> <root level="DEBUG"> <appender-ref ref="console"/> <appender-ref ref="rollingFileInfo"/> </root> </loggers> </configuration> Log4j 配置文件详解:彻底掌握Log4j2 - 蚂蚁小哥 - 博客园 修改 Spring 配置中的 SqlSessionFactoryBean,修改 MyBatis 使用的日志框架为 Log4j2: <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入数据源,dataSource 是在前面配置的数据库连接池--> <property name="dataSource" ref="dataSource"/> <!--从类路径下加载在 mybatis/mappers 包和它的子包中所有的 MyBatis 映射器 XML 文件--> <property name="mapperLocations" value="classpath*:com/wlplove/dao/*.xml"/> <!-- 给包下的所有对象起一个别名,用这个别名直接引用,不需要再加包名 --> <property name="typeAliasesPackage" value="com.wlplove.entity"/> <!-- MyBbatis 配置 --> <property name="configuration"> <bean class="org.apache.ibatis.session.Configuration"> <!-- 指定 MyBatis 所用日志的具体实现 --> <property name="logImpl" value="org.apache.ibatis.logging.log4j2.Log4j2Impl"/> </bean> </property> </bean>
2021年11月07日
1,782 阅读
0 评论
1 点赞
2021-11-05
Spring学习笔记(一) - Bean与依赖注入
目录: 一、Spring简介 二、Bean配置 2.1、基础配置 2.2、别名配置 2.3、作用域 三、Bean的生命周期 3.1、实例化 3.2、属性赋值 3.3、初始化 3.4、使用 3.5、销毁 3.6、示例:通过XML配置自定义初始化和销毁操作 3.7、示例:通过注解自定义初始化和销毁操作 四、实例化Bean的方式 4.1、无参构造方法 4.2、静态工厂 4.3、实例工厂 4.3.1、方式一 4.3.2、方式二(重要) 五、依赖注入(DI) 5.1、setter 注入 5.1.1、注入简单类型 5.1.2、注入引用类型 5.2、构造器注入 5.2.1、注入简单类型 5.2.2、注入引用类型 5.2.3、构造器注入的其他XML配置写法 type方式 index索引方式 5.3、自动装配 5.3.1、基于XML文件的自动装配 byType byName constructor 5.3.2、基于注解的自动装配 @Autowired @Resource 六、集合的注入 6.1、注入数组 6.2、注入 Set 对象 6.3、注入 List 对象 6.4、注入 Map 类型数据 6.5、注入 property 类型数据 七、Spring 容器的核心操作 7.1、创建容器 7.2、从 Srping 容器获取 Bean 7.2.1、根据 Bean 名称 7.2.2、根据 Bean 类型获取 7.2.3、根据 Bean 名称获取并指定类型 八、一图流总结 8.1、Bean: 8.2、依赖注入: 一、Spring简介 Spring 是一个开源的 Java 平台框架,它由 Rod Johnson 在 2003 年首次发布,旨在简化 Java 开发,为构建企业级应用程序提供全面的基础设施支持。Spring 的核心特性包括依赖注入(DI)和面向切面编程(AOP)。 在 pom.xml 文件中导入 Spring 的坐标依赖: <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency> 二、Bean配置 Bean 是 Spring 框架中最核心的概念之一,它是构成应用程序主干并由 Spring IoC 容器管理的对象。 Spring Bean 本质上是由 Spring IoC 容器实例化、组装和管理的对象,可以是任何 Java 类的实例,比如表示用户信息的 User 这类简单的 POJO(Plain Old Java Object),在 Spring 容器中被定义为一个 Bean 之后,Spring 可以对该 Java 类的实例进行各种诸如依赖注入等的管理操作。除此之外,Bean 也可以是一个复杂的企业级组件。 在 Java 中,对象一般通过 new 关键字来创建,但在 Spring 框架中由 IoC 容器负责这部分工作,完成对象的创建和依赖注入。这种机制允许开发者将对象的生命周期管理和依赖关系交给 Spring 容器管理,从而简化开发过程并提高代码的可维护性和灵活性。 2.1、基础配置 在 XML 文件中通过 <bean> 标签来配置需要让 Spring 管理的 Bean。 <!-- bean标签用来配置bean id属性是bean的名字,是唯一的,不能重复 class属性是bean的类型,值是全路径类名 --> <bean id="userDao" class="com.wlplove.dao.impl.UserDaoImpl"/> <bean id="userService" class="com.wlplove.service.impl.UserServiceImpl"> <!-- property标签用来配置当前bean中的的属性,需要其有set方法 name属性指定配置的哪一个属性,是属性的名称 ref属性表示参照哪一个bean,是当前容器中存在的bean --> <property name="userDao" ref="userDao"/> </bean> 在 XML 文件中经过上面的配置之后,就将这个类交给了 Spring IoC 容器进行管理,之后需要使用该类时就可以从容器中获取到实例对象了。 2.2、别名配置 name 属性表示 Bean 的别名,可以设置多个,使用逗号、分号、空格分开。 别名配置完成后,就可以通过别名直接获取 Bean 或者在其他 Bean 的配置中通过别名引用这个 Bean。 <bean id="bookService" name="service service2 bookEbi" class="com.wlplove.service.impl.BookServiceImpl"/> 2.3、作用域 Spring Bean 的 Scope 指的是 Bean 的作用域,它定义了 Bean 实例在 Spring 容器中的生命周期和可见性。一般常用的是 singleton(单例)、prototype(原型/非单例): singleton:默认作用域,Spring 容器只会创建一个 Bean 实例,并在整个应用程序中共享。这个实例从容器启动时被初始化,直到容器销毁才会被释放。这种模式适用于无状态的服务对象,如 DAO 组件、控制器等。 prototype:每次请求 Bean 时都会创建一个新的实例。这些实例由 Spring 容器创建,但容器不会跟踪它们的管理和销毁,这些任务交由 Java 的垃圾回收机制来处理。适用于需要频繁创建且生命周期较短的 Bean,如用户会话数据对象。 Bean 的作用域通过 scope 属性来配置: <bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl" scope="singleton"/> 也可以使用注解 @Scope 定义作用域: public class AppConfig { // @Scope("prototype") @Scope("singleton") public MyBean myBean() { return new MyBean(); } } 除此之外,还有 request、session 作用域。 request:一次 HTTP 请求都会产生一个新的 Bean,该 Bean 仅在当前 HTTP request 内有效。 session:每一次 HTTP 请求都会产生一个新的 Bean,该 Bean 仅在当前 HTTP session 内有效。 三、Bean的生命周期 Spring Bean 的生命周期是指 Bean 在 Spring 中从创建到销毁的整个过程。 对于作用域为 prototype 的 Bean(即非单例模式的 Bean)来说,Spring 在创建好 Bean 交给使用者之后则不会再管理后续的生命周期。 所以一般所说的 Bean 的生命周期主要针对于作用域为 singleton 的 Bean(即单例模式的 Bean),其生命周期主要包括实例化、属性赋值、初始化、使用和销毁五个阶段。 3.1、实例化 实例化指的是由 Spring 容器创建 Bean 实例的过程。 在这个阶段,Spring 容器通过反射机制来创建 Bean 的实例, 那就吗,给 Bean 分配内存空间。 3.2、属性赋值 Spring 容器根据 Bean 信息,为 Bean 的属性进行赋值操作。 这一阶段通常涉及到依赖注入(DI),Spring 容器会自动填充 Bean 的属性并将其他 Bean 的引用注入到当前 Bean 的属性中。 3.3、初始化 初始化阶段是 Spring Bean 生命周期中的一个重要环节,它标志着 Bean 已经准备好被使用。 在这个阶段,Spring 容器会执行一系列初始化操作,其中包括开发者自定义的初始化方法,开发者可以通过以下几种方式自定义初始化操作: XML 配置时在 <bean> 标签中定义属性 init-method 使用 @PostConstruct 注解定义初始化方法 实现接口 InitializingBean 的 afterPropertiesSet() 方法 3.4、使用 这一阶段,Bean 已经被完全初始化,并且可以被 Spring 容器提供给其他组件或服务进行调用。 3.5、销毁 销毁阶段是 Spring Bean 生命周期的最后一个阶段,它发生在 Bean 不再被需要时。 在这个阶段,Spring 容器会执行一系列的清理操作,包括开发者自定义的 Bean 的销毁方法,开发者可以通过以下几种方式: XML 配置时在 <bean> 标签中定义 destroy-method 属性 使用 @PreDestroy 注解定义销毁方法 实现接口 DisposableBean 的 destroy() 方法 这些操作旨在释放 Bean 所占用的资源,确保系统的稳定性和性能。 3.6、示例:通过XML配置自定义初始化和销毁操作 在 Java 类中分别定义一个初始化方法和销毁方法: public class UserServiceImpl implements UserService { @Override public void save() { System.out.println("save ..."); } // Bean 的初始化方法 publid void init() { System.out.println("init ...."); } // Bean 的销毁方法 public void destory() { System.out.println("destory ..."); } } 在 XML 配置中通过 init-method 方法指定 Bean 的初始化方法,destory-method 方法指定 Bean 的销毁方法: <!-- “init-method”方法指定Bean的初始化方法,“destory-method”方法指定Bean的销毁方法 --> <bean id="userService" class="com.wlplove.service.impl.UserServiceImpl" init-method="init" destroy-method="destory" /> 3.7、示例:通过注解自定义初始化和销毁操作 在初始化方法上使用 @PostConstruct 注解,在销毁方法上使用 @PreDestroy 注解 public class UserServiceImpl implements UserService { @Override public void save() { System.out.println("save ..."); } // Bean 的初始化方法 @PostConstruct publid void init() { System.out.println("init ...."); } // Bean 的销毁方法 @PreDestroy public void destory() { System.out.println("destory ..."); } } 四、实例化Bean的方式 4.1、无参构造方法 Java 接口继承类中提供一个无参构造方法: public class UserServiceImpl implements UserService { private UserServiceImpl() { System.out.println("constructor is running ...."); } public void save() { System.out.println("save ..."); } } XML 配置: <bean id="userService" class="com.wlplove.service.impl.UserServiceImpl"/> 在接口继承类中必须要提供无参构造方法,如果不存在,将抛出 BeanCreationException 异常 4.2、静态工厂 单独创建一个工厂类 UserServiceFactory,其中提供一个静态方法 getUserService 作为实例化 Bean 的方法: public class UserServiceImpl implements UserService { // ... } // 工厂类 public class UserServiceFactory { // 静态工厂方法 public static UserService getUserService() { return new UserServiceImpl(); } } 在 XML 配置中 factory-method 指定静态工厂方法: <!-- class属性指定工厂类,factory-method属性指定使用工厂类中的哪个静态方法实例化Bean --> <bean id="userService" class="com.wlplove.factory.UserServiceFactory" factory-method="getUserService"/> 4.3、实例工厂 4.3.1、方式一 定义一个工厂类 UserServiceFactory: // 工厂类 public class UserServiceFactory { // 工厂方法(非静态) public UserService getUserService() { return new UserServiceImpl(); } } XML 配置:' XML 中配置工厂类为 Bean,在需要实例化的 Bean 中通过 factory-bean 指定工厂类,factory-method 方法指定用工厂类的哪个方法来实例化 Bean。 <!-- 工厂 Bean --> <bean id="userServiceFactory" class="com.wlplove.factory.UserServiceFactory" /> <!-- 用前面定义好的工厂类userServiceFactory中的方法 getUserService 来实例化 userService --> <!-- factory-bean 指定工厂类,factory-method 方法指定用工厂类的哪个方法实例化 Bean --> <bean id="userService" factory-bean="userServiceFactory" factory-method="getUserService" /> 4.3.2、方式二(重要) FactoryBean 是 Spring 框架中用于创建复杂对象的一个特殊接口,它允许开发者创建一个工厂类,用于生成其他对象。这个接口定义了三个主要方法: getObject() 作用:返回由该 FactoryBean 创建的对象实例。这是 FactoryBean 最核心的方法,用于实际创建对象实例。 注意:这个方法可能会抛出异常,因为对象的创建过程可能涉及复杂的逻辑和资源管理。 getObjectType() 作用:返回由该 FactoryBean 创建的对象的类型。这有助于 Spring 容器在运行时了解 Bean 的类型信息,从而进行类型匹配和依赖注入。 默认实现:如果未重写此方法,则默认返回 Object.class。 isSingleton() 作用:指定返回的实例是否为单例。如果返回 true,则表示这个 FactoryBean 返回的对象在整个 Spring 容器中是唯一的;如果返回 false,则每次请求都会创建一个新的实例。 默认实现:如果没有明确指定,默认返回 true,即单例模式。 工厂类 UserServiceFactoryBean 继承 FactoryBean<T> 接口,并重写其中的 getObject() 和 getObjectType() 方法 // 省略 UserService 接口与 UserServiceImpl 继承类的定义... public class UserServiceFactoryBean implements FactoryBean<UserService> { @Override public UserServce getObject() throws Exception { return new UserServiceImpl(); } @Override public Class<?> getObjectType() { return UserService.class; } @Override public boolean isSingleton() { // true 返回单例对象,false 返回非单例对象 return ture; } } XML 配置: <bean id="userService" class="com.wlplove.factory.UserServiceFactoryBean" /> 五、依赖注入(DI) 依赖注入(Dependency Injection,简称 DI)是 Spring 框架的核心概念之一,用于实现控制反转(Inversion of Control,即 IoC),其核心思想是:对象的依赖关系由外部容器(如 Spring IoC 容器)在运行时注入,而不是由对象自己创建或查找依赖。 举个例子,假设有一个 UserService 类,它依赖于 UserDao 类。在没有依赖注入的情况下,UserService 需要自己创建 UserDao 的实例。这种方式的问题在于,UserService 和 UserDao 紧密耦合在一起,难以测试和维护。 而使用依赖注入后,UserService 不再自己创建 UserDao 实例,而是通过构造函数、Set 方法或自动装配的方式,由 Spring 容器提供 UserDao 的实例,从而提升代码的松耦合性、可测试性、可维护性和可扩展性 Spring 提供了三种主要的依赖注入方式:setter 注入、构造器注入、自动装配。 5.1、setter 注入 5.1.1、注入简单类型 Java 类中提供成员的 set 方法: public class User { private String name; private int age; public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } } Spring 配置中用 <property> 标签来定义具体成员: <!-- 简单类型 (通过 value 属性注入普通类型值) --> <bean id="user" class="com.wlplove.dto.User"> <property name="name" value="test" /> <property name="age" value="20" /> </bean> 5.1.2、注入引用类型 同样的,在 Java 类中提供对象的 set 方法: // 省略接口UserDao接口的定义 ... // 接口继承类 public class UserServiceImpl implements userService { private UserDao userDao; private setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void save() { userDao.insert(); } } XML 配置: <bean id="userDao" class="com.wlplove.dao.UserDao" /> <!-- 引用类型 (通过 ref 属性注入引用类型值) --> <bean id="userServiceImpl" class="com.wlplove.service.UserServiceImpl"> <property name="userDao" ref="userDao" /> </bean> 5.2、构造器注入 在对象创建时,所有的依赖都通过构造器参数传入,类似于 setter 注入需要提供成员 set 方法,构造方法注入需要在类中提供构造方法,并且 XML 配置里使用构造器注入的参数顺序必须要与 Java 类构造方法的形参顺序相同。 5.2.1、注入简单类型 构造方法: public class User { private String name; private int age; public void User(String name, int age) { this.name = name; this.age = age; } } XML 配置: <!-- 普通类型 (通过 value 属性注入引用类型值,name属性值是构造方法中的形参)--> <bean id="user" class="com.wlplove.dto.User"> <!-- 这里的参数顺序要与构造函数中的形参顺序相同 --> <construct-arg name="name" value="test" /> <construct-arg name="age" value="20" /> </bean> 5.2.2、注入引用类型 public class UserServiceImpl implements userService { private UserDao userDao; private UserServiceImpl(UserDao userDao) { this.userDao = userDao; } @Override public void save() { userDao.insert(); } } XML 配置: <bean id="userDao" class="com.wlplove.dao.UserDao" /> <!-- 引用类型 (通过 ref 属性注入引用类型值,name 属性的值是构造方法形参的值)--> <bean id="userServiceImpl" class="com.wlplove.service.UserServiceImpl"> <constructor-arg name="userDao" ref="userDao" /> </bean> 5.2.3、构造器注入的其他XML配置写法 type方式 在 constructor-arg 标签中使用 type 属性: <bean id="userDao" class="com.wlplove.dao.UserDao" /> <bean id="userServiceImpl" class="com.wlplove.service.UserServiceImpl"> <constructor-arg type="int" value="123" /> <constructor-arg type="java.lang.String" value="test" /> <constructor-arg type="com.wlplove.dao.UserDao" ref="userDao" /> </bean> type 方式解决了构造器注入时 name 属性绑定了形参名称从而造成强耦合的问题,如果一个 Bean 中某种类型(比如 int)的属性只有一个,那么可以用这种方式,反之,当相同类型的属性有很多个时,用这种方式就会给所有这种类型的属性都注入相同的值。 index索引方式 在 constructor-arg 标签中使用 index 属性: <!-- index索引方式,解决参数类型重复问题,使用索引进行参数匹配对应 --> <bean id="userDao" class="com.wlplove.dao.UserDao" /> <bean id="user" class="com.wlplove.dao.dto.User"> <constructor-arg index="0" value="20" /> <constructor-arg index="1" value="test" /> <constructor-arg index="2" ref="userDao" /> </bean> 5.3、自动装配 5.3.1、基于XML文件的自动装配 所谓自动装配就是省略手动配置 Bean 依赖的步骤,而使 Spring 容器自动解析 Bean 之间的依赖关系,并将依赖的 Bean 注入到目标 Bean 中。需要注意的是,自动装配只针对于 Bean 中引用类型的依赖,简单类型的依赖无法被自动装配。 byType Spring 容器会根据属性的类型来自动装配。它会在容器中查找与需要注入的属性类型相同的 Bean,并将其注入到该属性中。 要使 Spring 容器按 Bean 类型自动装配,加入 autowire="byType": <bean id="bookService" class="com.wlplove.service.UserServiceImpl" autowire="byType" /> 使用 byType 这种方式,必须保证配置文件中所有 Bean 的 class 属性值是唯一的,否则就会报错 byName Spring 容器会根据属性名称来自动装配,在容器中查找与需要注入的属性名称相同的 Bean,并将其注入到该属性中。严格来说,此处的属性名称并非是指成员变量名称,而是指 set 方法名去掉 set 之后将首字母小写的名称,比如 User 类中 age 属性的 set 方法为 setAge1(),那么将 set 方法处理之后得到的属性名称为 age1,Spring 容器根据属性名称 age1 自动装配,而不是 age。 要使 Spring 容器按 Bean 名称自动装配,加入 autowire="byName": <bean id="bookService" class="com.wlplove.service.UserServiceImpl" autowire="byName" /> constructor 基于构造函数的自动装配。Spring 容器会查找与构造函数中形参的数量与类型相同的 Bean,并通过构造函数注入。 <bean id="bookService" class="com.wlplove.service.UserServiceImpl" autowire="constructor" /> 5.3.2、基于注解的自动装配 @Autowired @Autowired 是 Spring 框架提供的自动装配注解,是目前最常用的方式,默认按类型(byType)匹配。 构造器注入(推荐):将 @Autowired 注解标注在构造器上,Spring 容器根据构造器参数类型来注入依赖。 public class UserServiceImpl implements userService { private UserDao userDao; @Autowired private UserServiceImpl(UserDao userDao) { this.userDao = userDao; } } 从 Spring 4.3 开始,如果类只有一个构造器,@Autowired 也可省略。 Setter方法注入:将 @Autowired 注解标注在 set 方法上,Spring 容器根据 set 方法的名称和参数类型来注入依赖。 public class UserServiceImpl implements userService { private UserDao userDao; @Autowired private setUserDao(UserDao userDao) { this.userDao = userDao; } } 字段注入:直接将 @Autowired 注解标注在字段上,Spring 容器根据字段类型来注入依赖。 public class UserServiceImpl implements userService { @Autowired("name") private String userName; } @Resource @Resource 由 Java EE 提供,默认按名称(byName)匹配(属性名或 name 属性)。若找不到同名 Bean,则回退到按类型匹配。 六、集合的注入 接下来的例子以 setter 注入为例进行说明,构造器注入类似,将 <property></property> 标签修改为 <constructor-arg></constructor-arg> 即可。 6.1、注入数组 <!-- name 属性设置将数据注入哪一个数组 --> <property name="test_array"> <!-- 此处 array 与 list 可以混用 --> <array> <!-- 引用类型 --> <value>123</value> <value>456</value> <value>789</value> <!-- 引用类型 --> <!-- <ref bean="beanId"></ref> --> </array> </property> 6.2、注入 Set 对象 <property name="test_set"> <set> <value>set_qwe1</value> <value>set_asd2</value> <value>set_zxc3</value> <!-- 重复会自动过滤 --> <value>set_zxc3</value> </set> </property> 6.3、注入 List 对象 <property name="test_list"> <!-- 此处 array 与 list 可以混用 --> <list> <value>list_qwe1</value> <value>list_asd2</value> <value>list_zxc3</value> </list> </property> 6.4、注入 Map 类型数据 注入 Map 对象: <property name="test_map"> <map> <entry key="contry" value="China"></entry> <entry key="province" value="Gansu"></entry> <entry key="city" value="Lanzhou"></entry> </map> </property> 6.5、注入 property 类型数据 <property name="test_properties"> <props> <prop key="contry">China</prop> <prop key="province">Hehei</prop> <prop key="city">Zhengzhou</prop> </props> </property> <property> 标签表示 setter 方式注入,构造方式注入 <constructor-arg> 标签内部也可以写 <array>、<list>、<set>、<map>、<props> 标签 List 的底层也是通过数组实现的,所以 <list> 和 <array> 标签可以混用 若要在集合中添加引用类型,只需要把 <value> 标签改成 <ref> 标签,但这种方式用的比较少 七、Spring 容器的核心操作 7.1、创建容器 使用 ClassPathXmlApplicationContext 类通过类路径来加载配置文件的方式创建容器: ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); // 将多个XML文件的Bean配置都加载到一个Spring容器中: ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext1.xml", "applicationContext2.xml"); 也可以使用 FileSystemXmlApplicationContext 通过文件路径来加载配置文件的方式创建容器: ApplicationContext ctx = new FileSystemXmlApplicationContext("E:\\applicationContext.xml"); // 将多个XML文件的Bean配置都加载到一个Spring容器中: ApplicationContext ctx = new FileSystemXmlApplicationContext("E:\\applicationContext1.xml", "E:\\applicationContext2.xml"); 7.2、从 Srping 容器获取 Bean 7.2.1、根据 Bean 名称 // 需要类型强制转换 BookDao bookDao = (BookDao) ctx.getBean("bookDao"); 7.2.2、根据 Bean 类型获取 // 参数是 类名.class BookDao bookDao = ctx.getBean(BookDao.class); 当按类型获取 Bean 时,必须保证该类型的 Bean 在 Spring 容器中只有一个,否则会抛出 NoUniqueBeanDefinitionException 异常。 7.2.3、根据 Bean 名称获取并指定类型 BookDao bookDao = ctx.getBean("bookDao", BookDao.class); 八、一图流总结 8.1、Bean: 8.2、依赖注入:
2021年11月05日
1,669 阅读
0 评论
4 点赞
2021-10-31
MySQL数据表中的auto_increment自增值属性及修改
目录: 0x01. 查看自增值 1、查看全局自增值 2、查看特定数据表的自增值 0x02. 自增值的取值问题 1、默认取值 2、建表时指定 3、直接指定 4、修改自增字段属性 环境说明: MySQL 5.7 、MySQL 8.0 长期以来,我的博客数据库中连续文章的主键编号一直都不是连续的,让我这个强迫症晚期患看着很不舒服。在忍受了这么长时间以后,趁着给博客换域名的时机,我把所有的文章编号全部改成了连续的,可算是舒服多了。 把改完主键编号的文章数据导入新数据库之后,就产生了一个新问题:现在新数据表的主键自增值还是旧数据表的主键自增值。比如说博客数据库的文章表中有80条数据,下次新创建文章的编号是以旧数据库的自增值202开始递增,生成的新文章编号是203,而不是81。如果要保持文章编号能连续的话,就只能每次发布完新博客之后再去改数据库中的编号,可谓是相当麻烦。 所以我便想着是否可以通过修改数据表里主键的 AUTO_INCREMENT 自动递增值来一步到位,查了一些资料之后,就有了这篇博客。 0x01. 查看自增值 一般来说,数据表中具有自增属性 AUTO_INCREMENT 的字段主要是数据表的主键或者具有唯一性的字段。 如果要查阅其递增值,有这么两种方式: 1、查看全局自增值 SHOW VARIABLES LIKE 'AUTO_INC%'; 这条命令的执行结果会返回两条数据: AUTO_INCREMENT_INCREMENT 表示自增的初始值,AUTO_INCREMENT_OFFSET 表示自增的步长,即每次的自增量。 修改自增初始值与自增量的命令: SET @@AUTO_INCREMENT_INCREMENT=新初始值; SET @@AUTO_INCREMENT_OFFSET=新步长; 注意:这个表示的是数据库全局的自增设置,因此修改以后只会影响到下次新增的带有 AUTO_INCREMENT 属性的列,其自增初始值与自增步长就是新设置的值,对当前已经带有 AUTO_INCREMENT 属性的列的自增初始值与自增步长不起作用。 2、查看特定数据表的自增值 要想查看某个数据表中自增字段的当前自增值,可用以下命令: SHOW TABLE STATUS FROM [数据库名] LIKE [表名]; FROM [数据库名] 与 LIKE [表名] 这两个子句是可选的。 FROM [数据库名] 表示指定查询所在的数据库;LIKE [表名] 指定该数据库下要查询的某个表,如果省略了 LIKE [表名] 子句,则表示查看该数据库下的所有表的信息。 另外,需要注意的是,表名要加引号。 比如查询 TEST 数据库下 test 表的信息: SHOW TABLE STATUS FROM TEST LIKE 'test'; 查询结果中有一个字段名为 “AUTO_INCREMENT”,表示的就是自增值,该表下一条记录的编号就是这个值。 0x02. 自增值的取值问题 1、默认取值 默认情况下,自增值从1开始,每增加一条新记录,自增值便会自增 1。 所以,对于具有 AUTO_INCREMENT 属性的列,不用特意设置列值,而是直接将 NULL 值插入到自增列中去,数据库会自动根据当前的自增值生成列值。 注意: 将 0 插入到自增列中的效果等同于插入 NULL 值; 当插入记录时,如果没有为自增列指明一个值,那么也等同于插入 NULL; 使用 INSERT 语句插入记录时,如果为自增列设置了一个值,那么会出现这样三种情况: 情况一,插入的值与已有的编号重复,则会出现报错 情况二,插入的值大于列的自增值,成功插入这条记录,并且会更新自增值为新值 情况三,插入的值小于列的自增值且与已有的编号不重复,则成功插入这条记录,但自增值不会更新,如果插入的值与已有的值重复,参考情况一 如果用 UPDATE 语句更新自增列,情况与 INSERT 语句相同。 2、建表时指定 我们也可在建表时使用 “AUTO_INCREMENT=自增值” 来指定一个自增的初始值,比如: CREATE TABLE TEST{ -- 建表语句 }AUTO_INCTEMENT=自增值; 3、直接指定 ALTER TABLE [表名] AUTO_INCREMENT=自增值; 如果执行完以后没有效果,那么可以再执行一次 commit 指令以提交更改,使其生效。 4、修改自增字段属性 ALTER TABLE [表名] MODIFY [字段名] [字段类型和约束条件], AUTO_INCREMENT=自增值; 同样的,如果执行完以后没有效果,再执行一次 commit 提交更改。 这个语句相当于直接修改自增字段的属性,包括其数据类型和约束条件。 另外,我在尝试中发现,使用 SQL 语句更改了自增值以后再执行 SHOW TABLE STATUS 语句来验证是否修改成功,本地的数据库显示自增值已经修改过来了。但是,服务器上的数据库自增值依然还是原来的值,可是,当我在重新插入一条记录时,新记录的自增值却是修改过后的值。这属实让我有点摸不着头脑,我也不太确定是不是 MySQL 版本不同的原因,也懒得再尝试了,所以把这种情况记录上来以供参考吧。
2021年10月31日
3,192 阅读
0 评论
0 点赞
1
2
...
4