Metadata-Version: 2.4
Name: springbootAI
Version: 2.0.0
Summary: Python版Spring Boot框架，集成PyMyBatis ORM
Author-email: YuConggen <1516933915@qq.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/YUCONGGEN/springbootAI
Project-URL: Repository, https://github.com/YUCONGGEN/springbootAI.git
Keywords: spring,springboot,mybatis,orm,web,framework,python
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Database
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi==0.141.1
Requires-Dist: uvicorn==0.39.0
Requires-Dist: websockets==15.0.1
Requires-Dist: pyyaml==6.0.3
Requires-Dist: python-dotenv==1.2.2
Requires-Dist: DBUtils==3.1.2
Requires-Dist: cryptography==50.0.0
Requires-Dist: bcrypt==5.0.0
Requires-Dist: pyjwt==2.13.0
Requires-Dist: pydantic==2.13.4
Requires-Dist: requests==2.34.2
Requires-Dist: httpx==0.28.1
Provides-Extra: mysql
Requires-Dist: PyMySQL==1.2.0; extra == "mysql"
Provides-Extra: postgresql
Requires-Dist: psycopg2-binary==2.9.12; extra == "postgresql"
Provides-Extra: sqlalchemy
Requires-Dist: sqlalchemy==2.0.40; extra == "sqlalchemy"
Provides-Extra: redis
Requires-Dist: redis==8.1.0; extra == "redis"
Provides-Extra: rabbitmq
Requires-Dist: pika==1.4.4; extra == "rabbitmq"
Provides-Extra: nacos
Requires-Dist: nacos-sdk-python==2.0.11; extra == "nacos"
Provides-Extra: prometheus
Requires-Dist: prometheus-client==0.26.0; extra == "prometheus"
Provides-Extra: logging
Requires-Dist: loguru==0.7.3; extra == "logging"
Provides-Extra: ast
Requires-Dist: sqlglot==27.28.1; extra == "ast"
Provides-Extra: excel
Requires-Dist: openpyxl==3.1.5; extra == "excel"
Provides-Extra: ai
Requires-Dist: langchain-openai==1.4.1; extra == "ai"
Requires-Dist: langchain-core==1.5.1; extra == "ai"
Requires-Dist: langchain-classic==1.0.8; extra == "ai"
Requires-Dist: langchain-text-splitters==1.1.2; extra == "ai"
Requires-Dist: langchain-community==0.4.2; extra == "ai"
Requires-Dist: numpy==2.2.6; extra == "ai"
Requires-Dist: pydantic==2.13.4; extra == "ai"
Provides-Extra: langchain
Requires-Dist: langchain-openai==1.4.1; extra == "langchain"
Requires-Dist: langchain-core==1.5.1; extra == "langchain"
Requires-Dist: langchain-classic==1.0.8; extra == "langchain"
Requires-Dist: langchain-text-splitters==1.1.2; extra == "langchain"
Requires-Dist: langchain-community==0.4.2; extra == "langchain"
Requires-Dist: numpy==2.2.6; extra == "langchain"
Requires-Dist: pydantic==2.13.4; extra == "langchain"
Requires-Dist: faiss-cpu<2,>=1.7.4; extra == "langchain"
Requires-Dist: pypdf<6,>=4.0.0; extra == "langchain"
Requires-Dist: beautifulsoup4<5,>=4.12.0; extra == "langchain"
Requires-Dist: sqlalchemy<3,>=2.0.0; extra == "langchain"
Requires-Dist: langchain-anthropic<2,>=0.2.0; extra == "langchain"
Requires-Dist: langchain-ollama<2,>=0.2.0; extra == "langchain"
Requires-Dist: langchain-chroma<2,>=0.1.0; extra == "langchain"
Requires-Dist: langchain-mistralai<2,>=0.2.0; extra == "langchain"
Requires-Dist: langchain-cohere<2,>=0.3.0; extra == "langchain"
Requires-Dist: langchain-google-vertexai<3,>=2.0.0; extra == "langchain"
Requires-Dist: langchain-deepseek<2,>=0.1.0; extra == "langchain"
Requires-Dist: langchain-zhipuai<2,>=0.0.1; extra == "langchain"
Requires-Dist: langchain-experimental<2,>=0.3.0; extra == "langchain"
Provides-Extra: dev
Requires-Dist: pytest==9.1.1; extra == "dev"
Requires-Dist: pytest-cov==7.1.0; extra == "dev"
Requires-Dist: redis==8.1.0; extra == "dev"
Requires-Dist: sqlglot==27.28.1; extra == "dev"
Provides-Extra: full
Requires-Dist: PyMySQL==1.2.0; extra == "full"
Requires-Dist: psycopg2-binary==2.9.12; extra == "full"
Requires-Dist: sqlalchemy==2.0.40; extra == "full"
Requires-Dist: redis==8.1.0; extra == "full"
Requires-Dist: pika==1.4.4; extra == "full"
Requires-Dist: nacos-sdk-python==2.0.11; extra == "full"
Requires-Dist: prometheus-client==0.26.0; extra == "full"
Requires-Dist: loguru==0.7.3; extra == "full"
Requires-Dist: sqlglot==27.28.1; extra == "full"
Requires-Dist: requests==2.34.2; extra == "full"
Requires-Dist: httpx==0.28.1; extra == "full"
Requires-Dist: openpyxl==3.1.5; extra == "full"
Requires-Dist: langchain-openai==1.4.1; extra == "full"
Requires-Dist: langchain-core==1.5.1; extra == "full"
Requires-Dist: langchain-classic==1.0.8; extra == "full"
Requires-Dist: langchain-text-splitters==1.1.2; extra == "full"
Requires-Dist: langchain-community==0.4.2; extra == "full"
Requires-Dist: numpy==2.2.6; extra == "full"
Requires-Dist: pydantic==2.13.4; extra == "full"
Requires-Dist: faiss-cpu<2,>=1.7.4; extra == "full"
Requires-Dist: pypdf<6,>=4.0.0; extra == "full"
Requires-Dist: beautifulsoup4<5,>=4.12.0; extra == "full"
Requires-Dist: langchain-experimental<2,>=0.3.0; extra == "full"
Requires-Dist: langchain-anthropic<2,>=0.2.0; extra == "full"
Requires-Dist: langchain-ollama<2,>=0.2.0; extra == "full"
Requires-Dist: langchain-chroma<2,>=0.1.0; extra == "full"
Requires-Dist: langchain-mistralai<2,>=0.2.0; extra == "full"
Requires-Dist: langchain-cohere<2,>=0.3.0; extra == "full"
Requires-Dist: langchain-google-vertexai<3,>=2.0.0; extra == "full"
Requires-Dist: langchain-deepseek<2,>=0.1.0; extra == "full"
Requires-Dist: langchain-zhipuai<2,>=0.0.1; extra == "full"
Dynamic: license-file

# SpringBootAI 综合使用指南

> **🐣 三句话搞懂这是什么**：SpringBootAI 就像一个"网站后台乐高套装"——你想写个网站接口？拼上 `@RestController` 积木。想操作数据库？拼上 `@Mapper` 积木。想加登录验证？拼上 `@Authenticate` 积木。所有的积木都有一套统一的拼法（注解），不需要自己从零搭轮子。它底层跑的是 Python + FastAPI，但写法上借鉴了 Java Spring Boot 的分层思路，让你用 `@Service`、`@Autowired` 这些熟悉的标签来组织代码。

