The ApplicationContext manages a collection of component definitions. Components are defined using helper classes that set the scope:
import { Context, Singleton, Prototype, Service } from '@alt-javascript/cdi';
const context = new Context([
new Singleton(MyService), // one shared instance
new Prototype(RequestHandler), // new instance per get()
new Service(UserRepository), // alias for Singleton (semantic)
]);Or as plain objects for full control:
const context = new Context([
{
Reference: MyService,
name: 'myService',
scope: 'singleton',
constructorArgs: ['dependency1'],
dependsOn: ['otherBean'],
primary: true,
},
]);| Scope | Behavior | Helper Class |
|---|---|---|
singleton |
One instance per context, shared | Singleton, Service |
prototype |
New instance on every get() call |
Prototype, Transient |
The container inspects each singleton's instance properties after creation. Any property that is null and matches a registered component name gets injected:
class OrderService {
constructor() {
this.orderRepository = null; // matched to 'orderRepository' component
this.emailService = null; // matched to 'emailService' component
this.internalState = []; // not null → not autowired
}
}This is the primary wiring mechanism — Spring developers will recognise it as analogous to @Autowired on fields.
Set a property to 'Autowired' to force autowiring even if the value isn't null:
class MyComponent {
constructor() {
this.service = 'Autowired'; // explicitly request injection
}
}Pass dependencies via constructor arguments by name:
{
Reference: OrderService,
name: 'orderService',
constructorArgs: ['orderRepository', 'emailService'],
}String arguments that match component names are resolved from the context. Non-matching strings and other types are passed through as-is. Circular constructor dependencies are detected and throw a clear error.
Bind config values to component properties using the Property helper:
import { Property } from '@alt-javascript/cdi';
class DatabaseConnection {
constructor() {
this.host = null;
this.port = null;
}
}
const context = new Context([
new Singleton(DatabaseConnection),
new Property({ name: 'databaseConnection', property: 'host', config: 'db.host' }),
new Property({ name: 'databaseConnection', property: 'port', config: 'db.port', value: 5432 }),
]);The config field is a dot-notation path into the config object. The value field provides a fallback.
Activate components only for specific runtime profiles:
const context = new Context([
{ Reference: MockEmailService, name: 'emailService', profiles: ['test'] },
{ Reference: SmtpEmailService, name: 'emailService', profiles: ['production'] },
]);
const appCtx = new ApplicationContext({
contexts: [context],
config,
profiles: ['production'],
});When multiple components share the same name, mark one as primary: true to win:
new Context([
{ Reference: DefaultCache, name: 'cache' },
{ Reference: RedisCache, name: 'cache', primary: true }, // wins
]);Two primaries with the same name still throw. Two non-primaries with the same name still throw.
Control initialization order explicitly:
new Context([
{ Reference: CacheWarmer, name: 'cacheWarmer', dependsOn: ['cache', 'database'] },
{ Reference: Cache, name: 'cache' },
{ Reference: Database, name: 'database' },
]);The container uses topological sort (Kahn's algorithm) to initialize singletons in dependency order. Circular dependsOn is detected and throws.