A typical Angular application contains many modules, each module is a collection of many components (controllers, directives, services, factories, ...).
These modules depend on each other, and their components as well depends on other compoenents.
Angular solves this dependencies with its Dependency Injection Subsystem.

Responsibilities of DI subsystem

Angular DI subsystem is pervasive throughout Angular and it works with the Module registration process. Whenever we register a new module with its components, the DI will be the registration mechanism to do the following:
  1. Creating components 
  2. Resolving their dependencies 
  3. Connecting them with other components as requested

Angular DI style is Construction Injection

Assuming you (the reader) know already about DI as a design pattern and the DI frameworks, in other languages like Java or C# the DI frameworks has three flavors:
  1. Construction Injection 
  2. Property Injection 
  3. Setter Injection 
Angular DI is a construction injection style, where we pass dependencies as function arguments.
As an example, with this controller code , the controller is depending on : $scope, authService , and as you see we are passing them as arguments.
var myApp = angular.module('myApp',[]);
myApp.service('Auth', function() {
        this.userName = /* some function to return user name */;
    });
myApp.controller('GreetingController', ['$scope', 'Auth',  
    function($scope, Auth) {
        $scope.greeting = 'Hola ' + Auth.userName + '!';
}]);

How does it work?:

When an Angular application bootstrap, it will create one, and only one "Injector"
The Injector is a service locator design pattern that will register, find, and couple components together.
Angular uses angular.injector function to create the $injector passing all listed modules.
This is what Angular run (behind the scene) to generate the injector:
// create an injector
var $injector = angular.injector(['ng']);

// use the injector to kick off your application
// use the type inference to auto inject arguments, or use implicit injection.
$injector.invoke(function($rootScope, $compile, $document) {
  $compile($document)($rootScope);
  $rootScope.$digest();
 });
If, for any reason you want to access the injector programmatically , you can get it from angular.element. For example , if we are adding dynamic html using jQuery, and want to hook it with the rest of the angular app:
var $div = $('
     <div ng-controller="MyCtrl">
     {{content.label}}</div>'
  );
  $(document.body).append($div);
  angular.element(document).injector().invoke(function($compile) {
    var scope = angular.element($div).scope();
    $compile($div)(scope);
 });

How Injector resolve the dependencies?

Angular style of resolving dependencies uses IDs which are string values to identify components and modules.
These IDs are called "Dependency Annotation".
There are three types of annotations:
  1. Using the inline array annotation (preferred) 
  2. Using the $inject property annotation 
  3. Implicitly from the function parameter names

  1. Inline Array Annotation
  2. Every component as an ID, and when we register it we pass its dependency IDs as an array.
    Code worth 1000 words, and we here list the code to register a module and its controller.
    var someModule = angular.module('someModule', []);
     someModule.controller('MyController', ['$scope', 'greeter', function($scope, greeter) {
     // .....}]);
    The Annotations are the string values: '$scope', 'greeter'.
    And the IDs are 'someModule', 'MyController'.
    The Injector will register a module with the ID : 'someModule', and its controller with ID: 'MyController'.
    And it will look for other components with IDs: '$scope' and 'greeter' and inject them in the controller.
    This is the safest way to do annotation, the problem with it, is that you have to make sure that the annotation array is in sync with the function parameters, and they are in the exact order.
    Any out of sync will make your application go nuts.

  3. Using the $inject property annotation
  4. $inject property is a property added to an Angular service (or component) which is an array of the annotations.
    This is an example of it
    var MyController = function($scope, greeter) {
        //....
     };
     MyController.$inject = ['$scope', 'greeter'];
     someModule.controller('MyController', MyController);
    As you can see the syntax is cleaner with this approach, but we still have to make sure that the annotation array is in sync with the function parameters.

  5. Implicit Annotation
  6. Implicit annotation means the Injector will get the dependency that has in ID the same as the parameter name.
    So the code will be as simple as :
     var MyController = function($scope, greeter) {
         //....
     };
     someModule.controller('MyController', MyController);
    We don't have to keep track of the parameters to keep them sync with the annotation.
    This method is the easiest, except that if we are going to minify our code then it won't work, because minification will minify as well the parameter names.

  7. Using ng-annotate
  8. In order to get advantage of the simplicity of implicit annotation, the community came up with a solution for the minification problem.
    ng-annotate is a third party tool that will add annotations to your code.
    You just write your code like you write using the implicit annotation, which means you don't write any annotations.
    And you just mark-up your function which require injection with the "ngInject" directive prologue.
    Directive prologue (a well known example of it is "use strict") , are string values which should be written at the beginning of a file, or a function.
    We can re-write our previous example as this:
     var MyController = function($scope, greeter) {
         "ngInject";
         //....
    };
    someModule.controller('MyController', MyController);
    ng-annotate tool will parse your code and when it encounters the "ngInject" directive prologue, it will add the $inject property annotation.
    Another way to do that is to add /*@ngInject*/ before the function, like this
     var MyController = /*@ngInject*/ 
    function($scope, greeter) {
       //....
    };
    someModule.controller('MyController', MyController);
    In order to do that we need first to install the tool:
     npm install -g ng-annotate
    and then run the tool on our code
     ng-annotate -a source.js

Using strict mode

If you are defining a new service, but you forgot to add the dependency annotations, then angular will assume that you are planning to use implicit annotation, and that might introduce bugs.
For that reason, Angular introduced the "strict mode" in using dependency injection.
To use the strict mode, add the directive ng-strict-di into the same HTML element that has the ng-app directive.
This is an example:
<html ng-app="myApp" ng-strict-di>
<body>
........
</body>
</html>
When you have in your code somewhere that try to use implicit annotations, then it will throw an error.