软件分层架构中PO、VO、DTO、BO、DAO核心概念解析与实战应用
1. 项目概述那些让人眼花缭乱的“O”们刚入行那会儿看项目代码最头疼的就是各种以“O”结尾的缩写PO、VO、DTO、BO、DAO……感觉每个字母都认识但组合在一起就不知道它们到底是谁该在哪一层出现互相之间又该怎么转换。我记得有一次为了一个简单的用户信息查询接口我创建了User、UserVO、UserDTO、UserPO四个几乎一模一样的类被同事review代码时笑称是“俄罗斯套娃”式开发。这其实不是个例很多新手甚至工作一两年的朋友在面对这些概念时都会感到困惑。这些“O”本质上是软件分层架构和领域驱动设计思想下的产物目的是为了解耦、职责分离让代码更清晰、更易维护。但如果我们只知其名不知其所以然就很容易陷入为了分层而分层的误区写出冗余且难以理解的代码。今天我们就来彻底拆解这些让人又爱又恨的“O”从它们的设计初衷、核心职责、应用场景到在实际项目中如何优雅地使用和转换让你下次再看到它们时心里有张清晰的地图。2. 核心概念拆解每个“O”的来龙去脉要理清这些概念我们不能孤立地看必须把它们放到软件架构的上下文中。通常一个典型的Web应用会采用分层架构比如经典的三层架构表现层Controller、业务逻辑层Service、数据访问层DAO。这些“O”就是在各层之间流转的数据载体它们的出现是为了解决不同层对数据形态的不同要求。2.1 基石POJO与POPOJO (Plain Old Java Object)这可以说是所有“O”的祖宗或者说是它们的本质。POJO就是一个普通的Java对象它不继承任何特定的框架类也不实现任何特定的框架接口没有侵入性。它只有私有的属性Fields和公共的Getter/Setter方法。它的意义在于“纯粹”意味着这个对象不依赖于任何特定的技术框架如Spring, Hibernate因此具有极高的可移植性和可测试性。我们后面讨论的PO、VO、DTO等在代码形态上首先都应该是一个POJO。注意很多人会使用Lombok的Data注解来快速生成Getter/Setter这确实方便但本质上它生成的类依然是一个POJO。不过要小心过度依赖Lombok可能会在团队协作或特定IDE环境下带来一些小麻烦比如需要所有成员都安装插件。PO (Persistent Object) - 持久化对象这是与数据库表结构直接映射的对象。每一个PO的实例通常对应数据库表中的一条记录。PO的属性应该与表的字段一一对应或通过ORM框架的映射配置关联。它的生命周期通常局限在数据访问层DAO层。核心职责承载从数据库查询出的原始数据或将要写入数据库的数据。典型特征属性与数据库表字段强相关。可能包含一些与数据库映射相关的注解如JPA的Entity,Table,Id,Column或MyBatis的映射。通常不包含业务逻辑方法只有数据。实操心得在设计PO时我的习惯是让它尽可能“傻”只做数据的容器。避免在其中加入任何业务判断或计算逻辑。比如一个UserPO它的属性就是id,username,password,email,create_time等与user表完全对应。2.2 业务层的核心BO与DO这两个概念在有些架构中区分明显在有些中则合二为一是混淆的重灾区。BO (Business Object) - 业务对象BO是业务逻辑的核心载体。它代表业务领域中的一个“东西”或“概念”这个“东西”不仅有数据还有行为方法。一个BO可能由多个PO组合、计算、衍生而来。核心职责封装业务数据和与之相关的业务逻辑。它是面向对象设计中“对象”该有的样子数据行为。典型特征可以是一个相对复杂的对象聚合了多个PO的数据。例如一个OrderBO订单业务对象可能包含了订单基本信息对应OrderPO、订单项列表每个对应OrderItemPO、用户收货地址对应AddressPO等。拥有业务方法。例如OrderBO可能有calculateTotalAmount()计算总金额、checkStock()检查库存、applyCoupon(CouponBO coupon)应用优惠券等方法。它的结构是为业务逻辑服务的可能与数据库表结构差异很大。应用场景主要在Service层内部使用用于组织复杂的业务逻辑。DO (Domain Object) - 领域对象DO是领域驱动设计DDD中的核心概念。它与BO非常相似都强调数据与行为的封装。在很多项目和团队中BO和DO被视为同义词可以互换使用。如果非要细究DO更侧重于在“领域层”中定义是领域模型的具体体现其边界和职责由限界上下文决定。核心职责体现领域知识封装领域内的状态和行为。与BO的细微差别在严格的DDD实践中DO或叫Entity有唯一标识ID并且其状态变化会贯穿整个生命周期关注的是对象本身的完整性和不变性。而BO可能更偏向于应用层的业务逻辑组织。但对于大多数不严格实施DDD的Web应用来说区分它们意义不大统一称为BO或DO均可。实操心得在我的项目中如果不采用复杂的DDD我通常统一使用BO。我会确保这个对象是“充血模型”即它不仅有getter/setter还有核心的业务方法。这能有效避免出现“贫血模型”——即只有数据没有方法的对象导致业务逻辑全部散落在Service中使得Service变得臃肿。2.3 层间传输的使者DTO与VO这两个对象专门用于不同层或不同系统之间的数据交互。DTO (Data Transfer Object) - 数据传输对象DTO的设计初衷是为了减少网络调用次数。早期分布式系统性能差如果需要多个数据就需多次远程调用。DTO将多个数据打包成一个对象一次传输完成。现在它更多用于服务层与表现层Controller之间或者微服务之间的数据传输。核心职责在不同进程或网络间传输数据通常是一个“扁平化”的数据结构。典型特征只有数据没有行为方法。根据接口的需求“量身定制”。一个常见的场景是一个UserBO很复杂但某个查询用户列表的接口只需要id、name、avatar三个字段。那么我们就可以定义一个UserSimpleDTO只包含这三个字段。常用于应对“前端需要的数据格式和后端存储的数据格式不一致”的情况。应用场景Service层方法出参或RPC/HTTP API的请求/响应体。VO (View Object) - 视图对象VO是专门为表现层通常是前端界面展示数据而设计的对象。它关注的是“界面需要显示什么”以及“如何显示”。核心职责承载展示给前端的数据其结构完全由前端视图决定。典型特征可能包含多个BO/DTO的数据聚合。例如一个订单详情页面VO可能包含订单信息、商品列表、用户地址、物流状态等。可能包含一些纯粹用于展示的派生字段。例如UserVO中可能有一个displayName字段其值是nickname不为空时取nickname否则取username。或者有一个statusText字段将数据库中的状态码如12转换为中文描述“进行中”“已完成”。可能包含一些格式化的数据。例如将Date类型的createTime格式化为yyyy-MM-dd HH:mm:ss字符串。与DTO的关系非常容易混淆。一个简单的区分原则是DTO关注传输和接口契约VO关注展示和视图渲染。在前后端分离的架构中Controller返回给前端的数据对象严格来说就是VO。但很多时候一个对象既承担了传输的职责也满足了视图的需求这时大家可能会混用。我个人的习惯是在Controller返回时明确使用VO后缀以强调其展示属性。2.4 操作的执行者DAODAO (Data Access Object) - 数据访问对象这是一个特殊的存在它不是数据对象而是一种设计模式是一个“对象”。DAO封装了所有对数据源通常是数据库的访问细节为上层提供统一的数据访问接口。核心职责隔离业务逻辑与数据访问逻辑。业务层Service通过调用DAO的接口方法来操作数据而无需关心底层用的是MySQL还是Oracle用的是JDBC还是MyBatis。典型特征通常是一个接口如UserDao加一个实现类如UserDaoImpl。接口中定义了数据访问的方法如findById,insert,update,delete。方法的参数和返回值通常是PO或PO的集合。实操心得在现代Spring Boot项目中我们通常使用RepositoryJPA或MapperMyBatis-Plus来替代传统的DAO但它们扮演的角色是相同的。保持DAO层方法的纯粹性很重要它只负责“增删改查”不要在这里面写业务逻辑判断。3. 核心流转与转换实践理解了每个对象是什么下一步就要看它们在代码中是如何协作和转换的。这是避免“俄罗斯套娃”的关键。3.1 标准数据流转路径一个典型的查询请求数据流如下Controller层接收前端请求参数通常是一个XXXRequestDTO或直接是VO的某个属性子集。Service层Controller调用Service方法并传入参数。Service内部可能先将传入的DTO转换为BO或内部使用的参数对象。调用一个或多个DAO/Mapper方法获取一个或多个PO。将PO组装、计算构建出业务所需的BO。执行复杂的业务逻辑校验、计算、流程控制等。返回过程Service将处理好的BO返回给Controller。Controller负责将BO转换为前端需要的VO然后通过HTTP响应返回。一个创建/更新请求的数据流类似但方向相反VO/DTO - (Controller) - BO - (Service) - PO - (DAO) - Database。3.2 对象转换的艺术与工具手动编写getter/setter进行对象转换是繁琐且容易出错的。以下是几种常见的转换策略和工具1. 手动转换最直接也最可控。在小型项目或转换逻辑极其复杂时使用。// 例如UserBO 转 UserVO public UserVO convertToVO(UserBO userBo) { if (userBo null) { return null; } UserVO userVo new UserVO(); userVo.setId(userBo.getId()); userVo.setName(userBo.getNickname() ! null ? userBo.getNickname() : userBo.getUsername()); userVo.setAvatarUrl(userBo.getAvatar()); // 格式化时间 userVo.setCreateTime(formatDate(userBo.getGmtCreate())); return userVo; }注意手动转换虽然代码多但胜在清晰尤其当字段名不一致或需要复杂计算时。务必做好空值判断。2. 使用Bean拷贝工具对于字段名和类型完全一致的对象使用工具极大提升效率。Spring BeanUtilsBeanUtils.copyProperties(source, target)。简单易用但性能一般且会忽略null值Spring默认行为需注意。Apache Commons BeanUtils类似但更老性能较差不推荐。Cglib BeanCopier性能极高但首次创建BeanCopier实例时较慢。适合在应用启动时初始化好用于大量对象转换的场景。MapStruct这是当前最推荐的方案。它是一个编译时生成代码的注解处理器。你只需要定义一个Mapper接口它会在编译期生成高效的、类型安全的转换实现类性能等同于手写代码。Mapper(componentModel spring) // 与Spring集成 public interface UserConverter { UserConverter INSTANCE Mappers.getMapper(UserConverter.class); // 基本字段映射 UserVO toVO(UserBO userBo); // 自定义映射规则 Mapping(source nickname, target name, defaultExpression java(userBo.getUsername())) Mapping(target createTime, expression java(formatDate(userBo.getGmtCreate()))) UserVO toVOWithCustom(UserBO userBo); }3. Lombok Builder模式对于创建对象特别是含有多个可选参数的对象使用Builder可以让代码更优雅避免过长的构造器或大量的setter调用。UserVO userVo UserVO.builder() .id(userBo.getId()) .name(userBo.getNickname()) .avatarUrl(userBo.getAvatar()) .build();4. 框架内置支持像MyBatis-Plus等框架在代码生成器如AutoGenerator中可以直接生成Controller、Service、Mapper以及对应的PO但VO和DTO通常需要根据业务手动创建。一些高级的代码生成模板可以配置生成简单的VO但复杂的聚合VO仍需手动处理。3.3 如何决定是否需要一个新的“O”这是避免过度设计的关键。我遵循以下几个原则字段差异原则如果前端需要的数据字段和后端存储的PO字段有超过30%的差异包括字段名、类型、是否存在就应该引入VO/DTO。例如PO里有passwordVO里绝对不能有。逻辑隔离原则如果某些字段需要经过业务逻辑计算或格式化才能展示如状态码转中文、时间格式化、金额单位转换这些逻辑不应该放在PO里而应该放在VO的构造过程或专门的转换器中。层间隔离原则Service层内部复杂的业务对象BO不应该直接暴露给Controller。这保证了业务逻辑的封装性当业务内部数据结构变化时不会直接影响接口。简单场景从简对于极其简单的增删改查CRUD模块如果PO的字段和前端需要的字段完全一致且不需要任何转换那么在一些追求简洁的小项目中直接用PO作为Controller的返回对象也并非绝对禁忌。但这需要团队共识并且要清楚知道这破坏了分层架构的隔离性未来可能带来维护成本。4. 常见问题与设计陷阱在实际开发中围绕这些对象会产生很多典型问题。4.1 “贫血模型”与“充血模型”之争这是面向对象设计中的一个经典问题也直接关系到BO/DO的设计。贫血模型对象只有属性数据和它们的getter/setter所有业务逻辑都放在Service类中。这会导致Service变成“上帝类”越来越臃肿而对象本身没有行为能力。充血模型对象既包含数据也包含与这些数据紧密相关的行为方法。例如AccountBO可以有withdraw(amount)取款、deposit(amount)存款方法。我的建议对于核心的、有明确行为的领域对象尽量采用“充血模型”。将属于该对象的核心行为封装进去。例如订单的calculateTotalAmount()、商品的reduceStock(count)。而对于那些跨多个对象的、流程性的业务逻辑则放在Service中协调。不要走极端。4.2 循环依赖与序列化问题当VO/BO对象结构复杂存在双向关联时如OrderVO里有ListOrderItemVO而OrderItemVO里又引用了OrderVO在通过Spring MVC返回JSON时Jackson等序列化工具可能会陷入循环引用导致栈溢出。解决方案使用注解在Jackson中使用JsonIgnore在其中一个方向的引用上忽略序列化。public class OrderItemVO { private String productName; JsonIgnore // 忽略对OrderVO的序列化打破循环 private OrderVO order; }使用DTO进行扁平化在需要传输的数据中避免设计复杂的嵌套关系用扁平化的DTO代替。例如OrderDetailDTO里直接包含订单基本信息和商品信息列表商品信息里不再包含订单信息。自定义视图使用Jackson的JsonView注解来定义不同的视图在不同的接口中序列化不同的字段。4.3 大量相似的VO/DTO如何管理随着业务增长针对同一个实体如User可能会衍生出UserSimpleVO列表用、UserDetailVO详情用、UserAdminVO后台管理用等。如何管理继承可以创建一个BaseUserVO包含公共字段其他VO继承它。但Java单继承的局限性可能导致组合更优。组合创建多个独立的VO它们之间没有继承关系。这是最清晰、耦合度最低的方式推荐使用。使用MapStruct等支持继承映射MapStruct可以很好地处理继承关系的映射。内部类如果某些VO只在一个很小的范围内使用可以考虑将其定义为Controller或Service的内部静态类。4.4 性能考量转换开销在高并发场景下大量对象的转换特别是使用反射工具如BeanUtils可能成为性能瓶颈。优化策略预编译使用MapStruct其转换代码在编译期生成运行期无反射开销性能最优。缓存BeanCopier如果使用Cglib的BeanCopier务必在应用启动时或类加载时初始化并缓存起来避免每次转换都创建。手动转换对于性能极其敏感的路径手写转换代码永远是最快的。减少不必要的转换审视你的设计是否每一层转换都是必须的在简单的服务中是否可以适当合并5. 现代架构下的演进随着微服务、云原生架构的普及这些概念也在发生一些变化。DTO的强化在微服务间通过HTTP或RPC调用时DTO成为了服务间API契约的核心。通常会使用IDL接口定义语言如Protobuf、Thrift来严格定义DTO的结构并生成多语言客户端保证一致性。VO的弱化在前后端分离且前端主导的BFFBackend For Frontend模式下后端提供的API返回值可能更接近于“原始数据”由前端的状态管理库如Vuex, Redux来组织成视图所需的形态。此时后端的“VO”属性减弱更偏向于通用DTO。PO的多样化除了关系型数据库的PO还可能存在对应Redis的RedisPO或叫Cache Object对应Elasticsearch的EsPO或叫Document。其核心思想不变与持久化介质的数据结构对应。聚合根与值对象在DDD中DO会进一步细分为“聚合根”Aggregate Root和“值对象”Value Object。聚合根是具有全局唯一标识和生命周期的实体是外部访问的入口值对象则描述一个事物的属性没有唯一标识通过属性值相等来比较。这为我们设计复杂的BO提供了更精细的指导。回过头看这些“O”的本质是软件工程中“关注点分离”和“单一职责”原则的体现。它们不是教条而是帮助我们写出更清晰、更易维护代码的工具。理解每个对象为什么存在比记住它们的名字更重要。在实际项目中我通常会从简单的PO和DAO开始随着业务复杂度的提升再逐步引入DTO、VO和BO。不要一开始就追求“完美”的分层适合当前团队和项目复杂度的才是最好的设计。下次当你再创建这些类时不妨先问自己这个对象承载的职责是什么它会在哪一层使用它的变化会因为什么而引起想清楚这些问题代码的结构自然就清晰了。

