Dependency injection and any othee type of code indirection is evil imo. Anything that breaks the ability to find your way in a codebase with something other than grep brings more pain in the long run than it saves.
It's a tradeoff between two different kinds of clarity. Yes, it's harder to see the "whole wiring" without better tools...
But it makes small, encapsulated classes much easier to reason about and develop! You can focus on assuring: "My `Foo` can interact with a supplied `Bar`" rather than dealing with all sorts of crap about what implementation of `Bar` it gets or how that `Bar` instance is configured.
Spring's DI is terrible but Guice (and on mobile, Dagger) is really easy to work with, statically typed and has increased the maintainability and testability of our code base tremendously.
DI is fine, it's just the way people overuse it that's an abomination. I general inject the most significant pieces, or the pieces that are changed. I won't even introduce DI unless I know I'm going to setup a different config of it. My coworkers (sigh), insist that nearly everything must be injected to "eliminate dependencies". (and because it makes it "easier to test"). YMMV
Dependency injection is an abomination. Working on DI code feels like slogging through the post-processed expansion of some more elegant language. It's hard to understand control flow, and DI adds exciting failure modes to otherwise-correct code. Don't forget that DI also bloats class and method counts.
Substitution for testability should be done at the runtime level, substituting class references as needed, instead of punishing all production code everywhere.