Java Annotations no Spring: Crie restrições em suas DTOs
Tags: java, spring, tip
Published: 2020-08-26 23:55:09

> Vibrante por encontrar pessoas que querem criar *annotations* comigo!

![Série Seinfeld: Elenco vibrando](https://dev-to-uploads.s3.amazonaws.com/i/j6jcxqh365b6qx2oc5dr.gif)

> _Olá queridos devs!
Hoje neste tutorial irei explicar **o que são** e **como se comportam** as famosas annotations, um mecanismo muito poderoso e nem tanto explorado pelos desenvolvedores no universo Java. Além da contextualização, irei apresentar **dois exemplos** muito simples que podem ser um bom norteador a você que está iniciando esta caminhada._

## -1. Alinhando expectativas

Neste tutorial você vai encontrar: 
- Conceitos sobre o que é uma anotação
- Disponibilidade de anotações
- Exemplos de anotações disponíveis em tempo de execução focadas em validar restrições
 
Neste tutorial você **não** vai encontrar: 
- Fundamentação teórica densa (deixarei bons materiais como referência) 
- Inclusão de atributos na anotação
- Uso de _reflections_

## 1. O que é? 
> "As annotations são responsáveis por fornecer informações complementares sobre um programa." (GeeksForGeeks, 2020, tradução nossa)

> Segundo Oracle ([20--]) "[...] uma forma de metadados, fornecem dados sobre um programa que não faz parte do próprio programa". 

> As annotations são responsáveis por embutir informações complementares no código fonte. (Groner, 2015)

As anotações surgiram a partir da versão 1.5 do Java e são precedidas por um arroba `@`. Provavelmente você já deve ter observado algumas dispersas em seu código, como por exemplo: `@Override`, `@RequestBody`, `@PathVariable`, `@Getter`, `@Setter`. 

## 2. Qual a finalidade?

As anotações podem ser utilizadas em três finalidades distintas dentro do ciclo de vida do seu projeto (quem define esta configuração é a meta-annotation chamada `@Retention`, na qual, entrarei em detalhes nas próximas seções). Esta definição também é importante para indicar em qual momento as instruções da anotação devem estar disponíveis e serem descartadas (JAVA2NOVICE, 2020). As três finalidades são exemplificadas abaixo:  
 
**Instruções em tempo de compilação (CLASS):** tem como objetivo gerar códigos fonte em tempo de compilação. Um exemplo famoso é o da biblioteca Mapstruct, que em tempo de compilação gera componentes que convertem objetos em outros, em cima de uma varredura em todas as interfaces que utilizam a anotação `@Mapper` (MAPSTRUCT, [201-]). 
**Instruções em tempo de construção (SOURCE):** tem como objetivo gerar empacotamento de arquivos, arquivos XML em tempo de construção do projeto, ou seja, na etapa que está sendo gerado o arquivo `.jar` ou `.war` (JENKOV, 2019).
**Instruções em tempo de execução(RUNTIME):** tem como objetivo criar validações ou injetar informações em tempo de execução, como por exemplo, a anotação `@NotNull` que é utilizada para validar obrigatoriedade de um atributo de uma classe. 

## 4. Como funciona: exemplo prático

> Entendendo as três finalidades apresentadas na seção anterior e sabendo que queremos criar anotações que validem nossas requisições, necessitamos que estas estejam disponíveis em **tempo de execução**.

Como o foco deste tutorial são anotações disponíveis em tempo de execução, usaremos como exemplo explicativo a anotação `@NotNull` presente no projeto `JakartaEE`. 

```java
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(NotNull.List.class)
@Documented
@Constraint(
    validatedBy = {}
)
public @interface NotNull {
    String message() default "{javax.validation.constraints.NotNull.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface List {
        NotNull[] value();
    }
}
```
Abaixo é possível acompanhar as meta-annotations e atributos com seu nome e significado:  
		
| Meta-Annotation/Atributo | Descrição |
| ---------------|----------------|
| @Target | Indica qual contexto esta anotação pode ser aplicada. Estes valores são indicados pelo enumerador `ElementType`. No exemplo acima, percebe-se que é possível aplicar esta anotação em níveis de: métodos, campos (atributos), classes, construtores e parâmetros|
| @Retention | Indica se a anotação estará disponível em tempo de compilação, execução ou construção. Estes valores são indicados pelo enumerador `RetentionPolicy`. No exemplo acima, a retenção é do tipo RUNTIME, ou seja, a anotação estará disponível para uso em tempo de execução.|
| @Repeatable | Indica possibilidade de utilizar mais de uma vez a anotação em uma classe. Observe que nesta meta-annotation é informado o tipo de anotação na qual deseja-se repetir. No final da implementação da mesma, é possível acompanhar uma espécie de "sub-annotation" que tem um `array` do tipo `NotNull`. Só é possível que a anotação utilize o mecanismo de repetição se for implementada esta estrutura com o `array` do mesmo tipo. |
| @Documented | Indica que essa anotação será documentada via javadoc ou uma ferramenta similar.|
| @Constraint | Indica que a anotação será marcada para uso de restrição de validação. O atributo `validatedBy` indica a classe que conterá essa validação de restrição (no exemplo acima, não foi elencada esta classe de validação, porém nos exemplos apresentados nas próximas seções irei apresentar o uso desse recurso). Com a adoção desta anotação, é necessário implementar os seguintes atributos: `message`, `groups`, `payload`. |
| message() | Indica a mensagem de erro na qual será informada caso a restrição de validação seja violada. Por padrão, é necessário que informe uma mensagem de erro, que provavelmente estará no arquivo `message.properties` do seu projeto. No exemplo acima, esta mensagem está internamente em arquivos `.properties` da biblioteca `JakartaEE`.|
| groups() | Este atributo é assunto que renderia um bom artigo. Mas resumidamente, este atributo serve para criar grupos de validação específicos. No exemplo, você percebe que não utilizamos este recurso. Ele apenas está ali como uma espécie de "contrato" necessário em detrimento do uso da anotação `@Constraint`.|
| payload() | Este atributo é assunto que renderia um bom artigo. Segundo a documentação JBoss (2020), "pode ser usada [...] para atribuir objetos de payload personalizados a uma restrição". No exemplo, você percebe que não utilizamos este recurso. Ele apenas está ali como uma espécie de "contrato" necessário em detrimento do uso da anotação `@Constraint`.|

## 5. Criando anotações
_Nosso case:_ Temos uma API RESTful responsável por cadastrar pessoas físicas e jurídicas. Estas pessoas necessitam ter informações como: nome,documento e tipo de documento. Iremos utilizar duas abordagens para validar estes campos: uso de anotação em nível de classe e campo. 

Primeiramente, é necessário que você adicione a anotação `@Valid` ao lado da `@RequestBody`. Só assim, é possível que as validações presentes nas anotações utilizadas pelo classe `PersonRequest` sejam aplicadas. A seguir você acompanha como fica o trecho do código onde se adiciona esta propriedade: 
```java
@RestController
public class PersonController {

    @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Object> create(@Valid @RequestBody PersonRequest personRequest) {

        ...
```
### 5.1 Nível de campo (Field Level)
Iremos criar uma anotação que seja capaz de verificar se o campo `fullName` tem mais de uma palavra. Caso este campo tenha apenas uma única palavra, uma exceção será lançada.
No trecho abaixo, é possível observar a classe `PersonRequest` com a anotação `@GreaterThanOneWord` acima do campo `fullName`:

```java
public class PersonRequest {

    @GreaterThanOneWord
    @NotBlank
    private String fullName;

    @NotBlank
    private String document;

    @NotNull
    private DocumentType documentType;

```
> Colocamos uma anotação inexistente acima do atributo, porém é hora de pôr a mão na massa e cria-la!  

Abaixo é possível acompanhar a implementação da anotação:
```java
@Repeatable(GreaterThanOneWord.List.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = GreaterThanOneWorldValidation.class)
public @interface GreaterThanOneWord {

    String message() default "{error.business.greater_than_one_word}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD})
    @interface List {
        GreaterThanOneWord[] value();
    }
}
```
Observe que no exemplo acima, utilizamos a meta-annotation `@Repeatable` para possibilitar o reuso desta anotação em outros atributos da mesma classe. Na `@Target`, informamos que a anotação só pode ser utilizada em nível de campo. Em `@Retention`, utilizamos o tipo `RUNTIME` em razão de sua utilização ser em tempo de execução. Em `@Constraint`, informamos no atributo `validatedBy` a classe que iremos aplicar a validação que indicará se ocorreu ou não violação da restrição. 

No trecho a seguir, é possível acompanhar o arquivo `messages_en_US.properties` com a mensagem padrão que será apresentada caso haja violação da restrição: 
```properties
error.business.greater_than_one_word=The field must be filled with more than one word

```
Abaixo é possível acompanhar a implementação da validação das restrições:
```java
public class GreaterThanOneWorldValidation implements ConstraintValidator<GreaterThanOneWord, String> {

    @Override
    public boolean isValid(String field, ConstraintValidatorContext constraintValidatorContext) {
        return field.split(" ").length > 1;
    }
}
```
Observe no exemplo acima que para utilizar esta classe na propriedade `validatedBy`, é necessário que esta implemente a interface `ConstraintValidator`. Nos dois generics apresentados pela interface, você deve informar: 
- 1º: o tipo da anotação criada
- 2º: tipo de objeto que será validado (caso queira trabalhar com atributos genéricos você deve informar `Object`)
 
Essa interface oferece dois métodos: 
- `initilize()`: pode ou não ser implementado. Normalmente utilizado quando a anotação recebe informações nos atributos que necessitam ser utilizados no método `isValid`.
- `isValid()`: método booleano responsável por validar se há restrição violada ou não. Ali é aplicado a lógica desejada. Caso retorne `true` significa que nenhuma restrição foi violada, caso contrário uma exceção será lançada. 

> Acesse o [LINK](https://gitlab.com/renata_fraga/java-annotation-spring/-/tree/master/annotation-field-level-example) do repositório do projeto. Lá você encontra a implementação completa, além de tratamento de exceções, internacionalização e testes unitários. 

### 5.2 Nível de classe (Type Level) 
Agora, iremos criar uma anotação para verificar se a quantidade de caracteres inseridos no campo `document` corresponde ao `documentType` informado, ou seja: 
- se `documentType` for igual a `CNPJ`, o campo `document` deve ter 14 caracteres
- se `documentType` for igual a `CPF`, o campo `document` deve ter 11 caracteres

> Peço que não levem em consideração a veracidade desta validação na qual criei, pois sabemos que existem algumas variações acerca da quantidade de caracteres de um CNPJ. Mas para fins didáticos, esse exemplo será útil.

No trecho abaixo, é possível observar a anotação `@IsValidDocumentFormat` sobre a classe `PersonRequest`: 
``` java
@IsValidDocumentFormat
public class PersonRequest {

    @NotBlank
    private String fullName;

    @NotBlank
    private String document;

    @NotNull
    private DocumentType documentType;
```
Abaixo é possível acompanhar a implementação da anotação:
```java
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IsValidDocumentFormatValidation.class)
public @interface IsValidDocumentFormat {

    String message() default "{error.business.is_valid_document_format}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

}
```
Como apresentado no exemplo acima, não utilizamos a meta-annotation `@Repeatable`, pois desejamos que esta seja utilizada apenas uma única vez na classe. Na `@Target`, informamos que a anotação só pode ser utilizada em nível de classe. As demais meta-annotations já foram esclarecidas na sub-seção anterior. 

No trecho a seguir, é possível acompanhar o arquivo `messages_en_US.properties` com a mensagem padrão que será apresentada caso haja violação da restrição: 
```properties
error.business.is_valid_document_format=Invalid document format for the type entered
```
Abaixo é possível acompanhar a implementação da validação das restrições: 
```java
public class IsValidDocumentFormatValidation implements ConstraintValidator<IsValidDocumentFormat,
        PersonRequest> {

    @Override
    public boolean isValid(PersonRequest personRequest, ConstraintValidatorContext constraintValidatorContext) {
        final boolean isValidCpf = personRequest.getDocument().length() == 11 && personRequest.getDocumentType()
                .equals(DocumentType.CPF);

        final boolean isValidCnpj = personRequest.getDocument().length() == 14 && personRequest.getDocumentType()
                .equals(DocumentType.CNPJ);

        return isValidCpf || isValidCnpj;
    }
}
```
Observe que nos dois generics apresentados pela interface, você deve informar: 
- 1º: o tipo da anotação criada
- 2º: tipo de classe que será validada (caso queira trabalhar com classes genéricas você deve informar `Object`)

> Acesse o [LINK](https://gitlab.com/renata_fraga/java-annotation-spring/-/tree/master/annotation-type-level-example) do repositório do projeto. Lá você encontra a implementação completa, além de tratamento de exceções, internacionalização e testes unitários. 

## 6. Conclusão e Próximos passos
O que podemos concluir acerca da adotação de anotações é que elas podem ser um ótimo mecanismo para realizar validações de entrada, reaproveitando códigos e abstraindo muitas estruturas.

É importante ressaltar que as anotações não são soluções mágicas que resolvem todos os seus problemas. Elas não podem ser utilizadas de forma demasiada, nem ter grande responsabilidade a ponto de sua camada de negócios tornar-se um mero coadjuvante dentro do seu serviço (acredito que isso seja pauta inclusive de maiores discussões). 

Neste exemplo, não pude apresentar anotações em tempo de compilação e construção, tampouco a adoção de anotações genéricas que fazem uso de Reflections. Resolvi trazer neste artigo esta parte introdutória para que possamos ir aprofundando aos poucos. Caso você queira que eu continue essa série de artigos deixe nos comentários ou no meu LinkedIn. 

Vou deixar abaixo as referências de minha pesquisa e recomendo fortemente que você as leia, principalmente na documentação oficial sobre as meta-annotations. 

## Referências:
DOLSZEWSKI. **Spring Custom Validation by Example**. Disponível em: http://dolszewski.com/spring/custom-validation-annotation-in-spring/. Acesso em: 26 Ago 2020.
GEEKS FOR GEEKS.**Annotations In Java**. Disponível em: https://www.geeksforgeeks.org/annotations-in-java/. Acesso em: 23 Ago 2020. 
GRONER, Loiane.**Vídeo: Curso de Java 65: Annotations (anotações)**. Disponível em: https://www.youtube.com/watch?v=6M8EE_oRwtM. Acesso em: 23 Ago 2020. 
JAKARTAEE.**Annotation Type NotNull**. Disponível em: https://jakarta.ee/specifications/platform/8/apidocs/javax/validation/constraints/NotNull.html. Acesso em: 23 Ago 2020.
JAVA2NOVICE. **What is Retention policy in java annotations?**. Disponível em: https://www.java2novice.com/java-annotations/retention-policy/#:~:text=Annotation%20with%20retention%20policy%20SOURCE,to%20the%20JVM%20through%20runtime. Acesso em: 26 Ago 2020.
JBOSS. **6. Creating custom constraints**. Disponível em: https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#validator-customconstraints. Acesso em: 26 Ago 2020. 
JENKOV, Jakob. **Java Annotations**. Disponível em: http://tutorials.jenkov.com/java/annotations.html. Acesso em: 23 Ago 2020. 
MAPSTRUCT. **Mapper Annotation Type**. Disponível em: https://mapstruct.org/documentation/dev/api/. Acesso em: 26 Ago 2020. 
ORACLE. **Annotation Type Constraint**. Disponível em: https://jakarta.ee/specifications/platform/8/apidocs/javax/validation/Constraint.html. Acesso em: 26 Ago 2020.
ORACLE. **Annotation Type Documented**. Disponível em: https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Documented.html?is-external=true. Acesso em: 26 Ago 2020.
ORACLE. **Annotation Type Repeatable**. Disponível em: https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Repeatable.html?is-external=true. Acesso em: 26 Ago 2020.
ORACLE. **Annotation Type Retention**. Disponível em: https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Retention.html. Acesso em: 26 Ago 2020. 
ORACLE. **Annotation Type Target**. Disponível em: https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Target.html. Acesso em: 26 Ago 2020.
ORACLE.**Lesson: Annotations**. Disponível em: https://docs.oracle.com/javase/tutorial/java/annotations/. 
Acesso em: 23 Ago 2020.