DEV Community

Cover image for Processo de seleção de implementações de interfaces no Spring: Entendendo e gerenciando escolhas
Eduardo Candido
Eduardo Candido

Posted on

Processo de seleção de implementações de interfaces no Spring: Entendendo e gerenciando escolhas

O Spring conta com a feature do IOC container que nos provê várias anotações para que possamos dizer a ele qual método ou classe ele irá gerenciar, essas anotações são importantes para que possamos aplicar conceitos de Design Patterns.

Por exemplo, em alguns códigos usando Spring é comum encontrarmos uma classe em que se é instanciado uma interface e essa interface contém uma implementação. Como no exemplo abaixo onde a classe CurriculoService instancia uma interface GeraCurriculo

Mas e se acontecesse dessa interface ter duas implementações como o caso abaixo, o que aconteceria?

O Spring lançaria a exceção NoUniqueBeanDefinitionException, por quê? Porque o Spring não seria capaz de determinar qual classe utilizar, não basta dizer ao Spring o que gerenciar, em alguns cenários será necessário dizer a ele qual implementação usar. Para isso separei três maneiras das quais podemos dizer ao Spring qual implementação utilizar.

Através da anotação Primary

O simples ato de anotar uma classe com @Primary irá dar prioridade aquela classe em caso de haver múltiplas implementações ou Beans, por exemplo caso eu queira que a classe GeraCurriculoWordService fosse chamada toda vez que eu proveja a interface GeraCurriculo basta apenas isso:

Através da anotação @Qualifier

Com essa anotação podemos "nomear" as classes e ao prover uma interface fazer referencia a ela, ainda usando nosso exemplo poderíamos dar prioridade para nossa classe GeraCurriculoPdfService da seguinte maneira.

Primeiramente anotamos nossa classe com o @Qualifier e passamos como parâmetro um identificador, no caso aqui selecionei o próprio nome da classe:

E por fim, ao provermos nossa interface podemos dizer para usar a implementação em especifico também com a anotação @Qualifier como no exemplo abaixo:

Note que desta maneira não ficamos preso a uma implementação como na anotação @Primary com o @Qualifier podemos ter mais dinamismo ao prover nossa interface e decidir a implementação. Isso não anula a @Primary mesmo usando qualifiers ainda poderíamos deixar uma implementação como default em caso de não especificarmos nenhuma classe ao provermos a interface.

Selecionando a implementação dinamicamente

Vamos supor agora que o usuário que está usando o sistema selecionará o formato de seu currículo, eu preciso ter uma maneira de selecionar a implementação em runtime.

Para isso podemos usar um dos recursos menos conhecidos do Spring. Como vimos no início do artigo quando temos apenas uma implementação de uma interface o Spring sabe injetar automaticamente essa implementação quando necessário. Mas o Spring também nos dá a capacidade de coletar todos os beans que são implementações de uma interface específica. Assim podemos mapear as implementações para usá-las posteriormente como no exemplo abaixo:

Veja que no construtor da classe passamos como parâmetro uma lista da interface, o Spring no entanto coleta todas as implementações existentes e popula automaticamente essa lista para nós, tornando fácil mapear da maneira como queremos, como foi feito na linha 8 do exemplo acima onde mapeamos o tipo do arquivo que a implementação ligado a própria implementação.

Para resolvermos o problema citado sobre gerar um currículo dinamicamente poderíamos fazer assim:

Veja que agora a classe CurriculoService está injetando a classe onde mapeamos a lista de implementações da interface GeraCurriculo. Então no método da linha 8 getCurriculo podemos nos preocupar em passar apenas o tipo do arquivo que queremos em vez de termos métodos para cada implementação, na linha 9 selecionamos a implementação que bate com o parâmetro informado e por fim temos acesso a nossa implementação e seus métodos.

Em resumo as anotações @Primary e @Qualifier são úteis quando temos várias implementações e precisamos selecionar uma em específico tendo a anotação @Qualifier nos permitindo maior especificidade. Vimos também que o Spring oferece uma abordagem mais dinâmica através da coleta de todas as implementações de uma interface, permitindo que você escolha qual implementação usar em tempo de execução. Isso é útil quando você precisa selecionar a implementação com base em informações do usuário ou em condições variáveis.

Caso queira ver a implementação do código para selecionar uma classe dinamicamente você pode acessar nesse link do github

Top comments (0)