fountain 服务器应用开发实战指南:应用模型、分层架构、常用写法与构建运行全解析

📅 发布时间:2026/10/15 11:53:52
fountain 服务器应用开发实战指南:应用模型、分层架构、常用写法与构建运行全解析
后端Web框架【免费下载链接】fountain一个用于服务器应用开发的综合工具库。 - 零配置文件 - 环境变量和命令行参数配置 - 约定优于配置 - 深刻利用仓颉语言特性 - 只需要开发动态链接库fboot负责加载、初始化并运行 - 覆盖服务器应用开发的各种场景项目地址https://gitcode.com/Cangjie-SIG/fountain点击查看免费下载fountain 是一套面向仓颉语言服务器应用开发的综合工具库其核心思想是「应用项目只写动态链接库由fboot负责加载、初始化并运行」。本文以仓库内 server-development.md 为骨架结合fdemo示例工程与各模块源码完整讲解 fountain 应用模型、推荐分层结构、Controller / 依赖注入 / ORM / AOP / 定时任务 / 认证 / 日志等常用写法以及fboot build / run / test / cleanUpdate的构建运行命令与环境变量配置。读完本文你将掌握从零搭建一个 fountain 服务器应用、正确配置连接池与事务、并完成测试验收的完整实战能力。1. 应用模型没有 main 函数的动态链接库应用fountain 应用最显著的特点是没有 main 函数。应用项目中的每个模块都编译为动态链接库cjpm.toml中output-type dynamic由fboot run加载并启动。以 fdemo 为例模块user与boot的cjpm.toml均声明output-type dynamic compile-option --dy-std override-compile-option --dy-std1.1 启动链路fboot run [PATH] --dylibPattern...的完整启动链路为fboot按--dylibPattern指定的正则表达式扫描并加载名字匹配的动态链接库各动态库的 static init静态初始化执行注册Initializer、子命令、Bean等按依赖顺序执行初始化InitializerCollection.doInit依据dependencies拓扑排序各start()被spawn到新线程中运行如 MVC 的 HTTP 服务启动后阻塞在服务循环里主线程执行while(true) { sleep(Duration.Max) }永久阻塞进程常驻。对应源码在 App.cj 的run()方法中可以看到load()解析出starters数组后逐一spawn { starter() }最后主线程进入死循环。这也解释了为什么「长驻进程需要交给用户或后台执行」。1.2 Initializer显式初始化接口需要显式初始化的功能f_bean、f_mvc、f_orm、f_ticktock都通过fountain::f_app.Initializer接口完成接口定义见 Initializer.cjpublic interface Initializer { prop name: String // 待初始化的功能名称 prop dependencies: ArrayString { get() { [] } } // 依赖项须先初始化 func initialize(): Unit // 完成初始化 func start(): Unit {} // 返回启动函数是否阻塞取决于具体实现 }应用代码通常不需要自己实现该接口——各模块已内置注册例如f_orm的ORMInitializer已注册到InitializerCollection应用启动时自动执行ORM.initialize()。InitializerCollection的doInit()会反复扫描队列只有当一个 Initializer 的所有依赖都已初始化完毕后才执行其initialize()并把start()收集到启动器列表。1.3 包级静态初始化启动前的额外准备如果需要在启动前做额外初始化例如注册数据库驱动、调用ORM.register(...)应放在单独的初始化模块里用包级静态初始化表达package fountain::boot import fountain::f_orm.* import postgres_driver.* private let _ { // 注册驱动 / ORM.register(...) 等 }()完整实例见 fdemo/boot/src/boot.cj文件内有完整注释与多种注册方式演示如ORM.getDriver(driverName)、ORM.register(driver)、ORM.register(NamedDatasource(...))、lookupMockdbDialect()等。boot模块必须被--dylibPattern命中否则这些静态初始化不会执行。2. 分层结构对齐 fdemofountain 推荐在一个业务模块内部按下表分层与fdemo/user完全对齐目录模块内职责关键写法fdemo 参考controller/HTTP 入口ControllerGetMapping[...]等UserController.cjservice/、service/impl/业务逻辑、依赖注入BeanBeanMetalookupT()UserServiceImpl.cjdao/数据库访问DAO接口: RootDAOexecutor.setSql(...)UserDAO.cjmodel/数据模型PO/DTO/校验DataAssist、ORMField、QueryMappersGeneratorfdemo/user/src/model/util/定时任务、认证等f_ticktockcron、工具类fdemo/user/src/util/模块划分建议一个业务模块对齐原项目/需求的分层 一个可选boot初始化模块仅在只用环境变量配置不够时添加。注意包名与目录结构一致——fdemo 的包名是fountain::user.controller这种形态且业务模块本身也应被--dylibPattern命中见第 4 节。3. 常用写法3.1 Controller 与路由f_mvcController 用Controller修饰类路由方法用GetMapping/PostMapping等注解声明路径与媒体类型package demo::user.controller // 包名按项目自定与目录结构一致fdemo 用 fountain::user.controller import fountain::f_mvc.* import fountain::f_mvc.macros.* Controller public class HelloController { private let helloService lookupHelloService() GetMapping[path: /hello, produces: text/plain, consumes: application/x-www-form-urlencoded] IgnoreSecurity public func hello(RequestParam username: String): String { helloService.sayHello(username) return hello } }真实的完整示例见 HellowordController.cj 与 UserController.cjGetMapping[path:/api/user/{id}, produces:application/json, consumes:application/x-www-form-urlencoded] public func queryUser(PathVariable id: Int64): JsonValue { ... } DeleteMapping[path:/api/user/{id}, produces:application/json, consumes:application/x-www-form-urlencoded] public func deleteUser(PathVariable id: Int64): JsonValue { ... }注解族速查细节以 f_mvc/README.md 为准类级Controller、RequestMapping方法级GetMapping/PostMapping/DeleteMapping等参数绑定RequestBody、RequestParam、PathVariable、RequestHeader返回类型Redirect重定向如Redirect.found(/helloworld)、JsonValue、String或任意 PO 对象安全豁免IgnoreSecurity/IgnorePrivilege/IgnoreAuthproduces是响应体格式、consumes是请求体格式可用|分割多个格式两者默认都是application/json见 f_mvc/README.md 第 119-120 行说明。3.2 依赖注入f_bean实现类加Bean配合BeanMeta注册进 IOC 容器使用处通过lookupT()取实例import fountain::f_bean.* import fountain::f_bean.macros.* Bean BeanMeta public class UserServiceImpl : UserService { ... }Controller 中注入private let userService lookupUserService()。需要事务控制的 Service 可直接用TransactionalService等价于WeavedBean会把 Service 类织入切面链配合orm_transactionalFuncExecution配置实现事务织入无需逐方法写Transactional。参考 UserServiceImpl.cj 与 f_bean/README.md。3.3 数据访问f_ormDAO 是DAO修饰的接口继承RootDAO方法体内写 SQL 或使用查询构造器DAO public interface UserDAO : RootDAO { func register(username: String, password: String): Int64 { executor.setSql( insert into user_info(username, password) values(${arg(username)}, ${arg(password)}) returning id).insert } }fdemo 实际同时演示了两种风格见 UserDAO.cj原始 SQLsetSql(...).insert/.update/.delete/.firstUserPO()/.singleFirstInt64()与类型安全的查询构造器executor.FROMUserPO().WHERE(UserPO.tableColumns().id.eq(id)).firstUserPO().getOrThrow()、INSERT_INTOUserPO(user)、UPDATEUserPO(map)、.pageUserPO(100, page: 1)分页等。模型映射注解ORMField字段/主键、DataAssist生成通用方法、QueryMappersGenerator查询映射参考 fdemo/user/src/model/ 与 f_orm/README.md。驱动数据库驱动是第三方依赖如postgres_driver在 workspace 根 cjpm.toml 声明output-typedynamicmock 场景用f_mockdborm_drivers中含mockdb时只注册mockdb。连接池与事务全部通过orm_*环境变量配置fdemo/boot.sh 第 35-75 行是完整清单详见本文第 3.8 节事务注解Transactional与orm_transactionalFuncExecution配置配合生效。fdemo 中TransactionalServiceTransactional的用法见 HelloworldServiceImpl.cj。3.4 AOPf_aspectAspectRoute[route ExecutionRouteRule(...)]或配置式AspectRoute[ConfigExecutionRouteRule(配置变量名)]修饰切面类实现Aspect接口的before/after/around/throwing/finalAspectRoute[ConfigExecutionRouteRule(controllerPointcut)] Bean public class ControllerAspect : Aspect { public func around(funcInfo: InvocationFuncInfo, point: (ArrayAny) - Any): Any { point(funcInfo.args) } }见 fdemo/boot/src/ControllerAspect.cj。执行顺序为before → around → 原函数体 → after → throwing → finalthrowing在前四步任一步抛异常时执行final在 finally 块执行见 f_aspect/README.md 对Aspect接口的说明。切点用环境变量声明变量名由开发者自定如controllerPointcut*::*..*Controller.*(**): *见 fdemo/boot.sh 第 29 行在切面声明处通过ConfigExecutionRouteRule(变量名)引用。参考 f_aspect/README.md 与 docs/快速开始/000.get-start.md 的 AOP 示例。3.5 定时任务f_ticktockCRON 定时器任务类加Bean继承CronTicktockTask实现cron属性与execute()Bean public class TickTockTaskImpl : CronTicktockTask { public prop cron: String { get() { 1/3-45 // CRON 表达式末尾的 * 可省略1/3-45 * * * * * * } } public func execute(): Unit { log.info(TickTockTaskImpl is executing) } }见 fdemo/user/src/util/cron/TickTockTest.cj。可选覆写once是否只执行一次、concurrentable是否允许并发执行、executing/reset执行状态记账等属性详见 f_ticktock/README.md。注意定时任务所在包必须出现在--dylibPattern里——fdemo 的 pattern 就包含cronuser\.util\.(auth|cron)。3.6 认证与权限f_jwt f_securityf_jwt负责令牌签发与校验f_security配合 MVC 做认证/权限Controller 上按需IgnoreSecurity豁免。AuthHandler是认证/权限处理器接口实现check(param: AuthParam): AuthStatus根据param.ignoreAuth与自定义校验返回OK或InvalidSession(HttpStatus.UNAUTHORIZED, json)见 fdemo/user/src/util/auth/AuthCheckerImpl.cj。令牌上下文示例UserSessionCache用JWTSecurityContextString(JWTHeapCacheStore(Duration.hour), ...)签发/校验 JWTHMAC 签名见 fdemo/user/src/util/UserSessionCache.cj。默认安全策略、令牌校验细节读 f_security/README.md f_jwt/README.md。3.7 日志f_logLoggerFactory.getLoggerT()按类型或LoggerFactory.getLogger(name)按名字获取 Loggerprivate static let log LoggerFactory.getLoggerUserServiceImpl()输出目标、级别、格式、异步缓冲全部用logger_*环境变量配置fdemo/boot.sh 第 19-28 行为完整清单export logger_appender_consoleFDemoConsole # 控制台 appender 名称可任意起名 export logger_appender_FDemoConsole_levelDEBUG export logger_appender_FDemoConsole_pattern[%level-%name]%d{yyyy/MM/dd,HH:mm:ss.SSS}|%tid;%m export logger_appender_fileFDemoFile # 文件 appender 名称 export logger_appender_FDemoFile_levelINFO export logger_appender_FDemoFile_path./log/fdemo.log export logger_appender_FDemoFile_rotateDurationDAY # 按自然天切割日志 # export logger_appender_FDemoFile_rotateSize100000000 # 或按文件字节数切割 export logger_asyncWaitTimeout5ms # 异步缓冲区等待时间默认 5ms # export loggerAsyncBufsize2 # 异步日志缓存池初始化大小默认 1024格式占位符%level当前日志级别、%name当前日志名称、%d{...}时间花括号内为时间格式、%m日志消息文本、%tid线程 ID、%pid进程 ID。配置键的统一形态是logger_appender_配置名appender名声明一个 appender再用logger_appender_appender名_字段配置其字段见 f_log/README.md。3.8 配置f_config / 环境变量fountain 是零配置文件框架配置以环境变量为主运行期配置优先级高于编译期fboot build --keyvalue会把--keyvalue作为编译期环境变量嵌入产物App.cj 的doBuild()中--开头且含的参数被转成环境变量传给cjpm build子进程。敏感信息数据库 URL、密钥建议只在运行期用环境变量提供确要嵌入时可用fboot randhex n生成随机密钥如sm4Key$(fboot randhex 32)、sm4Iv$(fboot randhex 32)配合 SM4 加密后嵌入编译产物——见 fdemo/boot.sh build 函数中的完整演示--postgres_orm_connectionUrl$POSTGRES、--postgres_orm_option_username...、--postgres_orm_option_password...、--sm4Key...、--sm4Iv...。f_config模块提供读取封装Config.getValueInt64(threadCount)、Config.getValuesInt64(ports)、Config.getDateTime(...)、Config.bufferSize(...)、Config.set(...)等命令行参数会覆盖同名环境变量读取优先级为「环境变量/命令行参数 运行期 set 注册 编译期内嵌值」见 f_config/README.md。数据库连接池与事务配置清单orm_*前缀fdemo/boot.sh 第 35-75 行 f_orm/README.md配置项默认值说明orm_drivers—逗号分隔的驱动名列表如postgres、mockdb,opengaussorm_defaultDriver空默认驱动未配置时回退为orm_drivers第一个driver_orm_connectionUrl—连接串如postgres_orm_connectionUrlorm_option_key—驱动初始化参数单数option如orm_option_usernameorm_noPoolfalsetrue表示不用连接池orm_useStdPooltruetrue使用标准库PooledDatasourcefalse使用 fountainDatabasePoolorm_useThirdPartyPoolfalsetrue时由业务代码ORM.register(datasource, default: false)自行注册第三方池orm_databasePoolInitSize/MinSize/MaxSize10fountain 连接池初始/最小/最大连接数orm_databasePoolCheckOnCreation/CheckOnBorrowing/CheckOnReturningfalse/true/true各环节连接有效性检查开关orm_databasePoolIdleTimeout0秒闲置过期时间0表示不过期orm_databasePoolConnectionLife3600秒连接存活时间orm_databasePoolCheckInterval300秒有效性检查周期orm_databasePoolConnectTimeout50毫秒获取连接超时orm_databasePoolMaxWaiting30000毫秒池耗尽且无限等待时的等待上限超过记 WARN 并返回 None0表示真无限等待orm_databasePoolCheckSqlselect 1检查连接有效性的 SQLorm_stdPoolMaxSize/MaxIdleSize/IdleTimeout/MaxLifeTime/ConnectionTimeout/KeepaliveTime—标准库PooledDatasource对应参数orm_transactionalFuncExecution—\|分隔的函数签名模式命中即织入事务切面如*::*..*ServiceImpl.del*(**): *事务说明orm_transactionalFuncExecution与Transactional注解只要有一个生效就会将事务切面织入函数还可配合orm_transactionIncluding/orm_transactionExcluding正则、orm_transactionPropagation/orm_transactionLevel等细化行为。orm_*为全局配置driverName_orm_key可按驱动覆盖且优先级更高见 f_orm/README.md 第 169-202 行。3.9 外部 HTTP 调用调用第三方 HTTP 服务用f_httpclient接口细节以其 README 与 fdemo 用例如有为准。模块选型速查可参考 references/module-index.md例如「调用外部 HTTP 服务 →f_httpclient」。4. 构建 / 运行 / 测试# 构建自动生成版本模块 AppVersion再 cjpm build--keyvalue 变编译期环境变量 fboot build [PATH] [--keyvalue ...] # 运行永久阻塞长驻进程交给用户或后台执行 fboot run [PATH] --dylibPattern正则 # 测试用 PATH/test/cjpm.toml 覆盖后 build run fboot test [PATH] --dylibPattern正则 # 清理重建cjpm clean 删 cjpm.lock cjpm update fboot cleanUpdate [PATH]4.1 关于 --dylibPattern--dylibPattern决定加载哪些动态库必须覆盖controller 包、service.impl 包、初始化包、cron 所在包。fdemo 的完整 pattern 见 fdemo/boot.shfboot run $target_path --dylibPattern(boot|user\.util\.(auth|cron)|\.(controller|service\.impl))目录形态照抄fdemo/boot.sh搜dylibPattern不确定时先fboot build再ls target/release或对应产物目录看实际库文件名。4.2 启动脚本与运行环境项目启动脚本boot.sh/boot-macos.sh/boot-win-gitbash.sh的生成与合并见 references/launch-scripts.md。fdemo 的 boot.sh 还演示了几个工程级要点用find $target_path/release -name lib*.so收集产物目录并置于LD_LIBRARY_PATH前面避免命中旧副本第三方驱动如postgres_driver可能建在target/名字/release/嵌套目录需一并扫描兜底fboot build中数据库敏感配置用--keyvalue以编译期环境变量形式嵌入。4.3 fboot 命令全览fboot help输出App.cj 的help()完整命令列表run、shutdown、restart、module、workspace、cleanUpdate、build、test、count、version、pub、randhex、help以及fboot subcmd --dylibPattern...运行实现了fountain::f_app.SubCommand的子命令。fboot build还有两个约定第一个参数是编译产物保存路径只有路径本身--开头且含的参数作为环境变量传入cjpm build子进程其余作为其命令行参数。4.4 单元测试单元测试用仓颉 unittest写法见cangjie-coding技能与~/.autocode/cj/doc/cj_unittest.mdcjpm test/fboot test均可运行。fboot test会用PATH/test/cjpm.toml覆盖后 build run。5. 需求覆盖检查与缺口处理功能点 → 模块初筛查 references/module-index.md 的选型速查表如「HTTP 接口 / REST / 路由 →f_mvc」「数据库 →f_orm」「日志 →f_log」「登录认证 / 权限 →f_jwtf_security」「定时任务 →f_ticktock」。命中核对python scripts/fountain_lookup.py search 能力关键词必要时--in code再读该模块 README 与src/。脚本按项目在用版本切文档副本读到的即该版本输出头部有版本标注换版本用--version。有缺口时的固定动作不得跳过停下来问用户给出四选一——继续用简单方案 / 用户提供第三方依赖 / 缩减范围 / 在项目内自行实现完整功能和要求的基础设施代码、工具代码并明确说明缺口是什么、影响哪些需求点。禁止项把缺失能力写成空实现或假数据引用没核对过的第三方库把「标准库/其它语言有」当成「fountain 有」。6. 测试与验收三类测试的分工与门禁见 references/delivery-workflow.md第 3-5 节单元测试TDD每个功能点先写测试再实现Red-Green-Refactorcjpm test/fboot test。回归测试每批功能完成后跑全量测试含既有用例交付节点提交前 / 打 tag 前必须再跑一次。冒烟测试起服务后验证关键路径——HTTP 项目curl核心接口fdemo 的loop脚本即循环curl http://localhost:8080/helloworld验证服务可用见 fdemo/boot.shRPC 项目runServerrunClient打通一次并检查日志logger_*无异常堆栈。数据库相关先用f_mockdb或真实库跑通最小 DAO 用例再铺开。结语fountain 的服务器应用开发范式可以概括为三条主线动态链接库 fboot加载没有 main 函数的应用模型、约定优于配置 环境变量驱动零配置文件运行期优先级高于编译期、注解 切面织入Controller / Bean / DAO / 事务 / 定时任务统一由注解声明、由初始化框架装配。本文涉及的所有配置项、代码示例与命令均可直接在 fdemo 示例工程、各模块 READMEf_mvc/README.md、f_bean/README.md、f_orm/README.md、f_aspect/README.md、f_ticktock/README.md、f_log/README.md、f_security/README.md、f_jwt/README.md以及 docs/快速开始/000.get-start.md 中复核验证。赞分享后端Web框架【免费下载链接】fountain一个用于服务器应用开发的综合工具库。 - 零配置文件 - 环境变量和命令行参数配置 - 约定优于配置 - 深刻利用仓颉语言特性 - 只需要开发动态链接库fboot负责加载、初始化并运行 - 覆盖服务器应用开发的各种场景项目地址https://gitcode.com/Cangjie-SIG/fountain点击查看免费下载相关推荐fountain::f_app 完全指南用 fboot 启动、构建与发布仓颉服务器应用fountain::f_app 完全指南用 fboot 启动、构建与发布仓颉服务器应用 本文围绕开源仓库 Cangjie SIG/fountain 的 f_a后端Web框架EVCC 实战让家充自动挑便宜时段充电EVCC 实战让家充自动挑便宜时段充电 早晨一看昨晚的充电账单忍不住开始算账高峰那几小时一度电花了多少EVCC 是一个开源的电动汽车充电管理项目你把后端前端智能硬件物联网能源管理Martini 框架实战指南用 Go 构建模块化 Web 应用与服务Martini 框架实战指南用 Go 构建模块化 Web 应用与服务 导读 Martini 是一个专为 Golang 设计的 Web 框架目标是用最小的心智后端上一篇pandoc Org-mode 表格的 LABEL/CAPTION 解析与转换原理基于 9279 号测试用例的深度剖析下一篇brpc 多协议支持与新协议扩展指南端口级协议甄别机制与三步接入自定义协议创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考