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

Ota++框架学习

一:框架结构

这是一幅展现 Web 应用程序架构的示意图,以下是对图中各部分的详细解释:
外部交互部分
Request(请求):位于架构图的左上角,用黄色虚线框表示 。代表来自客户端(如浏览器、移动应用等)的请求,是整个流程的起始点,触发服务器端的处理逻辑。
CROS(跨域资源共享):以红色矩形呈现。当浏览器发起跨域请求时,CROS 机制会处理跨域相关的配置和验证,确保不同源之间的资源请求符合安全策略。它是保障浏览器跨域请求正常进行的关键组件。
Response(响应):在左下角,以紫色虚线框展示。是服务器处理完请求后,返回给客户端的数据或状态信息,携带处理结果。
服务器内部组件
Controller(控制器):绿色矩形,处于 OAT++ SERVER(浅蓝色大框,代表服务器端整体环境 )内。它接收来自 CROS 的请求,负责接收和解析请求参数,调用相应的 Service 层方法,并决定返回给客户端的数据格式和内容。是请求进入服务器后的第一站,起到调度和控制流程的作用。
DTO(数据传输对象):蓝色矩形,与 Controller 有交互。用于在不同层之间传输数据,对数据进行封装和转换,保证数据在传输过程中的一致性和规范性,避免直接传递复杂的业务对象。
Service(服务):粉色矩形。主要处理业务逻辑,接收 Controller 的调用请求,进行具体的业务操作,如数据的增删改查等。它可以调用其他组件(如 Other 代表的其他服务或模块)来完成业务功能。
Other(其他组件):黄色矩形,与 Service 有双向交互。代表系统中其他可能被 Service 调用的组件或服务,比如外部 API、数据库访问层、缓存服务等,用于扩展业务功能。
Application(应用):橙色矩形,位于服务器环境内。可理解为应用程序的整体上下文或容器,承载上述各个组件,提供运行时环境,管理组件的生命周期等。

整个架构图体现了一个典型的分层架构思想,从请求的接收到业务逻辑处理,再到数据的传输和响应返回,各组件分工明确,协同完成 Web 应用程序的功能。

二:

DOT数据传输对象

(1)DTO 基础概念

DTO(Data Transfer Object)是一种设计模式,用于在不同层(如客户端与服务器、服务与服务之间)传输数据。

在 Oat++ 中,DTO 是实现数据序列化和反序列化的核心组件,它提供了以下优势:

数据结构标准化:定义清晰的接口规范

安全的数据交换:避免直接暴露业务对象

性能优化:通过选择性序列化减少传输数据量

类型安全:编译时类型检查

跨语言兼容性:支持多种格式(JSON、XML 等)

(2)DTO 定义与结构

2.1基本 DTO 定义

在 Oat++ 中,DTO 是通过继承 oatpp::DTO 并使用代码生成宏来定义的:

#include "oatpp/core/macro/codegen.hpp"
#include "oatpp/core/Types.hpp"/* 开始 DTO 代码生成 */
#include OATPP_CODEGEN_BEGIN(DTO)/*** 用户数据传输对象*/
class UserDto : public oatpp::DTO {DTO_INIT(UserDto, DTO)DTO_FIELD(Int32, id);                // 整数类型字段DTO_FIELD(String, name, "user-name"); // 字符串类型字段,指定 JSON 字段名DTO_FIELD(Boolean, active);         // 布尔类型字段DTO_FIELD(DateTime, created);       // 日期时间类型字段};/* 结束 DTO 代码生成 */
#include OATPP_CODEGEN_END(DTO)

