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 *…...
[ShaderLab] 【Unity】【图像编程】理解 Unity Shader 的结构
在计算机图形学领域,开发者经常面临着管理着色器复杂性的挑战。正如大卫惠勒(David Wheeler)所说:“计算机科学中的任何问题都可以通过增加一层抽象来解决。” Unity 提供了这样一层抽象,即 ShaderLab,它通过组织和定义渲染过程的各个步骤,简化了编写着色器的过程。 什…...
vue的前端架构 介绍各自的优缺点
Vue.js 是一个用于构建用户界面的渐进式框架,可以根据项目的复杂性和需求选择不同的前端架构。以下是几种常见的 Vue 前端架构及其优缺点: 1. 单页应用 (SPA) 单页应用(Single Page Application,简称 SPA)是一种现代…...
可信AI与零知识证明的概念
可信AI 可信AI是指人工智能的设计、开发和部署遵循一系列原则和方法,以确保其行为和决策是可靠、可解释、公平、安全且符合人类价值观和社会利益的.以下是关于可信AI的举例说明、实现方式及主流方案: 举例说明 医疗诊断领域:一个可信AI的医疗诊断系统,不仅能够准确地识别…...
JavaScript逆向时,常用的11个hook
提示:记录工作中遇到的需求及解决办法 文章目录 前言01、dom操作02、Cookie操作03、事件监听操作04、AJAX拦截操作05、函数替换操作06、Header操作07、URL操作08、JSON.stringify操作09、JSON.parse操作10、eval操作11、Function操作前言 在逆向分析JavaScript代码时,开发者…...

PCL点云库入门——PCL库可视化之CloudViewer类简单点云信息显示
1、前言 可视化(visualization)涉及运用计算机图形学和图像处理技术,将数据转换成图像并在屏幕上展示,同时支持交互式处理。在PCL库中,一系列强大的可视化工具可供使用,其中较为流行的包括CloudViewer和PCL…...
C++ 【衔接篇】
大名鼎鼎的c实际上是由c语言扩展而来的,它最初是由本贾尼在20世纪80年代开发。目的是支持面向对象编程,同时保持c语言高效和可移植等优点。c是c的扩展,在一定程度上解决了c语言在特殊场景下的使用局限。 1、命名空间 在详细说明命名空间之前…...
qcreator 调试原理
在 Qt 开发中,Qt Creator 是一个集成开发环境(IDE),用于开发 Qt 应用程序。Qt Creator 提供了强大的调试功能,可以帮助开发者高效地调试 C、QML 等语言的应用程序。Qt Creator 支持多种调试工具,包括基于 G…...

Windows 系统中的组策略编辑器如何打开?
组策略是 Windows 操作系统中用于设置计算机和用户配置的重要工具。它允许管理员控制各种系统功能,从桌面背景到安全设置等。对于 Windows 专业版、企业版和教育版用户来说,可以通过组策略编辑器(Group Policy Editor)来管理这些设…...
scala的泛型类
泛型:类型参数化 泛型类指的是把泛型定义到类的声明上, 即:该类中的成员的参数类型是由泛型来决定的. 在创建对象时, 明确具体的数据类型. 定义格式: class 类名(成员名:数据类型) class 类名[泛型名](成员名:泛型名) 参考代…...
基于Couchbase的数据构建方案:数仓分层
初步方案是将公共层和报表层分别放在不同的bucket中,这种设计从存储和访问优化的角度是合理的,但仍有以下细节需要考虑: 1. 数仓公共层设计(origin bucket) 合理性分析: 将ODS、DWD、DWS层的数据放在一个b…...