Axum官方推荐以下两种方式实现依赖注入,非必要不使用泛型,都通过AppState
存储依赖
添加crate
axum = "0.7.7"
rand = "0.8.5"
serde = { version = "1.0.127", features = ["derive"] }
tokio = { version = "1.40.0", features = ["full"] }
uuid = { version = "1.11.0", features = ["v4", "serde"] }
1、trait对象
- 优点:类型参数少,代码简单
- 缺点:不太灵活,只能使用对象安全的trait;少量的运行时开销(可以忽略不计)
use std::{ collections::HashMap, sync::{ Arc, Mutex } };
use axum::{ extract::{ Path, State }, http::StatusCode, routing::{ get, post }, Json, Router };
use serde::{ Deserialize, Serialize };
use tokio::net::TcpListener;
use uuid::{ self, Uuid };
#[tokio::main]
async fn main() {
// 实例依赖
let user_repo = InMemoryUserRepo::default();
// 使用动态分发的trait对象实现依赖注入
let using_dyn = Router::new()
.route("/users/{id}", get(get_user_dyn))
.route("/users", post(create_user_dyn))
.with_state(AppStateDyn {
// 注入到Appstate容器
user_repo: Arc::new(user_repo.clone()),
});
let app = Router::new().nest("/dyn", using_dyn);
let listener = TcpListener::bind("127.0.0.1:9000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
// 定义AppState
#[derive(Clone)]
struct AppStateDyn {
user_repo: Arc<dyn UserRepo>,
}
// User对象
#[derive(Debug, Serialize, Clone)]
struct User {
id: Uuid,
name: String,
}
// DTO对象
#[derive(Deserialize)]
struct UserParams {
name: String,
}
// 提取DTO对象并转换为User对象
async fn create_user_dyn(
State(state): State<AppStateDyn>,
Json(params): Json<UserParams>
) -> Json<User> {
let user = User {
id: Uuid::new_v4(),
name: params.name,
};
state.user_repo.save_user(&user);
Json(user)
}
// 提取路由上的uuid并根据id获取用户
async fn get_user_dyn(
State(state): State<AppStateDyn>,
Path(id): Path<Uuid>
) -> Result<Json<User>, StatusCode> {
match state.user_repo.get_user(id) {
Some(user) => Ok(Json(user)),
None => Err(StatusCode::NOT_FOUND),
}
}
// 定义trait,相当于接口,这里是定义公共的行为
trait UserRepo: Send + Sync {
fn get_user(&self, id: Uuid) -> Option<User>;
fn save_user(&self, user: &User);
}
// 模拟基础设施具体实现持久化方法
#[derive(Debug, Clone, Default)]
struct InMemoryUserRepo {
map: Arc<Mutex<HashMap<Uuid, User>>>,
}
impl UserRepo for InMemoryUserRepo {
fn get_user(&self, id: Uuid) -> Option<User> {
self.map.lock().unwrap().get(&id).cloned()
}
fn save_user(&self, user: &User) {
self.map.lock().unwrap().insert(user.id, user.clone());
}
}
2、使用泛型
- 优点:更灵活,所有trait都可以使用
- 缺点:额外的类型参数和trait会导致更复杂的代码
use std::{ collections::HashMap, sync::{ Arc, Mutex } };
use axum::{ extract::{ Path, State }, http::StatusCode, routing::{ get, post }, Json, Router };
use serde::{ Deserialize, Serialize };
use tokio::net::TcpListener;
use uuid::{ self, Uuid };
#[tokio::main]
async fn main() {
let user_repo = InMemoryUserRepo::default();
// 使用泛型实现依赖注入
let using_generic = Router::new()
.route("/users/{id}", get(get_user_generic::<InMemoryUserRepo>))
.route("/users", post(create_user_generic::<InMemoryUserRepo>))
// 使用泛型注入到AppSate中
.with_state(AppStateGeneric { user_repo });
let app = Router::new().nest("/generic", using_generic);
let listener = TcpListener::bind("127.0.0.1:9000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
// 定义AppSate
#[derive(Clone)]
struct AppStateGeneric<T> {
user_repo: T,
}
// User对象
#[derive(Debug, Serialize, Clone)]
struct User {
id: Uuid,
name: String,
}
// DTO对象
#[derive(Deserialize)]
struct UserParams {
name: String,
}
// 通过泛型实现UserRepo
// 这里多了where子句,相当于where子句约束了T必须实现UserRepo
async fn create_user_generic<T>(
State(state): State<AppStateGeneric<T>>,
Json(params): Json<UserParams>
) -> Json<User>
where T: UserRepo
{
let user = User {
id: Uuid::new_v4(),
name: params.name,
};
state.user_repo.save_user(&user);
Json(user)
}
// 提取路由上的uuid并根据id获取用户
// 这里多了where子句,相当于where子句约束了T必须实现UserRepo
async fn get_user_generic<T>(
State(state): State<AppStateGeneric<T>>,
Path(id): Path<Uuid>
) -> Result<Json<User>, StatusCode>
where T: UserRepo
{
match state.user_repo.get_user(id) {
Some(user) => Ok(Json(user)),
None => Err(StatusCode::NOT_FOUND),
}
}
// 定义trait,相当于接口,这里是定义公共的行为
trait UserRepo: Send + Sync {
fn get_user(&self, id: Uuid) -> Option<User>;
fn save_user(&self, user: &User);
}
// 模拟基础设施具体实现持久化方法
#[derive(Debug, Clone, Default)]
struct InMemoryUserRepo {
map: Arc<Mutex<HashMap<Uuid, User>>>,
}
impl UserRepo for InMemoryUserRepo {
fn get_user(&self, id: Uuid) -> Option<User> {
self.map.lock().unwrap().get(&id).cloned()
}
fn save_user(&self, user: &User) {
self.map.lock().unwrap().insert(user.id, user.clone());
}
}
复杂依赖的情况需要在程序启动时初始化依赖,手动进行注入
依赖注入要求
只有对象安全的trait
能被注入
对象安全的trait条件
一个 trait
要成为对象安全的(能够被转换为 trait
对象),需要满足以下几个条件:
-
- 所有
Super trait
必须是对象安全的
- 所有
-
Super trait
不能是Sized trait
-
- 不能有任何关联常量
-
- 所有关联函数要么必须能从
trait
对象进行调用,要么必须明确声明为不可调度
- 所有关联函数要么必须能从
可调度的函数必须满足以下条件:
- 4.1. 方法不能有泛型参数(除了
Self
类型相关的泛型) :如果 trait 中的方法有额外的泛型参数,编译器就没办法确定在运行时具体应该使用哪种类型来替换这些泛型参数,从而无法构建虚函数表(vtable
)来支持动态调度(这是trait
对象实现多态性的关键机制) - 4.2. 方法不能有
Self
类型作为返回值:因为当把trait
作为对象使用时,编译器不知道具体的实现类型是什么,也就没办法正确返回具体的Self
类型的值了 - 4.3. 方法的第一个参数不能是
Self
类型(除了关联函数中以&self
、&mut self
形式的接收者参数) :类似地,第一个参数如果是Self
,在运行时通过 trait 对象调用方法时,无法确定具体的Self
类型实例,导致无法正确传递这个参数 - 4.4. 函数参数接收者必须是以下类型之一
&Self(即 &self)
&mut Self(即 &mut self)
Box<Self>
Rc<Self>
Arc<Self>
Pin<P>
(其中 P 是上述类型之一)
- 4.5. 函数不能有不透明的返回类型:
- 4.5.1. 不能是一个
async fn
(其具有隐藏类型的Future
) - 4.5.2. 不能有返回位置类型为
impl Trait
的情况(例如fn example(&self) -> impl Trait
) - 4.5.3. 不能有约束(接收者类型为
Self
(即self
)意味着隐含此要求)
- 4.5.1. 不能是一个
明确不可调度的函数:
- 4.6. 要有一个约束(接收者类型为
Self
(即self
)意味着隐含此要求)
可调度函数与不可调度函数
- 可调度函数可以通过
trait
对象调用,适合多态调用,可调度函数会构建虚表(vtable
),在运行时通过trait
对象动态选择类型对应的函数实现版本 - 不可调度函数只能通过具体类型调用,服务于类型自身,在编译时根据函数调用的具体类型进行静态的类型检查和代码生成
举例:
- 所有
Super trait
必须是对象安全的,DerivedTrait
依赖于BaseTrait
(BaseTrait
就是DerivedTrait
的Super trait
),只有当BaseTrait
满足对象安全条件时,DerivedTrait
才有可能成为对象安全的trait
,进而可以像在use_trait_object
函数中那样被转换为trait
对象使用
// super trait
trait BaseTrait {
fn base_method(&self);
}
// BaseTrait是对象安全的,现在定义一个继承自BaseTrait的新trait也是对象安全的
trait DerivedTrait: BaseTrait {
fn derived_method(&self);
}
// 实现BaseTrait
struct MyStruct;
impl BaseTrait for MyStruct {
fn base_method(&self) {
println!("Base method called");
}
}
// 实现DerivedTrait
impl DerivedTrait for MyStruct {
fn derived_method(&self) {
println!("Derived method called");
}
}
fn use_trait_object() {
let obj: &dyn DerivedTrait = &MyStruct;
obj.base_method();
obj.derived_method();
}
Super trait
不能是Sized trait
- 错误示例中,由于
SizedTrait
要求实现它的类型必须是Sized
(也就是具有固定大小,这不符合trait
对象的要求,因为trait
对象本身大小在编译时不确定),所以不能将其转换为trait
对象
// 错误示例,将Sized作为Super trait(无法编译通过)
trait SizedTrait: Sized {
fn sized_method(&self);
}
// 正确示例,不把Sized作为约束,这是一个安全的trait
trait MyNormalTrait {
fn normal_method(&self);
}
struct AnotherStruct;
impl MyNormalTrait for AnotherStruct {
fn normal_method(&self) {
println!("Normal method called");
}
}
fn use_normal_trait_object() {
let obj: &dyn MyNormalTrait = &AnotherStruct;
obj.normal_method();
}
- 不能有任何关联常量
- 错误示例中,
TraitWithConstant
定义了关联常量MY_CONST
,这使它不符合对象安全的要求,不能被转换为对象安全的trait
对象
// 错误示例,包含关联常量的trait(不符合对象安全条件)
trait TraitWithConstant {
// 关联常量
const MY_CONST: i32 = 5;
fn method(&self);
}
// 正确示例,没有关联常量的trait
trait TraitWithoutConstant {
fn another_method(&self);
}
struct SomeStruct;
impl TraitWithoutConstant for SomeStruct {
fn another_method(&self) {
println!("Another method called");
}
}
fn use_trait_without_constant_object() {
let obj: &dyn TraitWithoutConstant = &SomeStruct;
obj.another_method();
}
- 所有关联函数要么必须能从
trait
对象进行调用,要么必须明确声明为不可调度
可调度的函数:
4.1. 方法不能有泛型参数(除了 Self
类型相关的泛型)
- 错误示例中,
BadTraitWithGenericParam
的method
函数有额外的泛型参数T
,编译器无法确定运行时T
的具体类型来构建虚函数表实现动态调度,所以不能作为trait
对象使用
// 错误示例,方法有额外泛型参数T(不符合对象安全)
trait BadTraitWithGenericParam {
fn method<T>(&self, value: T);
}
// 正确示例,没有额外泛型参数的方法(符合对象安全)
trait GoodTrait {
fn good_method(&self);
}
struct GoodStruct;
impl GoodTrait for GoodStruct {
fn good_method(&self) {
println!("Good method called");
}
}
fn use_good_trait_object() {
let obj: &dyn GoodTrait = &GoodStruct;
obj.good_method();
}
4.2. 方法不能有 Self
类型作为返回值
- 错误示例中,
BadTraitWithSelfReturn
的return_self
方法返回Self
类型,当作为trait
对象使用时,编译器不知道具体返回的实现类型是什么,所以不符合对象安全要求
// 错误示例,方法返回Self类型(不符合对象安全)
trait BadTraitWithSelfReturn {
fn return_self(&self) -> Self;
}
// 正确示例,不返回Self类型的方法(符合对象安全)
trait CorrectTrait {
fn correct_method(&self) -> i32;
}
struct CorrectStruct;
impl CorrectTrait for CorrectStruct {
fn correct_method(&self) -> i32 {
42
}
}
fn use_correct_trait_object() {
let obj: &dyn CorrectTrait = &CorrectStruct;
let result = obj.correct_method();
println!("Result: {}", result);
}
4.3. 方法的第一个参数不能是 Self
类型(除了关联函数中以 &self
、&mut self
形式的接收者参数)
- 错误示例中,
BadTraitWithSelfAsFirstParam
的bad_method
第一个参数是self
(即Self
类型),这在作为trait
对象调用方法时,无法确定具体的Self
类型实例来传递参数,不符合对象安全要求 - 正确示例中,
NiceTrait
的nice_method
以&self
作为接收者参数,符合条件,可以作为trait
对象使用
// 错误示例,方法第一个参数是Self(不符合对象安全)
trait BadTraitWithSelfAsFirstParam {
fn bad_method(self, value: i32);
}
// 正确示例,符合要求的方法参数形式(符合对象安全)
trait NiceTrait {
fn nice_method(&self, value: i32);
}
struct NiceStruct;
impl NiceTrait for NiceStruct {
fn nice_method(&self, value: i32) {
println!("Nice method called with value: {}", value);
}
}
fn use_nice_trait_object() {
let obj: &dyn NiceTrait = &NiceStruct;
obj.nice_method(10);
}
4.4. 可调度的函数参数接收者必须是以下类型之一:&Self(即 &self)
、&mut Self(即 &mut self)
、Box<Self>
、Rc<Self>
、Arc<Self>
、Pin<P>
(其中 P
是上述类型之一)
- 错误示例中,
BadTraitWithWrongReceiver
的wrong_receiver
方法接收者类型是Vec<Self>
,不在允许的接收者类型范围内,所以不符合对象安全条件
// 错误示例,不符合接收者类型要求(不符合对象安全)
trait BadTraitWithWrongReceiver {
fn wrong_receiver(self: Vec<Self>);
}
// 正确示例,符合接收者类型要求
trait GoodReceiverTrait {
fn good_receiver(&self);
}
struct GoodReceiverStruct;
impl GoodReceiverTrait for GoodReceiverStruct {
fn good_receiver(&self) {
println!("Good receiver method called");
}
}
fn use_good_receiver_trait_object() {
let obj: &dyn GoodReceiverTrait = &GoodReceiverStruct;
obj.good_receiver();
}
4.5.1. 可调度的函数不能有不透明的返回类型
- 不能是一个
async fn
(其具有隐藏类型的Future
) - 错误示例中,
BadAsyncTrait
的async_method
是异步函数,其返回类型涉及隐藏的Future
类型,不符合对象安全要求 - 正确示例中,
GoodSyncTrait
的sync_method
是普通同步函数,满足条件
// 错误示例,方法是async fn(不符合对象安全)
trait BadAsyncTrait {
async fn async_method(&self);
}
// 正确示例,普通的非async fn方法
trait GoodSyncTrait {
fn sync_method(&self);
}
struct GoodSyncStruct;
impl GoodSyncTrait for GoodSyncStruct {
fn sync_method(&self) {
println!("Sync method called");
}
}
fn use_good_sync_trait_object() {
let obj: &dyn GoodSyncTrait = &GoodSyncStruct;
obj.sync_method();
}
4.5.2. 不能有返回位置类型为 impl Trait
的情况(例如 fn example(&self) -> impl Trait
)
- 错误示例中,
BadTraitWithImplTraitReturn
的impl_trait_return
方法返回impl Trait
,这是不透明返回类型,编译器不知道具体返回什么类型,不符合对象安全要求 - 正确示例中,
GoodReturnTypeTrait
的good_return_type
返回具体的i32
类型,满足条件
// 错误示例,有返回位置类型为impl Trait的方法(不符合对象安全)
trait BadTraitWithImplTraitReturn {
fn impl_trait_return(&self) -> impl Trait;
}
// 正确示例,返回具体类型的方法
trait GoodReturnTypeTrait {
fn good_return_type(&self) -> i32;
}
struct GoodReturnTypeStruct;
impl GoodReturnTypeTrait for GoodReturnTypeStruct {
fn good_return_type(&self) -> i32 {
10
}
}
fn use_good_return_type_trait_object() {
let obj: &dyn GoodReturnTypeTrait = &GoodReturnTypeStruct;
let result = obj.good_return_type();
println!("Result: {}", result);
}
4.5.3. 不能有约束(接收者类型为 Self
(即 self
)意味着隐含此要求)
- 错误示例中,
BadTraitWithSelfBound
的self_bound_method
有Self: Sized
的约束,不符合对象安全要求,不能作为trait
对象使用
// 错误示例,方法有Self相关约束(不符合对象安全)
trait BadTraitWithSelfBound {
fn self_bound_method(&self) where Self: Sized;
}
// 正确示例,没有Self相关约束的方法
trait GoodUnboundTrait {
fn good_unbound_method(&self);
}
struct GoodUnboundStruct;
impl GoodUnboundTrait for GoodUnboundStruct {
fn good_unbound_method(&self) {
println!("Good unbound method called");
}
}
fn use_good_unbound_trait_object() {
let obj: &dyn GoodUnboundTrait = &GoodUnboundStruct;
obj.good_unbound_method();
}
不可调度的函数:
4.6. 要有一个约束(接收者类型为 Self
(即 self
)隐含此要求)
trait NonDispatchableTrait {
fn non_dispatchable_method(self) where Self: Sized;
}
struct MyNonDispatchableStruct;
impl NonDispatchableTrait for MyNonDispatchableStruct {
fn non_dispatchable_method(self) {
println!("Non-dispatchable method called");
}
}
// 注意,这里不能像对可调度函数那样将其转换为trait对象使用,它只能通过具体类型来调用相应方法
// 比如这样调用:
let my_struct = MyNonDispatchableStruct;
my_struct.non_dispatchable_method();
生产举例
错误示例
以下trait
抽象了底层的实现,但async
返回Future
不符合对象安全
AppResult
是自定义的结果,定义为pub type AppResult<T = ()> = std::result::Result<T, AppError>;
是对标准库和自定义错误AppError
的类型别名你可以直接使用标准库的
Result<T,E>
,正确代码示例放在文章最后
pub trait CustomerRepository: Send + Sync {
async fn find_all(&self) -> AppResult<Vec<Customer>>;
async fn find_by_email(&self, email: &str) -> AppResult<Option<Customer>>;
async fn save(&self, customer: Customer) -> AppResult<()>;
async fn find_by_id(&self, id: CustomerId) -> AppResult<Option<Customer>>;
async fn send_email(&self, email: &str) -> AppResult<()>;
async fn find_code_by_email(&self, email: &str) -> AppResult<Option<String>>;
}
实现trait
,以下方法使用了async
返回的是Future
,不是可调度的函数,违反了4.5. 函数不能有不透明的返回类型:
- 4.5.1. 不能是一个
async fn
(其具有隐藏类型的Future
)
pub struct CustomerRepositoryImpl {
db: Arc<DatabaseClient>,
}
// 注入数据库连接
impl CustomerRepositoryImpl {
pub fn new(db: Arc<DatabaseClient>) -> Self {
Self {
db,
}
}
}
// 这里使用了async 反回future,无法被注入
impl CustomerRepository for CustomerRepositoryImpl {
async fn find_all(&self) -> Result<Vec<Customer>, shared::error::InfraError> {
todo!();
}
async fn find_by_email(
&self,
email: String
) -> Result<Option<Customer>, shared::error::InfraError> {
todo!();
}
async fn save(&self, customer: Customer) -> Result<(), shared::error::InfraError> {
// 转 po
let active_model = ActiveModel {
user_id: Set(customer.user_id),
username: Set(customer.username),
email: Set(customer.email),
password: Set(customer.password),
avatar: Set(customer.avatar),
..Default::default()
};
// 插入
let _ = User::insert(active_model).exec(&*self.db).await?;
Ok(())
}
async fn find_by_id(&self, id: CustomerId) -> Result<Option<Customer>, InfraError> {
todo!()
}
async fn send_email(&self, email: String) -> Result<(), InfraError> {
// 生成验证码
let code = generate_random_code();
// 发送到redis
REDIS.set(&email, &code, Duration::from_secs(60)).await?;
// 发送到邮箱
let email = Email::new(
CONFIG.email.username.clone(),
CONFIG.email.username.clone(),
email.to_string(),
"锈化动力商城验证码".to_string(),
format!("您的验证码是:{}", code),
);
EMAIL.send_email(&email).await?;
Ok(())
}
async fn verify_code_send(&self, customer: Customer) -> Result<(), InfraError> {
todo!()
}
}
正确示例一:使用对象安全的trait
以下是一个对象安全的仓储trait
pub trait CustomerRepository: Send + Sync {
fn find_all(&self) -> AppResult<Vec<Customer>>;
fn find_by_email(&self, email: &str) -> AppResult<Option<Customer>>;
fn save(&self, customer: Customer) -> AppResult<()>;
fn find_by_id(&self, id: CustomerId) -> AppResult<Option<Customer>>;
fn send_email(&self, email: &str) -> AppResult<()>;
fn find_code_by_email(&self, email: &str) -> AppResult<Option<String>>;
}
实现仓储trait
静态分发:当你在代码中调用这个结构体实现的方法(如果后续为它实现了如 CustomerRepository
等相关 trait
的方法)时,编译器在编译阶段就可以明确知道具体要调用的是 CustomerRepositoryImpl
这个类型所实现的对应方法,因为类型是确定的
pub struct CustomerRepositoryImpl {
db: Arc<DatabaseClient>,
redis: Arc<RedisClient>,
}
/// 静态分发
impl CustomerRepositoryImpl {
pub fn new(db: Arc<DatabaseClient>, redis: Arc<RedisClient>) -> Self {
// 注入数据库、redis
Self {
db,
redis,
}
}
}
impl CustomerRepository for CustomerRepositoryImpl {
fn find_all(&self) -> AppResult<Vec<Customer>> {
todo!();
}
fn save(&self, customer: Customer) -> AppResult {
todo!();
}
fn find_by_id(&self, _id: CustomerId) -> AppResult<Option<Customer>> {
todo!()
}
fn send_email(&self, email: &str) -> AppResult {
todo!();
}
fn find_code_by_email(&self, email: &str) -> AppResult<Option<String>> {
todo!();
// println!("email:{}", email);
// self.redis.get(email).await.map_err(|_| InfraError::OtherError("验证码已过期".to_string()))
}
fn find_by_email(&self, email: &str) -> AppResult<Option<Customer>> {
todo!()
}
}
再抽象一个领域服务的trait
,这里的trait
也是对象安全的
pub trait CustomerService: Send + Sync {
// 注册用户
fn sign_up(&self, customer: Customer) -> AppResult<bool>;
}
实现领域服务trait
动态派发:编译器无法知道具体要调用的是 CustomerRepositoryImpl 这个类型所实现的对应方法,因为类型是不确定的;当一个类型实现trait时,编译器会生成一个虚表(vtable)并用一个指针指向这个虚表,其中虚表包含了该类型所实现的所有方法的函数指针;Arc包含了这两个指针,一个指向虚表的指针和一个指向数据的指针,当调用一个方法时,编译器会通过trait指向的虚表中的函数指针来确定具体要调用的方法
/// 动态分发
pub struct CustomerServiceImpl {
// 注入仓储依赖
customer_repository: Arc<dyn CustomerRepository>,
}
impl CustomerServiceImpl {
pub fn new(customer_repository: Arc<dyn CustomerRepository>) -> Self {
Self {
customer_repository,
}
}
}
// 这里是领域能力
impl CustomerService for CustomerServiceImpl {
// 校验用户是否已经存在
fn sign_up(&self, _customer: Customer) -> AppResult<bool> {
todo!();
}
}
依赖注入
在AppState
中使用Arc
包裹后clone()
本质上是增加引用计数
// 使用Arc来共享数据,避免数据的复制和所有权的转移
#[derive(Clone)]
pub struct AppState {
pub config: Arc<AppConfig>,
pub redis: Arc<RedisClient>,
pub db: Arc<DatabaseClient>,
pub email: Arc<EmailClient>,
// DI
pub customer_repository: Arc<dyn CustomerRepository>,
pub customer_service: Arc<dyn CustomerService>,
}
impl AppState {
pub async fn new(config: AppConfig) -> AppResult<Self> {
let redis = Arc::new(RedisClient::build_from_config(&config)?);
let db = Arc::new(DatabaseClient::build_from_config(&config).await?);
let email = Arc::new(EmailClient::build_from_config(&config)?);
// DI ,这里使用Arc来共享数据,避免数据的复制和所有权的转移,clone()本质上是增加引用计数
let customer_repository = Arc::new(CustomerRepositoryImpl::new(db.clone(), redis.clone()));
let customer_service = Arc::new(CustomerServiceImpl::new(customer_repository.clone()));
Ok(Self {
config: Arc::new(config),
db,
redis,
email,
customer_repository,
customer_service,
})
}
}
正确示例二:使用#[async_trait]
第一中方式在使用Sea-ORM时不好用,因为Sea-ORM查询的结果返回的是Future,获取其中的数据必须使用await
,而await
只能在async fn
中使用
使用#[async_trait]
以dyn Trait
的形式访问,即令其Object Safe
,最终编译后的trait
是一个Pin<Box<dyn Future>>
的,是动态调度的trait
,函数定义async
返回Future
本身不是对象安全的,标记后是对象安全的,但是有额外的开销
个人认为这个开销是可以接受的,Java依赖注入也有运行时开销,但Java有GC的开销,Rust没有GC
#[async_trait]
pub trait CustomerRepository: Send + Sync {
async fn find_page(&self, param: PageParams) -> AppResult<Vec<User>>;
async fn find_all(&self) -> AppResult<Vec<Customer>>;
async fn find_by_email(
&self,
tx: &DatabaseTransaction,
email: &str
) -> AppResult<Option<Customer>>;
async fn save(&self, tx: &DatabaseTransaction, customer: Customer) -> AppResult<()>;
async fn find_by_id(
&self,
tx: &DatabaseTransaction,
id: CustomerId
) -> AppResult<Option<Customer>>;
async fn send_email(&self, tx: &DatabaseTransaction, email: &str) -> AppResult<()>;
async fn find_code_by_email(
&self,
tx: &DatabaseTransaction,
email: &str
) -> AppResult<Option<String>>;
async fn check_unique_by_username(
&self,
tx: &DatabaseTransaction,
username: &str
) -> AppResult<bool>;
async fn check_unique_by_email(&self, tx: &DatabaseTransaction, email: &str) -> AppResult<bool>;
}
这种方式实现trait时也要标注#[async_trait]
,否则返回值需要写成以下形式并且需要添加生命周期
core::pin::Pin<
Box<dyn ::core::future::Future<Output = AppResult<Vec<User>>> +
::core::marker::Send +
'async_trait>
注入方式不改变
使用,从AppState
中取出依赖使用
use std::sync::Arc;
pub struct CustomerUseCase {
state: Arc<AppState>,
}
impl CustomerUseCase {
pub fn new(state: Arc<AppState>) -> Self {
Self {
state,
}
}
pub async fn sign_up(&self, signup_command: SignUpCommand) -> AppResult<String> {
info!("Register a new user request: {signup_command:?}.");
// 开启事务
let tx = self.state.db.begin().await?;
self.state.customer_repository.check_unique_by_username(
&tx,
&signup_command.username.clone()
).await?;
self.state.customer_repository.check_unique_by_email(
&tx,
&signup_command.email.clone()
).await?;
// 转bo
let customer = CustomerBuilder::new()
.username(signup_command.username)
.email(signup_command.email)
.password(signup_command.password)
.build();
// 保存用户
let user_id = self.state.customer_repository.save(&tx, customer).await?;
// 提交事务
tx.commit().await?;
Ok(user_id)
}
pub async fn list(&self, param: PageParams) -> AppResult<Vec<User>> {
let users = self.state.customer_repository.find_page(param).await?;
Ok(users)
}
}
案例代码见customer
模块:https://github.com/VCCICCV/MGR