
tinystruct-patterns
热门基于 tinystruct Java 框架开发的专家级指导。适用于开发 tinystruct 本身或任何基于其构建的项目——涵盖 Application 类创建、@Action 路由映射、单元测试、ActionRegistry、HTTP/CLI 双模式处理、内置 HTTP 服务器、事件系统、基于 Builder/Builders 的 JSON 处理、基于 AbstractData 的数据库持久化、POJO 自动生成、服务端发送事件(SSE)、文件上传以及出站 HTTP 网络请求等场景。
基于 tinystruct Java 框架开发的专家级指导。适用于开发 tinystruct 本身或任何基于其构建的项目——涵盖 Application 类创建、@Action 路由映射、单元测试、ActionRegistry、HTTP/CLI 双模式处理、内置 HTTP 服务器、事件系统、基于 Builder/Builders 的 JSON 处理、基于 AbstractData 的数据库持久化、POJO 自动生成、服务端发送事件(SSE)、文件上传以及出站 HTTP 网络请求等场景。
tinystruct 开发模式与实践
使用 tinystruct Java 框架构建模块的架构与实现模式。tinystruct 是一款轻量、高性能的框架,将 CLI 和 HTTP 视为一等公民,无需编写 main() 方法且配置极简。
核心理念
CLI 与 HTTP 皆是一等公民。 理想情况下,每个带有 @Action 注解的方法无需任何修改,即可同时在终端和 Web 浏览器中运行。这种“双模式”能力是 tinystruct 的核心设计哲学。
适用场景
何时使用
- 通过继承
AbstractApplication创建全新的Application模块。 - 使用
@Action定义路由和命令行 Action。 - 通过
Context处理单次请求的状态。 - 使用原生
Builder和Builders组件完成 JSON 序列化。 - 通过
AbstractDataPOJO 处理数据库持久化。 - 使用
generate命令基于数据库表生成 POJO 类。 - 实现 Server-Sent Events (SSE) 完成实时消息推送。
- 处理 multipart 格式的文件上传。
- 使用
URLRequest和HTTPHandler发起出站 HTTP 请求。 - 在
application.properties中配置数据库连接或系统属性。 - 排查路由冲突(Action 冲突)或 CLI 参数解析问题。
工作原理
tinystruct 框架将所有带有 @Action 注解的方法都视为可同时在终端和 Web 环境中路由的端点。应用继承自 AbstractApplication,该基类提供了 init() 等核心生命周期钩子以及对请求 Context 的访问接口。
路由由 ActionRegistry 统一调度,它会自动将路径片段映射到方法参数并注入依赖。对于纯数据接口服务,建议使用原生的 Builder 和 Builders 组件进行 JSON 序列化,以保持零外部依赖。数据库持久层则基于 AbstractData POJO 和 XML 映射文件,无需引入第三方 ORM 框架即可完成 CRUD 操作。
代码示例
基础 Application 示例 (MyService)
public class MyService extends AbstractApplication {
@Override
public void init() {
this.setTemplateRequired(false); // Disable .view lookup for data/API apps
}
@Override public String version() { return "1.0.0"; }
@Action("greet")
public String greet() {
return "Hello from tinystruct!";
}
// Path parameter: GET /?q=greet/James OR bin/dispatcher greet/James
@Action("greet")
public String greet(String name) {
return "Hello, " + name + "!";
}
}
HTTP 请求方式区分 (login)
@Action(value = "login", mode = Mode.HTTP_POST)
public String doLogin(Request<?, ?> request) throws ApplicationException {
request.getSession().setAttribute("userId", "42");
return "Logged in";
}
原生 JSON 数据处理 (Builder + Builders)
import org.tinystruct.data.component.Builder;
import org.tinystruct.data.component.Builders;
@Action("api/data")
public String getData() throws ApplicationException {
Builders dataList = new Builders();
Builder item = new Builder();
item.put("id", 1);
item.put("name", "James");
dataList.add(item);
Builder response = new Builder();
response.put("status", "success");
response.put("data", dataList);
return response.toString(); // {"status":"success","data":[{"id":1,"name":"James"}]}
}
SSE 服务端推送 (Server-Sent Events)
import org.tinystruct.http.SSEPushManager;
@Action("sse/connect")
public String connect() {
return "{\"type\":\"connect\",\"message\":\"Connected to SSE\"}";
}
// Push to a specific client
String sessionId = getContext().getId();
Builder msg = new Builder();
msg.put("text", "Hello, user!");
SSEPushManager.getInstance().push(sessionId, msg);
// Broadcast to all
// Broadcast to all
SSEPushManager.getInstance().broadcast(msg);
文件上传
import org.tinystruct.data.FileEntity;
@Action(value = "upload", mode = Mode.HTTP_POST)
public String upload(Request<?, ?> request) throws ApplicationException {
List<FileEntity> files = request.getAttachments();
if (files != null) {
for (FileEntity file : files) {
System.out.println("Uploaded: " + file.getFilename());
}
}
return "Upload OK";
}
MCP 服务与工具集成
从 SDK 1.7.26 版本开始,tinystruct 原生支持 Model Context Protocol (MCP)。
MCP 相关 API(例如 org.tinystruct.mcp.MCPTool、org.tinystruct.mcp.MCPServer、org.tinystruct.mcp.MCPException)已直接内置在核心依赖中:
<dependency>
<groupId>org.tinystruct</groupId>
<artifactId>tinystruct</artifactId>
<version>1.7.26</version>
</dependency>
安全警告(提示词注入 Prompt Injection):
工具的返回值会直接送回 AI 模型的上下文窗口中。在将调用方传入的参数拼接到工具返回值之前,你必须对其进行校验和清洗。如果不做输入清洗,攻击者可能会注入恶意指令(Prompt Injection)来篡改模型的行为。请始终校验参数长度、字符集合法性以及空值。
创建 MCP Tool 的步骤:
- 继承
org.tinystruct.mcp.MCPTool。 - 为操作方法添加
@Action注解,并在arguments数组中使用@Argument声明参数。 - 在方法签名中显式接收参数,且参数名须与
@Argument中的key保持一致(不要在工具参数中使用getContext().getAttribute(...)取值)。
import org.tinystruct.mcp.MCPTool;
import org.tinystruct.mcp.MCPException;
import org.tinystruct.system.annotation.Action;
import org.tinystruct.system.annotation.Argument;
public class MyCustomTool extends MCPTool {
public MyCustomTool() {
super("custom", "A custom tool for demonstrating MCP");
}
@Action(
value = "custom/hello",
description = "Say hello to someone",
arguments = {
@Argument(key = "name", description = "The name to greet", type = "string", optional = false)
}
)
public String hello(String name) throws MCPException {
// SECURITY: Validate/sanitize tool inputs before returning to the model
// to prevent prompt injection vulnerabilities.
if (name == null || name.length() > 50 || !name.matches("^[a-zA-Z0-9 ]+$")) {
throw new MCPException("Invalid name provided");
}
return "Hello, " + name + "!";
}
}
部署 MCP Server 的步骤:
- 继承
org.tinystruct.mcp.MCPServer。 - 重写
init()方法,并通过this.registerTool()注册自定义工具。框架会自动扫描并映射其中的@Action方法。
import org.tinystruct.mcp.MCPServer;
public class MyMCPServer extends MCPServer {
@Override
public void init() {
super.init();
this.registerTool(new MyCustomTool());
}
@Override
public String version() {
return "1.0.0";
}
}
通过 dispatcher 启动服务:
bin/dispatcher start --import org.tinystruct.system.HttpServer --import com.example.MyMCPServer
配置说明
配置文件位于 src/main/resources/application.properties。
# Database
driver=org.h2.Driver
database.url=jdbc:h2:~/mydb
database.user=sa
database.password=
# Server
default.home.page=hello
server.port=8080
# Locale
default.language=en_US
# Session (Redis for clustered environments)
# default.session.repository=org.tinystruct.http.RedisSessionRepository
# redis.host=127.0.0.1
# redis.port=6379
在代码中获取配置值:
String port = this.getConfiguration("server.port");
避坑指南与反模式
| 常见症状 / 误用方式 | 正确做法 / 规范模式 |
|---|---|
引入了 com.google.gson 或 com.fasterxml.jackson |
使用原生的 org.tinystruct.data.component.Builder / Builders。 |
使用 List<Builder> 表示 JSON 数组 |
使用 Builders 以避免泛型类型擦除问题。 |
报 ApplicationRuntimeException: template not found 错误 |
纯 API/数据类应用需在 init() 中显式调用 setTemplateRequired(false)。 |
在 private 方法上添加 @Action 注解 |
Action 方法必须声明为 public 才能被框架自动注册。 |
在应用模块中硬编码 main(String[] args) |
统一使用 bin/dispatcher 作为所有模块的入口。 |
手动调用 ActionRegistry 进行注册 |
优先使用 @Action 注解,利用框架的自动扫描与注册机制。 |
| 运行时提示找不到 Action | 确保类已通过 --import 引入或已在 application.properties 中声明。 |
| CLI 参数取不到 | 命令行按 --key value 格式传递,在代码中通过 getContext().getAttribute("--key") 获取。 |
| 两个方法使用同一路径,导致触发了错误的 Action | 显式设置 mode(如区分 HTTP_GET 与 HTTP_POST)来区分请求。 |
最佳实践
- 细粒度拆分应用:按功能将业务逻辑拆分为职责单一的小型 Application,避免写成单体大类。
- 在
init()中进行初始化:配置读取和数据库关联等初始化逻辑应放在init()钩子中,而不是构造函数里;不要手动调用setAction(),统一使用@Action注解。 - 明确 Mode 约束:合理使用
@Action中的mode参数,将敏感操作限定为仅CLI可运行或仅响应特定 HTTP 请求方法。 - 使用 Context 处理可选标志:对于可选的 CLI 标志位,通过
getContext().getAttribute("--flag")获取,而不是直接加在方法形参列表里。 - 异步事件处理:对于由事件触发的重型耗时任务,在事件监听器内配合
CompletableFuture.runAsync()进行异步处理。
技术参考
更详细的指南可参阅 references/ 目录:
- 架构与配置 — 抽象层、包结构地图、属性配置
- 路由与 @Action — 注解细节、模式(Modes)、参数映射
- 数据处理 — Builder、Builders、JSON 序列化与解析
- 数据库持久化 — AbstractData POJO、CRUD 操作、XML 映射文件、POJO 自动生成
- 系统与常用功能 — Context、Session 管理、SSE、文件上传、事件机制、网络通信
- 测试模式 — JUnit 5 单元测试与 HTTP 集成测试
参考源码文件(内部)
src/main/java/org/tinystruct/AbstractApplication.java— 包含生命周期钩子的核心基类src/main/java/org/tinystruct/system/annotation/Action.java— Action 注解与 Mode 定义src/main/java/org/tinystruct/application/ActionRegistry.java— 路由引擎src/main/java/org/tinystruct/data/component/Builder.java— JSON 对象序列化组件src/main/java/org/tinystruct/data/component/Builders.java— JSON 数组序列化组件src/main/java/org/tinystruct/data/component/AbstractData.java— 支持 CRUD 的 POJO 基类src/main/java/org/tinystruct/data/Mapping.java— XML 映射解析器src/main/java/org/tinystruct/data/tools/MySQLGenerator.java— POJO 代码生成器参考实现src/main/java/org/tinystruct/data/component/FieldType.java— SQL 与 Java 类型映射关系src/main/java/org/tinystruct/data/component/Condition.java— 链式 SQL 查询构建器src/main/java/org/tinystruct/http/SSEPushManager.java— SSE 连接管理src/test/java/org/tinystruct/application/ActionRegistryTest.java— 路由注册表测试示例src/test/java/org/tinystruct/system/HttpServerHttpModeTest.java— HTTP 集成测试模式示例





