What is the EK9 equivalent of Spring @Autowired?

← Dependency Injection · Ref: Q324

EK9 replaces Spring's @Autowired with the injection suffix '!' on field declarations. Any field ending with '!' is an injection point that the compiler validates at compile time.

SPRING @AUTOWIRED VS EK9 '!'

In Spring, @Autowired marks a field for runtime injection:

  @Autowired private UserService userService;

In EK9, the '!' suffix marks a field for compile-time validated injection:

  userService as UserService!

The '!' suffix is shorter, more visible, and validated before any code runs.

COMPILE-TIME GUARANTEE

Spring discovers missing @Autowired bindings at runtime (NoSuchBeanDefinitionException, sometimes minutes into startup). EK9 discovers missing injection bindings during compilation. If your program compiles, every injection point is satisfied.

FIELD INJECTION ONLY

EK9 supports field injection (the '!' suffix). There is no constructor injection or method injection — fields with '!' are populated during the application's prepare phase before the program body executes.

WHY NO CONSTRUCTOR INJECTION

Spring advocates constructor injection for immutability and testability. EK9 achieves both through different mechanisms: compile-time validation ensures completeness, and the singleton lifecycle ensures stability. Constructor injection adds complexity without additional safety in EK9's model.

See Q227 for compile-time DI validation. See Q228 for registration ordering. See Q231 for program-application linking. See Q331 for testing injected components.

See Q667 for abstract injection requirement. See Q669 for injection reassignment.

Example

defines module qa.di.spring.autowired

  defines component

    UserRepository as abstract
      findUser() as abstract
        -> userId as String
        <- userName as String?

      default operator ?

    InMemoryUserRepository is UserRepository
      override findUser()
        -> userId as String
        <- userName as String: "User-" + userId

      default operator ?

    <?-
      UserService uses '!' to mark injection point.
      This is the EK9 equivalent of @Autowired.
    -?>
    UserService as abstract
      getUser() as abstract
        -> userId as String
        <- greeting as String?

      default operator ?

    DefaultUserService is UserService
      repo as UserRepository!

      override getUser()
        -> userId as String
        <- greeting <- String()
        userName <- repo.findUser(userId)
        greeting: "Hello, " + userName

      default operator ?

  defines application

    AutowiredEquivalentApp
      register InMemoryUserRepository() as UserRepository
      register DefaultUserService() as UserService

  defines program

    AutowiredDemo() with application of AutowiredEquivalentApp
      stdout <- Stdout()

      // === '!' SUFFIX IS THE EK9 EQUIVALENT OF @Autowired ===

      service as UserService!

      result <- service.getUser("42")
      stdout.println(result)

      stdout.println("Injection validated at compile time, not runtime")

Common mistakes

E08150 — Injection fields must declare abstract component types. The '!' suffix marks a field for injection, but the type must be the abstract contract, not the concrete implementation. See ek9 -h E08150 for details.

Incorrect:

repo as InMemoryUserRepository!

Correct:

repo as UserRepository!

E08210 — Removing the UserRepository registration leaves DefaultUserService's injection field 'repo as UserRepository!' unsatisfied. Every injection field must have a matching registration. See ek9 -h E08210 for details.

Incorrect:

register DefaultUserService() as UserService

Correct:

register InMemoryUserRepository() as UserRepository
      register DefaultUserService() as UserService
Other ways to ask this
  • How do I inject dependencies in EK9 like Spring @Autowired?
  • What replaces @Autowired in EK9?
  • How do I wire dependencies without @Autowired?

Coming from another language?

Java Spring: @Autowired on fields, constructors, or setters. Runtime injection via reflection. Missing beans throw NoSuchBeanDefinitionException at startup. Constructor injection recommended for immutability. Guice: @Inject annotation, similar to @Autowired. .NET: constructor injection via IServiceProvider. Python: no built-in DI. Go: no built-in DI. EK9: '!' suffix on field declarations, compile-time validation, zero runtime injection failures.

Keywords: exclamation, suffix, field, binding, autowired, inject, dependency, spring, wiring, migration