# Dependency Injection

> Configura, revisa e debuga DI Guice em Sankhya Addon Studio — `@Inject` de `com.google.inject`, `@Component`, `@CustomModule`, `Provider<T>`, `Multibinder`, `@Singleton`, escopos. Use ao montar wiring, criar/alterar módulos Guice, revisar/auditar dependências, diagnosticar `ConfigurationException`/`CreationException`/binding ausente, ou ao tocar em código com `@Inject`/`@CustomModule`/`AbstractMo…

- **Type:** Skill
- **Install:** `agentstack add skill-snk-devcenter-addon-studio-dependency-injection`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [snk-devcenter](https://agentstack.voostack.com/s/snk-devcenter)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [snk-devcenter](https://github.com/snk-devcenter)
- **Source:** https://github.com/snk-devcenter/addon-studio/tree/main/plugins/addon-studio/skills/dependency-injection

## Install

```sh
agentstack add skill-snk-devcenter-addon-studio-dependency-injection
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Injeção de Dependência (Guice) — Addon Studio 2.0

Addon Studio 2.0 usa **Google Guice** como container DI. Anotações estereótipo customizadas fazem auto-scan. Doc descreve regras, padrões, boas práticas DI.

---

## 1. Regra de Ouro

**Sempre injeção via construtor** com `@Inject` de `com.google.inject.Inject`.

```java
import com.google.inject.Inject;

@Component
public class MeuService {

    private final MeuRepository repository;
    private final MeuGateway gateway;

    @Inject
    public MeuService(MeuRepository repository, MeuGateway gateway) {
        this.repository = repository;
        this.gateway = gateway;
    }
}
```

> **NUNCA** `@Inject` de `javax.inject`. Sempre `com.google.inject.Inject`.

---

## 2. Estereótipos (Anotações de Auto-Scanning)

Framework scaneia e registra classes com:

| Anotação | Pacote | Gerenciamento | Uso |
|:---------|:-------|:--------------|:----|
| `@Controller` | `br.com.sankhya.studio.annotations` | **Automático** — NÃO adicionar `@Component` | Entrypoints REST |
| `@Repository` | `br.com.sankhya.studio.stereotypes` | **Automático** — NÃO adicionar `@Component` | Interfaces de acesso a dados (`JapeRepository`) |
| `@Component` | `br.com.sankhya.studio.stereotypes` | **Automático** | Classes gerais: Services, Adapters, Gateways, Mappers auxiliares |
| `@ControllerAdvice` | `br.com.sankhya.studio.web` | **Automático** | Tratamento global de exceções |
| `@CustomModule` | `br.com.sankhya.studio.stereotypes` | **Automático** | Módulos Guice customizados (equivale a `AbstractModule`) |

### Quando usar cada estereótipo

```
@Controller       -> Entrypoints REST (serviceName obrigatório com sufixo "SP")
@Repository       -> Interfaces JapeRepository (NÃO crie implementação manual)
@Component        -> Todo o resto que precisa ser injetado
@CustomModule     -> Módulos de configuração Guice (bindings manuais)
@ControllerAdvice -> Handler global de exceções
```

---

## 3. Classes por Estereótipo

### 3.1 `@Controller` — Entrypoints REST

Regras DI relevantes:

- `@Inject` de `com.google.inject.Inject` no construtor; deps `private final`.
- **NÃO** adicionar `@Component` — `@Controller` já gerenciado pelo framework.
- Anatomia completa (`serviceName`, `transactionType`, `@Transactional`, DTOs): ver skill `controller`.

### 3.2 `@Repository` — Interfaces de Acesso a Dados

```java
import br.com.sankhya.sdk.data.repository.JapeRepository;
import br.com.sankhya.studio.stereotypes.Repository;

@Repository
public interface MeuProdutoRepository extends JapeRepository {
    // métodos declarativos — o framework gera a implementação
}
```

**Regras:**
- Sempre **interface** (nunca classe concreta).
- Estende `JapeRepository`.
- **NÃO** adicionar `@Component` — framework gera implementação e registra no Guice.
- Injetável direto em qualquer `@Component` ou `@Controller`.

### 3.3 `@Component` — Classes Gerais

Pra qualquer classe injetável que não encaixa em `@Controller` ou `@Repository`.

**Exemplos `@Component`:**