相关新闻

HBF标准前瞻:2026年将重塑数据中心存储与内存互联架构

HBF标准前瞻:2026年将重塑数据中心存储与内存互联架构

如果你是一位存储工程师,最近可能被各种新名词搞得有点晕——CXL、UCIe、HBM、HBM3e…… 技术迭代快得让人应接不暇。但有一个即将在2026年落地的标准,值得你现在就保持关注,因为它很可能重塑未来几年数据中心和高性能计算的存储架构格局。这…

2026/8/7 1:44:05 阅读更多 →
酰基转移酶文献精读:从机制解析到理性设计的科研实战指南

酰基转移酶文献精读:从机制解析到理性设计的科研实战指南

1. 项目概述:一次关于酰基转移酶的深度文献精读之旅最近在整理自己过去几年的文献阅读笔记,翻到了2021年做的一个专题系列,当时我给自己定了个目标,要把“酰基转移酶”这个领域近五年的重要进展啃下来,并整理成一套可以…

2026/8/7 1:44:05 阅读更多 →
深入解析msvcr110.dll:从C++运行时库原理到Windows依赖问题实战解决

深入解析msvcr110.dll:从C++运行时库原理到Windows依赖问题实战解决

1. 项目概述:为什么一个DLL文件能让你焦头烂额?如果你是一名C开发者,或者只是一个普通的Windows电脑用户,那么“msvcr110.dll”这个文件名对你来说可能既熟悉又令人头疼。熟悉,是因为它常常在游戏启动失败、专业软件报…

