2

I find it odd that that I can't find this information, so please direct me to a creditable source if possible. This questions pertains only to Java.

In short, I want to know how dependency injections actually happens syntactically.

My understanding of dependency injection is the following:

public class Car {
    private Engine engine
    
    @Inject
    public Car(Engine engine) {
        this.engine = engine
    }
}

Is the equivalent of

public class Car {
    private Engine engine
    
    public Car(Engine engine) {
        this.engine = engine
    }
}

Where the keyword @Inject is syntactic sugar to let Java know that the dependency engine is to be injected. This way Car won't be responsible for creating engine and therefore have a hard dependency of Engine. However, no examples have shown me how to inject it. In short:

public MyClass {
    public static void main(String[] args) {
        ToyotaEngine toyotaEngine = new ToyotaEngine();
        HondaEngine hondaEngine = new HondaEngine();
        // ??? which one to inject?
        Car myCar = new Car(); // syntax?
    }
}

How do I actually trigger the injection? Simply call new Car() and Engine will be pass to the constructor for me? How does Java know which Engine to inject?

Everything I've googled pertains to how to use the @Inject annotation on the class but nothing about how to actually trigger it. This article describes a configuration that looks specific to Spring and doesn't explain much. And I'm not sure what Spring is.

bli00
  • 2,215
  • 2
  • 19
  • 46

2 Answers2

5

There is no "syntax" about it, and @Inject is not syntactic sugar. An annotation is a piece of metadata that gets recorded on an element (class, method, field, etc.), and then other software has a chance to inspect it. In the case of @Inject, some framework that you're using (Spring, CDI, Guice) looks for the annotation and, if present, executes some code that looks up and provides you with the dependency. (This is typically called a container because it contains a bunch of objects that can be looked up and injected for you. Among other things, Spring provides a DI container.)

The constructor (or setter) works entirely normally, and you can't just use new Car(). Instead, the framework, which has found an Engine somewhere, invokes new Car(engine) for you, passing in that object. If you're simply using new, then you have to provide your own values; this is very useful for tests, where you can pass in mocks or test data.

(This, by the way, is the reason that using constructor injection is nearly always the best choice; it prevents you from using new Car() when there are hidden dependencies, which wouldn't be initialized properly.)

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
  • So how do I invoke the `Car` class? I'm used to the paradigm where `main` is the entrance point that starts your code. And by this logic, somewhere down the stack after entering `main`, `new Car(engine)` will need to be called, meaning I'm manually invoking it again. If I'm not doing that, then how does my program know when I would like to invoke `Car`? – bli00 Feb 27 '21 at 06:43
  • @bli00 The dependency framework you are using will resolve the parameter values for you, assuming you use the dependency framework to request such an `Car` object. How to do that depends on the dependency framework you are using and the API it provides. – Progman Feb 27 '21 at 09:12
  • @bli00 As an example of what Progman said, in Spring Boot, your `main` calls `SpringApplication.run(MyMainClass.class)`, and then Spring takes over, looking at your configuration settings to select what beans are needed (e.g., Web controllers or message listeners) and instantiating everything as necessary. Other setups, [especially for mobile](https://dagger.dev/), need you to be more explicit about _what_ is included but take care of the actual wiring for you. – chrylis -cautiouslyoptimistic- Feb 27 '21 at 16:09
  • @bli00 The [Getting Started guide for Spring Boot](https://spring.io/guides/gs/spring-boot/) shows a minimal example of how this works; the `main` method calls `SpringApplication.run`, but then Spring itself does the rest, using the `@RestController` annotation to decide that that class needs to be instantiated and added to the container. – chrylis -cautiouslyoptimistic- Feb 27 '21 at 16:11
  • That makes sense, but how does Spring know which value to use to resolve a parameter? In the example I gave in the post, how does Spring know which `Engine` to use? There's `ToyotaEngine` and there's `HondaEngine`. – bli00 Feb 27 '21 at 18:48
  • @bli00 If you fed this entire setup to Spring, you'd get an error message complaining about exactly that. In most cases, you only have one bean available of a given type; otherwise, you either have to specify, such as with `@Qualifier`, or you can inject a collection to get all of them (e.g., for a command interpreter). – chrylis -cautiouslyoptimistic- Feb 28 '21 at 02:40
  • Understood. So nothing needs to be specified if there's only one bean available. – bli00 Feb 28 '21 at 06:10
1

Maybe this article (https://www.objc.io/issues/11-android/dependency-injection-in-java/) can explain, how the concept DI works in general.

In order to use DI, you need to pick a DI framework. Each framework then provides a mechanism to do and trigger DI. Spring is a framework that uses DI, but it's also more than DI and designed to deal with Server-Client-based WebApps and Rest-Services, which makes it hard to single out the sole DI aspects.

Sorin
  • 977
  • 3
  • 12
  • I've seen this article. It doesn't explain how the framework know which `Engine` to pass in. Like the example above. Worst yet, if I have something like `@Inject String var`, how in the world would the framework know what string it is? I must inform the framework somehow right? – bli00 Feb 27 '21 at 08:13