当前位置: 首页 > news >正文

MFC界面美化第三篇----自绘按钮(重绘按钮)

1.前言

最近发现读者对我的mfc美化的专栏比较感兴趣,因此在这里进行续写,这里我会计划写几个连续的篇章,包括对MFC按钮的美化,菜单栏的美化,标题栏的美化,list列表的美化,直到最后形成一个完整的成品效果。

2.最终效果展示

点击启动按钮之后,能响应功能。

博主不会UI设计图片显示的效果比较差,用更好的图片贴上去,就能显示更好的效果的

3.思路分析

1.使用我们设计好的图片,来美化按钮。把图片贴到按钮上

2.创建一个按钮类,继承CButton,来重绘按钮

3.核心还是重写了 DrawItem,函数来实现重绘的效果

4.实现过程

1.在MFC的界面那里,创建两个按钮,启动和停止按钮。

2.找到按钮的属性那里,把所有者描述改成true,不然重绘是不能成功的,这一步是一定要做的

3.核心代码

头文件里面声明按钮变量

public:CMyButton m_button_start;CMyButton m_button_stop;afx_msg void OnBnClickedButton2();afx_msg void OnBnClickedButton1();

OnInitDialog()函数

BOOL CMFCDrawnTitleDlg::OnInitDialog()
{CDialogEx::OnInitDialog();// 将“关于...”菜单项添加到系统菜单中。// IDM_ABOUTBOX 必须在系统命令范围内。ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != nullptr){BOOL bNameValid;CString strAboutMenu;bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);ASSERT(bNameValid);if (!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);}}// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动//  执行此操作SetIcon(m_hIcon, TRUE);			// 设置大图标SetIcon(m_hIcon, FALSE);		// 设置小图标// TODO: 在此添加额外的初始化代码//退出按钮绘制CRect rtBtnClo;GetClientRect(&rtBtnClo);rtBtnClo.left = rtBtnClo.right - 20;m_btnExit.SetImagePath(_T(".\\res\\icon_popup_off.png"), _T(".\\res\\icon_popup_off.png"), _T(".\\res\\icon_popup_off.png"));m_btnExit.InitMyButton(rtBtnClo.left, 5, 16, 16, true);//最大化按钮绘制GetClientRect(&rtBtnClo);rtBtnClo.left = rtBtnClo.right - 50;m_btnMax.SetImagePath(_T(".\\res\\icon_square.png"), _T(".\\res\\icon_square.png"), _T(".\\res\\icon_square.png"));m_btnMax.InitMyButton(rtBtnClo.left, 5, 16, 16, true);//最小化按钮// GetClientRect(&rtBtnClo);rtBtnClo.left = rtBtnClo.right - 80;m_btnMin.SetImagePath(_T(".\\res\\icon_minimiz.png"), _T(".\\res\\icon_minimiz.png"), _T(".\\res\\icon_minimiz.png"));m_btnMin.InitMyButton(rtBtnClo.left, 5, 16, 16, true);return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

5.button重绘的头文件和cpp文件

mybutton.h

pch.h, 和framework.h这两个文件的路径,可以改的。根据你的项目文件实际放置情况

#pragma once#include <afxwin.h>
#include "../pch.h"
#include "../framework.h"
// CMyButtonclass CMyButton : public CButton
{DECLARE_DYNAMIC(CMyButton)public:CMyButton();virtual ~CMyButton();protected://正常状态图像路径CString m_strNormalImgPath;//按下状态图像路径CString m_strPressImgPath;//悬浮状态图像路径CString m_strFloatImgPath;//正常状态图像CImage m_imgNormal;//按下状态图像CImage m_imgPress;//悬浮状态图像CImage m_imgFloat;//窗口背景图片CImage m_BkImg;public://设置按钮图片路径void SetImagePath(CString strNoramlImgPath, CString strPressImgPath, CString strFloatImgPath);//初始化按钮,主要是调整按钮的位置,处理透明色  bool InitMyButton(int nX/*左上角X坐标*/, int nY/*左上角Y坐标*/, int nW/*图像宽*/, int nH/*图像高*/, bool bIsPng/*是否是PNG图片*/);//自绘制函数  void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);//初始化窗口背景void SetBkImg(CString strBkImg);//释放图片资源,方便最大化 void ReleaseImg();
protected://光标是否在窗口内BOOL m_bIsInWnd;DECLARE_MESSAGE_MAP()
public:afx_msg void OnMouseMove(UINT nFlags, CPoint point);afx_msg void OnMouseHover(UINT nFlags, CPoint point);afx_msg void OnMouseLeave();};

mybutton.cpp

// MyButton.cpp : 实现文件
//#include "pch.h"
#include "MyButton.h"// CMyButtonIMPLEMENT_DYNAMIC(CMyButton, CButton)CMyButton::CMyButton()
{m_bIsInWnd = FALSE;
}CMyButton::~CMyButton()
{
}BEGIN_MESSAGE_MAP(CMyButton, CButton)ON_WM_MOUSEMOVE()ON_WM_MOUSEHOVER()ON_WM_MOUSELEAVE()
END_MESSAGE_MAP()// CMyButton 消息处理程序//设置按钮图片路径
void CMyButton::SetImagePath(CString strNoramlImgPath, CString strPressImgPath, CString strFloatImgPath)
{m_strNormalImgPath = strNoramlImgPath;m_strPressImgPath = strPressImgPath;m_strFloatImgPath = strFloatImgPath;
}void CMyButton::SetBkImg(CString strBkImg)
{if (strBkImg.IsEmpty())return;m_BkImg.Load(strBkImg);
}//初始化按钮,主要是调整按钮的位置,处理透明色 
void CMyButton::ReleaseImg()
{if (m_imgNormal){m_imgNormal.Destroy();}if (m_imgPress){m_imgPress.Destroy();}if (m_imgFloat){m_imgFloat.Destroy();}
}
bool CMyButton::InitMyButton(int nX/*左上角X坐标*/, int nY/*左上角Y坐标*/, int nW/*图像宽*/, int nH/*图像高*/, bool bIsPng/*是否是PNG图片*/)
{HRESULT hr = 0;if (m_strNormalImgPath.IsEmpty())return false;if (m_strPressImgPath.IsEmpty())return false;if (m_strFloatImgPath.IsEmpty())return false;hr = m_imgNormal.Load(m_strNormalImgPath);int a = GetLastError();if (FAILED(hr))return false;hr = m_imgPress.Load(m_strPressImgPath);if (FAILED(hr))return false;hr = m_imgFloat.Load(m_strFloatImgPath);if (FAILED(hr))return false;if (bIsPng){if (m_imgNormal.GetBPP() == 32){int i = 0;int j = 0;for (i = 0; i < m_imgNormal.GetWidth(); i++){for (j = 0; j < m_imgNormal.GetHeight(); j++){byte * pbyte = (byte *)m_imgNormal.GetPixelAddress(i, j);pbyte[0] = pbyte[0] * pbyte[3] / 255;pbyte[1] = pbyte[1] * pbyte[3] / 255;pbyte[2] = pbyte[2] * pbyte[3] / 255;}}}if (m_imgPress.GetBPP() == 32){int i = 0;int j = 0;for (i = 0; i < m_imgPress.GetWidth(); i++){for (j = 0; j < m_imgPress.GetHeight(); j++){byte * pbyte = (byte *)m_imgPress.GetPixelAddress(i, j);pbyte[0] = pbyte[0] * pbyte[3] / 255;pbyte[1] = pbyte[1] * pbyte[3] / 255;pbyte[2] = pbyte[2] * pbyte[3] / 255;}}}if (m_imgFloat.GetBPP() == 32){int i = 0;int j = 0;for (i = 0; i < m_imgFloat.GetWidth(); i++){for (j = 0; j < m_imgFloat.GetHeight(); j++){byte * pbyte = (byte *)m_imgFloat.GetPixelAddress(i, j);pbyte[0] = pbyte[0] * pbyte[3] / 255;pbyte[1] = pbyte[1] * pbyte[3] / 255;pbyte[2] = pbyte[2] * pbyte[3] / 255;}}}}MoveWindow(nX, nY, nW, nH);return true;}
//自绘制函数  
void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{if (!lpDrawItemStruct)return;HDC hMemDC;HBITMAP bmpMem;HGDIOBJ hOldObj;bmpMem = CreateCompatibleBitmap(lpDrawItemStruct->hDC, lpDrawItemStruct->rcItem.right - lpDrawItemStruct->rcItem.left, lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top);if (!bmpMem)return;hMemDC = CreateCompatibleDC(lpDrawItemStruct->hDC);if (!hMemDC){if (bmpMem){::DeleteObject(bmpMem);bmpMem = NULL;}return;}hOldObj = ::SelectObject(hMemDC, bmpMem);int nW = lpDrawItemStruct->rcItem.right - lpDrawItemStruct->rcItem.left;int nH = lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top;RECT rectTmp = { 0 };rectTmp = lpDrawItemStruct->rcItem;MapWindowPoints(GetParent(), &rectTmp);if (m_BkImg.IsNull() == false)m_BkImg.Draw(hMemDC, 0, 0, rectTmp.right - rectTmp.left, rectTmp.bottom - rectTmp.top, rectTmp.left, rectTmp.top, rectTmp.right - rectTmp.left, rectTmp.bottom - rectTmp.top);if (lpDrawItemStruct->itemState & ODS_SELECTED){//按钮被选择  m_imgPress.AlphaBlend(hMemDC, 0, 0, nW, nH, 0, 0, nW, nH);}else{//默认状态  m_imgNormal.AlphaBlend(hMemDC, 0, 0, nW, nH, 0, 0, nW, nH);}::BitBlt(lpDrawItemStruct->hDC, 0, 0, nW, nH, hMemDC,0,0,SRCCOPY);SelectObject(hMemDC, hOldObj);if (bmpMem){::DeleteObject(bmpMem);bmpMem = NULL;}if (hMemDC){::DeleteDC(hMemDC);hMemDC = NULL;}return;
}void CMyButton::OnMouseMove(UINT nFlags, CPoint point)
{// TODO:  在此添加消息处理程序代码和/或调用默认值CButton::OnMouseMove(nFlags, point);if (!m_bIsInWnd){TRACKMOUSEEVENT       tme;tme.cbSize = sizeof(TRACKMOUSEEVENT);tme.dwFlags = TME_HOVER | TME_LEAVE;tme.dwHoverTime = 10;tme.hwndTrack = m_hWnd;_TrackMouseEvent(&tme);m_bIsInWnd = TRUE;}
}void CMyButton::OnMouseHover(UINT nFlags, CPoint point)
{// TODO:  在此添加消息处理程序代码和/或调用默认值HDC hMemDC;HBITMAP bmpMem;HGDIOBJ hOldObj;HDC hDC = ::GetDC(GetSafeHwnd());CRect rcItem;GetClientRect(&rcItem);if (hDC){bmpMem = CreateCompatibleBitmap(hDC, rcItem.Width(), rcItem.Height());if (!bmpMem){::ReleaseDC(GetSafeHwnd(), hDC);return;}hMemDC = CreateCompatibleDC(hDC);if (!hMemDC){if (bmpMem){::DeleteObject(bmpMem);bmpMem = NULL;}::ReleaseDC(GetSafeHwnd(), hDC);return;}hOldObj = ::SelectObject(hMemDC, bmpMem);RECT rectTmp = { 0 };rectTmp = rcItem;MapWindowPoints(GetParent(), &rectTmp);if (m_BkImg.IsNull() == false)m_BkImg.Draw(hMemDC, 0, 0, rectTmp.right - rectTmp.left, rectTmp.bottom - rectTmp.top, rectTmp.left, rectTmp.top, rectTmp.right - rectTmp.left, rectTmp.bottom - rectTmp.top);int nW = rcItem.right - rcItem.left;int nH = rcItem.bottom - rcItem.top;m_imgFloat.AlphaBlend(hMemDC, 0, 0, nW, nH, 0, 0,nW,nH);::BitBlt(hDC, 0, 0, nW, nH, hMemDC, 0, 0, SRCCOPY);SelectObject(hMemDC, hOldObj);if (bmpMem){::DeleteObject(bmpMem);bmpMem = NULL;}if (hMemDC){::DeleteDC(hMemDC);hMemDC = NULL;}::ReleaseDC(GetSafeHwnd(), hDC);}CButton::OnMouseHover(nFlags, point);
}void CMyButton::OnMouseLeave()
{// TODO:  在此添加消息处理程序代码和/或调用默认值CButton::OnMouseLeave();InvalidateRect(NULL);m_bIsInWnd = FALSE;
}

相关文章:

MFC界面美化第三篇----自绘按钮(重绘按钮)

1.前言 最近发现读者对我的mfc美化的专栏比较感兴趣&#xff0c;因此在这里进行续写&#xff0c;这里我会计划写几个连续的篇章&#xff0c;包括对MFC按钮的美化&#xff0c;菜单栏的美化&#xff0c;标题栏的美化&#xff0c;list列表的美化&#xff0c;直到最后形成一个完整…...

设计模式|工厂模式

文章目录 1. 工厂模式的三种实现2. 简单工厂模式和工厂方法模式示例3. 抽象工厂模式示例4. 工厂模式与多态的关系5. 工程模式与策略模式的关系6. 面试中可能遇到的问题6.1 **工厂模式的概念是什么&#xff1f;**6.2 **工厂模式解决了什么问题&#xff1f;**6.3 **工厂模式的优点…...

CHAT~(持续更新)

CHAT&#xff08;持续更新&#xff09; 实现一个ChatGPT创建API设计页面布局业务操作技术架构 实现安装工具 其他 实现一个ChatGPT 创建API 最简单也最需要信息的一步 继续往下做的前提 此处省略&#xff0c;想要获取接口创建方式联系 设计 页面布局 按照官网布局 业务操作…...

linux系统------------Mysql数据库介绍、编译安装

目录 一、数据库基本概念 1.1数据(Data) 1.2表 1.3数据库 1.4数据库管理系统(DBMS) 数据库管理系统DBMS原理 1.5数据库系统&#xff08;DBS) 二、数据库发展史 1、第一代数据库 2、第二代数据库 3、第三代数据库 三、关系型数据库 3.1关系型数据库应用 3.2主流的…...

文件操作3

随机读写数据文件 一、随机读写原理 在我们写数据时&#xff0c;有一个光标不断的在随着新写入的数据往后移动&#xff1b; 而读数据时&#xff0c;也有一个看不见光标&#xff0c;随着已经读完的数据&#xff0c;往后移动 这里的文件读写位置标记——可以想象成图形界面里的…...

算法D57 | 动态规划17 | 647. 回文子串 516.最长回文子序列 动态规划总结篇

647. 回文子串 动态规划解决的经典题目&#xff0c;如果没接触过的话&#xff0c;别硬想 直接看题解。 代码随想录 Python: class Solution:def countSubstrings(self, s: str) -> int:n len(s)dp [[0]*n for _ in range(n)]dp[0] [1]*nresult nfor i in range(1, n)…...

go的限流

背景 服务请求下游&#xff0c;oom&#xff0c;排查下来发现是一个下游组件qps陡增导致 但是司内网络框架比较挫&#xff0c;竟然不负责框架内存问题&#xff08;有内存管理模块&#xff0c;但逻辑又是无限制使用内存&#xff09; 每个请求一个r、w buffer&#xff0c;请求无限…...

补充--广义表学习

第一章 逻辑结构 &#xff08;1&#xff09;A()&#xff0c;A是一个空表&#xff0c;长度为0&#xff0c;深度为1。 &#xff08;2&#xff09;B(d,e)&#xff0c;B的元素全是原子&#xff0c;d和e&#xff0c;长度为2&#xff0c;深度为1。 &#xff08;3&#xff09;C(b,(c,…...

【笔记】KaiOS SPN显示逻辑

更新流程code 1、gonk/dom/system/gonk/radio/RadioInterfaceLayer.jsm handleNetworkStateChanged -> requestNetworkInfo() -> handleRilResponse的getOperator -> handleOperator handleNetworkStateChanged:网络状态变化请求网络信息 this.requestNetworkInfo…...

Visual Basic6.0零基础教学(4)—编码基础,数据类型与变量

编码基础,数据类型与变量 文章目录 编码基础,数据类型与变量前言一、VB中的编程基础二、VB的基本字符集和词汇集1、字符集2、词汇集 VB中的数据类型VB中的变量与常量一.变量和常量的命名规则二.变量声明1.用Dim语句显式声明变量三. 常量 运算符和表达式一. 运算符 1. 算术运算符…...

VPCFormer:一个基于transformer的多视角指静脉识别模型和一个新基准

文章目录 VPCFormer:一个基于transformer的多视角指静脉识别模型和一个新基准总结摘要介绍相关工作单视角指静脉识别多视角指静脉识别Transformer 数据库基本信息 方法总体结构静脉掩膜生成VPC编码器视角内相关性的提取视角间相关关系提取输出融合IFFN近邻感知模块(NPM) patch嵌…...

Android 图形渲染和显示系统关系

SurfaceFlinger&#xff1a;作为 Android 系统中的一个系统服务&#xff0c;SurfaceFlinger 负责管理整个屏幕的渲染和合成工作。它管理和合成多个 Surface&#xff0c;并与硬件加速器以及 Hardware Composer (HWC) 进行交互&#xff0c;最终将图像数据发送给显示硬件进行显示。…...

3.C++:类与对象(下)

一、再谈构造函数 1.1构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值。 class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;}private:int _year;int _month;i…...

iOS开发之SwiftUI

iOS开发之SwiftUI 在iOS开发中SwiftUI与Objective-C和Swift不同&#xff0c;它采用了声明式语法&#xff0c;相对而言SwiftUI声明式语法简化了界面开发过程&#xff0c;减少了代码量。 由于SwiftUI是Apple推出的界面开发框架&#xff0c;从iOS13开始引入&#xff0c;Apple使用…...

2024-简单点-pandas

pandas pandas to numpy 尽量不用.values提取数据 numexpr 和 bottleneck加速 布尔操作 describe 自定义describe .pipe df.apply 行或者列级别函数级别应用...

面试笔记——Redis(双写一致、持久化)

双写一致 双写一致性&#xff1a; 当修改了数据库中的数据&#xff0c;也要更新缓存的数据&#xff0c;使缓存和数据库中的数据保持一致。 相关问题&#xff1a;使用Redis作为缓存&#xff0c;mysql的数据如何与Redis进行同步&#xff1f;——双写一致性问题 回答时&#xff0…...

【漏洞复现】科立讯通信指挥调度平台editemedia.php sql注入漏洞

漏洞描述 在20240318之前的福建科立讯通信指挥调度平台中发现了一个漏洞。该漏洞被归类为关键级别,影响文件/api/client/editemedia.php的未知部分。通过操纵参数number/enterprise_uuid可导致SQL注入。攻击可能会远程发起。 免责声明 技术文章仅供参考,任何个人和组织使…...

css的active事件在手机端不生效的解决方法

需求&#xff1a;需求就是实现点击图中的 “抽奖” 按钮&#xff0c;实现一个按钮Q弹的放大缩小动画 上面是实现的效果&#xff0c;pc端&#xff0c;点击触发 :active 问题&#xff1a;但是这种方式在模拟器上可以&#xff0c;真机H5一调试就没生效了&#xff0c;下面是简单…...

00. 认识 Java 语言与安装教程

认识 Java Java 在 20 多年发展过程中&#xff0c;与时俱进&#xff0c;为了适应时代的需要&#xff0c;经历过两次重大的版本升级&#xff0c;一个是 Java 5&#xff0c;它提供了泛型等重要的功能。另一个是提供了 Lambda 表达式等重要的功能的 Java 8。 一些重要的 Java 的…...

数据结构-栈-004

1链栈 1.1栈结点结构体定义 /*定义一个数据结构*/ typedef struct student {char name[32];char sex;int age; }DATA_TYPE;/*定义一个栈结点*/ typedef struct stack_node {DATA_TYPE data;//数据域struct stack_node *pnext;//指针域 }STACK_NODE;1.2栈顶结点结构体定义 /*…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

mac 安装homebrew (nvm 及git)

mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用&#xff1a; 方法一&#xff1a;使用 Homebrew 安装 Git&#xff08;推荐&#xff09; 步骤如下&#xff1a;打开终端&#xff08;Terminal.app&#xff09; 1.安装 Homebrew…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...

微服务通信安全:深入解析mTLS的原理与实践

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言&#xff1a;微服务时代的通信安全挑战 随着云原生和微服务架构的普及&#xff0c;服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...