Spring Boot 工程分层实战(五个分层维度)
1、分层思想
计算机领域有一句话:计算机中任何问题都可通过增加一个虚拟层解决。这句体现了分层思想重要性,分层思想同样适用于Java工程架构。
分层优点是每层只专注本层工作,可以类比设计模式单一职责原则,或者经济学比较优势原理,每层只做本层最擅长的事情。
分层缺点是层之间通信时,需要通过适配器,翻译成本层或者下层可以理解的信息,通信成本有所增加。
工程分层需要从五个维度思考:
(1) 单一
每层只处理一类事情,满足单一职责原则
(2) 降噪
信息在每一层进行传输,满足最小知识原则,只向下层传输必要信息
(3) 适配
每层都需要一个适配器,翻译信息为本层或者下层可以理解的信息
(4) 业务
业务对象可以整合业务逻辑,例如使用充血模型整合业务
(5) 数据
数据对象尽量纯净,尽量不要聚合业务
1.2 九层结构
综上所述SpringBoot工程可以分为九层:
工具层:util
整合层:integration
基础层:infrastructure
服务层:service
领域层:domain
门面层:facade
控制层:controller
客户端:client
启动层:boot
2、 分层详解
创建测试项目user-demo-service:
user-demo-service-user-demo-service-boot-user-demo-service-client-user-demo-service-controller-user-demo-service-domain-user-demo-service-facade-user-demo-service-infrastructure-user-demo-service-integration-user-demo-service-service-user-demo-service-util
2.1 util
-
工具层承载工具代码
-
不依赖本项目其它模块
-
只依赖一些通用工具包
user-demo-service-util-/src/main/java-date-DateUtil.java-json-JSONUtil.java-validate-BizValidator.java
2.2 infrastructure
基础层核心是承载数据访问,entity实体对象承载在本层。
2.2.1 项目结构
代码层分为两个领域:
- player:运动员
- game:比赛
每个领域具有两个子包:
- entity
- mapper
user-demo-service-infrastructure-/src/main/java-player-entity-PlayerEntity.java-mapper-PlayerEntityMapper.java-game-entity-GameEntity.java-mapper-GameEntityMapper.java-/src/main/resources-mybatis-sqlmappers-gameEntityMapper.xml-playerEntityMapper.xml
2.2.2 本项目间依赖关系
infrastructure只依赖工具模块
<dependency><groupId>com.test.javafront</groupId><artifactId>user-demo-service-util</artifactId>
</dependency>
2.2.3 核心代码
创建运动员数据表:
CREATE TABLE `player` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`player_id` varchar(256) NOT NULL COMMENT '运动员编号',`player_name` varchar(256) NOT NULL COMMENT '运动员名称',`height` int(11) NOT NULL COMMENT '身高',`weight` int(11) NOT NULL COMMENT '体重',`game_performance` text COMMENT '最近一场比赛表现',`creator` varchar(256) NOT NULL COMMENT '创建人',`updator` varchar(256) NOT NULL COMMENT '修改人',`create_time` datetime NOT NULL COMMENT '创建时间',`update_time` datetime NOT NULL COMMENT '修改时间',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
运动员实体对象,gamePerformance字段作为string保存在数据库,这体现了数据层尽量纯净,不要整合过多业务,解析任务应该放在业务层:
public class PlayerEntity {private Long id;private String playerId;private String playerName;private Integer height;private Integer weight;private String creator;private String updator;private Date createTime;private Date updateTime;private String gamePerformance;
}
运动员Mapper对象:
@Repository
public interface PlayerEntityMapper {int insert(PlayerEntity record);int updateById(PlayerEntity record);PlayerEntity selectById(@Param("playerId") String playerId);
}
2.3 domain
2.3.1 概念说明
领域层是DDD流行兴起之概念
可以通过三组对比理解领域层
- 领域对象 VS 数据对象
- 领域对象 VS 业务对象
- 领域层 VS 业务层
(1) 领域对象 VS 数据对象
数据对象字段尽量纯净,使用基本类型
public class PlayerEntity {private Long id;private String playerId;private String playerName;private Integer height;private Integer weight;private String creator;private String updator;private Date createTime;private Date updateTime;private String gamePerformance;
}
以查询结果领域对象为例
领域对象需要体现业务含义
public class PlayerQueryResultDomain {private String playerId;private String playerName;private Integer height;private Integer weight;private GamePerformanceVO gamePerformance;
}
public class GamePerformanceVO {// 跑动距离private Double runDistance;// 传球成功率private Double passSuccess;// 进球数private Integer scoreNum;
}
(2) 领域对象 VS 业务对象
业务对象同样会体现业务,领域对象和业务对象有什么不同呢?其中一个最大不同是领域对象采用充血模型聚合业务。
运动员新增业务对象:
public class PlayerCreateBO {private String playerName;private Integer height;private Integer weight;private GamePerformanceVO gamePerformance;private MaintainCreateVO maintainInfo;
}
运动员新增领域对象:
public class PlayerCreateDomain implements BizValidator {private String playerName;private Integer height;private Integer weight;private GamePerformanceVO gamePerformance;private MaintainCreateVO maintainInfo;@Overridepublic void validate() {if (StringUtils.isEmpty(playerName)) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}if (null == height) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}if (height > 300) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}if (null == weight) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}if (null != gamePerformance) {gamePerformance.validate();}if (null == maintainInfo) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}maintainInfo.validate();}
}
(3) 领域层 VS 业务层
领域层和业务层都包含业务,二者不是替代关系,而是互补关系。业务层可以更加灵活组合不同领域业务,并且可以增加流控、监控、日志、权限,分布式锁等控制,相较于领域层功能更为丰富。
2.3.2 项目结构
代码层分为两个领域:
- player:运动员
- game:比赛
每个领域具有三个子包:
- domain:领域对象
- event:领域事件
- vo:值对象
user-demo-service-domain-/src/main/java-base-domain-BaseDomain.java-event-BaseEvent.java-vo-BaseVO.java-MaintainCreateVO.java-MaintainUpdateVO.java-player-domain-PlayerCreateDomain.java-PlayerUpdateDomain.java-PlayerQueryResultDomain.java-event-PlayerUpdateEvent.java-vo-GamePerformanceVO.java-game-domain-GameCreateDomain.java-GameUpdateDomain.java-GameQueryResultDomain.java-event-GameUpdateEvent.java-vo-GameSubstitutionVO.java
2.3.3 本项目间依赖关系
domain依赖本项目两个模块:
- util
- client
之所以依赖client模块是因为领域对象聚合了业务校验,以下信息需要暴露至外部:
- BizException
- ErrorCodeBizEnum
<dependency><groupId>com.test.javafront</groupId><artifactId>user-demo-service-util</artifactId>
</dependency>
<dependency><groupId>com.test.javafront</groupId><artifactId>user-demo-service-client</artifactId>
</dependency>
2.3.4 核心代码
以运动员修改领域对象为例:
// 运动员修改领域对象
public class PlayerUpdateDomain extends BaseDomain implements BizValidator {private String playerId;private String playerName;private Integer height;private Integer weight;private String updator;private Date updatetime;private GamePerformanceVO gamePerformance;private MaintainUpdateVO maintainInfo;@Overridepublic void validate() {if (StringUtils.isEmpty(playerId)) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}if (StringUtils.isEmpty(playerName)) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}if (null == height) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}if (height > 300) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}if (null == weight) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}if (null != gamePerformance) {gamePerformance.validate();}if (null == maintainInfo) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}maintainInfo.validate();}
}
// 比赛表现值对象
public class GamePerformanceVO implements BizValidator {// 跑动距离private Double runDistance;// 传球成功率private Double passSuccess;// 进球数private Integer scoreNum;@Overridepublic void validate() {if (null == runDistance) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}if (null == passSuccess) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}if (Double.compare(passSuccess, 100) > 0) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}if (null == runDistance) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}if (null == scoreNum) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}}
}
// 修改人值对象
public class MaintainUpdateVO implements BizValidator {// 修改人private String updator;// 修改时间private Date updateTime;@Overridepublic void validate() {if (null == updator) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}if (null == updateTime) {throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);}}
}
2.4 service
2.4.1 项目结构
user-demo-service-service-/src/main/java-player-adapter-PlayerServiceAdapter.java-event-PlayerMessageSender.java-service-PlayerService.java-game-adapter-GameServiceAdapter.java-event-GameMessageSender.java-service-GameService.java
2.4.2 本项目间依赖关系
service依赖本项目四个模块:
- util
- domain
- integration
- infrastructure
<dependency><groupId>com.test.javafront</groupId><artifactId>user-demo-service-domain</artifactId>
</dependency>
<dependency><groupId>com.test.javafront</groupId><artifactId>user-demo-service-infrastructure</artifactId>
</dependency>
<dependency><groupId>com.test.javafront</groupId><artifactId>user-demo-service-util</artifactId>
</dependency>
<dependency><groupId>com.test.javafront</groupId><artifactId>user-demo-service-integration</artifactId>
</dependency>
2.4.3 核心代码
以运动员编辑服务为例:
// 运动员服务
public class PlayerService {@Resourceprivate PlayerEntityMapper playerEntityMapper;@Resourceprivate PlayerMessageSender playerMessageSender;@Resourceprivate PlayerServiceAdapter playerServiceAdapter;public boolean updatePlayer(PlayerUpdateDomain player) {AssertUtil.notNull(player, new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT));player.validate();PlayerEntity entity = playerServiceAdapter.convertUpdate(player);playerEntityMapper.updateById(entity);playerMessageSender.sendPlayerUpdatemessage(player);return true;}
}
// 运动员消息服务
public class PlayerMessageSender {@Resourceprivate PlayerServiceAdapter playerServiceAdapter;public boolean sendPlayerUpdatemessage(PlayerUpdateDomain domain) {PlayerUpdateEvent event = playerServiceAdapter.convertUpdateEvent(domain);log.info("sendPlayerUpdatemessage event={}", event);return true;}
}
// 服务适配器
public class PlayerServiceAdapter {// domain -> entitypublic PlayerEntity convertUpdate(PlayerUpdateDomain domain) {PlayerEntity player = new PlayerEntity();player.setPlayerId(domain.getPlayerId());player.setPlayerName(domain.getPlayerName());player.setWeight(domain.getWeight());player.setHeight(domain.getHeight());if (null != domain.getGamePerformance()) {player.setGamePerformance(JacksonUtil.bean2Json(domain.getGamePerformance()));}String updator = domain.getMaintainInfo().getUpdator();Date updateTime = domain.getMaintainInfo().getUpdateTime();player.setUpdator(updator);player.setUpdateTime(updateTime);return player;}// domain -> eventpublic PlayerUpdateEvent convertUpdateEvent(PlayerUpdateDomain domain) {PlayerUpdateEvent event = new PlayerUpdateEvent();event.setPlayerUpdateDomain(domain);event.setMessageId(UUID.randomUUID().toString());event.setMessageId(PlayerMessageType.UPDATE.getMsg());return event;}
}
2.5 intergration
本项目可能会依赖外部服务,那么将外部DTO转换为本项目可以理解的对象,需要在本层处理。
2.5.1 项目结构
假设本项目调用了用户中心服务:
user-demo-service-intergration-/src/main/java-user-adapter-UserClientAdapter.java-proxy-UserClientProxy.java
2.5.2 本项目间依赖关系
intergration依赖本项目两个模块:
- util
- domain
之所以依赖domain模块,是因为本层需要将外部DTO转换为本项目可以理解的对象,这些对象就放在domain模块。
<dependency><groupId>com.test.javafront</groupId><artifactId>user-demo-service-domain</artifactId>
</dependency>
<dependency><groupId>com.test.javafront</groupId><artifactId>user-demo-service-util</artifactId>
</dependency>
2.5.3 核心代码
现在我们将外部对象UserClientDTO
转换为本项目领域对象UserInfoDomain
(1) 外部服务
// 外部对象
public class UserInfoClientDTO implements Serializable {private String id;private String name;private Date createTime;private Date updateTime;private String mobile;private String cityCode;private String addressDetail;
}
// 外部服务
public class UserClientService {// RPC模拟public UserInfoClientDTO getUserInfo(String userId) {UserInfoClientDTO userInfo = new UserInfoClientDTO();userInfo.setId(userId);userInfo.setName(userId);userInfo.setCreateTime(DateUtil.now());userInfo.setUpdateTime(DateUtil.now());userInfo.setMobile("test-mobile");userInfo.setCityCode("test-city-code");userInfo.setAddressDetail("test-address-detail");return userInfo;}
}
(2) 本项目领域对象
domain模块新增user领域:
user-demo-service-domain-/src/main/java-user-domain-UserDomain.java-vo-UserAddressVO.java-UserContactVO.java
user领域对象代码:
// 用户领域
public class UserInfoDomain extends BaseDomain {private UserContactVO contactInfo;private UserAddressVO addressInfo;
}
// 地址值对象
public class UserAddressVO extends BaseVO {private String cityCode;private String addressDetail;
}
// 联系方式值对象
public class UserContactVO extends BaseVO {private String mobile;
}
(3) 适配器
public class UserClientAdapter {// third dto -> domainpublic UserInfoDomain convertUserDomain(UserInfoClientDTO userInfo) {UserInfoDomain userDomain = new UserInfoDomain();UserContactVO contactVO = new UserContactVO();contactVO.setMobile(userInfo.getMobile());userDomain.setContactInfo(contactVO);UserAddressVO addressVO = new UserAddressVO();addressVO.setCityCode(userInfo.getCityCode());addressVO.setAddressDetail(userInfo.getAddressDetail());userDomain.setAddressInfo(addressVO);return userDomain;}
}
(4) 调用外部服务
public class UserClientProxy {@Resourceprivate UserClientService userClientService;@Resourceprivate UserClientAdapter userClientAdapter;public UserInfoDomain getUserInfo(String userId) {UserInfoClientDTO user = userClientService.getUserInfo(userId);UserInfoDomain result = userClientAdapter.convertUserDomain(user);return result;}
}
2.6 facade + client
设计模式中有一种Facade模式,称为门面模式或者外观模式。这种模式提供一个简洁对外语义,屏蔽内部系统复杂性。
client承载数据对外传输对象DTO,facade承载对外服务,这两层必须满足最小知识原则,无关信息不必对外透出。
这样做有两个优点:
- 简洁性:对外服务语义明确简洁
- 安全性:敏感字段不能对外透出
2.6.1 项目结构
(1) client
user-demo-service-client
-/src/main/java-base-dto-BaseDTO.java-error-BizException.java-BizErrorCode.java-event-BaseEventDTO.java-result-ResultDTO.java-player-dto-PlayerCreateDTO.java-PlayerQueryResultDTO.java-PlayerUpdateDTO.java-enums-PlayerMessageTypeEnum.java-event-PlayerUpdateEventDTO.java-service-PlayerClientService.java
(2) facade
user-demo-service-facade
-/src/main/java-player-adapter-PlayerFacadeAdapter.java-impl-PlayerClientServiceImpl.java-game-adapter-GameFacadeAdapter.java-impl-GameClientServiceImpl.java
2.6.2 本项目间依赖关系
client不依赖本项目其它模块,这一点非常重要,因为client会被外部引用,必须保证这一层简洁和安全。
facade依赖本项目三个模块:
- domain
- client
- service
<dependency><groupId>com.test.javafront</groupId><artifactId>user-demo-service-domain</artifactId>
</dependency>
<dependency><groupId>com.test.javafront</groupId><artifactId>user-demo-service-client</artifactId>
</dependency>
<dependency><groupId>com.test.javafront</groupId><artifactId>user-demo-service-service</artifactId>
</dependency>
2.6.3 核心代码
(1) DTO
以查询运动员信息为例,查询结果DTO只封装最关键字段,例如运动员ID、创建时间、修改时间等业务不强字段就无须透出:
public class PlayerQueryResultDTO implements Serializable {private String playerName;private Integer height;private Integer weight;private GamePerformanceDTO gamePerformanceDTO;
}
(2) 客户端服务
public interface PlayerClientService {public ResultDTO<PlayerQueryResultDTO> queryById(String playerId);
}
(3) 适配器
public class PlayerFacadeAdapter {// domain -> dtopublic PlayerQueryResultDTO convertQuery(PlayerQueryResultDomain domain) {if (null == domain) {return null;}PlayerQueryResultDTO result = new PlayerQueryResultDTO();result.setPlayerId(domain.getPlayerId());result.setPlayerName(domain.getPlayerName());result.setHeight(domain.getHeight());result.setWeight(domain.getWeight());if (null != domain.getGamePerformance()) {GamePerformanceDTO performance = convertGamePerformance(domain.getGamePerformance());result.setGamePerformanceDTO(performance);}return result;}
}
(4) 服务实现
public class PlayerClientServiceImpl implements PlayerClientService {@Resourceprivate PlayerService playerService;@Resourceprivate PlayerFacadeAdapter playerFacadeAdapter;@Overridepublic ResultDTO<PlayerQueryResultDTO> queryById(String playerId) {PlayerQueryResultDomain resultDomain = playerService.queryPlayerById(playerId);if (null == resultDomain) {return ResultCommonDTO.success();}PlayerQueryResultDTO result = playerFacadeAdapter.convertQuery(resultDomain);return ResultCommonDTO.success(result);}
}
2.7 controller
facade服务实现可以作为RPC提供服务,controller则作为本项目HTTP接口提供服务,供前端调用。
controller需要注意HTTP相关特性,敏感信息例如登陆用户ID不能依赖前端传递,登陆后前端会在请求头带一个登陆用户信息,服务端需要从请求头中获取并解析。
2.7.1 项目结构
user-demo-service-controller-/src/main/java-config-CharsetConfig.java-controller-player-PlayerController.java-game-GameController.java
2.7.2 本项目依赖关系
controller依赖本项目一个模块:
facade
根据依赖传递原理同时依赖以下模块:
- domain
- client
- service
<dependency><groupId>com.test.javafront</groupId><artifactId>user-demo-service-facade</artifactId>
</dependency>
2.7.3 核心代码
@RestController
@RequestMapping("/player")
public class PlayerController {@Resourceprivate PlayerClientService playerClientService;@PostMapping("/add")public ResultDTO<Boolean> add(@RequestHeader("test-login-info") String loginUserId, @RequestBody PlayerCreateDTO dto) {dto.setCreator(loginUserId);ResultCommonDTO<Boolean> resultDTO = playerClientService.addPlayer(dto);return resultDTO;}@PostMapping("/update")public ResultDTO<Boolean> update(@RequestHeader("test-login-info") String loginUserId, @RequestBody PlayerUpdateDTO dto) {dto.setUpdator(loginUserId);ResultCommonDTO<Boolean> resultDTO = playerClientService.updatePlayer(dto);return resultDTO;}@GetMapping("/{playerId}/query")public ResultDTO<PlayerQueryResultDTO> queryById(@RequestHeader("test-login-info") String loginUserId, @PathVariable("playerId") String playerId) {ResultCommonDTO<PlayerQueryResultDTO> resultDTO = playerClientService.queryById(playerId);return resultDTO;}
}
2.8 boot
boot作为启动层,只有启动入口代码
2.8.1 项目结构
所有模块代码均必须属于com.user.demo.service子路径
user-demo-service-boot-/src/main/java-com.user.demo.service-MainApplication.java
2.8.2 本项目间依赖
boot引用本项目所有模块
- util
- integration
- infrastructure
- service
- domain
- facade
- controller
- client
2.8.3 核心代码
@MapperScan("com.user.demo.service.infrastructure.*.mapper")
@SpringBootApplication
public class MainApplication {public static void main(final String[] args) {SpringApplication.run(MainApplication.class, args);}
}
相关文章:
Spring Boot 工程分层实战(五个分层维度)
1、分层思想 计算机领域有一句话:计算机中任何问题都可通过增加一个虚拟层解决。这句体现了分层思想重要性,分层思想同样适用于Java工程架构。 分层优点是每层只专注本层工作,可以类比设计模式单一职责原则,或者经济学比较优势原…...

