Desvendando os Beans do Spring usando Kotlin
Tags: kotlin, springboot, development, code
Published: 2021-06-17 18:18:00

> Tenho visto pessoas usando o Spring Framework de forma totalmente mecânica, sem saber **O QUE** e **O POR QUÊ** decoram as classes com as Annotations: @Controller, @RestController, @Service, @Repository, @Component e etc.
Hoje vamos entender um pouco sobre o que acontece por traz dessas Annotations.

O que acontece na prática, é que decoramos que devemos estruturar nosso projeto com os seguintes packages: controller, service, repository.
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9pejc8pse9au99fa6x8g.png)

E que toda classe no package **controller** deve ser anotada com `@Controller` ou `@RestController`.
Toda classe no package **service** deve ser anotada com `@Service`.
Toda classe no package **repository** deve ser anotada com `@Repository`.

> E as classes de modelo que ficam no package **model** não precisam ser anotadas

Então, quando precisamos usar algumas dessas classes anotadas, usamos o mágico `@Autowired`.
Com isso em mente, usamos uma IDE ou o site Spring Initializr para criar nossos projetos, aplicamos a ideia acima de packages e Annotations e voilà, tudo funciona magicamente, bola pra frente, check no LinkedIn que manjo tudo de **Spring Framework** ;)
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mtxjdwukwl7r6hw0hg0d.png)

E de verdade, não vejo problema em começar assim, acho legal começar simplesmente codando e vendo as coisas funcionarem, mas acho que na sequencia, é preciso se esforçar um pouquinho para entender o que aconteceu e o que exatamente o **Framework** resolveu para nós.
 
## Component Scan

Bom, dado o problema, vamos tentar esclarecer um pouco as coisas.
Quando criamos um projeto usando **Spring** como **Framework Web**, durante o Boot da aplicação existem alguns processos que o **Spring** executa para preparar esse ambiente, e um desses processo se chama "**Component Scan**".
Na prática, o **Spring** Escaneia nossos pacotes em busca de classes Anotadas com Estereótipos: `@Component` e suas especializações como `@Controller`, `@RestController`, `@Service`, `@Repository`.

Podemos usar a Annotation `@ComponentScan` para customizar onde queremos que o **Spring** procure classes **Beans**.
 
```kotlin
@ComponentScan("com.example.demo")
class MyCustomScan {}
```

Cada classe que o **Spring** encontrar, ele vai registrar em um Container chamado "**ApplicationContext**", a partir daí essa classe virou um **Bean**.
O que ele faz é tentar instanciar essas classes e adicioná-las em uma lista de **Bean** (Spring-managed).
Então ele passa por todos os `@Autowired` e procura em sua lista de **Beans** algum **Bean** que equivalente. 
Assim sua aplicação sobe com as classes magicamente instanciadas e Injetadas.

## The IoC container

O **Spring Framework** aplica o princípio de Inversão de Controle IoC (**I**nversion **O**f **C**ontrol). IoC também é conhecido como injeção de dependência (DI). É um processo pelo qual os objetos definem suas dependências, ou seja, os outros objetos com os quais trabalham, apenas por meio de argumentos do construtor, argumentos para um método de fábrica ou propriedades que são definidas na instância do objeto após serem construídas ou retornadas a partir de um método de fábrica. O contêiner injeta essas dependências quando criar o **Bean**. Esse processo é fundamentalmente o inverso, daí o nome Inversion of Control (IoC), do próprio **Bean** que controla a instanciação ou localização de suas dependências, usando a construção direta de classes ou um mecanismo como o padrão Service Locator.

> Aqui vale uma observação importante: é uma boa prática programar para Interface e não para Implementações, assim mantendo suas classes com baixo acoplamento. 
Então, onde queremos Injetar nossas classes com `@Autowired` criamos as variáveis ou parâmetros do tipo da Interface e não da Implementação, ai o Spring tem o trabalho de encontrar algum **Bean** que implemente aquela Interface para então injetar.

---

## Exemplo prático

```kotlin
interface PersonService {
    fun findAll()
}
@Service
class PersonServiceImpl : PersonService {
    override fun findAll() {
        println("Returning all people")
    }
}
@RestController
@RequestMapping("/people")
class PersonController {

    @Autowired
    lateinit var personService: PersonService

    @GetMapping
    fun findAll() = personService.findAll()
}
```

