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

Spring Boot 项目五维度九层次分层架构实现实践研究——持续更新中

说明:本博文主要参考来自 https://blog.csdn.net/BASK2311/article/details/128198005

据实践内容及代码持续总结更新中。

五个分层维度:SpringBoot工程分层实战

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
-gameEntityMappler.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);}
}

3 文章总结

我们再次回顾分层五个思考维度:

(1) 单一

每层只处理一类事情,例如util只承载工具对象,integration只处理外部服务,每层职责单一且清晰

(2) 降噪

如无必要无增实体,例如查询结果DTO只透出最关键字段,例如运动员ID、创建时间、修改时间等业务不强字段无须透出

(3) 适配

service、facade、intergration层都存在适配器,翻译信息为本层或者下层可以理解的信息

(4) 业务

业务对象可以通过充血模型聚合业务,例如在业务对象中聚合业务校验逻辑

(5) 数据

数据对象要纯净,例如通过string类型保存比赛表现,数据层无需解析

相关文章:

Spring Boot 项目五维度九层次分层架构实现实践研究——持续更新中

说明&#xff1a;本博文主要参考来自 https://blog.csdn.net/BASK2311/article/details/128198005 据实践内容及代码持续总结更新中。 五个分层维度&#xff1a;SpringBoot工程分层实战 1 分层思想 计算机领域有一句话&#xff1a;计算机中任何问题都可通过增加一个虚拟层解…...

stm32常见数据类型

stm32的数据类型的字节长度 s8 占用1个byte&#xff0c;数据范围 -2^7 到 (2^7-1) s16 占用2个byte&#xff0c;数据范围 -2^15 到 (2^15-1) s32 占用 4个byte&#xff0c;数据范围 -2^31 到 (231-1)231 2147483647 int64_t占用8个byte&#xff0c;数据范围 -2^63 到 (2^63-1)…...

mac m1使用docker安装kafka

