The problem with static singletons is not the global state per se. The problem is that they are usually used for shady things - one of the most common examples is a database connection.
Static singletons are wrong for one main reason: you can get an instance of a class the singleton wraps anywhere in the application, in any class, without ever letting a user of some class know the dependency actually exists.
Obviously, you can just to the same even when you don't have a static singleton and are somehow following dependency injection, by constructing a dependency in place (instead of passing it). But usually when you do that you realize much sooner that something is not right (even more so when the constructed dependency follows the dependency injection strictly and you are suddenly seeing not one, but like 7
new calls to construct the object graph). Singleton gives off the vibe that it's in fact ok, while in some cases it is not.
With hidden dependencies it just so happens you run a function taking a string parameter in development mode (or as a part of your unit test suite). This function will suddenly call the production database because it internally uses the singleton. If the dependency was known beforehand (the dependency would be passed into the function), you'd probably ask yourself why does the function need a database in the first place and be more careful before calling it.
The thing with Spring is, it will not fix design issues. But it can make constructing a heavily decoupled design (where pretty much everything is being passed as a dependency) easier to construct by running its magic and wiring the classes together based on some rules.
If you retrieve service classes and modules from Spring (or any other class type for that matter), you no longer need to tinker with factories and carefully manage your instances.
To ensure a thing which should exist only once actually exists only once you configure the framework and you are set. And that is amazing. The obvious downside is that it's done somehow and for less experienced programmers who have little to no experience with IoC container the magic may be off-puting (see Curse of knowledge).
I am personally somewhere in between whether a complete IoC framework is a good or a bad thing. I like to have some container responsible for managing my classes and assuring their scopes, but I am not a big fan of doing all this configuration using some kind of a XML configurator. I prefer
newing the classes myself, inserting the manually
newed instances into a container of choice and then always using the container to retrieve them back.
The manual part goes only one way, into the container. If I have a class demanding a dependency, I will ask for it from the container, meaning I will have to set it there first.