- SpringBootAI 版本：`2.0.0`
- 内嵌 PyMyBatis 版本：`1.4.0`
- Python：3.10+
- 仓库：[GitHub - YUCONGGEN/springbootAI](https://github.com/YUCONGGEN/springbootAI)
- License：MIT

---

## 🚀 10 分钟快速体验

想在 10 分钟内跑通第一个接口？按以下步骤来：

```powershell
# 1. 创建项目并安装框架（如果还没装过）
mkdir my-first-app
cd my-first-app
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install springbootAI

# 2. 创建目录结构
mkdir demo
mkdir demo\controller
New-Item -Path "demo\__init__.py" -ItemType File -Force
New-Item -Path "demo\controller\__init__.py" -ItemType File -Force

# 3. 启动应用（先创建以下三份代码文件：demo/Application.py、demo/controller/HelloController.py、demo/application.yml）
python -m demo.Application
# 看到 "Uvicorn running on http://127.0.0.1:8080" 就成功了！

# 4. 测试（另开一个终端）
curl http://127.0.0.1:8080/api/hello/Alice
# 返回：{"code":200,"message":"success","data":{"message":"Hello, Alice"}}
```

> 完整的代码文件内容和详细解释，请看 [新手入门指南](doc/BEGINNER_GUIDE.md) 第 4 节"快速开始"。这份入门指南从安装到验证，每一步都写好了，代码可以直接复制粘贴。

---

## 模块文档导航

第一次使用请先读 [新手入门指南](doc/BEGINNER_GUIDE.md)。它从安装开始，带你创建第一个接口，并解释 Controller、Service、Bean、依赖注入和配置文件是什么。各模块文档统一按 **① 这解决什么问题？→ ② 怎么用？（贴代码）→ ③ 怎么验证？** 三步走模式组织，按需查阅即可。

| 模块 | 文档 | 安装方式 | 一句话说明 |
|------|------|----------|-----------|
| ✅ 新手入门 | [BEGINNER_GUIDE.md](doc/BEGINNER_GUIDE.md) | 随核心包 | 从零安装、创建项目、运行接口、打开 Swagger |
| ✅ 常用注解模块 | [ANNOTATION_MODULES.md](doc/ANNOTATION_MODULES.md) | 随核心包 | Bean Validation / 条件装配 / 缓存增强 / CSV / `@Version` / `@Transient` |
| 📦 AI（对接大模型） | [AI_MODULE.md](doc/AI_MODULE.md) | `pip install springbootAI[ai]` | ChatClient / Advisor / Tools / RAG / Function Calling / 多厂商适配 |
| 📦 LangChain | [LANGCHAIN_MODULE.md](doc/LANGCHAIN_MODULE.md) | `pip install springbootAI[langchain]` | Chains / Agents / Memory / Retrievers / VectorStores / 30+ 提供商 |
| ✅ 内嵌 PyMyBatis ORM | [ORM_MODULE.md](doc/ORM_MODULE.md) | 随核心包 | Mapper 注解 / XML Mapper / 分页 / SQL 安全 / DDL 自动建表 |
| ✅ Cloud 微服务 | [CLOUD_MODULE.md](doc/CLOUD_MODULE.md) | 随核心包 | 服务注册发现 / 配置刷新 / Feign / Sentinel / Gateway / 分布式事务 |
| 📦 Excel 读写 | [EXCEL_MODULE.md](doc/EXCEL_MODULE.md) | `pip install springbootAI[excel]` | `@ExcelProperty` / `@ExcelIgnore` 注解驱动读写 |
| 📦 CSV 读写 | [CSV_MODULE.md](doc/CSV_MODULE.md) | `pip install springbootAI[csv]` | `@CsvProperty` / `@CsvIgnore` 注解驱动读写 |
| ✅ Swagger 文档 | [SWAGGER_MODULE.md](doc/SWAGGER_MODULE.md) | 随核心包 | `@Tag` / `@Operation` 注解驱动 API 文档 |
| ✅ 八大模块 | [EIGHT_MODULES.md](doc/EIGHT_MODULES.md) | 随核心包 | 分页 / Actuator / 多数据源 / i18n / WebSocket 等 |
| ✅ 安全 | [SECURITY.md](doc/SECURITY.md) | 随核心包 | JWT 生成校验 / 密码加密 / SQL 注入防护 / 访问控制 |
| ✅ BeanUtils | [BEAN_UTILS.md](doc/BEAN_UTILS.md) | 随核心包 | `copy_properties` / `clone` 属性复制工具 |
| — AI 与 LangChain 测试 | [AI_LANGCHAIN_TEST_GUIDE.md](doc/AI_LANGCHAIN_TEST_GUIDE.md) | — | 162 个测试用例详解 |
| — 测试报告 | [TEST_REPORT.md](doc/TEST_REPORT.md) | — | 全量测试用例与覆盖范围 |

> 图例：✅ = 随核心包自带，不需要额外安装 | 📦 = 需要单独安装 extras | — = 参考文档，不是功能模块

所有模块文档统一存放于 [`doc/`](doc/) 目录。

### 🎯 新手推荐阅读顺序

1. 先按 [新手入门指南](doc/BEGINNER_GUIDE.md) 跑通 `/api/hello/{name}`。
2. 阅读本页第 4、6、7 章，理解配置、依赖注入和 Controller。
3. 做数据库 CRUD 时阅读 [ORM_MODULE.md](doc/ORM_MODULE.md)。
4. 需要输入校验、缓存或条件开关时阅读 [ANNOTATION_MODULES.md](doc/ANNOTATION_MODULES.md)。
5. 最后再按业务需要选择安全、Cloud、AI、LangChain、Excel、WebSocket 等文档。

---

## 目录

1. [框架概述与定位](#1-框架概述与定位)
2. [能力状态](#2-能力状态)
3. [安装与快速开始](#3-安装与快速开始)
4. [配置系统（5 分钟看懂）](#4-配置系统5-分钟看懂)
5. [注解参考](#5-注解参考)
6. [IoC 与依赖注入（厨房比喻版）](#6-ioc-与依赖注入厨房比喻版)
7. [Web 控制器](#7-web-控制器)
8. [内嵌 PyMyBatis ORM 与 DDL](#8-内嵌-pymybatis-orm-与-ddl)
9. [事务](#9-事务)
10. [安全与权限](#10-安全与权限)
11. [缓存、任务与高级 AOP](#11-缓存任务与高级-aop)
12. [AI 与 LangChain 模块](#12-ai-与-langchain-模块)
13. [Java 开发者看这里](#13-java-开发者看这里)
14. [生产部署](#14-生产部署)
15. [项目结构](#15-项目结构)
16. [测试](#16-测试)
17. [常见问题与排错](#17-常见问题与排错)
18. [性能与容量验证](#18-性能与容量验证)

---

## 1. 框架概述与定位

### 1.1 这是什么？

SpringBootAI 是一个 **Python Web 框架**。它把 Java Spring Boot 的"注解 + Controller/Service/Mapper 分层"思路搬到了 Python 世界——你写的是 Python 代码，用的是 `@Service`、`@RestController` 这些看起来像 Spring Boot 的注解，但底层真正跑起来的是 FastAPI 和 Uvicorn。

### 1.2 三句话版本

1. **写法像 Spring Boot**：用 `@RestController`、`@Service`、`@Mapper` 组织代码，Java 开发者一眼就懂。
2. **运行在 Python**：底层是 FastAPI + Uvicorn，不依赖 Java、JAR 包或 Maven。
3. **功能开箱即用**：数据库、缓存、安全、文档、AI 等能力已经打包好，装完就能用。

### 1.3 版本

| 组件 | 当前版本 |
|------|----------|
| `spring` 框架 API | 2.0.0 |
| `spring.orm.pymybatis` | 1.4.0 |
| `spring.ai` AI 模块 | 1.3.0 |
| `spring.langchain` LangChain 模块 | 1.0.0 |
| Python | 3.10+ |

### 1.4 适合什么场景

- 内部管理接口、轻量业务服务、教学和原型验证。
- 希望用 Controller/Service/Mapper 分层方式写 Python 的团队。
- SQLite 本地工具，或经过目标数据库集成测试的服务。
- 微服务架构（内置服务发现、限流熔断、分布式追踪、分布式事务）。

### 1.5 能力边界（使用前必读）

- 自动化 ORM 测试使用 SQLite；MySQL、PostgreSQL、Oracle 需单独验证。
- `@Transactional` 支持七种 Spring 传播模式；`REQUIRES_NEW` 和 `NOT_SUPPORTED` 需要连接池有额外可用连接。
- Profile 会筛选 `@Profile` Bean，但不会自动合并 `application-{profile}.yml`。
- Nacos、RabbitMQ、Prometheus 依赖外部服务；Sentinel 限流熔断和 OpenTelemetry 追踪可内嵌运行。
- HTTP 事务模式是持久化补偿协调器，不提供 Seata AT 强一致性；生产强一致场景必须使用真实 Seata Server。
- 限流、分布式锁、幂等和缓存语义依赖 Redis 等后端，Redis 不可用时有本地降级路径。

### 1.6 注解使用总览

SpringBootAI 注解会先把元数据放到 `__spring_annotations__`。之后是否生效，取决于有没有对应的扫描器或切面：

| 状态 | 含义 |
|------|------|
| 容器执行 | `ApplicationContext`、`BeanFactory` 或 Web 上下文会读取并执行 |
| 受管 Bean 执行 | 只有被组件扫描并由容器创建的实例方法才会被 AOP 包装；自己 `ClassName()` 创建的对象不生效 |
| 直接执行 | 装饰器本身返回包装函数，不依赖 IoC 容器 |
| 仅元数据 | 当前有注解类，但主运行链路没有消费者，写上不会得到注解名字所暗示的功能 |

> **⚠️ 这是最容易混淆的地方**：同名的注解（如 `@Transactional`），在 Java Spring 和 SpringBootAI 中的具体行为可能不同。不要因为名字一样就假设效果也一样。

---

## 2. 能力状态

| 模块 | 状态 | 一句话说明 |
|------|------|-----------|
| IoC 容器 | ✅ 可用 | 组件扫描、构造器/字段注入、Bean、延迟初始化、生命周期回调、Profile 过滤 |
| Web MVC | ✅ 可用 | 基于 FastAPI 的 GET/POST/PUT/PATCH/DELETE 路由、参数绑定、异常处理、CORS 和静态文件 |
| 配置 | ✅ 可用 | YAML、`${ENV:default}`、固定环境变量覆盖、标量类型保留 |
| 应用事件 | ✅ 可用 | `ApplicationEvent`、`@EventListener`、同步有序发布和异步监听 |
| 内嵌 ORM + DDL Auto | ✅ 可用 | PyMyBatis + JPA ddl-auto 自动建表(create/update/validate)，支持 XML/注解 SQL、事务、缓存 |
| 本地事务 | ✅ 可用 | `@Transactional` 支持七种 Spring 传播模式 |
| JWT 与方法安全 | ✅ 可用 | access/refresh token、`@Authenticate`、角色/权限授权、401/403 映射 |
| 重试/异步 | ✅ 可用 | 受管 Bean 的退避重试、恢复方法和 Future/Task 异步调度 |
| Redis/缓存 | ✅ 可用 | 分布式锁、KV/Hash/List/Set/Counter，需要 Redis 服务 |
| RabbitMQ | ✅ 可用 | `@RabbitListener` 自动注册并后台消费，`RabbitTemplate` 发送 |
| Nacos 服务发现 | ✅ 可用 | 服务注册/发现/订阅 |
| Sentinel 限流熔断 | ✅ 可用 | 内嵌引擎，QPS 限流、异常比例熔断、热点参数限流，无需 Dashboard |
| 分布式追踪 | ✅ 可用 | 原生 OpenTelemetry(W3C traceparent)，自动 HTTP/Feign 注入 |
| Seata 分布式事务 | ⚠️ 有边界 | `distributed` 对接真实 Seata SDK；`http` 仅提供持久化补偿，不等同 AT |
| API Gateway | ✅ 可用 | 轻量 ASGI/WSGI 网关，路由转发、路径重写、过滤器链、负载均衡 |
| Prometheus 监控 | ✅ 可用 | Counter/Gauge/Histogram 指标暴露 |
| Feign 声明式 HTTP | ✅ 可用 | 声明式接口、Fallback 降级、自动传播 XID 和 trace 头 |
| 高级 AOP | ✅ 可用 | 限流、熔断、幂等、审计、锁、指标、追踪、缓存 |
| AI 模块 | ✅ 可用 | ChatClient/ChatModel/EmbeddingModel/Advisor/Tools，OpenAI/Ollama/DeepSeek/Moonshot 适配 |
| LangChain 模块 | ✅ 可用 | Chains/Agents(6 种)/Memory/Retrievers/VectorStores + 30+ 提供商，双向适配器 |

---

## 3. 安装与快速开始

### 3.1 环境准备

```bash
cd springboot
python -m venv .venv
```

激活虚拟环境：

```powershell
# PowerShell
.\.venv\Scripts\Activate.ps1
```

```bash
# Linux/macOS
source .venv/bin/activate
```

### 3.2 安装框架

```bash
python -m pip install --upgrade pip
python -m pip install -e .
```

核心依赖包含 FastAPI、Uvicorn、PyYAML、python-dotenv、DBUtils、PyJWT、cryptography、bcrypt 和 Pydantic。**核心安装已包含内嵌 `spring.orm.pymybatis`，使用 Mapper 模式不需要再安装独立 `pymybatis`。**

### 3.3 可选 extras

```bash
python -m pip install -e ".[mysql]"             # PyMySQL
python -m pip install -e ".[postgresql]"        # psycopg2-binary
python -m pip install -e ".[oracle]"            # cx-Oracle
python -m pip install -e ".[sqlalchemy]"        # SQLAlchemy 模式
python -m pip install -e ".[redis]"             # Redis 能力
python -m pip install -e ".[ast]"               # sqlglot AST 校验
python -m pip install -e ".[rabbitmq]"          # pika
python -m pip install -e ".[nacos]"             # Nacos 客户端
python -m pip install -e ".[prometheus,logging]" # 指标和 loguru
python -m pip install -e ".[dev]"               # 测试和静态工具
```

AI 模块为可选依赖：

```bash
pip install -r requirements-ai.txt   # langchain-openai/langchain-community/numpy
```

LangChain 模块复用 AI 模块的依赖，额外按需安装 partner 包（30+ 提供商懒加载，未安装的自动跳过）：

```bash
pip install langchain-anthropic      # Anthropic Claude
pip install langchain-deepseek       # DeepSeek
pip install langchain-ollama         # Ollama 本地模型
pip install faiss-cpu                # FAISS 向量库
pip install langchain-chroma         # Chroma 向量库
```

### 3.4 验证安装

```bash
python -c "import spring; print(spring.__version__)"
python -c "from spring.orm.pymybatis import __version__; print(__version__)"
```

### 3.5 最小应用

仓库中的 `example`、`example1`、`example5` 只用于源码参考和回归验证，不会打包进 `springbootAI`。安装后请按下面结构创建自己的应用包。**每个被扫描目录都必须包含 `__init__.py`，并从项目根目录启动。**

创建包结构：

```text
demo/
|-- __init__.py
|-- Application.py
|-- application.yml
`-- controller/
    |-- __init__.py
    `-- HelloController.py
```

创建 `demo/Application.py`：

```python
from spring.annotations import SpringBootApplication
from spring.main import run


@SpringBootApplication(scan_base_packages=["demo"])
class Application:
    pass


if __name__ == "__main__":
    run(Application)
```

创建 `demo/controller/HelloController.py`：

```python
from spring.annotations import GetMapping, RequestMapping, RestController


@RequestMapping("/api")
@RestController
class HelloController:
    @GetMapping("/hello/{name}")
    def hello(self, name: str):
        return {"message": f"Hello, {name}"}
```

创建 `demo/application.yml`：

```yaml
server:
  host: 127.0.0.1
  port: 8080
  cors:
    allow_origins: []
    allow_credentials: false

redis:
  enabled: false

database:
  enabled: false

jwt:
  secret_key: development-only-secret
  algorithm: HS256
```

运行和验证：

```bash
python -m demo.Application
curl http://127.0.0.1:8080/api/hello/Alice
curl http://127.0.0.1:8080/actuator/health/liveness
curl http://127.0.0.1:8080/actuator/info
```

默认响应会统一包装为 `Result`：

```json
{
  "code": 200,
  "message": "success",
  "data": {"message": "Hello, Alice"}
}
```

交互式 API 文档由 FastAPI 提供，默认访问 `http://127.0.0.1:8080/docs`；原始规范位于 `/openapi.json`。

### 3.6 生产 ASGI 入口

开发时可以使用 `run()`；生产进程管理应使用 `create_app()` 构建 ASGI 应用：

```python
# asgi.py
from spring.main import create_app
from demo.Application import Application

app = create_app(Application)
```

```bash
uvicorn asgi:app --host 0.0.0.0 --port 8080 --workers 2
```

多 worker 会创建多个独立进程、IoC 容器和连接池。连接池总量应按 `worker 数 x max_size` 评估。

---

## 4. 配置系统（5 分钟看懂）

> **🔑 核心概念**：配置文件（`application.yml`）就像餐厅的"运营手册"——写着餐厅地址（`host`）、门牌号（`port`）、要不要开外卖（`redis.enabled`）。换地方开店只改手册，不用重新装修。这一节 5 分钟帮你看懂配置的核心用法。

### 4.1 配置放哪里

`ApplicationContext` 按以下顺序找配置文件：

1. 启动类文件所在目录的 `application.yml`。
2. 启动类目录下的 `config/application.yml`。
3. 两处都不存在时使用代码默认值和环境变量。

两处都存在时第一项优先，不会合并。

### 4.2 环境变量占位符

```yaml
server:
  port: ${SERVER_PORT:8080}
database:
  enabled: ${DB_ENABLED:false}
  password: ${DB_PASSWORD}
```

- `${NAME}`：环境变量必填，未设置时报错。
- `${NAME:default}`：未设置时用冒号后的默认值。
- 占位符占满整个值时，YAML 会把 `8080`、`false`、`null` 保留为 int、bool、None（标量类型不变）。
- 占位符嵌入普通字符串时结果是字符串。

### 4.3 固定覆盖变量（常用）

除了 YAML 里的 `${...}` 占位符，加载器还会直接读取以下环境变量：

| 分类 | 环境变量 |
|------|----------|
| 服务 | `SERVER_HOST`、`SERVER_PORT` |
| 环境 | `SPRING_PROFILES_ACTIVE`、`STARTUP_FAIL_FAST` |
| JWT | `JWT_SECRET_KEY`、`JWT_ALGORITHM` |
| 数据库 | `DB_ENABLED`、`DB_URL`、`DB_HOST`、`DB_PORT`、`DB_NAME`、`DB_USERNAME`、`DB_PASSWORD`、`DB_DRIVER` |
| Redis | `REDIS_ENABLED`、`REDIS_HOST`、`REDIS_PORT`、`REDIS_DB`、`REDIS_PASSWORD` |
| CORS | `CORS_ALLOW_ORIGINS`、`CORS_ALLOW_CREDENTIALS` |
| 日志 | `LOG_LEVEL`、`LOG_DIR`、`LOG_RETENTION`、`LOG_ROTATION` |
| 中间件 | `DISCOVERY_*`、`NACOS_SERVER`、`NACOS_USERNAME`、`NACOS_PASSWORD`、`SEATA_*`、`RABBITMQ_*`、`PROMETHEUS_*` |

`SPRING_PROFILES_ACTIVE` 用于 `@Profile` 组件筛选、生产安全校验，以及**自动加载并深度合并** `application-{profile}.yml`（v1.8.5 起实现）。Profile 文件与主 `application.yml` 同目录，加载顺序：主配置 → profile 配置深度合并（profile 覆盖主配置的同名键），合并后再解析 `${ENV:default}` 占位符。例如 `SPRING_PROFILES_ACTIVE=prod` 会自动合并 `application-prod.yml`。

### 4.4 Docker 容器 IP 自动检测（开发环境）

在开发环境中，当 `database.host` 设为 `127.0.0.1` 或 `localhost` 时，框架会自动通过 `docker ps` 和 `docker inspect` 查找映射了目标端口的容器内部 IP 进行连接。

- 支持通过端口映射精确匹配（如 `0.0.0.0:3306->3306/tcp`）
- 支持 MySQL/MariaDB/PostgreSQL 数据库镜像兜底匹配
- 设置 `SPRING_DISABLE_DOCKER_IP_DETECT=1` 可禁用（生产环境推荐）

### 4.5 在代码里读配置

```python
from spring.config import ConfigLoader

loader = ConfigLoader("./myapp/application.yml")
port = loader.get("server.port", 8080)
database = loader.get_prefix_config("database")
snapshot = loader.get_config()
```

返回的配置是深拷贝，你改了不会影响原始配置。

### 4.6 Profile 的真实行为

```python
from spring.annotations import Profile, Service


@Profile("dev")
@Service
class DevelopmentService:
    pass
```

Profile 用于 Bean 过滤和生产安全校验。多环境配置可使用以下方式之一：

1. 在部署流程中生成最终 `application.yml`。
2. 大量使用环境变量占位符。
3. 显式创建 `ConfigLoader(config_path=...)` 和 `ApplicationContext`。

### 4.7 生产配置校验

当 Profile 是 `prod` 或 `production` 时：

- 默认 JWT 密钥、空密钥或少于 32 字符的密钥会导致启动失败。
- `startup.fail_fast` 默认视为开启。
- CORS 开启凭证时配置 `*` 来源会直接失败。

### 4.8 健康检查

| 地址 | 用途 |
|------|------|
| `/actuator/health` | 聚合组件健康状态；降级时返回 503 |
| `/actuator/health/liveness` | 进程存活检查（用于 K8s livenessProbe） |
| `/actuator/health/readiness` | 服务就绪检查（用于 K8s readinessProbe） |
| `/actuator/info` | 应用名称、当前 Profile、框架和 Python 版本 |

`database.enabled: false` 时数据库状态为 `DISABLED`，不会创建 `test.db`。

> **⚠️ 新手常见错误**：
> - ❌ 错误："我改了 YAML，重新请求接口怎么没生效？"
> - ✅ 正解：修改 YAML 后需要**重启应用**（`Ctrl+C` 停掉再重新运行）。YAML 配置是启动时一次性读取的。

---

## 5. 注解参考

> 说明：本节是框架最完整的注解参考。所有 AOP 类注解（事务、缓存、重试、异步、定时、高级 AOP、安全等）都要求**方法所在类带组件注解（`@Service`/`@Component`/`@Repository`/`@Controller` 等）并由容器取得实例**，自己 `ClassName()` 创建的对象不会生效。

### 5.1 启动与扫描

#### @SpringBootApplication

**含义**：应用启动类注解，组合了 `@Configuration`、`@ComponentScan` 的功能。

**参数**：

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| scan_base_packages | List[str] | None | 扫描的基础包路径 |

```python
from spring.annotations import SpringBootApplication

@SpringBootApplication(scan_base_packages=["com.example.service", "com.example.controller"])
class Application:
    pass
```

**注意事项**：每个应用只能有一个启动类；`scan_base_packages` 是可导入包名，不是文件路径。

### 5.2 组件与依赖注入

#### @Component / @Service / @Repository

```python
from spring.annotations import Component, Service, Repository

@Component
class EmailUtil:
    def send(self, to: str, content: str):
        pass

@Service
class UserService:
    def get_user(self, user_id: int):
        return {"id": user_id, "name": "test"}

@Repository
class UserRepository:
    def find_by_id(self, user_id: int):
        pass
```

#### @Autowired

```python
from spring.annotations import Service, Autowired

@Service
class UserService:
    # 构造函数注入（推荐）
    @Autowired
    def __init__(self, user_repository):
        self.user_repository = user_repository
```

**推荐构造器注入**。依赖参数应写类型注解，构造器注入能在启动阶段暴露缺失和循环依赖。

#### @Qualifier / @Primary / @Profile / @Lazy

完整参数、示例和边界说明见原文档第 5.2 节。

### 5.3 Web 控制器注解

#### @Controller / @RestController

`@RestController` 组合了 `@Controller` 和 `@ResponseBody`，返回值自动序列化为 JSON。

```python
from spring.annotations import RestController, GetMapping

@RestController
class UserController:
    @GetMapping("/api/users/{id}")
    def get_user(self, id: int):
        return {"id": id, "name": "test"}
```

#### @RequestMapping / @GetMapping / @PostMapping / @PutMapping / @PatchMapping / @DeleteMapping

```python
from spring.annotations import RestController, GetMapping, PostMapping, PutMapping, PatchMapping, DeleteMapping

@RestController
class UserController:
    @GetMapping("/api/users/{id}")
    def get_user(self, id: int):
        return {"id": id, "name": "test"}

    @PostMapping("/api/users")
    def create_user(self, name: str, email: str):
        return {"id": 1, "name": name, "email": email}

    @PutMapping("/api/users/{id}")
    def update_user(self, id: int, name: str):
        return {"id": id, "name": name}

    @PatchMapping("/api/users/{id}")
    def patch_user(self, id: int, name: str = ""):
        return {"id": id, "name": name, "method": "PATCH"}

    @DeleteMapping("/api/users/{id}")
    def delete_user(self, id: int):
        return {"status": "deleted", "id": id}
```

#### @ControllerAdvice / @ExceptionHandler

```python
from spring.annotations import ControllerAdvice, ExceptionHandler

@ControllerAdvice
class GlobalExceptionHandler:
    @ExceptionHandler(ValueError, TypeError)
    def handle_validation_error(self, e: Exception):
        return {"code": 400, "message": f"参数错误: {str(e)}"}

    @ExceptionHandler(Exception)
    def handle_generic_error(self, e: Exception):
        return {"code": 500, "message": f"服务器错误: {str(e)}"}
```

### 5.4 参数绑定注解

> 参数标记的正确语法是 **"作为默认值"写在方法参数上**，而不是写在函数上方。

| 写法 | 来源 |
|------|------|
| 参数名出现在 `{...}` 路径 | 路径参数 |
| 参数类型是 `dict` | JSON 请求体 |
| 有普通默认值 | 可选查询参数 |
| 无默认值且不在路径 | 必填查询参数 |
| 默认值为 `RequestParam(...)` | 显式查询参数 |
| 默认值为 `RequestBody()` | 显式请求体 |
| 默认值为 `RequestHeader(...)` | Header |
| 默认值为 `CookieValue(...)` | Cookie |

完整参数和示例见原文档第 5.4 节。

### 5.5 配置与属性注解

```python
from spring.annotations import Configuration, Bean, Service, Value, ConfigurationProperties, Component

@Configuration
class AppConfig:
    @Bean(name="dataSource", init_method="init", destroy_method="close")
    def data_source(self):
        return DataSource()

@Service
class AppService:
    @Value("${app.name}")
    def set_app_name(self, value: str):
        self.app_name = value

@Component
@ConfigurationProperties(prefix="spring.datasource")
class DataSourceProperties:
    def __init__(self):
        self.url = ""
        self.username = ""
        self.password = ""
```

### 5.6 日志与生命周期

```python
from spring.annotations import Service, Slf4j, PostConstruct, PreDestroy

@Service
@Slf4j  # 自动创建 self.logger
class UserService:
    def create_user(self, name: str):
        self.logger.info(f"正在创建用户: {name}")
        return {"id": 1, "name": name}

@Service
class InitService:
    @PostConstruct
    def init(self):
        self.config = self.load_config()

    @PreDestroy
    def cleanup(self):
        if self.connection:
            self.connection.close()
```

### 5.7 应用事件

```python
from spring.annotations import ApplicationEvent, Autowired, EventListener, Service
from spring.event import ApplicationEventPublisher


class UserCreatedEvent(ApplicationEvent):
    def __init__(self, user_id: int):
        super().__init__(source="user-service")
        self.user_id = user_id


@Service
class UserEventHandler:
    @EventListener(event_type=UserCreatedEvent, order=1)
    def on_user_created(self, event: UserCreatedEvent):
        print(f"created: {event.user_id}")


@Service
class UserService:
    @Autowired
    def __init__(self, publisher: ApplicationEventPublisher):
        self.publisher = publisher

    def create(self, user_id: int):
        self.publisher.publish_event(UserCreatedEvent(user_id))
```

### 5.8 核心高级注解（10 个）

#### @RateLimit - 接口限流

**解决什么问题**：限制接口被调用的频率，防止被刷爆。

```python
from spring.annotations import RateLimit, Service

@Service
class OrderService:
    # 每分钟最多100次请求（全局限制）
    @RateLimit(max_requests=100, time_window=60)
    def create_order(self, user_id: str, product_id: str):
        return {"order_id": "ORD_123"}

    # 按用户ID限流：每个用户每秒最多10次
    @RateLimit(max_requests=10, time_window=1, key="user_id")
    def get_user_info(self, user_id: str):
        return {"user_id": user_id}
```

#### @CircuitBreaker - 熔断器

**解决什么问题**：当某个方法持续失败时，暂时停止调用它（"熔断"），等一段时间后再试。

```python
from spring.annotations import CircuitBreaker, Service

@Service
class PaymentService:
    @CircuitBreaker(failure_threshold=3, recovery_timeout=10, fallback_method="payment_fallback")
    def process_payment(self, order_id: str, amount: float):
        if amount > 10000:
            raise Exception("支付网关超时")
        return {"status": "success", "transaction_id": "TXN_123"}

    def payment_fallback(self, order_id: str, amount: float):
        return {"status": "degraded", "message": "支付服务暂时不可用，请稍后重试"}
```

#### @Idempotent - 幂等性

**解决什么问题**：用户手抖点了两次"下单"，保证只有一次生效。

```python
from spring.annotations import Idempotent, Service

@Service
class OrderService:
    @Idempotent(key="order_id", expire=300, prefix="order")
    def create_order(self, order_id: str, user_id: str, amount: float):
        return {"order_id": order_id, "status": "created"}
```

#### @AuditLog / @FeatureToggle / @Lock / @Metrics / @Synchronized / @Validate / @Trace

这些高级注解的完整参数、示例和边界，沿用上方 @RateLimit 和 @CircuitBreaker 的模式。详细参数表见原文档第 5.8 节。

### 5.9 事务、缓存、任务与异步注解

```python
from spring.annotations import Service, Transactional, Cacheable, Retryable, Async, Scheduled
from spring.retry.retry_annotations import Backoff

@Service
class OrderService:
    @Transactional(rollback_for=[Exception])
    def create_order(self, user_id: int, product_id: int):
        return {"order_id": 1}

    @Cacheable(value="users", key="#user_id")
    def get_user(self, user_id: int):
        return {"id": user_id, "name": "test"}

    @Retryable(value=(ConnectionError,), max_retries=3, backoff=Backoff(delay=1000, multiplier=2.0))
    def call_remote(self):
        pass

    @Async
    def send_email(self, to: str, content: str):
        time.sleep(1)
        print(f"Email sent to {to}")

@Service
class ScheduledTasks:
    @Scheduled(fixed_rate=5000)
    def report_current_time(self):
        print("Current time:", time.time())
```

**边界要点**：`max_retries=3` 包含首次调用；`@Async` 同步方法返回 `Future`；`@Scheduled` 多 worker 会重复执行。

### 5.10 安全、Cloud 与消息注解

| 注解 | 设计意图 | 当前真实状态 |
|------|----------|--------------|
| `@Authenticate` | 校验 JWT 并建立安全上下文 | 受管 Bean 实际执行；HTTP 控制器自动读取 `Authorization: Bearer ...` |
| `@PreAuthorize` | 按角色/权限表达式授权 | 受管 Bean 实际执行；未认证返回 401，权限不足返回 403 |
| `@Secured` | 按任一角色授权 | 受管 Bean 实际执行 |
| `@SentinelResource` | 限流、业务异常 fallback | 受管 Bean 方法会包装；已内嵌限流熔断引擎 |
| `@GlobalTransactional` | 通过 Seata 管理全局事务 | 受管 Bean 方法调用 Seata manager |
| `@RabbitListener` | 注册 RabbitMQ 消费者 | 可直接装饰受管 Bean 方法 |

> Cloud 注解完整参数已分离至：[CLOUD_MODULE.md](doc/CLOUD_MODULE.md)。MyBatis 注解已分离至：[ORM_MODULE.md](doc/ORM_MODULE.md)。

### 5.13 注解组合使用与执行顺序

**注解执行顺序**（AOP 从外到内）：

```
1. @SentinelResource / @CircuitBreaker  （最外层，熔断降级）
2. @RateLimit                           （限流）
3. @Lock / @Synchronized                （锁）
4. @Metrics                             （监控）
5. @Trace                               （追踪）
6. @AuditLog                            （审计）
7. @Idempotent                          （幂等）
8. @Validate / @Valid / @Validated      （参数校验）
9. 业务方法
```

**常用组合模式**：

```python
# 接口防护三件套
@SentinelResource(value="xxx", fallback="xxx_fallback")
@Metrics(name="xxx")
@RateLimit(max_requests=100, time_window=60)
def xxx_method(self):
    pass

# 支付操作完整组合
@Metrics(name="payment.create")
@Lock(key="payment_{order_id}", expire=10, wait_timeout=3)
@Idempotent(key="payment_{order_id}", expire=300)
@Validate(field="amount", min=0.01, message="金额必须大于0")
def create(self, order_id: str, amount: float):
    return {"order_id": order_id, "amount": amount}
```

---

## 6. IoC 与依赖注入（厨房比喻版）

> 🍽️ **厨房比喻**：想象你开一个餐厅。IoC 容器就是一个"自动 HR 系统"——你只要在员工简历上贴标签（`@Service`=厨师、`@Controller`=服务员、`@Mapper`=仓管员），系统就自动把他们招来、办好入职、安排工位。依赖注入（`@Autowired`）就是——厨师说"我需要一个仓管员配合我"，HR 自动把人分过去，不用你自己跑仓库找人。

### 6.1 组件类型

| 注解 | 用途 | 厨房角色 |
|------|------|----------|
| `@Component` | 通用组件 | 任何员工 |
| `@Service` | 业务服务 | 后厨大厨 |
| `@Repository` | 数据访问封装 | 仓库管理员 |
| `@RestController` / `@Controller` | Web 控制器 | 前台服务员 |
| `@Configuration` | Bean 配置类 | HR 经理（定义"怎么招人"） |
| `@Bean` | 工厂方法产生 Bean | 招聘流程 |
| `@Primary` | 同类型多个 Bean 时的首选 | "优先选这个人" |
| `@Profile` | 按环境筛选 | "这个人只在旗舰店上班" |
| `@Lazy` | 延迟创建 | 弹性用工（需要时才入职） |

### 6.2 构造器注入（推荐方式）

```python
from spring.annotations import Autowired, Service


@Service
class GreetingService:
    def greet(self, name: str) -> str:
        return f"Hello, {name}"


@Service
class UserService:
    @Autowired
    def __init__(self, greeting_service: GreetingService):
        self.greeting_service = greeting_service
```

构造器注入能在启动阶段暴露缺失和循环依赖，优先于字段注入。

### 6.3 多实现与 @Qualifier

同类型存在多个 Bean 时，使用 `@Primary` 或 `@Qualifier` 指定名称。

### 6.4 配置类和 @Bean

```python
from spring.annotations import Bean, Configuration


@Configuration
class AppConfig:
    @Bean(name="clock")
    def clock(self):
        import time
        return time.time
```

### 6.5 生命周期

```python
from spring.annotations import Component, PostConstruct, PreDestroy


@Component
class ResourceHolder:
    @PostConstruct
    def start(self):
        # 初始化资源：打开数据库连接、加载配置等
        pass

    @PreDestroy
    def stop(self):
        # 清理资源：关闭连接、保存状态等
        pass
```

> **⚠️ 新手常见错误**：
> - ❌ 错误：手动 `service = UserService()` 创建对象，然后问"为什么 `@Cacheable` 不生效？"
> - ✅ 正解：容器创建的 Bean 才是"正式员工"，有事务、缓存、重试等 AOP 能力。你自己 `new` 出来的是"临时工"，什么福利都没有。

---

## 7. Web 控制器

> 🍽️ **厨房比喻**：Controller 就是餐厅的前台服务员——客人进来点菜（发 HTTP 请求），服务员把菜单传给后厨（Service），再把做好的菜端回来（返回 JSON）。服务员不炒菜，只接单和上菜。

### 7.1 类和方法映射

```python
from spring.annotations import (
    DeleteMapping, GetMapping, PatchMapping, PostMapping, PutMapping,
    RequestMapping, RestController,
)


@RequestMapping("/users")
@RestController
class UserController:
    @GetMapping("/{user_id}")
    def get(self, user_id: int):
        return {"id": user_id}

    @PostMapping("")
    def create(self, body: dict):
        return body

    @PutMapping("/{user_id}")
    def update(self, user_id: int, body: dict):
        return {"id": user_id, **body}

    @PatchMapping("/{user_id}")
    def patch(self, user_id: int, body: dict):
        return {"id": user_id, **body, "partial": True}

    @DeleteMapping("/{user_id}")
    def delete(self, user_id: int):
        return {"deleted": user_id}
```

未指定映射路径时默认使用方法名；类级路径前缀必须使用 `@RequestMapping("/users")`。

### 7.2 统一返回值

```python
from spring.web import Result

return Result.success({"id": 1}, message="创建成功")
return Result.bad_request("姓名不能为空")
return Result.not_found("用户不存在")
```

### 7.3 全局异常处理 & CORS & 拦截器

```python
from spring.annotations import ControllerAdvice, ExceptionHandler, Component
from spring.web import Result
from spring.web.interceptor import HandlerInterceptor


@ControllerAdvice
class GlobalExceptionHandler:
    @ExceptionHandler(ValueError)
    def handle_value_error(self, error: ValueError):
        return Result.bad_request(str(error))


@Component
class AuditInterceptor(HandlerInterceptor):
    async def pre_handle(self, request, handler):
        request.state.started = True
        return True
```

CORS 配置：

```yaml
server:
  cors:
    allow_origins:
      - https://console.example.com
    allow_credentials: true
```

---

## 8. 内嵌 PyMyBatis ORM 与 DDL

> 🍽️ **厨房比喻**：数据库就是仓库，Mapper 就是仓库管理员。厨师说要什么食材，管理员去仓库精准取货。你不用自己写繁琐的库存查询，只要告诉管理员"我要用户 ID 为 1 的信息"。

> 本节（Mapper 注解、XML Mapper、分页、SQL 安全、DDL 自动建表等）已分离至：[ORM_MODULE.md](doc/ORM_MODULE.md)。

---

## 9. 事务

> 🍽️ **厨房比喻**：事务就像"做一道菜"——切菜、下锅、调味、装盘，必须全部完成才能端给客人。中间任何一步失败，前面切好的菜也要扔掉（回滚）。

### 9.1 Service 事务

```python
from spring.annotations import Autowired, Service, Transactional


@Service
class RegistrationService:
    @Autowired
    def __init__(self, user_mapper: UserMapper, audit_mapper: AuditMapper):
        self.user_mapper = user_mapper
        self.audit_mapper = audit_mapper

    @Transactional(rollback_for=[Exception])
    def register(self, name: str, email: str):
        user_id = self.user_mapper.insert(name, email)
        self.audit_mapper.insert("USER_CREATED", user_id)
        return user_id
```

**执行过程**：进入方法时创建会话并开始事务 → 当前上下文内所有 Mapper 共用该会话 → 正常返回时提交 → 满足回滚规则的异常导致回滚 → 退出后归还连接池。

### 9.2 传播级别（支持全部七种）

```python
@Transactional(propagation="REQUIRED")
@Transactional(propagation="NESTED")
```

`NESTED` 在已有事务中创建 savepoint；`REQUIRES_NEW` 使用独立 Session/连接，连接池 `max_size` 至少应能容纳并发的外层和内层连接。

### 9.3 嵌套事务 & 手动事务

嵌套 `REQUIRED` 采用 rollback-only 语义。显式 `NESTED` 时内层异常回滚到 savepoint，外层仍可提交。

```python
with factory.open_session() as session:
    with session.transaction():
        session.insert("INSERT INTO users(name) VALUES (#{name})", {"name": "A"})
        session.insert("INSERT INTO audit(event) VALUES (#{event})", {"event": "created"})
```

---

## 10. 安全与权限

> ✈️ **安检通道比喻**：安全模块就像机场安检——`@Authenticate` 检查登机牌（JWT Token），`@PreAuthorize` 检查是不是头等舱（角色/权限），`@Secured` 检查有没有进入某个区域的权限。

### 10.1 JWT 初始化

```yaml
jwt:
  secret_key: ${JWT_SECRET_KEY}
  algorithm: HS256
  expires_in: 3600
  issuer: springpy-api
  audience: springpy-client
  leeway: 5
```

生产密钥至少 32 字符。

### 10.2 access/refresh token

```python
from spring.security.jwt_utils import JwtUtils, jwt_utils

access = jwt_utils.generate_token({"sub": "user-1"})
refresh = jwt_utils.generate_refresh_token({"sub": "user-1"})
claims = jwt_utils.decode_token(access)
new_access = jwt_utils.refresh_token(refresh)
```

**易错点**：access token 不能当作 refresh token 使用；不同密钥生成的 token 不能交叉校验。

### 10.3 方法权限

```python
from spring.annotations import Authenticate, GetMapping, PreAuthorize, RequestMapping, RestController


@RestController
@RequestMapping("/admin")
class AdminController:
    @GetMapping("/report")
    @Authenticate
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    def report(self):
        return {"scope": "admin"}
```

认证失败 → HTTP 401，授权失败 → HTTP 403。

### 10.4 安全基线

- `SPRING_PROFILES_ACTIVE=production` + `STARTUP_FAIL_FAST=true`
- `JWT_SECRET_KEY` 使用至少 32 字符的随机密钥
- `CORS_ALLOW_CREDENTIALS=true` 时不能用 `*` 来源
- SQL 值始终使用 `#{name}` 参数绑定

---

## 11. 缓存、任务与高级 AOP

### 11.1 @Cacheable

```python
from spring.annotations import Service, Cacheable

@Service
class UserService:
    @Cacheable(value="users", key="#user_id")
    def get_user(self, user_id: int):
        return {"id": user_id, "name": "test"}
```

本地内存缓存，最多 1000 项、TTL 300 秒，不跨进程。生产多 worker 应接入共享 Redis。

### 11.2 @Retryable

```python
from spring.annotations import Retryable
from spring.retry.retry_annotations import Backoff

@Retryable(value=(ConnectionError,), max_retries=3, backoff=Backoff(delay=1000, multiplier=2.0))
def call_remote(self):
    pass
```

**只对幂等操作开启自动重试**（如读操作）。写操作必须先设计幂等键。

### 11.3 @Async & @Scheduled

```python
from spring.annotations import Service, Async, Scheduled

@Service
class EmailService:
    @Async
    def send_email(self, to: str, content: str):
        time.sleep(1)
        print(f"Email sent to {to}")

@Service
class CleanupJob:
    @Scheduled(cron="0 */5 * * * *")
    def cleanup(self):
        pass
```

`@Async` 线程池任务不继承 MyBatis 事务；`@Scheduled` 多 worker 会重复执行。

### 11.4 高级 AOP 上线前验证

| 注解 | 上线前必须验证 |
|------|--------------|
| `@RateLimit` | 多进程/多副本一致性、Redis 故障降级 |
| `@CircuitBreaker` | 状态存储、半开恢复、超时 |
| `@Idempotent` | 键设计、TTL、并发竞争 |
| `@Lock` | 租约续期、误释放、时钟同步 |

---

## 12. AI 与 LangChain 模块

### 12.1 AI 模块（对接大模型）

> 完整文档：[AI_MODULE.md](doc/AI_MODULE.md)。安装：`pip install springbootAI[ai]`。
>
> 提供 ChatClient（链式对话）、Advisor（对话顾问）、Tools（工具调用）、RAG（知识库检索增强生成）、Function Calling 等能力。支持 OpenAI / Ollama / DeepSeek / Moonshot 等多家大模型。

### 12.2 LangChain 模块

> 完整文档：[LANGCHAIN_MODULE.md](doc/LANGCHAIN_MODULE.md)。安装：`pip install springbootAI[langchain]`。
>
> 封装 langchain classic 全套：Chains / Agents(6 种) / Memory / Retrievers / VectorStores / Parsers / Loaders + 30+ 提供商。双向适配器复用 `spring.ai` 的模型 Bean。

**最小示例**（无需 API Key）：

```python
from spring.context.registry import BeanRegistry
from spring.ai.autoconfig import configure_ai
from spring.langchain.autoconfig import configure_langchain

registry = BeanRegistry()
configure_ai(registry=registry)
beans = configure_langchain(registry=registry)

chain = beans["lcChainService"]
print(chain.run_llm_chain("回答: {q}", q="你好"))
```

---

## 13. Java 开发者看这里

> 📌 **Java 开发者专用**：如果你之前用 Java Spring Boot / Spring Cloud Alibaba / MyBatis，这一节告诉你如何迁移到 SpringBootAI。

### 13.1 核心原则（5 条）

1. 先迁移接口契约和测试，再迁移框架注解。
2. Python 使用类型标注、Pydantic 和显式依赖，比模拟 Java 反射更可靠。
3. **只有由容器创建的 Bean 才获得事务、缓存、重试等 AOP 行为**——手工 `new` 的对象不受容器管理。
4. Java 中的 XML SQL 可以大部分保留，但数据库函数、分页、类型名和连接配置需要按目标 Python 驱动验证。
5. 不把"有同名注解"理解为"与 Java 完全等价"。

### 13.2 项目结构对照

| Java Spring Boot | SpringBootAI |
|---|---|
| `src/main/java/com/acme/Application.java` | `acme/Application.py` |
| `src/main/resources/application.yml` | `acme/application.yml` 或 `acme/config/application.yml` |
| `controller/` | `acme/controller/` |
| `service/` | `acme/service/` |
| `mapper/` 和 `resources/mapper/` | `acme/mappers/` 和同目录/配置指定的 XML |
| `mvn spring-boot:run` | `python -m acme.Application` 或 `uvicorn asgi:app` |

### 13.3 启动和依赖注入对照

**启动类**：Java `@SpringBootApplication(scanBasePackages = "com.acme")` + `SpringApplication.run()` → Python `@SpringBootApplication(scan_base_packages=["acme"])` + `run(Application)`

**Bean 注解映射**：

| Java 注解 | SpringBootAI | 说明 |
|---|---|---|
| `@Component` / `@Service` / `@Repository` | 同名 | 行为一致 |
| `@RestController` | `@RestController` | 注册 FastAPI JSON 路由 |
| `@Controller` | `@Controller` | 当前按 API 响应处理，不提供模板视图语义 |
| `@Configuration` + `@Bean` | 同名 | 行为一致 |
| `@Primary` / `@Qualifier` / `@Profile` / `@Lazy` | 同名 | 行为基本一致 |

**推荐构造器注入**：

```python
from spring.annotations import Autowired, Service

@Service
class UserService:
    @Autowired
    def __init__(self, user_mapper: UserMapper):
        self.user_mapper = user_mapper
```

### 13.4 Web 层 & AOP & MyBatis 迁移

| Java | SpringBootAI | 注意事项 |
|---|---|---|
| `@GetMapping` / `@PostMapping` 等 | 同名 | `@PathVariable` 等参数绑定写在默认值位置 |
| `@Transactional` | `@Transactional` | 支持全部七种传播模式 |
| `@Cacheable` | `@Cacheable` | 本地缓存默认 TTL 300 秒 |
| `@Retryable` | `@Retryable` | `max_retries` 包含首次调用 |
| `@Async` | `@Async` | 返回 `Future`/`Task`，不继承线程事务 |
| `@Scheduled` | `@Scheduled` | 每个 worker 都会调度 |
| MyBatis `@Mapper` | `@Mapper` + 注解/SQL | XML 功能矩阵基本对齐 |

### 13.5 MyBatis 到 PyMyBatis（代码对照）

Java：
```java
@Mapper
public interface UserMapper {
  @Select("select id, name from users where id = #{id}")
  User findById(@Param("id") long id);
}
```

Python：
```python
from dataclasses import dataclass
from typing import Optional
from spring.orm import Mapper, Param, Select


@dataclass
class User:
    name: str
    id: Optional[int] = None


@Mapper
class UserMapper:
    @Select("SELECT id, name FROM users WHERE id = #{id}")
    def find_by_id(self, id: int) -> Optional[User]:
        pass
```

### 13.6 Cloud & DDL 迁移

| Java | SpringBootAI | 说明 |
|---|---|---|
| `@EnableDiscoveryClient` + Nacos | `@EnableDiscoveryClient` + `discovery` 配置 | 需部署 Nacos 并做集成测试 |
| `@FeignClient` | 同名 + `spring.cloud.feign` | 不兼容 Java interface proxy |
| `@SentinelResource` | 同名 | 已内嵌引擎，无需 Dashboard |
| JPA `hibernate.ddl-auto` | `@entity` + `ddl-auto` 配置 | 支持 create/update/validate/create-drop |

### 13.7 验证顺序

1. 创建虚拟环境，安装依赖。
2. 运行内置测试。
3. 用 SQLite 验证 Mapper SQL、事务、动态 SQL。
4. 用目标数据库版本执行相同测试。
5. 启动 ASGI 应用，检查 `/docs`、`/actuator/health`。
6. 接入外部中间件，演练断线、重复投递和回滚。

---

## 14. 生产部署

### 14.1 环境要求

| 组件 | 版本要求 | 说明 |
|------|---------|------|
| Python | 3.10+ | 推荐 3.12 |
| Redis | 6.0+ | 分布式锁、限流、缓存 |
| MySQL | 5.7+ / 8.0+ | 业务数据存储 |
| Nacos | 2.0+ | 服务注册发现（可选） |

### 14.2 基础服务部署

**Redis**：
```bash
sudo apt update && sudo apt install redis-server  # Ubuntu/Debian
redis-cli ping   # 应返回 PONG
```

**MySQL 8+ 用户创建**：
```sql
CREATE USER 'spring_python'@'%' IDENTIFIED BY 'your_secure_password';
GRANT ALL PRIVILEGES ON your_database.* TO 'spring_python'@'%';
FLUSH PRIVILEGES;
```

### 14.3 生产配置与启动

```yaml
# application-prod.yml
server:
  port: 8080

redis:
  enabled: true
  host: your-redis-host
  port: 6379
  password: your-redis-password

jwt:
  secret_key: your-strong-secret-key-change-in-production
  expires_in: 7200

database:
  enabled: true
  url: mysql+pymysql://user:password@localhost:3306/your_database?charset=utf8mb4
  ddl-auto:
    mode: validate
    entity_packages: app.entity
```

**生产启动**：
```bash
export SPRING_PROFILES_ACTIVE=production
export JWT_SECRET_KEY="使用密钥管理系统注入至少32字符的随机值"
export STARTUP_FAIL_FAST=true
uvicorn myapp.asgi:app --host 0.0.0.0 --port 8080 --workers 4
```

**Gunicorn（推荐）**：
```bash
pip install gunicorn uvicorn
gunicorn -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8080 myapp.asgi:app
```

### 14.4 生产环境变量速查

| 环境变量 | 说明 | 默认值 |
|---------|------|--------|
| `SERVER_PORT` | 服务端口 | 8080 |
| `JWT_SECRET_KEY` | JWT 密钥 | spring-python-secret-key-change-in-production |
| `DB_URL` | 数据库连接 URL | sqlite:///./test.db |
| `REDIS_HOST` / `REDIS_PORT` / `REDIS_PASSWORD` | Redis 连接 | localhost/6379/空 |
| `NACOS_SERVER` | Nacos 地址 | localhost:8848 |
| `SPRING_DISABLE_DOCKER_IP_DETECT` | 禁用容器 IP 检测 | 0 |

### 14.5 验证部署 & 故障排查

```bash
curl http://localhost:8080/actuator/health
# 返回 {"status":"UP","components":{"redis":"UP","database":"UP",...}}
```

**常见故障**：Nacos Docker 退出码 255 → 配置认证 Token；MySQL 认证失败 → 检查 `allowPublicKeyRetrieval=true`；Redis 连接 111 → `redis-cli ping` 检查。

---

## 15. 项目结构

`example`、`example1`、`example5`、`example_langchain` 是仓库级示例，不属于 `springbootAI` 安装包。实际项目应创建自己的应用包。

推荐目录结构：

```text
myapp/
|-- __init__.py
|-- Application.py
|-- application.yml
|-- controller/
|   |-- __init__.py
|   `-- UserController.py
|-- service/
|   |-- __init__.py
|   `-- UserService.py
|-- mappers/
|   |-- __init__.py
|   `-- UserMapper.py
|-- config/
|   `-- AppConfig.py
`-- exception/
    `-- GlobalExceptionHandler.py
```

每个被扫描目录都应包含 `__init__.py`，并从项目根目录启动。`scan_base_packages` 和 `@MapperScan` 接受的是可导入包名。

---

## 16. 测试

从工作区根目录运行：

```bash
python -m pytest -q tests
```

重点覆盖：
- 独立和内嵌 ORM 源码一致。
- 连接池共享、扩容、归还和未提交回滚。
- 普通事务与嵌套 rollback-only。
- Spring Mapper 在事务中复用 Session。
- JWT access/refresh、生产密钥校验。
- AI 模块 87 用例，LangChain 模块 75 用例，全量 707 用例 0 失败。

> 详细测试环境、套件覆盖和集成测试结果，见 [TEST_REPORT.md](doc/TEST_REPORT.md)。

---

## 17. 常见问题与排错

### 17.1 启动时找不到组件

1. 目录是否有 `__init__.py`。
2. `scan_base_packages` 是否是可导入包名，不是文件路径。
3. 启动工作目录是否包含项目根目录。
4. 组件类是否带 `@Service`、`@RestController` 等注解。
5. `@Profile` 是否与当前环境一致。

### 17.2 Mapper 未注册

检查 `database.enabled: true`、`database.orm: mybatis`、`@Mapper`、`@MapperScan` 路径。

### 17.3 `@Transactional` 报缺少工厂

说明 MyBatis 没有初始化。确认数据库已启用、ORM 模式正确、Service 是由容器创建而不是手工 `UserService()`。

### 17.4 数据库连接耗尽

检查 Session 是否通过上下文管理器关闭、请求是否有长事务、`实例 x worker x max_size` 是否超过数据库上限。

### 17.5 生产启动拒绝 JWT

设置 `SPRING_PROFILES_ACTIVE=production`、`STARTUP_FAIL_FAST=true`、`JWT_SECRET_KEY=<至少32字符随机密钥>`。

### 17.6 Nacos / PATCH / 配置同步排错

- **Nacos Docker 退出码 255**：配置认证 Token 和相关环境变量。
- **`PATCH /api/...` 返回 404**：确认方法用 `@PatchMapping`，框架已接入 `fastapi_app.patch()`。
- **`ConfigLoader()` 读不同文件**：确认通过 `ApplicationContext` 启动，不是在不同工作目录直接实例化加载器。

### 17.7 LangChain 模块排错

- **`@Autowired` 注入 `lcChainService` 失败**：确认调用了 `configure_ai()` + `configure_langchain()`。
- **Partner 注册失败（跳过）**：按告警提示 `pip install langchain-<partner>`。
- **RAG 报`嵌入模型未装配`**：设置 `AI_ALLOW_FAKE=true` 降级或提供真实 API Key。

### 17.8 上线前清单

- 使用实际数据库版本运行 CRUD、事务、断连恢复测试。
- 使用迁移工具管理结构，不让应用运行账号执行 DDL。
- 锁定依赖，执行漏洞扫描。
- 为 JWT、数据库、Redis 使用密钥管理系统。
- 配置 TLS、CORS 白名单、请求限制。
- 验证备份恢复、主从切换。
- 对定时任务设计唯一执行或幂等。
- 执行越权、SQL 注入、重放测试。

---

## 18. 性能与容量验证

仓库提供 Docker 化的 SpringBootAI 基准服务和 k6 `smoke`、`baseline`、`stress`、`soak` 四档压测。快速验证：

```powershell
.\scripts\run-load-test.ps1 -Profile smoke
```

完整参数和说明见 [`tests_performance/README.md`](tests_performance/README.md)。

---

## 附录 A：完整环境变量清单

```bash
# Server
export SERVER_PORT=8080
export SERVER_HOST=0.0.0.0

# Redis
export REDIS_ENABLED=true
export REDIS_HOST=localhost
export REDIS_PORT=6379
export REDIS_PASSWORD=
export REDIS_DB=0

# JWT
export JWT_SECRET_KEY=your-secret-key
export JWT_ALGORITHM=HS256
export JWT_EXPIRES_IN=3600

# Database
export DB_ENABLED=false
export DB_URL=sqlite:///./test.db
export DB_USERNAME=
export DB_PASSWORD=
export DB_DRIVER=sqlite
export DB_HOST=localhost
export DB_PORT=3306
export DB_DATABASE=./test.db

# ORM DDL Auto
export DB_DDL_AUTO=none  # none|validate|update|create|create-drop
export DB_ENTITY_PACKAGES=

# Nacos
export DISCOVERY_ENABLED=false
export NACOS_SERVER=localhost:8848
export NACOS_NAMESPACE=
export NACOS_GROUP=DEFAULT_GROUP
export NACOS_USERNAME=nacos
export NACOS_PASSWORD=nacos

# Docker 辅助
export SPRING_DISABLE_DOCKER_IP_DETECT=0

# Retry
export RETRY_ENABLED=true
export RETRY_MAX_RETRIES=3
export RETRY_DELAY=1000
export RETRY_MAX_DELAY=10000
export RETRY_MULTIPLIER=2.0

# RabbitMQ
export RABBITMQ_ENABLED=false
export RABBITMQ_HOST=localhost
export RABBITMQ_PORT=5672
export RABBITMQ_USERNAME=guest
export RABBITMQ_PASSWORD=guest

# Prometheus
export PROMETHEUS_ENABLED=false
export PROMETHEUS_PORT=8000

# Logging
export LOG_LEVEL=INFO
export LOG_DIR=logs

# AI 模块
export AI_PROVIDER=openai
export AI_ALLOW_FAKE=true
export OPENAI_API_KEY=sk-xxx
export OPENAI_CHAT_MODEL=gpt-4o-mini
export OLLAMA_BASE_URL=http://localhost:11434
export OLLAMA_CHAT_MODEL=llama3

# LangChain 模块
export LC_ENABLED=true
export LC_DEFAULT_LLM=auto
export LC_AGENT_TYPE=react
export LC_AGENT_MAX_ITER=10
export LC_VECTOR_STORE=faiss
export LC_RETRIEVER=similarity
export LC_RETRIEVER_K=4
export LC_MEMORY=buffer
export LC_MEMORY_MAX=20
```

## 附录 B：Docker Compose 示例

```yaml
version: '3.8'

services:
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

  mysql:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: example_db
    volumes:
      - mysql_data:/var/lib/mysql

  nacos:
    image: nacos/nacos-server:v2.3.0
    ports:
      - "8848:8848"
      - "9848:9848"
    environment:
      MODE: standalone
      NACOS_AUTH_ENABLE: "true"
      NACOS_AUTH_TOKEN: "c3ByaW5ncHktbmFjb3MtaGFuZHNoYWtlLXNlY3JldC0yMDI2LTA4LTA0LTAx"
      NACOS_AUTH_IDENTITY_KEY: "springpy"
      NACOS_AUTH_IDENTITY_VALUE: "springpy-local"
      JAVA_TOOL_OPTIONS: "-XX:-UseContainerSupport"

volumes:
  redis_data:
  mysql_data:
```