| Tipo | Exemplo |
|:-----|:--------|
| Servico de aplicacao | `ImportarProdutoService` |
| Servico de negocio  | `PedidoCreateService` |
| Servico de infraestrutura | `IntegrationConfigurationService` |
| Gateway/Adapter | `DynamicProdutoGateway` |
| Mapper auxiliar (usado por MapStruct `uses={}`) | `StringMappingNormalizer` |
| Provider/Resolver | `IntegrationPlatformProvider` |

```java
import br.com.sankhya.studio.stereotypes.Component;
import com.google.inject.Inject;

@Component
public class ImportarProdutoService {

    private final ProdutoRepository repository;
    private final DynamicProdutoGateway gateway;

    @Inject
    public ImportarProdutoService(ProdutoRepository repository,
                                  DynamicProdutoGateway gateway) {
        this.repository = repository;
        this.gateway = gateway;
    }

    public List execute() { ... }
}
```

### 3.4 `@ControllerAdvice` — Tratamento Global de Exceções

Tratamento centralizado de exceções vindas dos `@Controller`. Auto-gerenciado — não adicionar `@Component`.

> Ver `controller-advice` para anatomia completa, regras críticas (handler nunca retorna `void`, múltiplas exceções por handler, rollback automático, proibição de `Exception.class`) e níveis de log.

---

## 4. Módulos Customizados (`@CustomModule`)

Quando auto-scan insuficiente (bindings manuais, `Multibinder`, `@Provides`), crie módulo Guice com `@CustomModule`.

### 4.1 Multibinder — Strategy Pattern

Registra múltiplas implementações de interface pra resolução dinâmica runtime.

```java
import br.com.sankhya.studio.stereotypes.CustomModule;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;

@CustomModule
public class IntegrationPlatformConfig extends AbstractModule {

    @Override
    protected void configure() {
        Multibinder binder =
            Multibinder.newSetBinder(binder(), IntegrationPlatformAdapter.class);

        binder.addBinding().to(WebReceitaAdapter.class);
        // binder.addBinding().to(OutraPlataformaAdapter.class);
    }
}
```

`Set` injetável:

```java
@Singleton
public class IntegrationPlatformResolver {

    private final Map adapters;

    @Inject
    public IntegrationPlatformResolver(Set adapters) {
        this.adapters = adapters.stream()
            .collect(Collectors.toMap(IntegrationPlatformAdapter::getTipo, Function.identity()));
    }

    public IntegrationPlatformAdapter resolve(TipoPlataforma tipo) {
        IntegrationPlatformAdapter adapter = adapters.get(tipo);
        if (adapter == null) {
            throw new IllegalArgumentException("Nenhuma integracao implementada para: " + tipo);
        }
        return adapter;
    }
}
```

### 4.2 `@Provides` — Factory Methods

Pra criar instâncias com configuração especial (ex: clientes HTTP).

> Para wiring completo de cliente Retrofit (deps no `build.gradle`, interface, interceptor), ver skill `retrofit`. Os exemplos abaixo cobrem só o lado DI.

```java
import br.com.sankhya.studio.stereotypes.CustomModule;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;

@CustomModule
public class MeuClientConfig extends AbstractModule {

    @Provides
    @Singleton
    public MeuApiClient provideMeuApiClient(RetrofitClientFactory factory) {
        return factory.create(
            MeuApiClient.class,
            Collections.emptyList(),
            "https://api.exemplo.com/"
        );
    }

    @Provides
    @Singleton
    public MeuApiAuthClient provideMeuAuthClient(RetrofitClientFactory factory,
                                                  MeuAuthInterceptor authInterceptor) {
        return factory.create(
            MeuApiAuthClient.class,
            Collections.singletonList(authInterceptor),
            "https://api.exemplo.com/"
        );
    }
}
```

**Regras:**
- `@Provides` marca método como factory.
- `@Singleton` garante instância única.
- Parâmetros resolvidos automaticamente pelo Guice.
- Classe **deve** estender `AbstractModule` e ter `@CustomModule`.

---

## 5. Escopo: `@Singleton`

Guice padrão cria **nova instância** a cada injeção. Use `@Singleton` pra instância única.

```java
import com.google.inject.Singleton;

@Component
@Singleton
public class RetrofitCallExecutor {
    // Uma única instância reutilizada em toda a aplicação
}
```