Neste exemplo criamos uma interface **PersonInterface** e implementamos essa interface através da classe **PersonServiceImpl** e anotamos a classe com `@Service`.
Então no controller **PersonController** pedimos para o Spring Injetar uma **PersonService** por meio do `@Autowired`.
Perceba que criamos a variável do tipo da Interface e não da classe e deixamos a critério do Spring encontrar em sua lista de **Beans** uma implementação equivalente.
Uma outra maneira de fazer essa Injeção de Dependência é usando o *construtor* invés de um atributo da classe, vamos ver como ficaria:

```kotlin
@RestControllerSpring-managed
@RequestMapping("/people")
class PersonController(val personService: PersonService) {

    @GetMapping
    fun findAll() = personService.findAll()
}
```

## O QUE aconteceu até aqui ?

Quando executarmos nossa aplicação o **Spring** começará a fazer sua magia.
Passando pelo processo de **Component Scan**, ele perceberá que temos nossa classe **PersonService** anotada com `@Service`, irá instanciar essa classe e registrá-la em seu **ApplicationContext**, tornando nossa classe um **Bean** *Spring-managed*.

Então ele continua o processo e encontra na classe **PersonController** anotada com `@RestController`, logo ele sabe que precisa Instanciar essa classe e registrá-la no **ApplicationContext**. Porém, essa classe tem uma dependência em seu Construtor e ele percebe que essa dependência é uma Interface, então ele percorre sua lista de **Beans**, em busca de alguém que implemente essa Interface. Encontra nossa implementação **PersonServiceImpl** e aplica a *IoC | DI*.

Perceba que não precisamos usar o `@Autowired` no construtor, e não teria problema em usar, funcionaria do mesmo jeito, mas o **Spring** é inteligente o suficiente para saber que para instanciar o **PersonController** ele precisa Injetar o **PersonService** que é uma dependência do construtor, até porque usamos val que irá criar um atributo da classe somente leitura e imutável, então é obrigatório a passagem do valor neste momento.
A partir daí o Contexto do **Spring** é carregado com nossas classes instanciadas e quem está controlando o ciclo de vida desses objetos é o próprio **Spring**.
Se você está atento a tudo o que aconteceu você deve ter se perguntado:
Mas e se eu tiver duas implementações da Interface **PersonService**, como o Spring saberia qual Injetar ???? :(

Vamos criar mais uma implementação e testar esse comportamento do Spring:

```kotlin
@Service
class CustomPersonServiceImpl : PersonService {
    override fun findAll() {
        println("CustomPersonServiceImpl - Returning all people")
    }
}
```

Se executarmos nossa aplicação, veja o comportamento do Spring:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/toiotik1d4t264wgcq8u.png)

Veja que foi lançado um *Erro* ao iniciar o **ApplicationContext**, ele encontrou nossas duas implementações e não soube o que fazer. Neste caso precisamos dizer para ele o que queremos, e perceba que ele até sugeriu uma solução que é anotar uma de nossas implementações com `@Primary`. Vamos fazer isso anotando nossa primeira implementação com `@Primary`. 

```kotlin
@Primary
@Service
class PersonServiceImpl : PersonService {
    override fun findAll() {
        println("Returning all people")
    }
}
```

Agora se executarmos novamente nossa aplicação tudo estará funcionando.
Mas e quando precisarmos que ele Injete nossa segunda implementação??

```kotlin
@RestController
@RequestMapping("/custom-people")
class CustomPersonController(
        @Qualifier("customPersonServiceImpl") val personService: PersonService
) {
    @GetMapping
    fun findAll() = personService.findAll()
}
```

Criei um novo controller **CustomPersonController** e nele quero que seja injetado nossa segunda implementação de **PersonService** - **CustomPersonServiceImpl**. 
Para isso usei a Annotation `@Qualifier`, passando como value o nome da implementação começando com *LowerCase* `@Qualifier(“customPersonServiceImpl”)`. 
Isso é possível porque quando o Spring está registrando os **Beans** no **ApplicationContext**, ele usa o próprio nome da classe começando com LowerCase, uma prática muito comum utilizado por qualquer programador que vem de **Java**.

Se quisermos customizar o nome de nossos **Beans** podemos passar como value da Annotation `@Service(“myCustomPersonServiceImpl”)` e depois usamos este mesmo nome no `@Qualifier(“myCustomPersonServiceImpl”)`.


