A API Bean Validations permite incluir anotações no código que dizem como os campos serão validados pela aplicação.

Abaixo temos um exemplo de algumas anotações básicas existentes na API:

public class User {
    @NotNull @Email
    private String email;

    public String getEmail() {
      return email;
    }

    public void setEmail(String email) {
      this.email = email;
    }
}

@ValidateParams
public class UserService {

  public void createUser(@Email String email,
                         @NotNull String name) {
    ...
  }
}

Junto com a estrutura desenvolvida no Identity, tais anotações são validadas automaticamente quando a classe ou o método possui a anotação @ValidateParams.

Para isso, as anotações de validação devem ser colocadas na classe de Implementação e também nas Interfaces onde os métodos são definidos.

A anotação @ValidateParams deve ser colocada somente na implementação.

public class ServiceInterface {
  public void createUser(@Email String email, @NotNull String name);
}
@ValidateParams
public class ServiceImpl {
  public void createUser(@Email String email, @NotNull String name){
    //...
  }
}

Agrupamento de Validações

É possível agrupar validações em uma mesma validação personalizada, conforme exemplo abaixo, que faz várias validações em um único parâmetro.:

@ValidateParams
public class CarService {

  public void findCar(@NotNull @NotBlank @NotEmpty @Pattern(regex="[A-Z]{3}\d{4}") String plate) {
    ...
  }
}

Para simplificar, poderíamos criar uma validação personalizada, combinando todas essas validações em uma única validação (@Plate)

@ValidateParams
public class CarService {

  public void findCar(@Plate String plate) {
    ...
  }
}

Para isso, é necessário criar uma anotação nova, seguindo alguns padrões.

// agrupamento das validações utilizadas
@NotNull
@NotEmpty
@NotBlank
@Pattern(regex="[A-Z]{3}\d{4}")
 
// Elementos onde essa anotação pode ser colocada
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
 
// Anotação que indica que esta é uma anotação de validação
@Constraint(validatedBy = {})


// Anotação que indica que todas as anotações agrupadas serão tratadas como uma unica violação
@ReportAsSingleViolation
public @interface Plate {
	// Identificação da mensagem a ser exibida
    String message() default "{com.totvs.custom.validation.plate.message}";

	// Permite definir grupos com outras anotações
    Class<?>[] groups() default {};

	// Área para inclusão de dados genéricos
    Class<? extends Payload>[] payload() default {};
}

É possível também criar validadores customizados para as anotações.

Seguindo o exemplo acima, poderíamos incluir uma verificação no Detran, para verificar se a placa é realmente válida, para isso, é necessário escrever código para realizar essa consulta.

Com algumas modificações, é possível incluir um validador customizado para essa tarefa:

@NotNull
@NotEmpty
@NotBlank
@Pattern(regex="[A-Z]{3}\d{4}")
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)

// Classe que irá fazer a validação no Detran 
@Constraint(validatedBy = DetranValidator.class)
@ReportAsSingleViolation
public @interface Plate{
    String message() default "{com.totvs.custom.validation.plate.message}";

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

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



public class DetranValidator implements ConstraintValidator<Plate, String> {

	private CaseMode caseMode;

	@Override
	public void initialize(CheckCase constraintAnnotation) {
		this.caseMode = constraintAnnotation.value();
	}

	@Override
	public boolean isValid(String object, ConstraintValidatorContext constraintContext) {
		var validDetran = pesquisaDetran(); // logica de pesquisa no detran
		if ( validDetran ) {
			return true;
		}
		return false;
	}
}