simple-starter-macro

📅 2026/8/19 10:49:12
simple-starter-macro
simple-starter-macrosimple-starter-macro提供 simple-starter 框架的全部过程宏负责将声明式注解展开为组件注册、依赖注入、路由挂载、安全资源收集等底层代码。注意本 crate 是纯过程宏 crate用户无需直接依赖它——核心宏由simple-starter-core重导出Web 宏由simple-starter-web重导出安全宏由simple-starter-security重导出。仅在编写插件模块需要同时使用三个模块的宏时可能需要直接依赖。一、基本原理1. inventory 编译期静态收集Rust 没有运行时反射。宏的核心思路是把声明式注解展开为静态注册元数据交给inventory编译期收集。#[component(name userService)]structUserService{/* ... */}展开为示意structUserService{/* ... */}// 构造元数据依赖列表 构造闭包 生命周期闭包::simple_starter_core::submit!{::simple_starter_core::ComponentProcessorFactory{dependencies:[],trait_dependencies:[],type_dependencies:[],primary_dependencies:[],name:userService,condition:None,constructor:||{letwrapper::simple_starter_core::ComponentWrapper::UserService::new(/* create_fn: 组装字段注入并构造实例 */,/* init_fn: 调用 init_method */,/* destroy_fn: 调用 destroy_method */,);Box::new(wrapper)}}}应用启动时遍历 inventory 收集到的全部ComponentProcessorFactory完成注册、条件过滤、拓扑排序与创建。2. 绝对路径引用运行时宏展开代码通过绝对路径引用运行时与兄弟模块::simple_starter_core::...、::simple_starter_web::...、::simple_starter_security::...与用户的use导入无关任何命名空间下展开都能正确解析。3. 条件惰性闭包condition参数接受任意表达式宏将其包进惰性闭包Some(|| expr)在注册期求值一次不受 static 初始化上下文限制#[component(condition simple_starter_core::ComponentCondition::on_missing_trait::dyn CacheService())]pubstructDefaultCacheService;二、导出宏用法核心宏由 simple-starter-core 重导出1.#[component]—— 结构体组件标记结构体为组件纳入生命周期管理。支持参数参数说明name组件名默认用结构体短名init_method初始化方法名所有组件创建完成后按序调用。对应签名async fn init(self)以共享引用调用实例所有权仍在仓库仅能读取/借用自身destroy_method销毁方法名退出时按创建逆序调用。对应签名async fn destroy(self)以「有所有权」的实例调用可消费字段、取出内部资源condition注册条件表达式不满足则不注册#[component(name databaseComponent, init_method init, destroy_method disconnect)]structDatabaseComponent{url:String,// 非注入字段用 Default::default() 填充#[inject]cache:ArcdynCacheService,// 注入字段见 #[inject]}implDatabaseComponent{asyncfninit(self)-anyhow::Result(){Ok(())}asyncfndisconnect(self)-anyhow::Result(){Ok(())}}注意两个方法拿到组件的形式不同init以self共享引用调用实例所有权仍在仓库destroy以self所有权调用实例已从仓库移出可取出内部资源如归还连接池、写回文件。2.#[provider]—— 函数工厂把函数注册为组件工厂适用于第三方库类型或需要复杂初始化的对象。函数参数自动按类型注入规则同#[inject]返回类型自动剥离Result外层即组件类型。#[provider(destroy_method db_destroy)]asyncfndb_factory(cfg:ArcDbConfig)-ResultDatabaseConnection,DbErr{letdbDatabase::connect(cfg.url).await?;Ok(db)}asyncfndb_destroy(db:DatabaseConnection)-anyhow::Result(){Ok(())}3.#[primary]—— 首要实例标记与#[provider]一起标注在同一函数上声明该返回类型的首要实例当按类型获取get_primary_component::T()/#[inject_primary]时优先返回它。必须显式指定实例名且与#[provider]注册名一致。// 两个同类型实例mainDb 是按类型获取时的首要实例#[provider(name mainDb)]#[primary(name mainDb)]asyncfncreate_main_db()-anyhow::ResultDatabase{/* ... */}#[provider(name backupDb)]asyncfncreate_backup_db()-anyhow::ResultDatabase{/* ... */}#[component]structUserService{#[inject_primary]db:ArcDatabase,// 注入 mainDb#[inject(name backupDb)]backup:ArcDatabase,// 按名注入}4.#[configuration]—— 配置组件将结构体注册为配置组件启动时从全局配置TOML按prefix反序列化要求结构体实现serde::Deserialize。单参数简写#[configuration(server.http)]完整写法支持name与condition。#[derive(serde::Deserialize)]#[configuration(database)]structDbConfig{url:String}5.#[inject]—— 依赖注入标记作用于组件字段或 provider 参数。支持形式形式语义#[inject]按类型注入#[inject(name)]/#[inject(name name)]按名称注入配合类型形态ArcT具体类型、Arcdyn Traittrait 唯一实现 / 按名称指定实现、VecArcdyn Trait全部实现。#[component]structParserController{#[inject]json:ArcdynFileParser,// 唯一实现#[inject(name CsvFileParser)]csv:ArcdynFileParser,// 指定实现#[inject]all:VecArcdynFileParser,// 全部实现}6.#[inject_primary]—— primary 实例注入与#[inject]互斥单独使用即隐含注入语义。仅限具体类型ArcT注入该类型的 primary 实例。7.#[injectable]—— trait 实现注册作用于impl Trait for Type块注册 trait → 实现映射trait_type_idimpl_type_id 类型擦除 accessor记录 coercion 瞬间的真实 vtable供 trait 还原。#[injectable]implFileParserforJsonParser{fnparse(self,content:str)-anyhow::ResultVecString{/* ... */}}8.#[cron_job]—— 声明式定时任务作用于async fn用函数名注册任务#[cron_job(*/5 * * * * *)]// 每 5 秒执行asyncfnheartbeat_task(){tracing::info!(心跳检查);}9.#[event_listener]—— 事件监听器作用于 impl 块注册事件监听器详见 core README 事件系统#[component]structLoginListener;#[event_listener]#[async_trait::async_trait]implEventListenerUserLoginEventforLoginListener{asyncfnon_event(self,event:UserLoginEvent)-anyhow::Result(){Ok(())}}Web 宏由 simple-starter-web 重导出宏作用详见#[get]/#[post]/#[put]/#[delete]自由函数路由注册path/state参数web README#[rest_controller]impl 块级 REST 控制器基础路径 方法批量注册web README#[get_mapping]/#[post_mapping]/#[put_mapping]/#[delete_mapping]控制器方法级路由标记web README#[json_response]将 handler 返回值自动包装为axum::JsonTweb README安全宏由 simple-starter-security 重导出宏作用详见#[security]自由函数安全资源注册配合路由宏security README#[security_controller]impl 块级安全模块标记配合#[rest_controller]security README#[security_resource]方法级资源标记显式标记才注册security README三、组合使用示例以下示例串联多个宏实现一个 Controllerusesimple_starter_core::{component,inject};usesimple_starter_security::{security_controller,security_resource};usesimple_starter_web::{post_mapping,rest_controller,json_response_wrap,JsonResponse};usesimple_starter_web::axum::extract;usestd::sync::Arc;#[component]pubstructStudentController{#[inject]student_service:ArcStudentService,}#[security_controller]#[rest_controller(/student)]implStudentController{#[post_mapping(/add)]#[security_resource]pubasyncfnadd_student(self,extract::Json(student):extract::JsonStudentDto,)-JsonResponse{json_response_wrap!(function_name添加学生,{self.student_service.add(student).await?;Ok(())})}}展开后StudentController注册为组件并注入StudentService#[rest_controller]为方法生成 Axum 路由 handler 并注册RouteFactory#[security_controller]#[security_resource]注册ResourceEntry安全资源。