Dependency Injection can really be done in a dark magic way (the java / spring way), but after reading the source code, i can tell you angular.js way of doing it is really not that dark. More importantly, is extremely easy to use. No .xml file, no configuration file, just put the service in that function signature, done (unless you want to minifiy, which means you'll also have to add it as a string right next to it).
Well, in my option DI is one of the worst parts of Angular. It's a module system which can't be integrated with anything. Doing something like commonjs/amd would be much nicer:
app.controller('Name', function(require) {
var $scope = require('$scope');
var y = require('some-service');
});
Require is a service locator, so you are getting some of the inversion of control benefits, but I greatly prefer the injections of the dependencies I need instead of relying on a locator.
In practice the DI approach is very nice, and works consistently. It also makes unit tests more straight forward.
As far as I can tell, the DI is "magic" in most of the examples in that it's using your controller function's parameter names to decide which services to pass in. The Angular docs I've read seem to indicate that this is not actually the recommended way to do it, but rather you should use the controller.$inject array to indicate which services you want. The annoying part to me is that this other, more magical behavior is used in just about every example I've encountered and IT IS NEVER EXPLAINED ANYWHERE. I was glancing through the code and I saw reference to gleaning the dependencies automatically by calling .toString() on the controller function and then parsing the arg list, which is ick ick ick. Who in the hell expects their arbitrarily-named anon function args to actually have meaning outside their own scope? This is a total misfeature, IMO.