Standard library klyn.di Lifecycle

Dependency Injection

klyn.di discovers application components, resolves typed property dependencies, and controls initialization and destruction. The container remains explicit: the application creates, scans, uses, and closes its DIContext.

The Component Model
ElementRole
@ComponentDeclares a general container-managed class.
@ServiceDeclares an application-service component.
@AutowiredMarks a property that must receive a dependency.
@InitMethodMarks a zero-argument method invoked after injection.
@DestroyMethodMarks a zero-argument method invoked before destruction.
DIContextOwns registration, discovery, injection, and lifecycle.
Property injection is explicit

The current container injects properties annotated with @Autowired. Components must provide a zero-argument constructor. Constructor parameters and unannotated properties are not injected implicitly.

Define a Service Contract

Keep interfaces and public types in their own matching source files. In this example, the class-loader root contains the package inventory.services.

# inventory/services/StockService.kn
package inventory.services

public interface StockService:
    public available(productId as Int) as Int
# inventory/services/StockServiceImpl.kn
package inventory.services

import klyn.di

@Service(name="stockService")
public class StockServiceImpl implements StockService:

    public available(productId as Int) as Int:
        return 12
Inject a Dependency

The annotated property retains its static interface type. The context injects an assignable component; application code does not depend on the implementation class.

# inventory/services/InventoryReport.kn
package inventory.services

import klyn.di

@Component(name="inventoryReport")
public class InventoryReport:

    @Autowired
    public property stockService as StockService

    public printAvailability(productId as Int) as Void:
        print(this.stockService.available(productId))

Resolution first checks a registered component whose name matches the property name. If none exists, the context searches for exactly one component assignable to the property's declared type. No match or several matches is an error; the container never chooses an arbitrary implementation.

Create and Close the Context
import inventory.services
import klyn.di

context = DIContext(packages=["inventory.services"])
try:
    context.scanAnnotations()

    report = context.getComponent("inventoryReport") as InventoryReport
    report.printAvailability(42)
finally:
    context.close()

Package filters keep startup deterministic and avoid scanning unrelated application types. scanAnnotations() loads those packages, creates every discovered component, registers all instances, injects dependencies, then runs initialization callbacks.

Lifecycle Callbacks
package inventory.services

import klyn.di

@Service(name="catalogCache")
public class CatalogCache:

    private _ready as Boolean = false

    @InitMethod
    public start() as Void:
        this._ready = true

    @DestroyMethod
    public stop() as Void:
        this._ready = false

Initialization runs only after every discovered instance has been registered and dependencies have been injected. close() invokes destruction callbacks in reverse creation order and still attempts the remaining callbacks if one fails.

Programmatic Registration

Existing objects can be registered directly, and reflected types can be created through the context. These two operations deliberately have different ownership rules.

import inventory.services
import klyn.di

context = DIContext()
try:
    external = CatalogCache()
    context.registerComponent("external", external)

    cache = context.createComponent(CatalogCache.type, "cache") as CatalogCache
    assert context.getComponent("cache") is cache
finally:
    context.close()
  • registerComponent() stores a caller-owned instance; the context does not run its destroy callback.
  • createComponent() invokes the zero-argument constructor, injects properties, runs initialization, and owns destruction.
  • destroyComponent() destroys and unregisters one managed component by name.
Fail Fast on Invalid Graphs

Treat a failed context startup as an application configuration error. Missing dependencies raise ComponentNotFoundException; duplicate registration names, invalid lifecycle methods, and ambiguous typed dependencies also fail instead of silently producing a partial graph.

Do not use the container as a global map

Prefer constructor arguments for ordinary application values and explicit method parameters for transient data. Register long-lived services whose lifecycle and substitutability benefit from a container; do not hide every object lookup behind getComponent().