### Quando usar `@Singleton`

| Usar `@Singleton` | Não usar (padrão) |
|:-------------------|:-------------------|
| Clientes HTTP, executors, factories | Services de domínio |
| Resolvers e registries | Gateways e Adapters |
| Interceptors (OkHttp, Auth) | Controllers |
| Caches e pools | Mappers auxiliares |

> `@Singleton` **sem** `@Component` não auto-scaneada. Precisa estar em `@CustomModule` ou ser injetada por classe que Guice conhece.

---

## 6. `Provider` — Injeção Lazy / Circular

Em dependência circular ou resolução lazy, injete `Provider` em vez de `T`.

```java
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;

@Singleton
public class MeuAuthInterceptor implements Interceptor {

    private final Provider authClientProvider;
    private final Provider configProvider;

    @Inject
    public MeuAuthInterceptor(Provider authClientProvider,
                               Provider configProvider) {
        this.authClientProvider = authClientProvider;
        this.configProvider = configProvider;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        // .get() resolve a dependência no momento da chamada (lazy)
        MeuAuthClient client = authClientProvider.get();
        IntegrationConfigurationService config = configProvider.get();
        // ...
    }
}
```

### Quando usar `Provider`

- **Dependência circular:** A depende de B que depende de A.
- **Singleton com dep request-scoped:** ex: interceptor singleton que precisa service com contexto.
- **Lazy init:** adiar criação de objeto custoso até primeiro uso.

---

## 7. MapStruct e o Container Guice

Mappers MapStruct registrados automaticamente no Guice (config global `defaultComponentModel = "jakarta"`) — injetáveis direto via `@Inject` no construtor. Classe em `uses` **deve** ser `@Component`; com `uses` (ou `abstract class` com `@Inject`), declare `injectionStrategy = InjectionStrategy.CONSTRUCTOR` pra garantir que o Guice injete via construtor. Detalhes e tipos de mapper: ver skill `mapstruct`.

---

## 8. Padrão tipico de wiring DI

Fluxo comum (**ilustrativo** — skill nao opina sobre arquitetura, ajuste a sua):

```
@Controller (entrada)
  └── @Inject Service (@Component)
       └── @Inject Repository (@Repository — interface)
       └── @Inject Gateway concreto (@Component)
            └── @Inject Provider / Resolver / Adapter (@Component)
                 └── resolvido em runtime via @CustomModule + Multibinder
```

`@CustomModule` registra bindings explicitos (Multibinder, `@Provides @Singleton`).

---

## 9. Interfaces e implementacoes via Guice

Interfaces abstraem implementacao concreta. Guice resolve a implementacao quando ela e marcada com `@Component` e implementa a interface.

> **Interface so quando ha polimorfismo real** — duas ou mais implementacoes, ou troca em runtime (Multibinder, resolucao por parametro). Com uma unica implementacao, injete a classe concreta: `@Component` sozinho ja e resolvido pelo Guice, e a interface extra nao adiciona nada alem de um arquivo. O limite de **um `@Component` por interface** (abaixo) pune justamente a interface criada por habito.

**Interface:**

```java
public interface ProdutoGateway {
    List findAll();
}
```

**Implementação:**

```java
@Component
public class DynamicProdutoGateway implements ProdutoGateway {

    private final IntegrationPlatformProvider provider;

    @Inject
    public DynamicProdutoGateway(IntegrationPlatformProvider provider) {
        this.provider = provider;
    }

    @Override
    public List findAll() {
        return provider.get().produto().findAll();
    }
}
```

> Guice resolve `ProdutoGateway` -> `DynamicProdutoGateway` automático porque `DynamicProdutoGateway` é `@Component` e implementa interface.

> **Exceção — JIT binding do Guice:** classe **concreta** com construtor público sem argumentos (ou construtor `@Inject`) é resolvida just-in-time pelo Guice mesmo sem stereotype (ex.: `RetrofitClientFactory` da skill `retrofit`). **Interface** sempre exige implementação `@Component` ou binding em `@CustomModule`.

### Limite: UM `@Component` por interface (`Guice/BindingAlreadySet`)

O `DependencyInjector` gerado binda **cada** `@Component` a **todas** as interfaces que ele implementa (`bind(Interface.class).to(Impl.class)`). Consequência: no máximo **um** `@Component` por interface **no addon inteiro** (o limite cruza pacotes — cada pacote gera seu `DependencyInjector`, mas todos entram no mesmo injector no deploy).