vscode IntelliSense Configurations
IntelliSense 是一个强大的代码补全和代码分析功能,它可以帮助开发者提高编程效率。图中显示的是 VSCode 的 IntelliSense 配置界面,具体配置如下: Compiler path(编译器路径): 这里指定了用于构建项目的编译器的完整路…...

hbase读写操作后hdfs内存占用太大的问题
hbase读写操作后hdfs内存占用太大的问题 查看内存信息hbase读写操作 查看内存信息 查看本地磁盘的内存信息 df -h查看hdfs上根目录下各个文件的内存大小 hdfs dfs -du -h /查看hdfs上/hbase目录下各个文件的内存大小 hdfs dfs -du -h /hbase查看hdfs上/hbase/oldWALs目录下…...

C++----入门篇
引言 C是在C的基础之上,容纳进去了面向对象编程思想,并增加了许多有用的库,以及编程范式等。熟悉C语言之后,对C学习有一定的帮助,本章节主要目标: 1. 补充C语言语法的不足,以及C是如何对C语言…...

C语言程序设计P5-5【应用函数进行程序设计 | 第五节】—知识要点:变量的作用域和生存期
知识要点:变量的作用域和生存期 视频: 目录 一、任务分析 二、必备知识与理论 三、任务实施 一、任务分析 有一个一维数组,内放 10 个学生成绩,写一个函数,求出平均分、最高分和最低分。 任务要求用一个函数来完…...
用 Sass 模块化系统取代全局导入,消除 1.80.0 引入的 @import 弃用警告
目录 前言 问题 import 的缺陷 命名冲突 重复导入 模块系统 use 规则 forward 规则 实际修改 前言 最初,Sass 使用 import 规则通过单个全局命名空间加载其他文件,所有内置函数也可全局使用。由于模块系统(use 和 forward 规则&…...

安卓低功耗蓝牙BLE官方开发例程(JAVA)翻译注释版
官方原文链接 https://developer.android.com/develop/connectivity/bluetooth/ble/ble-overview?hlzh-cn 目录 低功耗蓝牙 基础知识 关键术语和概念 角色和职责 查找 BLE 设备 连接到 GATT 服务器 设置绑定服务 设置 BluetoothAdapter 连接到设备 声明 GATT 回…...
搭建fastapi项目
环境准备 # 创建项目目录 mkdir my_fastapi_project cd my_fastapi_project# 创建和激活虚拟环境 python -m venv venv .\venv\Scripts\activate安装必要的包 pip install fastapi uvicorn python-dotenv创建项目基本结构 my_fastapi_project/ │ .env # …...

Maven学习(Maven项目模块化。模块间“继承“机制。父(工程),子项目(模块)间聚合)
目录 一、Maven项目模块化? (1)基本介绍。 (2)汽车模块化生产再聚合组装。 (3)Maven项目模块化图解。 1、maven_parent。 2、maven_pojo。 3、maven_dao。 4、maven_service。 5、maven_web。 6…...
华为云云原生中间件DCS DMS 通过中国信通院与全球IPv6测试中心双重能力检测
近日,中国信息通信研究院(以下简称“中国信通院”)与全球IPv6测试中心相继宣布,华为云的分布式缓存服务(Distributed Cache Service,简称DCS)和分布式消息服务(Distributed Message …...

PostgreSQL中事件触发器Event Trigger
在PostgreSQL中,事件触发器(Event Trigger)是一种特殊的触发器类型,它允许你在特定的数据库系统事件发生时执行特定的操作。与普通的触发器不同,事件触发器并不与特定的表或视图相关联,而是与数据库级别的全…...
uni.request流式(Stream)请求,实现打印机效果
最近使用扣子 - 开发指南 (coze.cn)和智谱AI开放平台开发小程序AI导诊和用药对话指南。 开发的过程中也是走了不少坑,下面就来聊聊走了哪些坑。 坑1 :coze试了v2和v3的接口,两个接口请求还是有点差别的,v2拿到了botId和accessToken可以直接请求不需要做任何处理,v3还需要…...
canvas保存图片
需求:上面有几个按钮,其中有一个切换是图片 用v-if会导致图片加载慢 实现方法: 一进来就加载,通过监听元素显示,用于控制canvas的宽高,从而达到隐藏的效果 组件dowolad.vue <template><view …...
DNS到底有什么用?
举个例子,对于我们来说访问的域名是www.baidu.com,但是实际在计算机并不认识这个域名,计算机是需要通过IP地址去访问这个网站,所以呢?这个时候就需要一个dns解析器,来把这串域名转换为IP地址给计算机去访问…...

什么是CRM系统?CRM系统的功能、操作流程、生命周期
CRM系统作为企业管理和维护客户关系的重要工具,在商业活动中扮演着越来越重要的角色。今天,就让我们一起揭开它的神秘面纱,看看这个“幕后英雄”到底是怎么工作的。 什么是CRM系统? 首先,我们要了解什么是CRM。简单来…...

美畅物联丨JS播放器录像功能:从技术到应用的全面解析
畅联云平台的JS播放器是一款功能十分强大的视频汇聚平台播放工具,它已经具备众多实用功能,像实时播放、历史录像回放、云台控制、倍速播放、录像记录、音频播放、画面放大、全屏展示、截图捕捉等等。这些功能构建起了一个高效、灵活且用户友好的播放环境…...

我们来学mysql -- 事务并发之不可重复读(原理篇)
事务并发之不可重复读 题记不可重复读系列文章 题记 在《事务之概念》提到事务对应现实世界的状态转换,这个过程要满足4个特性这世界,真理只在大炮射程之类,通往和平的道路,非“常人”可以驾驭一个人生活按部就班,人多…...

ABAQUS进行焊接仿真分析(含子程序)
0 前言 焊接技术作为现代制造业中的重要连接工艺,广泛应用于汽车、船舶、航空航天、能源等多个行业。焊接接头的质量和性能直接影响到结构件的安全性、可靠性和使用寿命。因此,在焊接过程中如何有效预测和优化焊接过程中的热效应、应力变化以及材料变形等问题,成为了焊接研…...
BAPI_GOODSMVT_CREATE物料凭证增强字段
目的:增加字段LSMNG LSMEH的赋值 项目MSEG 的 BAPI 表增强结构 BAPI_TE_XMSEG 抬头MKPF 的 BAIP 表增强 BAPI_TE_XMKPF 1. 在结构BAPI_TE_XMSEG中appending structure附加结构 ZMSEG_001,增加字段LSMNG, LSMEH In The method IF_EX_MB_H…...

tomcat的优化和动静分离
tomcat的优化 1.tomcat的配置优化 2.操作系统的内核优化 注意:设置保存后,需要重新ssh连接才会看到配置更改的变化 vim /etc/security/limits.conf # 65535 为Linux系统最大打开文件数 * soft nproc 65535 * hard nproc 65535 * soft nofile 65535 *…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...

9-Oracle 23 ai Vector Search 特性 知识准备
很多小伙伴是不是参加了 免费认证课程(限时至2025/5/15) Oracle AI Vector Search 1Z0-184-25考试,都顺利拿到certified了没。 各行各业的AI 大模型的到来,传统的数据库中的SQL还能不能打,结构化和非结构的话数据如何和…...