DEV Community

Cover image for Jakarta Validator em Construtores
Uiratan Cavalcante
Uiratan Cavalcante

Posted on

Jakarta Validator em Construtores

As as anotações de validação do Jakarta Validation (Bean Validation) @notblank, @Email e @Size aplicadas nos parâmetros do construtor não impedem a criação do objeto em estado inconsistente automaticamente.

❌ Por que essas anotações não impedem a criação do objeto?

  • As anotações de validação do Jakarta Validation (ex: @NotBlank, @Size) não são verificadas automaticamente dentro do construtor. Elas só são aplicadas quando o Spring ou outro framework as invoca explicitamente com um mecanismo de validação.
  • No código apresentado, nada impede que o construtor receba valores inválidos, pois o Java puro não interpreta essas anotações.

🛠 Como evitar a criação de um objeto inconsistente?

Para garantir que a validação ocorra dentro do construtor, você pode fazer uma das seguintes abordagens:

1️⃣ Utilizar @Valid em um DTO e converter para a entidade (Recomendado ✅)

Em um Controller do Spring Boot, a validação acontece automaticamente quando você usa @Valid na entrada de um método.

import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/autores")
public class AutorController {

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public void cadastrarAutor(@RequestBody @Valid AutorRequest autorRequest) {
        Autor autor = autorRequest.toModel(); // Conversão segura após validação
        System.out.println("Autor cadastrado: " + autor.getNome());
    }
}
Enter fullscreen mode Exit fullscreen mode

No DTO:

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public class AutorRequest {
    @NotBlank(message = "O nome é obrigatório")
    private String nome;

    @NotBlank(message = "O email é obrigatório")
    @Email(message = "Formato de email inválido")
    private String email;

    @NotBlank(message = "A descrição é obrigatória")
    @Size(max = 400, message = "A descrição não pode passar de 400 caracteres")
    private String descricao;

    public AutorRequest(String nome, String email, String descricao) {
        this.nome = nome;
        this.email = email;
        this.descricao = descricao;
    }

    public Autor toModel() {
        return new Autor(nome, email, descricao);
    }
}
Enter fullscreen mode Exit fullscreen mode

✅ Aqui a validação acontece antes do objeto Autor ser criado!


2️⃣ Forçar a validação manualmente no construtor

Caso queira manter as anotações no construtor e garantir que a validação seja feita dentro da própria classe, você pode chamar um Validator manualmente:

import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import jakarta.validation.constraints.*;

import java.time.Instant;
import java.util.Set;
import jakarta.validation.ConstraintViolation;

public class Autor {

    @NotBlank(message = "O nome é obrigatório")
    private final String nome;

    @NotBlank(message = "O email é obrigatório")
    @Email(message = "Formato de email inválido")
    private final String email;

    @NotBlank(message = "A descrição é obrigatória")
    @Size(max = 400, message = "A descrição não pode passar de 400 caracteres")
    private final String descricao;

    private final Instant instanteCriacao;

    public Autor(String nome, String email, String descricao) {
        this.nome = nome;
        this.email = email;
        this.descricao = descricao;
        this.instanteCriacao = Instant.now();
        validar(this); // Chama a validação manualmente
    }

    private void validar(Autor autor) {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        Set<ConstraintViolation<Autor>> violations = validator.validate(autor);

        if (!violations.isEmpty()) {
            throw new IllegalArgumentException(violations.iterator().next().getMessage());
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

🚨 Desvantagem: Essa abordagem é um pouco mais burocrática, pois exige a criação de um Validator manualmente.


3️⃣ Usar @Validated com Spring para validar no construtor

Caso você utilize o Spring, pode anotar a classe como @Validated e injetar um Validator:

import jakarta.validation.constraints.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.Instant;

@Component
@Validated
public class Autor {

    private final String nome;
    private final String email;
    private final String descricao;
    private final Instant instanteCriacao;

    @Autowired
    public Autor(
        @NotBlank(message = "O nome é obrigatório") String nome,
        @NotBlank(message = "O email é obrigatório") @Email(message = "Formato de email inválido") String email,
        @NotBlank(message = "A descrição é obrigatória") @Size(max = 400, message = "A descrição não pode passar de 400 caracteres") String descricao
    ) {
        this.nome = nome;
        this.email = email;
        this.descricao = descricao;
        this.instanteCriacao = Instant.now();
    }
}
Enter fullscreen mode Exit fullscreen mode

✅ Essa abordagem usa o Spring para injetar automaticamente a validação.


🎯 Conclusão

🔴 As anotações @NotBlank, @Email, @Size dentro do construtor NÃO impedem a criação do objeto automaticamente.

✅ Para evitar um estado inconsistente, é melhor validar os dados antes de criar a instância, usando um DTO (@Valid no Controller).

✅ Se necessário, você pode chamar a validação manualmente dentro do construtor ou usar Spring para gerenciá-la com @Validated.

Top comments (0)