Dependency Injection (DI)
Core Concepts
In awe-axios, Dependency Injection (DI) is implemented through the @Inject decorator and an IoC (Inversion of Control) container, which decouples dependencies between components and automatically manages instance creation and injection. The following details the implementation and usage.
Basic Usage
The @Inject decorator supports multiple configuration methods to meet various dependency lookup scenarios:
String Expression Injection
Format: [moduleName.]alias (module name is optional, default module is __default__)
// Inject "userService" from the default module
@Inject('userService')
private userService: UserService;
// Inject "productApi" from the "api" module
@Inject('api.productApi')
private productService: ProductService;Configuration Object Injection
You can also use a configuration object for injection lookup. The configuration object structure is:
type GetInstanceConfig = {
/**
* Module name
*/
module?: string | symbol;
/**
* Alias
*/
alias?: string;
/**
* Constructor (class)
*/
ctor?: DecoratorClass;
/**
* Instance creation scope
*/
scope: InstanceScope | string;
/**
* Backup instances
*/
backups?: InjectBackups;
};Example:
// Lookup by class (most common, type-safe)
@Inject({ ctor: UserService })
private userService: UserService;
// Lookup by module + alias
@Inject({ module: 'api', alias: 'productApi' })
private productService: ProductService;When both ctor and alias are configured
If you configure both ctor and alias, awe-axios will only use module + ctor for the lookup and ignore alias.
About scope and backups
The scope configuration controls the creation strategy for dependency instances, with a default value of SINGLETON. The backups configuration specifies alternative instances. These will be explained in detail later.
Direct Constructor Injection
You can also pass the class directly as a parameter (equivalent to { ctor: Class }):
@Inject(UserService)
private userService: UserService;Instance Scopes
awe-axios provides multiple instance injection modes during dependency injection, including Singleton, Transient, Prototype, Shallow Clone, etc., to meet the needs of different scenarios. The creation strategy is controlled by the scope configuration, with a default value of SINGLETON:
| Scope | Description | Suitable Scenarios |
|---|---|---|
SINGLETON | Singleton pattern, globally unique instance | Utility classes, configuration services |
TRANSIENT | Transient pattern, creates a new instance each time | Stateful objects, request context |
PROTOTYPE | Prototype pattern, creates a new object based on the prototype instance | Scenarios requiring inheritance from the original instance's state |
SHALLOWCLONE | Shallow clone pattern, copies the top-level properties of the original instance | Quick copying of simple objects |
DEEPCLONE | Deep clone pattern, completely copies the original instance | Independent copies of complex objects |
Transient Scope
In Transient scope, a new instance is created each time it is injected. awe-axios uses Transient scope by default, as shown in the following example:
@Component()
class User {}
class UserService {
@Inject(User)
user1!: User;
@Inject({
module: '__default__',
alias: 'user',
scope: 'TRANSIENT',
})
user2!: User;
}
let userService = new UserService();
// Because Transient scope is used by default, the two injected user instances are different
console.log(userService.user1 === userService.user2); // falseSingleton Scope
In Singleton scope, the same instance is returned each time it is injected, as shown in the following example:
@Component()
class User {}
class UserService {
@Inject({
ctor: User,
scope: 'SINGLETON',
})
user1!: User;
@Inject({
module: '__default__',
alias: 'user',
scope: 'SINGLETON',
})
user2!: User;
}
let userService = new UserService();
// Because Singleton scope is used, the two injected user instances are the same
console.log(userService.user1 === userService.user2); // truePrototype Scope
In Prototype scope, a new instance is created each time, using the class instance as the prototype, as shown in the following example:
@Component()
class User {}
class UserService {
@Inject({
ctor: User,
scope: 'PROTOTYPE',
})
user1!: User;
}
let userService = new UserService();
// user1 has the User class instance as its prototype
console.log(userService.user1 instanceof User); // true
console.log(Object.getPrototypeOf(userService.user1)); // User {}Shallow Clone Scope
In Shallow Clone scope, a new instance is created each time, but only the top-level properties of the original instance are copied, as shown in the following example:
@Component()
class User {
obj: any = { a: 1 };
}
class UserService {
@Inject({
ctor: User,
scope: 'SINGLETON',
})
user1!: User;
@Inject({
ctor: User,
scope: 'SHALLOWCLONE',
})
user2!: User;
}
let userService = new UserService();
// Change property 'a' inside the 'obj' of user1
userService.user1.obj.a = 2;
// Because Shallow Clone is used, property 'a' inside the 'obj' of user2 also changes
console.log(userService.user2.obj.a); // 2Deep Clone Scope
In Deep Clone scope, a new instance is created each time, and all properties of the original instance are recursively copied, as shown in the following example:
@Component()
class User {
obj: any = { a: 1 };
}
class UserService {
@Inject({
ctor: User,
scope: 'SINGLETON',
})
user1!: User;
@Inject({
ctor: User,
scope: 'DEEPCLONE',
})
user2!: User;
}
let userService = new UserService();
// Change property 'a' inside the 'obj' of user1
userService.user1.obj.a = 2;
// Because Deep Clone is used, property 'a' inside the 'obj' of user2 remains unchanged
console.log(userService.user2.obj.a); // 1Dependency Backups
To ensure injection stability, awe-axios provides a dependency backup mechanism. When the primary instance lookup fails, alternative instances can be specified via the backups configuration. The backups configuration can be a constructor, an object, or an array of constructors and objects, as shown in the following example:
class User {
obj: any = { a: 1 };
}
class Student {}
class UserService {
@Inject({
alias: 'person',
backups: [User, Student],
// or
// backups: [new User(), new Student()]
// or
// backups: User
})
user1!: User;
}
let userService = new UserService();
// No 'person' instance exists, so the backup User class is used as the instance: User { obj: { a: 1 } }
console.log(userService.user1);Backup Order
When there are multiple backup instances, awe-axios will try them in the order of registration until an instance is found. If none are found, undefined is injected.