There are different types of dependency injection the trivial implementation is a registry pattern aka. service-locator / service provider where for example you define certain keys and you can access them via get
registry.set('service-name', (param) => { return new Service(param); });
let bla = registry.get('service-name');
this helps you to have one place to reuse certain parts and replace them for mocking purposes;
because in your application you just take whatever is inside that registry and if the interface is the same you can access it.
that's the "service locator"/"registry-pattern" simply put .
there are multiple ways to implement such a thing for example using virtual variable names with a generic getter / setter / decorators. Decorators in ES6 / ES7 are the way to go i think.
@httpClient
class myClass {
constructor(){
this.httpClient.request('get','www.example.com');
}
}
The real DI is more complex and usually does not need an existing instance as the service pattern does. you just tell it what you need and annotate / decorate it
@DI('classname', options....)
@DI(MyClass myClass, param1, param2)
public void myFancyMethod(Classname class, MyClass myClass) {}
in the DI you can define multiple behaviours so for example you could use defined meta abstractions like yml files or xml to define building plans for those instances
or since in java all objects are on the heap you could use references from inside the framework.
The idea opposing to the service-locator is that the DI is more declarative. you just say what you want and where you want it. You don't pass an reference and resolve it with some deferred references.
tbh. I don't know how they implemented it in Angular :) but that's a rough concept of it.