 2.2字段属性详解

基本数据类型。Oat++ 支持多种基本数据类型:

DTO_FIELD(Int8, int8Field); // 8位整数

DTO_FIELD(Int16, int16Field); // 16位整数

DTO_FIELD(Int32, int32Field); // 32位整数

DTO_FIELD(Int64, int64Field); // 64位整数

DTO_FIELD(Float32, float32Field); // 32位浮点数

DTO_FIELD(Float64, float64Field); // 64位浮点数

DTO_FIELD(Boolean, boolField); // 布尔值

DTO_FIELD(String, stringField); // 字符串

DTO_FIELD(DateTime, dateTimeField); // 日期时间

复杂数据类型。支持嵌套 DTO 和集合类型:

// 嵌套 DTO DTO_FIELD(Object<AddressDto>, address);

// 集合类型

DTO_FIELD(List<String>, tags);

DTO_FIELD(Fields<String>, metadata);

DTO_FIELD(Vector<Object<ItemDto>>, items); 

字段选项。可以为字段设置默认值、验证规则等:

// 设置默认值

DTO_FIELD(String, language, "lang") = "en";

// 标记为必需字段

DTO_FIELD_REQUIRED(Int32, age);

// 可选字段(默认值为 nullptr)

DTO_FIELD(String, phone);

(3) DTO 高级特性

3.1 自定义序列化 / 反序列化

可以通过 DTO_FIELD_GET 和 DTO_FIELD_SET 宏自定义字段的序列化和反序列化逻辑:

class CustomDto : public oatpp::DTO {DTO_INIT(CustomDto, DTO)DTO_FIELD(String, encryptedField);// 自定义序列化逻辑DTO_FIELD_GET(encryptedField) {// 模拟加密return encrypt(value);}// 自定义反序列化逻辑DTO_FIELD_SET(encryptedField) {// 模拟解密value = decrypt(inValue);}};

3.2 验证与约束

Oat++ 提供了基本的验证功能:

class ValidatedUserDto : public oatpp::DTO {DTO_INIT(ValidatedUserDto, DTO)// 必需字段,最小长度为 3DTO_FIELD_REQUIRED(String, username)->addValidator(Validator::minLength(3));// 邮箱格式验证DTO_FIELD_REQUIRED(String, email)->addValidator(Validator::email());// 年龄范围验证DTO_FIELD_REQUIRED(Int32, age)->addValidator(Validator::min(18))->addValidator(Validator::max(100));};

 3.3 继承与多态

DTO 支持继承,可以创建基类 DTO 和派生类 DTO:

/* 基类 DTO */
class AnimalDto : public oatpp::DTO {DTO_INIT(AnimalDto, DTO)DTO_FIELD(String, type);DTO_FIELD(String, name);};/* 派生类 DTO */
class DogDto : public AnimalDto {DTO_INIT(DogDto, AnimalDto)DTO_FIELD(String, breed);};class CatDto : public AnimalDto {DTO_INIT(CatDto, AnimalDto)DTO_FIELD(Boolean, lazy);};

(4)DTO 与 API 集成

4.1 在 API 中使用 DTO

在控制器中,可以直接使用 DTO 作为请求体和响应体:

ENDPOINT("POST", "user", createUser,BODY_DTO(Object<UserDto>, userDto)) {// 处理用户创建逻辑auto createdUser = userService->createUser(userDto);return createDtoResponse(Status::CODE_201, createdUser);
}ENDPOINT("GET", "user/{id}", getUser,PATH(Int32, id)) {// 获取用户逻辑auto user = userService->getUserById(id);return createDtoResponse(Status::CODE_200, user);
}

4.2 集合类型响应 

返回列表或集合类型:

ENDPOINT("GET", "users", getUsers) {auto users = userService->getAllUsers();return createDtoResponse(Status::CODE_200, users);
}

4.3 分页响应 

class PageDto : public oatpp::DTO {DTO_INIT(PageDto, DTO)DTO_FIELD(Vector<Object<UserDto>>, items);DTO_FIELD(Int32, page);DTO_FIELD(Int32, size);DTO_FIELD(Int32, total);};ENDPOINT("GET", "users", getUsers,QUERY(Int32, page, "page", 0),QUERY(Int32, size, "size", 10)) {auto page = userService->getUsersPage(page, size);return createDtoResponse(Status::CODE_200, page);
}

(5)DTO 与数据库集成 

5.1 DTO 与数据库实体映射

通常需要在 DTO 和数据库实体之间进行映射:

// 数据库实体
class User {
public:int id;std::string name;std::string email;bool active;
};// DTO 到实体的映射
User userDtoToEntity(const std::shared_ptr<UserDto>& dto) {User user;user.id = dto->id;user.name = dto->name->c_str();user.email = dto->email->c_str();user.active = dto->active;return user;
}// 实体到 DTO 的映射
std::shared_ptr<UserDto> userEntityToDto(const User& user) {auto dto = UserDto::createShared();dto->id = user.id;dto->name = user.name;dto->email = user.email;dto->active = user.active;return dto;
}