2026/8/7 1:44:05 阅读更多 →

最新新闻

5分钟快速上手微信公众号爬虫:3个实战技巧获取文章数据、阅读量和点赞数

5分钟快速上手微信公众号爬虫:3个实战技巧获取文章数据、阅读量和点赞数

5分钟快速上手微信公众号爬虫:3个实战技巧获取文章数据、阅读量和点赞数 【免费下载链接】wechat_articles_spider 微信公众号文章的爬虫 项目地址: https://gitcode.com/gh_mirrors/we/wechat_articles_spider 微信公众号爬虫是数据分析师和运营人员必备的数…

2026/8/7 2:23:26 阅读更多 →
2026年pdf合并网站实测盘点:这几款在线合并与拆分工具各有什么特点

2026年pdf合并网站实测盘点:这几款在线合并与拆分工具各有什么特点

上个月报销差旅费,财务要求把所有票据按时间顺序合并成一个PDF上传。我手里散落着机票行程单、酒店水单、打车发票,格式五花八门——有的从邮箱下载,有的是手机截图,还有两张是同事转发的扫描件。文件名乱成一团,手动排…

2026/8/7 2:23:26 阅读更多 →
PDF合并拆分工具有哪些?2026免费方案盘点,线上本地都覆盖

PDF合并拆分工具有哪些?2026免费方案盘点,线上本地都覆盖