Dois `@Component` implementando a mesma interface derrubam o **deploy** — mesmo que nenhum ponto de injeção peça a interface (todos pedindo o tipo concreto não salva):

```
[Guice/BindingAlreadySet]: okhttp3.Interceptor was bound multiple times.
```

> **Feedback tardio:** build e testes passam. O erro só aparece na criação do injector no Wildfly, durante o deploy.

**Fix:** a classe "excedente" perde o stereotype (`@Component`/`@Singleton`) e é provida via `@Provides @Singleton` num `@CustomModule` — `@Provides` binda **apenas o tipo concreto**, sem tocar na interface:

```java
@Provides
@Singleton
public MeuSegundoInterceptor provideMeuSegundoInterceptor() {
    return new MeuSegundoInterceptor();
}
```

Pontos de injeção que pedem o tipo concreto continuam funcionando sem alteração.

---

## 10. Checklist

### Nova classe injetável

1. [ ] Identificar estereótipo correto (`@Controller`, `@Repository`, `@Component`).
2. [ ] Usar `@Inject` de `com.google.inject.Inject` no construtor.
3. [ ] Declarar deps `private final`, inicializar no construtor.
4. [ ] **NÃO** usar `new` pra criar deps — sempre injetar.
5. [ ] **NÃO** misturar estereótipos (ex: `@Component` + `@Controller`).

### Novo módulo customizado

1. [ ] Criar classe que estende `AbstractModule`.
2. [ ] Anotar com `@CustomModule`.
3. [ ] Usar `Multibinder` pra Strategy Pattern.
4. [ ] Usar `@Provides @Singleton` pra factories.
5. [ ] Colocar conforme arquitetura do projeto.

### Novo interceptor ou singleton

1. [ ] Anotar com `@Singleton`.
2. [ ] Dep circular? Usar `Provider`.
3. [ ] Não é `@Component`? Garantir registro em algum `@CustomModule`.

---

## 11. Erros Comuns

| Erro | Correção |
|:-----|:---------|
| Usar `javax.inject.Inject` | Sempre `com.google.inject.Inject`. |
| Adicionar `@Component` em `@Controller` | `@Controller` já gerenciado. Remove `@Component`. |
| Adicionar `@Component` em `@Repository` | `@Repository` já gerenciado. Remove `@Component`. |
| Criar implementação manual de Repository | Use interface `JapeRepository` — framework gera implementação. |
| Usar `new` pra instanciar dep | Injete via construtor. Guice resolve automático. |
| Dep circular com `@Singleton` | Use `Provider` pra quebrar ciclo. |
| Classe sem estereótipo que precisa injeção | Adicione `@Component` ou registre em `@CustomModule`. |
| `@Singleton` sem `@Component` e sem módulo | Guice não encontra. Adicione um dos dois. |
| Mapper MapStruct com `uses` sem `injectionStrategy` | Adicione `injectionStrategy = InjectionStrategy.CONSTRUCTOR`. |
| `@Provides` em classe sem `@CustomModule` | Guice não encontra provider. Adicione `@CustomModule`. |
| `Guice/BindingAlreadySet` no deploy (build/testes passam) | Dois `@Component` implementam a mesma interface. Remova o stereotype de um deles e proveja via `@Provides @Singleton` do tipo concreto em `@CustomModule` (ver seção 9). |

## Skills relacionadas

- `addon-studio` — regras universais sobre @Inject (com.google.inject) e proibição de `new`
- `controller` — controllers gerenciados automaticamente pelo framework — NÃO anotar @Component
- `controller-advice` — advice gerenciado automaticamente pelo framework — NÃO anotar @Component
- `mapstruct` — mappers são registrados no container Guice
- `retrofit` — wiring completo de cliente HTTP (interface + interceptor + `@Provides`) e exemplos práticos de `@Provides @Singleton`
- `value` — injeção de valores de configuração via `@Value`

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [snk-devcenter](https://github.com/snk-devcenter)
- **Source:** [snk-devcenter/addon-studio](https://github.com/snk-devcenter/addon-studio)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-snk-devcenter-addon-studio-dependency-injection
- Seller: https://agentstack.voostack.com/s/snk-devcenter
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
