Dependency injection is a way for implementing the inversion of control pattern for avoiding global state for dependency resolution. You can use the inversion of control pattern with or without dependency-injection. Yes not using global variables is an important part of the equation weather you use dependency-injection or not.
Dependency injection is really nothing more than if you were to write a program in a bottom up style where the most top level portion of the application resolved dependencies for all subsystems. Say I had a program with a dependency injection configuration like:
<bean id="world" class="com.game.World" start-method="play">
<property name="player1" ref="player1"/>
<property name="player2" ref="player2"/>
</bean>
<bean id="player1" class="com.game.LocalPlayer"/>
<bean id="player2" class="com.game.NetworkPlayer/>
That would really be no different than if you created the objects by hand:
public static void main() {
World world = new World();
world.player1 = new LocalPlayer();
world.player2 = new NetworkPlayer();
world.play();
}
Using dependency injection simply means writing code like the above is handled for you. In this simple example you can't make much of a case for using it over just using code, but in larger programs it does save you a lot of time. It also prevents you or team members from taking shortcuts because it's not as wide open as when you write code.
Dependency-injection frameworks change your program from imperative style code to a declarative-style language for dependencies. So you're writing a program through this declarative language and you can augment that with lots of other features.
Features like having the framework resolve order of construction and cycle dependencies for you. Declaring external configuration and injection of values into your declared objects (i.e. property files, XML configuration, etc) which is really nice. All of these together makes dependency injection frameworks quite compelling to use over doing all of this on your own.