 5.2 使用 ORM 简化映射

// 定义数据库模式
#include OATPP_CODEGEN_BEGIN(DbClient)class UserDbClient : public oatpp::orm::DbClient {
public:QUERY(createUser,"INSERT INTO User (name, email, active) VALUES (:user.name, :user.email, :user.active);",PARAM(oatpp::Object<UserDto>, user))QUERY(getUserById,"SELECT * FROM User WHERE id=:id;",PARAM(Int32, id))};#include OATPP_CODEGEN_END(DbClient)

 (6)DTO 最佳实践

6.1 命名规范

使用清晰的命名,避免缩写(如 userId 而非 uid

保持与数据库字段或前端约定一致

使用 PascalCase 命名 DTO 类(如 UserProfileDto

6.2 字段设计原则

仅包含需要传输的数据

避免敏感信息(如密码哈希)

使用有意义的字段名,避免技术术语

考虑未来扩展性,预留字段

6.3 性能优化

避免过度嵌套,保持扁平化结构

使用集合类型代替重复嵌套

对于大数据量,考虑分页或流式传输

使用字段组选择性序列化(如仅返回必要字段)

(7)常见问题与解决方案

7.1 序列化 / 反序列化错误

  • 问题:JSON 字段名与 DTO 字段名不匹配

  • 解决方案:使用 DTO_FIELD 的第三个参数指定 JSON 字段名

  • 问题:类型不匹配

  • 解决方案:确保 DTO 字段类型与 JSON 数据类型一致

7.2 嵌套 DTO 处理

问题:复杂嵌套结构导致性能问题

解决方案:考虑扁平化设计或使用单独的 API 获取嵌套数据

问题:循环引用

解决方案:使用 ID 引用代替直接嵌套,或在序列化时忽略循环引用字段

7.3 验证失败

问题:验证规则不满足

解决方案:检查验证规则,确保数据符合要求

进阶方案:自定义验证器处理复杂业务规则

ENDPOINT

ENDPOINT 是 OAT++ 框架中用于定义 HTTP API 端点(路由) 的核心机制。它通过声明式语法将 HTTP 请求映射到具体的处理函数,并自动完成参数绑定、数据序列化/反序列化、错误处理等流程。以下是对其作用的详细讲解:

(1)ENDPOINT 的核心作用

1、定义路由规则

将特定的 HTTP 方法(如 GET、POST)和 URL 路径(如 /users/{id})绑定到一个处理函数。

示例:ENDPOINT("GET", "/users", getUsers) 表示当客户端发送 GET 请求到 /users 时,调用 getUsers 函数处理。

2、参数自动绑定

自动从 HTTP 请求中提取参数(路径参数、查询参数、请求体等),并转换为 C++ 类型或 DTO 对象。

示例:PATH(Int64, id) 自动将 URL 路径中的 {id} 转换为 int64_t 类型。

3、标准化响应生成

通过 createResponse 生成符合 HTTP 规范的响应(状态码、头部、数据体),并自动将 C++ 对象序列化为 JSON 等格式。

4、依赖注入与中间件支持

支持注入全局组件(如数据库连接池、日志服务),并可集成中间件(如身份验证、CORS 处理)。

(2)ENDPOINT 的语法结构

在 OAT++ 中,ENDPOINT 是一个宏(Macro),其基本语法如下:

ENDPOINT(
    HTTP方法,       // 如 "GET", "POST", "PUT", "DELETE"
    URL路径,        // 如 "/users", "/users/{id}"
    处理函数名,      // 自定义的函数名,如 getUserById
    参数绑定列表...  // 如 PATH(...), QUERY(...), BODY_DTO(...)
) {
    // 业务逻辑代码
    return createResponse(...); // 生成HTTP响应
}

(3)执行流程 

  1. 请求接收:OAT++ Server 监听并解析 HTTP 请求。

  2. 路由匹配:根据 URL 和方法匹配到对应的 ENDPOINT 处理函数。

  3. 参数绑定:自动提取路径、查询等参数,并进行类型转换与校验。

  4. 业务执行:控制器调用服务层处理逻辑,可能涉及数据库或外部服务。

  5. 响应生成:将结果序列化为标准格式(如 JSON),附加头部并返回客户端。

(4)ENDPOINT的关键特性

(一)支持的 HTTP 方法

所有标准方法:GETPOSTPUTDELETEPATCHHEADOPTIONS

示例:ENDPOINT("POST", "/users", createUser, BODY_DTO(...))

(二)参数绑定类型

参数类型示例
路径参数PATH(Type, Name)PATH(Int64, id)
查询参数QUERY(Type, Name)QUERY(String, name)
请求体(DTO)BODY_DTO(...)BODY_DTO(UserDto, user)
HTTP 头部HEADER(Type, Name)HEADER(String, authToken)
表单字段FORM_FIELD(...)FORM_FIELD(File, avatar)

 (三) 自动错误处理

类型不匹配:若客户端传递的参数无法转换为声明类型(如将字符串传给 Int64),OAT++ 自动返回 400 Bad Request

参数缺失:若未提供必需参数(如未指定 PATH 参数),返回 404 Not Found 或 400 Bad Request

(四)依赖注入

通过 OATPP_COMPONENT 注入全局依赖(如数据库连接、配置对象):

ENDPOINT("GET", "/users", getAllUsers,
         // 注入数据库组件
         OATPP_COMPONENT(std::shared_ptr<Database>, db)) {
  auto users = db->queryAllUsers();
  return createResponse(Status::CODE_200, users);
}

(5)最佳实践与注意事项

(一)保持Controller轻量

在 ENDPOINT 中仅处理参数绑定和响应生成,业务逻辑应交给 Service 层。

(二)合理使用DTO

对于复杂请求体,优先通过 DTO 定义数据结构,确保类型安全和可维护性。

(三)统一错误响应

使用 createResponse 返回标准化的错误格式:

return createResponse(Status::CODE_404, "User not found");

(四)路由分组与模块化 

按功能将端点分组到不同的 Controller 类中(如 UserControllerOrderController

(五)性能优化

避免在 ENDPOINT 中进行阻塞操作(如复杂计算),使用异步任务或线程池处理耗时逻辑。

API Controller

Oat++ API 控制器详解:构建强大的 RESTful 服务

(1)API 控制器基础概念

1.1什么是API 控制器?

在 Oat++ 中,API 控制器 是处理 HTTP 请求的核心组件,它负责:

1、定义 API 端点的路径、方法和参数

2、接收客户端请求并解析数据

3、调用相应的业务逻辑

4、返回格式化的响应给客户端

控制器通过继承 oatpp::web::server::api::ApiController 并使用代码生成宏来定义,使代码简洁且类型安全。

1.2 控制器与 MVC 模式

在 MVC(模型 - 视图 - 控制器)架构中,Oat++ 的控制器对应 MVC 中的 "C" 角色:

模型(Model):DTO 和业务逻辑

视图(View):通常由客户端处理,Oat++ 专注于数据返回

控制器(Controller):处理请求和响应

这种分离使代码结构清晰,易于维护和测试。

(2)控制器的基本结构

2.1 定义控制器类

#include "oatpp/web/server/api/ApiController.hpp"
#include "oatpp/parser/json/mapping/ObjectMapper.hpp"
#include "oatpp/core/macro/codegen.hpp"/* 开始控制器代码生成 */
#include OATPP_CODEGEN_BEGIN(ApiController)/*** 示例 API 控制器*/
class ExampleController : public oatpp::web::server::api::ApiController {
public:/*** 构造函数,需要传入 ObjectMapper 用于序列化/反序列化*/ExampleController(const std::shared_ptr<ObjectMapper>& objectMapper): oatpp::web::server::api::ApiController(objectMapper){}/* 端点定义将在这里 */};/* 结束控制器代码生成 */
#include OATPP_CODEGEN_END(ApiController)

2.2 注册控制器到路由器 

// 创建路由器
auto router = HttpRouter::createShared();// 创建 ObjectMapper
auto objectMapper = oatpp::parser::json::mapping::ObjectMapper::createShared();// 创建并注册控制器
auto controller = ExampleController::createShared(objectMapper);
controller->addEndpointsToRouter(router);

(3)端点(Endpoint)定义

上一节已介绍并讲解了Endpoint,此处不再赘述。

(4)请求处理与响应

4.1 请求对象

可以直接访问完整的请求对象:

ENDPOINT("GET", "request-info", getRequestInfo,REQUEST(std::shared_ptr<IncomingRequest>, request)) {auto path = request->getPath();auto headers = request->getHeaders();auto method = request->getMethod();// 构建响应auto responseDto = RequestInfoDto::createShared();responseDto->path = path;responseDto->method = method;responseDto->headers = headers;return createDtoResponse(Status::CODE_200, responseDto);
}

4.2 响应类型 

4.2.1 简单文本响应

return createResponse(Status::CODE_200, "Plain text response");

4.2.2 DTO 响应

return createDtoResponse(Status::CODE_200, userDto); 

4.2.3 自定义响应头

auto response = createResponse(Status::CODE_200, "Custom header response"); response->putHeader("X-Custom-Header", "Custom Value");

return response; 

4.2.4 文件响应 

auto response = createResponse(Status::CODE_200, "File content");

response->putHeader(Header::CONTENT_TYPE, "application/octet-stream");

response->putHeader(Header::CONTENT_DISPOSITION, "attachment; filename=example.txt");

return response;

4.2.5 错误响应

return createResponse(Status::CODE_404, "Resource not found"); 

(5)中间件与拦截器 

5.1 请求拦截器

可以为控制器或特定端点添加拦截器:

class AuthInterceptor : public oatpp::web::server::handler::RequestInterceptor {
public:std::shared_ptr<OutgoingResponse> intercept(const std::shared_ptr<IncomingRequest>& request) override {auto authHeader = request->getHeader("Authorization");if(!authHeader || !authService->validateToken(authHeader)) {return ResponseFactory::createResponse(Status::CODE_401, "Unauthorized");}// 将用户信息添加到请求上下文中request->putBundleData("userId", authService->getUserIdFromToken(authHeader));return nullptr; // 继续处理请求}};

 5.2 注册拦截器

// 为控制器添加全局拦截器
ENDPOINT("GET", "protected", getProtected,INTERCEPTOR(AuthInterceptor, authInterceptor)) {// 只有通过认证的请求才能到达这里auto userId = request->getBundleData("userId");return createResponse(Status::CODE_200, "Access granted");
}// 为控制器所有端点添加全局拦截器
void ExampleController::addEndpointsToRouter(const std::shared_ptr<HttpRouter>& router) {auto authInterceptor = std::make_shared<AuthInterceptor>();// 为所有端点添加拦截器router->addGlobalRequestInterceptor(authInterceptor);// 添加端点ApiController::addEndpointsToRouter(router);
}

(6)错误处理

6.1 自定义错误处理器

class CustomErrorHandler : public oatpp::web::server::handler::ErrorHandler {
public:std::shared_ptr<OutgoingResponse> handleError(const Status& status,const oatpp::String& message,const Headers& headers) override {auto errorDto = ErrorDto::createShared();errorDto->statusCode = status.code;errorDto->message = message;auto response = createDtoResponse(status, errorDto);// 添加额外的响应头for(auto& pair : headers.getAll()) {response->putHeader(pair.first.toString(), pair.second.toString());}return response;}};

6.2 注册错误处理器

// 创建并设置错误处理器
auto errorHandler = std::make_shared<CustomErrorHandler>();
serverConnectionHandler->setErrorHandler(errorHandler);

6.3 在控制器中抛出错误

ENDPOINT("GET", "user/{id}", getUser,PATH(Int32, id)) {auto user = userService->getUserById(id);if(!user) {throw oatpp::web::protocol::http::HttpError(Status::CODE_404,"User not found");}return createDtoResponse(Status::CODE_200, user);
}

(7)控制器组织与最佳实践

7.1 按业务功能拆分控制器

// 项目结构示例
src/
├── controller/
│   ├── UserController.hpp  // 用户相关 API
│   ├── ProductController.hpp  // 产品相关 API
│   └── OrderController.hpp  // 订单相关 API

7.2 使用服务层分离业务逻辑 

class UserController : public ApiController {
private:std::shared_ptr<UserService> m_userService;
public:UserController(const std::shared_ptr<ObjectMapper>& objectMapper,const std::shared_ptr<UserService>& userService): ApiController(objectMapper), m_userService(userService){}ENDPOINT("GET", "user/{id}", getUser,PATH(Int32, id)) {return createDtoResponse(Status::CODE_200, m_userService->getUserById(id));}};

7.3 版本控制 

// V1 控制器
class UserControllerV1 : public ApiController {// 旧版本 API
};// V2 控制器
class UserControllerV2 : public ApiController {// 新版本 API
};// 注册时区分路径
v1Router->addController(std::make_shared<UserControllerV1>(objectMapper));
v2Router->addController(std::make_shared<UserControllerV2>(objectMapper));

7.4 代码复用与基类 

class BaseController : public ApiController {
protected:std::shared_ptr<AuthService> m_authService;bool isAuthenticated(const std::shared_ptr<IncomingRequest>& request) {// 认证逻辑}std::shared_ptr<UserDto> getCurrentUser(const std::shared_ptr<IncomingRequest>& request) {// 获取当前用户}
};class UserController : public BaseController {// 继承认证和用户获取功能
};

相关文章:

Ota++框架学习

一&#xff1a;框架结构 这是一幅展现 Web 应用程序架构的示意图&#xff0c;以下是对图中各部分的详细解释&#xff1a; 外部交互部分 Request&#xff08;请求&#xff09;&#xff1a;位于架构图的左上角&#xff0c;用黄色虚线框表示 。代表来自客户端&#xff08;如浏览器…...

Chrome安装最新vue-devtool插件

本vue-devtool版本是官方的 v7.6.8版本&#xff0c;兼容性好、功能齐全且稳定。 操作步骤&#xff1a; 方法一&#xff1a; 打开谷歌浏览器 --> 右上角三个点 --> 扩展程序 --> 管理扩展程序 --> 加载已解压的扩展程序&#xff0c; 然后选择解压后的文件夹即可。…...

Android锁

引言 &#x1f512; 在 Android 应用的开发过程中&#xff0c;随着业务需求的复杂度不断提升&#xff0c;多线程并发场景层出不穷。为了保证数据一致性与线程安全&#xff0c;锁&#xff08;Lock&#xff09;成为了不可或缺的工具。本篇博客将深入剖析 Android 中常用的锁机制…...

bfs-最小步数问题

最小步长模型 特征&#xff1a; 主要是解决权值为1且状态为字符串类型的最短路问题&#xff0c;实质上是有向图的最短路问题&#xff0c;可以简化为bfs求最短路问题。 代表题目&#xff1a; acwing 845 八数码问题&#xff1a; 八数码题中由于每次交换的状态是由x进行上下左右…...

sqlalchemy库详细使用

SQLAlchemy 是 Python 中最强大、最受欢迎的 ORM&#xff08;对象关系映射&#xff09;库&#xff0c;它允许你使用 Python 对象来操作数据库&#xff0c;而不需要直接编写 SQL 语句。同时&#xff0c;它也提供了对底层 SQL 的完全控制能力&#xff0c;适用于从简单脚本到大型企…...

java----------->代理模式

目录 什么是代理模式&#xff1f; 为什么会有代理模式&#xff1f; 怎么写代理模式&#xff1f; 实现代理模式总共需要三步&#xff1a; 什么是代理模式&#xff1f; 代理模式&#xff1a;给目标对象提供一个代理对象&#xff0c;并且由代理对象控制目标对象的引用 代理就是…...

ET ProcessInnerSender类(实体) 分析

ProcessInnerSender 作用是进程内部发送Actor消息 字段 TIMEOUT_TIME 超时时间RpcId 用来累加requestCallback 存储RPC的回调事件list 用来获取MessageQueue中的Actor消息 方法 Awake 初始化在MessageQueue中注册待处理的消息队列Destroy 移除在MessageQueue中的消息队列U…...

Untiy基础学习(十四)核心系统—物理系统之碰撞检测代码篇 刚体,碰撞体,材质

目录 一、碰撞器&#xff08;Collider&#xff09;与触发器&#xff08;Trigger&#xff09; 二、碰撞检测条件 三、碰撞事件与触发器事件&#xff0c;可以理解为特殊的生命周期函数。 四、讲讲如何选择 ​编辑 五、总结 一、碰撞/触发事件函数对照表 二、Collider 与 …...

SAP学习笔记 - 开发08 - Eclipse连接到 BTP Cockpit实例

有关BTP&#xff0c;之前学了一点儿&#xff0c;今天继续学习。 SAP学习笔记 - 开发02 - BTP实操流程&#xff08;账号注册&#xff0c;BTP控制台&#xff0c;BTP集成开发环境搭建&#xff09;_sap btp开发-CSDN博客 如何在Eclipse中连接BTP Cockpit开发环境实例。 1&#xf…...

如何用Redis实现分布式锁?RedLock算法的核心思想?Redisson的看门狗机制原理?

一、Redis分布式锁基础实现 public class RedisDistributedLock {private JedisPool jedisPool;private String lockKey;private String clientId;private int expireTime 30; // 默认30秒public boolean tryLock() {try (Jedis jedis jedisPool.getResource()) {// NX表示不…...

Java项目层级介绍 java 层级 层次

java 层级 层次 实体层 控制器层 数据连接层 Service : 业务处理类 Repository &#xff1a;数据库访问类 Java项目层级介绍 https://blog.csdn.net/m0_67574906/article/details/145811846 在Java项目中&#xff0c;层级结构&#xff08;Layered Architecture&#xf…...

Git的安装和配置(idea中配置Git)

一、Git的下载和安装 前提条件&#xff1a;IntelliJ IDEA 版本是2023.3 &#xff0c;那么配置 Git 时推荐使用 Git 2.40.x 或更高版本 下载地址&#xff1a;CNPM Binaries Mirror 操作&#xff1a;打开链接 → 滚动到页面底部 → 选择2.40.x或更高版本的 .exe 文件&#xf…...

【2025版】Spring Boot面试题

文章目录 1. Spring, Spring MVC, SpringBoot是什么关系&#xff1f;2. 谈一谈对Spring IoC的理解3. Component 和 Bean 的区别&#xff1f;4. Autowired 和 Resource 的区别&#xff1f;5. 注入Bean的方法有哪些&#xff1f;6. 为什么Spring 官方推荐构造函数注入&#xff1f;…...

火山引擎实时音视频 高代码跑通日志

实时音视频 SDK 概览--实时音视频-火山引擎 什么是实时音视频 火山引擎实时音视频&#xff08;Volcengine Real Time Communication&#xff0c;veRTC&#xff09;提供全球范围内高可靠、高并发、低延时的实时音视频通信能力&#xff0c;实现多种类型的实时交流和互动。 通…...

atoi函数,sprintf函数,memcmp函数,strchar函数的具体原型,功能,返回值;以及使用方法

以下是这四个C语言标准库函数的详细说明&#xff1a; 1. atoi() - 字符串转整数 **原型**&#xff1a; int atoi(const char *str); **功能**&#xff1a; 将字符串参数str转换为整数&#xff08;int类型&#xff09;。函数会跳过前面的空白字符&#xff08;如空格、制表符&am…...

C++学习之打车软件git版本控制

目录 01 3-git的简介 02 4-git的下载和提交代码 03 5-git添加一个新文件 04 5-删除一个文件 05 6-git的批量添加和提交文件 06 7-git重命名文件名 07 8-git解决代码冲突 08 9-git的分支的概念 09 10-创建项目代码仓库 10 1-git提交代码复习 01 3-git的简介 1 --------…...

基于 PostgreSQL 的 ABP vNext + ShardingCore 分库分表实战

&#x1f680; 基于 PostgreSQL 的 ABP vNext ShardingCore 分库分表实战 &#x1f4d1; 目录 &#x1f680; 基于 PostgreSQL 的 ABP vNext ShardingCore 分库分表实战✨ 背景介绍&#x1f9f1; 技术选型&#x1f6e0;️ 环境准备✅ Docker Compose&#xff08;多库 & 读…...

jenkins 启动报错

java.lang.UnsatisfiedLinkError: /opt/application/jdk-17.0.11/lib/libfontmanager.so: libfreetype.so.6: cannot open shared object file: No such file or directory。 解决方案&#xff1a; yum install freetype-devel 安装完成之后重启jenkins。...

C++ 套接字函数详细介绍

目录 头文件1. 套接字创建与配置2. 绑定地址与端口3. 连接建立4. 数据传输5. 套接字选项6. 地址转换7. 套接字关闭8. 其他实用函数 C 套接字函数详细介绍 套接字(Socket)是网络通信的基本端点&#xff0c;C中通常使用BSD套接字API进行网络编程。以下是主要的套接字相关函数及其…...

【合新通信】无人机天线拉远RFOF(射频光纤传输)解决方案

无人机天线拉远RFOF方案通过光纤替代传统射频电缆&#xff0c;实现无人机与地面控制站之间的高保真、低损耗信号传输&#xff0c;尤其适用于高频段&#xff08;如毫米波&#xff09;、远距离或复杂电磁环境下的无人机作业场景。 核心应用场景 军事侦察与电子战 隐蔽部署&…...

程序设计语言----软考中级软件设计师(自用学习笔记)

目录 1、解释器和编译器 2、程序的三种控制结构 3、程序中的数据必须具有类型 4、编译、解释程序翻译阶段 5、符号表 6、编译过程 7、上下文无关文法 8、前、中、后缀表达式 9、前、后缀表达式计算 10、语法树中、后序遍历 11、脚本语言和动态语言 12、语法分析方法…...

火山RTC 7 获得远端裸数据

一、获得远端裸数据 1、获得h264数据 1&#xff09;、远端编码后视频数据监测器 /*** locale zh* type callback* region 视频管理* brief 远端编码后视频数据监测器<br>* 注意&#xff1a;回调函数是在 SDK 内部线程&#xff08;非 UI 线程&#xff09;同步抛出来的&a…...

通过SMTP协议实现Linux邮件发送配置指南

一、环境准备与基础配置 1. SMTP服务开通&#xff08;以qq邮箱为例&#xff09; 登录qq邮箱网页端&#xff0c;进入「设置」-「POP3/SMTP/IMAP」 开启「SMTP服务」并获取16位授权码&#xff08;替代邮箱密码使用&#xff09; 记录关键参数&#xff1a; SMTP服务器地址&#…...

若依框架页面

1.页面地址 若依管理系统 2.账号和密码 管理员 账号admin 密码admin123 运维 账号yuwei 密码123456 自己搭建的地址方便大家学习&#xff0c;不要攻击哦&#xff0c;谢谢啊...

44、私有程序集与共享程序集有什么区别?

私有程序集&#xff08;Private Assembly&#xff09;与共享程序集&#xff08;Shared Assembly&#xff09;是.NET框架中程序集部署的两种不同方式&#xff0c;它们在部署位置、版本控制、访问权限等方面存在显著差异&#xff0c;以下是对二者的详细比较&#xff1a; 1. 部署…...

【Java面试题】——this 和 super 的区别

&#x1f381;个人主页&#xff1a;User_芊芊君子 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 &#x1f50d;系列专栏&#xff1a;【Java】内容概括 【前言】 在Java的世界里&#xff0c;this和 super是两个非常重要且容易混淆的关键字。无论是在日常…...

记录 QT 在liunx 下 QFileDialog 类调用问题 ()Linux下QFileDialog没反应)

1. 2025.05.14 踩坑记录 因为QT 在 liunx 文件系统不同导致的 Windows &#xff1a; QString filePath QFileDialog::getOpenFileName(nullptr, "选择文件", ".", "文本文件 (*.txt);所有文件 (*.*)"); 没问题 liunx 下 打不开&#xff…...

CentOS 7 内核升级指南:解决兼容性问题并提升性能

点击上方“程序猿技术大咖”&#xff0c;关注并选择“设为星标” 回复“加群”获取入群讨论资格&#xff01; CentOS 7 默认搭载的 3.10.x 版本内核虽然稳定&#xff0c;但随着硬件和软件技术的快速发展&#xff0c;可能面临以下问题&#xff1a; 硬件兼容性不足&#xff1a;新…...

【前端】:单 HTML 去除 Word 批注

在现代办公中&#xff0c;.docx 文件常用于文档编辑&#xff0c;但其中的批注&#xff08;注释&#xff09;有时需要在分享或归档前被去除。本文将从原理出发&#xff0c;深入剖析如何在纯前端环境下实现对 .docx 文件注释的移除&#xff0c;并提供完整的实现源码。最后&#x…...

解决 PicGo 上传 GitHub图床及Marp中Github图片编译常见难题指南

[目录] 0.行文概述 1.PicGo图片上传失败 2.*关于在Vscode中Marp图片的编译问题* 3.总结与启示行文概述 写作本文的动机是本人看到了Awesome Marp&#xff0c;发现使用 Markdown \texttt{Markdown} Markdown做PPT若加持一些 CSS , JavaScript \texttt{CSS},\texttt{JavaScript} …...