Poderíamos ir um pouco além do convencional, imagine que vc tem classes de domínio que você gostaria que o Spring usasse como um **Bean** Spring-managed, porém, você não quer acoplar seu domínio a nenhum Framework, então não quer anotar nenhum de suas classes de domínio com `@Service` ou `@Component`. 

Vamos resolver isso:
```kotlin
interface PersonService {
    fun findAll()
}
class PersonServiceImpl : PersonService {
    override fun findAll() {
        println("PersonServiceImpl Returning all people")
    }
}

class CustomPersonServiceImpl : PersonService {
    override fun findAll() {
        println("CustomPersonServiceImpl - Returning all people")
    }
}
```

Tiramos as Annotations `@Service` e agora nossas classes de Serviço/Domínio não estão acopladas ao **Spring**. 
Mas queremos que em algum momento, o **Spring** registre elas no *ApplicationContext* para ser injetada em algum lugar fora da nossa camada de Domínio, por exemplo na camada de **Controller**.

```kotlin
@Configuration
class PersonConfiguration {

    @Primary
    @Bean
    fun personServiceImpl(): PersonService = PersonServiceImpl()

    @Bean
    fun customPersonServiceImpl(): PersonService =    CustomPersonServiceImpl()
}
```

Criamos uma classe de configuração anotando com `@Configuration`, onde usamos a Annotation `@Bean` sobre uma *function* que tem como tipo de retorno nossa *Interface*. 
Só que em cada um retornamos uma instância diferente e, por convenção, usamos o nome da *function* como o nome da classe começando com LowerCase, usando a mesma regra do **Spring** quando está registrando os **Beans** em seu *ApplicationContext*.

Então, agora quando acontecer o "**Component Scan**" ele perceberá que tem uma classe `@Configuration` que está solicitando para o **Spring** register alguns **Beans** no *ApplicationContext*. 
A partir daí tudo volta a funcionar, mas resolvemos o problema de acoplamento do Framework em nossas classes de domínio e perceba que usamos novamente a Annotation `@Primary` para direcionar o **Spring** que quando ele encontrar uma Injection da nossa Interface **PersonService** sem um `@Qualifier` ele deve priorizar o **Bean** personServiceImpl.

Para finalizar, quero só explicar que as Annotations `@Controller`, `@RestController`, `@Service`, `@Repository` são todas especializações de `@Component`:

```java
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
  ...
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
  ...
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
   ...
}
```

O que são apenas *stereotype* para deixar mais explícito o que as classes anotadas com elas quer tratar:
- Service = Business
- Repository = Data
- Controller = Web

E em alguns casos, por exemplo, `@Repository` o **Spring** vai tratar diferente as *Exceptions*. 
Uma vantagem de usar esta anotação é que ela possui a tradução automática de exceção de persistência ativada. 
E ao usar uma estrutura de persistência como o *Hibernate*, as exceções ativas lançadas usarão classes anotadas com `@Repository` para lançar automaticamente convertidas em subclasses do *DataAccessExeption* do **Spring**.

---

## O POR QUÊ devemos usar tudo isso ?

Isso é importante para podermos tirar um maior proveito do *Framework* que estamos utilizando. 
Assim, usamos o *Framework* para resolver problemas de implementação de alguns *Patterns*, por exemplo **MVC**, **IoC/DI**, **DAO/Repository**, e ainda deixamos a cargo do *Framework* a responsabilidade de gerenciar o Ciclo de Vida dos nossos Objetos/Beans.

Com isso conseguimos deixar nosso código mais coeso e menos acoplado melhorando o *Design* do Software e ainda facilitando a implementação de *Tests* Automatizados. 
Se entrarmos mais a fundo na questão do **Spring Boot** em volta de tudo isso temos muito mais problemas resolvidos tornando nosso dia-a-dia como Desenvolvedores muito mais produtivo.

Bom, se você chegou até aqui você foi muito guerreiro porque foi um Post bem teórico, mas como mencionei lá no começo, alguns momentos é preciso entender O QUE estamos fazendo e O POR QUÊ estamos fazendo determinadas coisas em nossos sistemas.

Espero que este Post seja útil para melhorar seu entendimento sobre **Spring Framework**.

Um abraço e até a próxima.

---

## Referencias

- https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html
- https://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch02s02.html
- https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/ApplicationContext.html
- https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/package-summary.html
- https://www.baeldung.com/spring-bean-annotations

#java #kotlin #spring #springframework #springboot #jvm #beans