Не совсем. Проблема здесь в том, что когда вы пишете фрагмент кода вроде:
Runnable r = new MyFooRunnable();
вы решаете, что вам необходим Runnable - это MyFooRunnable (а не MyBarRunnable или третий). Иногда вам может понадобиться отложить это решение со времени компиляции до времени развертывания , чтобы развертыватель мог решить, как отдельные модули, из которых состоит ваше приложение, должны быть склеены.
Традиционно это было сделано с фабриками, но это просто перемещает реальное решение в коде, и вы все же должны знать все возможности при кодировании фабрики или позволить ей читать инструкции из файла конфигурации ( который имеет тенденцию быть хрупким к рефакторингу).
Внедрение зависимостей - это формализация сконфигурированных фабрик таким образом, что коду не нужно почти ничего знать о том, как все работает. Именно поэтому аннотации оказались настолько полезными для указания, где должно произойти внедрение зависимости. Если запускать код в настройках без DI (например, в тесте junit), то ничего не произойдет (что было бы трудно сделать с заваленными фабриками полностью).
Таким образом, Dependency Injection, используемое в широком смысле, позволяет вам писать модули, которые «хорошо сочетаются» друг с другом, не зная друг друга во время компиляции. Это очень похоже на концепцию jar-файла, но на его взросление ушло больше времени.