Nós não precisamos dos frameworks. É totalmente possível implementar a injeção de dependência manualmente, mesmo para um projeto relativamente grande.
Por outro lado, o uso de uma estrutura torna mais fácil, especialmente uma baseada em anotações ou detecção automática de dependências, pois simplifica o processo: se eu decidir que preciso de uma nova dependência em uma classe, tudo o que preciso do é adicioná-lo ao construtor (ou declarar um setter) e o objeto é injetado - não preciso alterar nenhum código de instanciação.
Além disso, as estruturas geralmente contêm outras funcionalidades úteis. O Spring, por exemplo, contém uma estrutura útil para programação orientada a aspectos, incluindo demarcação de transação declarativa (que é extremamente útil) e uma variedade de implementações de adaptadores que facilitam a integração de muitas bibliotecas de terceiros.