I think a great analogy is a six-year-old with a lego set.
You want your objects to be like the lego bricks. Each one is independent of all the others, and yet offers a clear interface for connecting them to the others. When connecting them together, it doesn't really matter exactly which two bricks you hook together so long as they have a matching interface.
Your dependency injection framework is like the six-year-old. He follows the instructions (i.e., your config file, annotations, etc.) to connect specific bricks together in certain ways to make a particular model.
Of course, since the bricks' interfaces are pretty generalized, they can go together in lots of different ways, so it's easy to come up with new sets of instructions which the six-year-old can use to make a completely different model out of the same bricks.