Este projeto é uma Prova de Conceito (PoC) didática para demonstrar na prática a diferença comportamental entre Injeção via Construtor e Injeção via Campo (@Autowired) ao lidar com Dependências Circulares.
O objetivo é provar por que o princípio de Fail-Fast (Falhar Rápido) torna o construtor uma escolha arquitetural mais segura.
O Spring tenta resolver as dependências antes de instanciar o objeto. Como um depende do outro para nascer, o ciclo é detectado imediatamente.
graph TD
Start((🚀 App Start)) -->|Scan Beans| A[Service A]
A -->|Precisa de| B[Service B]
B -->|Precisa de| A
A -.->|❌ Bloqueio| Error[BeanCurrentlyInCreationException]
style Error fill:#ffcccc,stroke:#ff0000,stroke-width:2px,color:#ff0000
style Start fill:#e6f3ff,stroke:#333,stroke-width:2px
O Spring cria os objetos vazios e injeta as dependências depois (via Reflection). O app sobe, mas quebra em Tempo de Execução (Runtime).
sequenceDiagram
participant User
participant C as Service C
participant D as Service D
Note over C,D: ✅ App Inicia com Sucesso
User->>C: GET /test-loop
activate C
C->>D: cumprimentar()
activate D
D->>C: responder()
C->>D: cumprimentar()
D->>C: responder()
Note right of D: ♾️ Loop Infinito
C--xUser: 💥 StackOverflowError (Runtime)
deactivate D
deactivate C
Local: com.joseanderson.circulardemo.constructor
- Comportamento: A aplicação NÃO INICIA.
- Erro:
BeanCurrentlyInCreationException. - Vantagem: O erro é pego no momento do deploy/inicialização. Não existe risco de subir um código quebrado para produção.
- Bônus: Permite o uso de atributos
final(Imutabilidade).
Local: com.joseanderson.circulardemo.field
- Configuração: Habilitado
spring.main.allow-circular-references=truepara simular sistemas legados. - Comportamento: A aplicação INICIA normalmente.
- Erro:
StackOverflowErrorapenas quando o método é chamado. - Risco: O bug pode ficar oculto por meses até alguém chamar aquela rota específica em produção.
- Java 17+
- Maven
-
Clone o repositório:
git clone https://github.com/dessima/spring-circular-dependency-demo.git
-
Teste o Fail-Fast (Construtor):
- Rode a aplicação (
CircularDemoApplication). - Observe o console. O Spring desenhará o ciclo de dependência e encerrará o processo.
- Rode a aplicação (
-
Teste o Runtime Error (Field):
- Vá nas classes
ServiceAeServiceBe comente a anotação@Service(para permitir que o app suba sem o erro do construtor). - Rode a aplicação novamente.
- Acesse no navegador ou Postman:
http://localhost:8080/test-loop - Veja o estouro de pilha (
StackOverflowError) no console.
- Vá nas classes
| Característica | Construtor | @Autowired (Field) |
|---|---|---|
| Detecção de Ciclo | ✅ No Start (Fail-Fast) | ❌ No Uso (Runtime) |
| Imutabilidade | ✅ Sim (final) |
❌ Não |
| Testabilidade | ✅ Fácil (POJO puro) | |
| Acoplamento | ✅ Explícito |
Desenvolvido com 💙 por José Anderson para fins educativos na comunidade ONE.