1.拉取镜像 docker pull zookeeper docker pull wurstmeister/kafka 2.启动zookeeper docker run -d --name zookeeper -p 2181:2181 zookeeper 3.设置zookeeper容器对外服务的ip Zookeeper_Server_IP$(docker inspect zookeeper --format{{ .NetworkSettings.IPAddress }}…...

SpringBoot核心配置和注解

目录 一、注解 元注解 基本注解 启动注解 二、配置 格式介绍 读取配置文件信息 案例演示1 嵌套读取bean信息 案例演示2 读取Map&#xff0c;List 以及 Array 类型配置数据 案例演示3 三、总结 一、注解 之前我们了解了SpringBoot基础和AOP简单应用&#xff0c;这期来讲…...

第三章 图论 No.3 flody之多源汇最短路,传递闭包,最小环与倍增

文章目录 多源汇最短路&#xff1a;1125. 牛的旅行传递闭包&#xff1a;343. 排序最小环&#xff1a;344. 观光之旅345. 牛站 flody的四个应用&#xff1a; 多源汇最短路传递闭包找最小环恰好经过k条边的最短路 倍增 多源汇最短路&#xff1a;1125. 牛的旅行 1125. 牛的旅行 …...

Leetcode-每日一题【剑指 Offer 17. 打印从1到最大的n位数】

题目 输入数字 n&#xff0c;按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3&#xff0c;则打印出 1、2、3 一直到最大的 3 位数 999。 示例 1: 输入: n 1输出: [1,2,3,4,5,6,7,8,9] 说明&#xff1a; 用返回一个整数列表来代替打印 n 为正整数 解题思路 前置知识 M…...

远程调试MySQL内核

1 vscode 需要安装remote-ssh插件 安装成功后&#xff0c;登录&#xff1a; 默认远程服务器的登录 ssh rootip注意&#xff0c;Linux需要设置root远程登录&#xff1b; 2 安装debug扩展 C\C extemsion Pack C\C3 设置Attach进程 {// Use IntelliSense to learn about poss…...

前端学习---vue2--选项/数据--data-computed-watch-methods-props

写在前面&#xff1a; vue提供了很多数据相关的。 文章目录 data 动态绑定介绍使用使用数据 computed 计算属性介绍基础使用计算属性缓存 vs 方法完整使用 watch 监听属性介绍使用 methodspropspropsData data 动态绑定 介绍 简单的说就是进行双向绑定的区域。 vue实例的数…...

UML-构件图

目录 1.概述 2.构件的类型 3.构件和类 4.构件图 1.概述 构件图主要用于描述各种软件之间的依赖关系&#xff0c;例如&#xff0c;可执行文件和源文件之间的依赖关系&#xff0c;所设计的系统中的构件的表示法及这些构件之间的关系构成了构件图 构件图从软件架构的角度来描述…...

uniapp使用视频地址获取视频封面

很多时候我们都需要使用视频的第一帧当作视频的封面&#xff0c;今天我们从uni-app的安卓app这个环境来实现下这个需求。文中需要你对uniapp的renderjs有一定了解&#xff0c;可以先看我的这篇文章初识renderjs uniapp 安卓APP端&#xff08;ios未测试&#xff09; 方法&…...

java操作PDF:转换、合成、切分

将PDF每一页切割成图片 PDFUtils.cutPNG("D:/tmp/1.pdf","D:/tmp/输出图片路径/"); 将PDF转换成一张长图片 PDFUtils.transition_ONE_PNG("D:/tmp/1.pdf"); 将多张图片合并成一个PDF文件 PDFUtils.merge_PNG("D:/tmp/测试图片/"); 将多…...

递增子序列——力扣491

文章目录 题目描述递归枚举 + 减枝题目描述 递归枚举 + 减枝 递归枚举子序列的通用模板 vector<vector<int>> ans; vector<int> temp; void dfs(int cur...

解密!品牌独立站为何能成为外国消费者的心头爱?

中国人做事强调要知其然、知其所以然、知其所以必然。这一理念非常符合新时代中国跨境出海品牌独立站的发展思路。在做好品牌独立站之前&#xff0c;我们也必须知其然&#xff08;什么是独立站&#xff1f;&#xff09;&#xff0c;知其所以然&#xff08;为什么要建独立站&…...

【HDFS】每天一个RPC系列----complete(二):客户端侧

上图给出了最终会调用到complete RPC的客户端侧方法链路(除去Router那条线了)。 org.apache.hadoop.hdfs.DFSOutputStream#completeFile(org.apache.hadoop.hdfs.protocol.ExtendedBlock): 下面这个方法在complete rpc返回true之前,会进行重试,直到超过最大重试次数抛异…...

五、PC远程控制ESP32 LED灯

1. 整体思路 2. 代码 # 整体流程 # 1. 链接wifi # 2. 启动网络功能(UDP) # 3. 接收网络数据 # 4. 处理接收的数据import socket import time import network import machinedef do_connect():wlan = network.WLAN(network.STA_IF)wlan.active(True)if not wlan.isconnected(…...

详解PHP反射API

PHP中的反射API就像Java中的java.lang.reflect包一样。它由一系列可以分析属性、方法和类的内置类组成。它在某些方面和对象函数相似&#xff0c;比如get_class_vars()&#xff0c;但是更加灵活&#xff0c;而且可以提供更多信息。反射API也可与PHP最新的面向对象特性一起工作&…...

打开虚拟机进行ip addr无网络连接

打开虚拟机进行ip addr无网络连接 参考地址&#xff1a;https://www.cnblogs.com/Courage129/p/16796390.html 打开虚拟机进行ip addr无网络连接。 输入下面的命令&#xff0c; sudo dhclient ens33 会重新分配一个新的ip地址&#xff0c;但是此时的ip地址已经不是我原先在虚…...

Spring Boot如何整合mybatisplus

文章目录 1. 相关配置和代码2. 整合原理2.1 spring boot自动配置2.2 MybatisPlusAutoConfiguration2.3 debug流程2.3.1 MapperScannerRegistrar2.3.2MapperScannerConfigurer2.3.3 创建MybatisPlusAutoConfiguration2.3.4 创建sqlSessionFactory2.3.5 创建SqlSessionTemplate2.…...

webpack基础知识一:说说你对webpack的理解?解决了什么问题?

一、背景 Webpack 最初的目标是实现前端项目的模块化&#xff0c;旨在更高效地管理和维护项目中的每一个资源 模块化 最早的时候&#xff0c;我们会通过文件划分的形式实现模块化&#xff0c;也就是将每个功能及其相关状态数据各自单独放到不同的JS 文件中 约定每个文件是一…...

小研究 - 基于 MySQL 数据库的数据安全应用设计(二)

信息系统工程领域对数据安全的要求比较高&#xff0c;MySQL 数据库管理系统普遍应用于各种信息系统应用软件的开发之中&#xff0c;而角色与权限设计不仅关乎数据库中数据保密性的性能高低&#xff0c;也关系到用户使用数据库的最低要求。在对数据库的安全性进行设计时&#xf…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing

Muffin 论文 现有方法 CRADLE 和 LEMON&#xff0c;依赖模型推理阶段输出进行差分测试&#xff0c;但在训练阶段是不可行的&#xff0c;因为训练阶段直到最后才有固定输出&#xff0c;中间过程是不断变化的。API 库覆盖低&#xff0c;因为各个 API 都是在各种具体场景下使用。…...

Leetcode33( 搜索旋转排序数组)

题目表述 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...

[USACO23FEB] Bakery S

题目描述 Bessie 开了一家面包店! 在她的面包店里&#xff0c;Bessie 有一个烤箱&#xff0c;可以在 t C t_C tC​ 的时间内生产一块饼干或在 t M t_M tM​ 单位时间内生产一块松糕。 ( 1 ≤ t C , t M ≤ 10 9 ) (1 \le t_C,t_M \le 10^9) (1≤tC​,tM​≤109)。由于空间…...