八月初入职,我被财务主管一句“报销单全部合并成一个PDF交过来,不要一张一张发”钉在工位上整整一下午。手机扫出来的发票零零散散七八个文件,微信里还躺着同事转来的三张收据截图,我在办公室电脑前翻了好一阵子也没找到能直接用的…

2026/8/7 2:23:26 阅读更多 →
Ubuntu系统配置全攻略:为鸿蒙开发打造高效Linux环境

Ubuntu系统配置全攻略:为鸿蒙开发打造高效Linux环境

1. 项目概述:为什么要在Ubuntu上准备鸿蒙开发环境?最近身边不少朋友开始琢磨鸿蒙开发,尤其是想搞点设备侧或者系统底层的应用,第一道坎往往就是环境搭建。很多人习惯了在Windows上用各种IDE点点鼠标,但真到了鸿蒙这种偏…

2026/8/7 2:23:26 阅读更多 →
本地部署AI角色绘画:从Stable Diffusion到特定角色风格化实践

本地部署AI角色绘画:从Stable Diffusion到特定角色风格化实践

这次我们来看一个基于AI图像生成技术的本地部署项目,它能够将二次元角色“古明地恋”的立绘风格进行高精度复现与创作。这个项目并非一个通用AI绘画工具,而是针对特定角色风格进行深度优化的模型与应用方案,核心目标是在本地环境下&#xff0…

