Fountain 使用手册
快速开始
概述
简介
本指南提供了 Fountain 如何帮助您加速应用程序开发的示例。当您阅读更多相关指南时,您将看到 Fountain 的更多用例。本指南旨在让您快速体验 Fountain。
您将构建什么
您将使用 Fountain 构建一个简单的 Web 应用程序,并为其添加一些有用的服务。
您需要什么
- 时间要求:约20分钟
- 开发工具:文本编辑器或 IDE(推荐:VSCode)
- 所需依赖:
- 仓颉SDK:cangjie-sdk-linux-x64-1.0.3
- 仓颉Stdx:cangjie-stdx-linux-x64-1.0.3.1
如何完成本指南
与大多数技术入门指南一样,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的基本设置步骤。无论哪种方式,您最终都会得到可运行的代码。
Linux 环境配置
环境准备
仓颉环境配置
- 参考 仓颉SDK配置 进行配置。
Stdx 环境配置
- 本项目依赖仓颉Stdx包,参考 仓颉stdx配置 进行配置。
Fountain 环境配置
- 克隆 Fountain 源代码至本地环境,执行命令:
git clone https://gitcode.com/Cangjie-SIG/fountain.git - 安装 fboot 项目依赖,在 fboot 目录下执行:
cjpm install - 将 Fountain 动态链接库所在的路径加入环境变量,执行命令:
export FOUNTAIN_HOME=~/.cjpm/libs/fboot - 将编译后的动态链接库加入环境变量,执行命令:
export LD_LIBRARY_PATH=$FOUNTAIN_HOME:$LD_LIBRARY_PATH
环境配置参考(.bashrc)
export CANGJIE_HOME="~/cangjie"
export CANGJIE_STDX_PATH="~/stdx/1.0.3.1/linux_x86_64_llvm"
export CANGJIE_STDX_DYNAMIC_PATH="~/cangjie/stdx/1.0.3.1/linux_x86_64_llvm/dynamic/stdx"
export FOUNTAIN_HOME="~/.cjpm/libs/fboot"
export LD_LIBRARY_PATH=$FOUNTAIN_HOME:$LD_LIBRARY_PATH
快速搭建基于 Fountain 的项目
本指南详细介绍了如何快速搭建一个使用 Fountain 框架并以动态库(Dynamic Library)形式构建的项目。
1. 项目初始化和目录结构
首先,创建项目根目录并初始化主项目(project_name)和子模块(hello),具体步骤如下表:
| 步骤 | 命令 | 描述 |
|---|---|---|
| 1 | fboot workspace project_name |
使用project_name在当前路径创建新项目并初始化成workspace fboot workspace会把fountain和stdx的依赖添加到cjpm.toml |
| 1 | fboot module hello |
在当前路径用hello创建动态链接库模块,并把模块加入当前目录下的cjpm.toml |
2. 配置 cjpm.toml 文件(fountain会自动配置)
项目中有两个 cjpm.toml 文件需要配置:主项目(fountain_demo/cjpm.toml)和子模块(fountain_demo/user/cjpm.toml)。
2.1 主项目配置
[dependencies]
# 配置 fountain 项目依赖
fountain = {git = "https://gitcode.com/Cangjie-SIG/fountain.git", branch="master"}
[workspace]
version = "1.0.0"
# 声明 hello 目录为一个 workspace 成员
members=["hello"]
# 通用的编译选项
compile-option = "-O2 --dy-std -Woff unused"
override-compile-option = "-O2 --dy-std -Woff unused"
[profile.build]
incremental = true
[profile.build.combined]
# 将 fdemo 库类型设置为 dynamic
fdemo = "dynamic"
[target]
# --- 针对不同操作系统的配置 ---
[target.x86_64-unknown-linux-gnu]
compile-option = "-O2 --lto=full --dy-std -Woff unused"
override-compile-option = "-O2 --lto=full --dy-std -Woff unused"
[target.x86_64-unknown-linux-gnu.bin-dependencies]
path-option = ["${CANGJIE_STDX_DYNAMIC_PATH}"]
[target.aarch64-unknown-linux-gnu]
compile-option = "-O2 --lto=full --dy-std -Woff unused"
override-compile-option = "-O2 --lto=full --dy-std -Woff unused"
[target.aarch64-unknown-linux-gnu.bin-dependencies]
path-option = ["${CANGJIE_STDX_DYNAMIC_PATH}"]
[target.x86_64-w64-mingw32]
compile-option = "-O2 --dy-std -Woff unused"
override-compile-option = "-O2 --dy-std -Woff unused"
[target.x86_64-w64-mingw32.bin-dependencies]
path-option = ["${CANGJIE_STDX_DYNAMIC_PATH}"]
[target.x86_64-apple-darwin]
compile-option = "-O2 --dy-std -Woff unused"
override-compile-option = "-O2 --dy-std -Woff unused"
[target.x86_64-apple-darwin.bin-dependencies]
path-option = ["${CANGJIE_STDX_DYNAMIC_PATH}"]
[target.aarch64-apple-darwin]
compile-option = "-O2 --dy-std -Woff unused"
override-compile-option = "-O2 --dy-std -Woff unused"
[target.aarch64-apple-darwin.bin-dependencies]
path-option = ["${CANGJIE_STDX_DYNAMIC_PATH}"]
[target.aarch64-linux-ohos]
compile-option = "-O2 --dy-std -ldl"
override-compile-option = "-O2 --dy-std -ldl"
[target.aarch64-linux-ohos.bin-dependencies]
path-option = ["${CANGJIE_STDX_DYNAMIC_PATH}"]
[target.x86_64-linux-ohos]
compile-option = "-O2 --dy-std -ldl"
override-compile-option = "-O2 --dy-std -ldl"
[target.x86_64-linux-ohos.bin-dependencies]
path-option = ["${CANGJIE_STDX_DYNAMIC_PATH}"]
2.2 子模块配置
[package]
cjc-version = "1.0.0"
name = "hello"
description = "Fountain hello module"
version = "1.0.0"
target-dir = ""
src-dir = ""
output-type = "dynamic"
compile-option = "-O2 --dy-std"
override-compile-option = "-O2 --dy-std"
link-option = ""
package-configuration = {}
3. 创建控制器文件
在 hello 模块中创建源代码文件,实现一个简单的 Web 控制器,具体步骤如下:
| 步骤 | 命令 | 描述 |
|---|---|---|
| 1 | cd hello/src |
进入 hello 模块的源代码目录。 |
| 2 | touch hello/src/HellowordController.cj |
创建控制器源文件。 |
将以下内容写入 project_name/hello/src/HelloWordController.cj:
package hello
import fountain.mvc.*
import fountain.mvc.macros.*
@Controller
public class HelloWordController {
/**
* 定义一个 GET 请求处理器,路径为 /
* 生产纯文本内容
* 忽略安全检查(仅作示例,实际项目中请谨慎使用)
*/
@GetMapping[path:"/", produces:'text/plain']
@IgnoreSecurity
public func helloWorld(): String {
return "hello Fountain!"
}
}
4. 构建和运行项目
在项目根目录 (project_name) 下执行构建和运行命令。
| 步骤 | 命令 | 描述 |
|---|---|---|
| 4.1 | fboot build out |
构建项目,并将编译后的结果输出到名为 out 的目录中。 |
| 4.2 | fboot run ./ --dylibPattern='hello' |
基于 Fountain 框架运行当前项目(默认使用 out 目录中的编译产物)。参数说明:--dylibPattern='hello' 指定要加载的动态库匹配模式。在本例中,它指示 Fountain 仅加载名称中包含 hello 的动态库(即 hello 模块编译生成的动态库)。 |
4.3 验证结果 项目成功启动后,您可以通过访问以下地址来验证控制器是否正常工作:
- 访问地址: http://localhost:8080/
- 预期输出: hello Fountain!
常用的注解与宏的使用和使用案例
1. MVC相关注解
@Controller: 标记类为控制器@RequestMapping: 定义API路由@RequestBody: 表示从请求体获取数据@RequestParam: 获取请求参数@RequestHeader: 绑定请求头
Example: @fdemo/user/src/controller/UserController.cj
@Controller
public class HelloController {
@GetMapping[path:'/hello',consumes:'application/x-www-form-urlencoded', produces:'text/plain']
@IgnoreSecurity //这个注解功能是忽略jwt的认证
public func register( @RequestParam username: String, @RequestParam password: String): String {
println("${username}, ${password}")
}
}
2. 数据模型相关注解
@DataAssist: 辅助生成数据类的通用方法(如equals, hashCode, toString等)@QueryMappersGenerator: 生成查询映射器@ORMField: ORM字段映射@DAO: ORM字段映射
Example 1:
@DataAssist[props fields]//生成get/set的方法,返回包含所有字段的元组
@QueryMappersGenerator[med_allocation]//用于生成数据库查询映射的注解
public class MedAllocationPO {
@ORMField[true 'id']//对象关系映射(ORM)的字段注解
private var id: Int64 = 0
@ORMField['pre_amount']
private var preAmount: Decimal = Decimal.zero
}
Example 2: @fdemo/user/src/dao/UserDAO.cj
@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
}
...
}
3. Bean相关注解
@Bean: 将类标记为管理的 Bean,使其可以被依赖注入。@BeanMeta: 为 Bean 添加额外的元数据,通常与@src/bean/bean.cj一起使用,用于配置 Bean 的元信息
Example:
package user.service.impl
@Bean
@BeanMeta
public class UserServiceImpl <: UserService {
...
}
package user.controller
@Controller
public class UserController {
private let userService = lookup<UserService>()
...
}
4. AOP切面
@AspectRoute- 修饰切面实现类
Example:
@AspectRoute(route = ExecutionRouteRule("MyService", "doSomething", "String", "String"))
public class LoggingAspect <: Aspect {
// 在目标方法执行前调用
public func before(funcInfo: InvocationFuncInfo): Unit {
println("方法调用前 - 方法名: " + funcInfo.methodName)
println("参数: " + funcInfo.args.toString())
}
// 在目标方法执行后调用
public func after(funcInfo: InvocationFuncInfo, result: Any): Any {
println("方法返回后 - 方法名: " + funcInfo.methodName)
println("返回值: " + result.toString())
return result
}
// 环绕通知,可以控制目标方法的执行
public func around(funcInfo: InvocationFuncInfo, point: (Array<Any>) -> Any): Any {
println("环绕通知 - 方法执行前: " + funcInfo.methodName)
try {
let result = point(funcInfo.args)
println("环绕通知 - 方法执行后,结果: " + result.toString())
return result
} catch (e) {
println("环绕通知 - 方法执行异常: " + e.message)
throw e
}
}
// 异常处理
public func throwing(funcInfo: InvocationFuncInfo, e: Exception): Exception {
println("方法执行异常 - 方法名: " + funcInfo.methodName)
println("异常信息: " + e.message)
return e
}
// 最终通知,无论是否发生异常都会执行
public func final(funcInfo: InvocationFuncInfo): Unit {
println("最终通知 - 方法名: " + funcInfo.methodName)
}
}
5. 测试相关
@fdemo/user/src/util/auth/TickTockTest.cj- 测试类注解@TestCase- 测试用例@Assert- 断言,断言失败时能够快速定位问题
Example:
@Test
public class MyTest {
@TestCase // 标记这是一个测试方法
public func testAddition(): Unit {
// 测试代码
@Assert(1 + 1, 2) // 断言,用于验证代码行为是否符合预期。
}
}