2026/8/7 2:23:26 阅读更多 →
轩辕镜像:轻量级容器化解决方案与性能优化实践

轩辕镜像:轻量级容器化解决方案与性能优化实践

1. 轩辕镜像概述:新一代容器化解决方案轩辕镜像是近期在开发者社区中备受关注的一款轻量级容器镜像解决方案。作为一名长期在云计算和容器化领域实践的工程师,我最初接触这个项目是在一次技术沙龙上,当时演讲者演示了如何用轩辕镜像在3秒内完…

2026/8/7 2:22:26 阅读更多 →

日新闻

为什么scrcpy成为Android投屏的终极解决方案:完整实战指南

为什么scrcpy成为Android投屏的终极解决方案:完整实战指南

为什么scrcpy成为Android投屏的终极解决方案:完整实战指南 【免费下载链接】scrcpy Display and control your Android device 项目地址: https://gitcode.com/GitHub_Trending/sc/scrcpy 想要将Android手机屏幕完美投射到电脑上,享受大屏操作的自…

2026/8/7 0:00:19 阅读更多 →
如何在5分钟内掌握Tom Select:打造现代化表单选择器的终极指南

如何在5分钟内掌握Tom Select:打造现代化表单选择器的终极指南

如何在5分钟内掌握Tom Select:打造现代化表单选择器的终极指南 【免费下载链接】tom-select Tom Select is a lightweight (~16kb gzipped) hybrid of a textbox and select box. Forked from selectize.js to provide a framework agnostic autocomplete widget wi…

2026/8/7 0:00:19 阅读更多 →
5分钟快速上手:NSZ压缩工具终极指南,轻松管理Switch游戏文件

5分钟快速上手:NSZ压缩工具终极指南,轻松管理Switch游戏文件

5分钟快速上手:NSZ压缩工具终极指南,轻松管理Switch游戏文件 【免费下载链接】nsz NSZ - Homebrew compatible NSP/XCI compressor/decompressor 项目地址: https://gitcode.com/gh_mirrors/ns/nsz 你是否在为Nintendo Switch游戏文件占用大量存储…

2026/8/7 0:00:19 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/6 22:02:27 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/6 22:02:27 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/6 22:02:27 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/5 23:28:39 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/6 22:02:28 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/5 23:46:51 阅读更多 →