Anotaciones de restricciones personalizadas
Cree una anotación @UniqueEmail personalizada con una implementación de ConstraintValidator.
Anotaciones de restricciones personalizadas es una lección gratuita de Java Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Java Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Java Academy incluye 4 lecciones en total.
Cuando las restricciones estándar no son suficientes
Las anotaciones estándar de Bean Validation cubren los casos habituales. Para reglas específicas del dominio (por ejemplo, correo electrónico único, IBAN válido o fortaleza de la contraseña), cree anotaciones de restricciones personalizadas.
Definir la anotación de restricción
Cree una anotación con @Constraint(validatedBy = ...), los elementos obligatorios message, groups y payload, además de la retención y el destino adecuados.
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Documented
@Constraint(validatedBy = UniqueEmailValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface UniqueEmail {
String message() default "Email already registered";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}Implementar ConstraintValidator
Implemente ConstraintValidator<UniqueEmail, String>. El método initialize lee los elementos de la anotación; isValid realiza la comprobación efectiva.
@Component
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
@Autowired private UserRepository repo;
@Override
public boolean isValid(String email, ConstraintValidatorContext ctx) {
if (email == null) return true; // let @NotBlank handle null
return !repo.existsByEmail(email);
}
}Usar la restricción personalizada
Aplique la anotación como cualquier restricción estándar a un campo, parámetro o valor devuelto.
public record CreateUserRequest(
@NotBlank
String name,
@NotBlank @Email @UniqueEmail
String email
) {}Mensaje personalizado con interpolación
Haga referencia a los elementos de la anotación en el mensaje mediante {elementName}. Utilice ctx.buildConstraintViolationWithTemplate() para añadir mensajes personalizados con datos dinámicos.
@Constraint(validatedBy = RangeValidator.class)
public @interface InRange {
int min() default 0;
int max() default 100;
String message() default "Must be between {min} and {max}";
// ...
}Restricción a nivel de clase
Aplique una restricción a nivel de clase para validar varios campos conjuntamente, lo que resulta útil para reglas entre campos, como la confirmación de una contraseña.
@Documented
@Constraint(validatedBy = PasswordMatchValidator.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PasswordMatch {
String message() default "Passwords do not match";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}Implementación del validador a nivel de clase
El validador recibe el objeto completo. Acceda a varios campos para realizar la validación entre campos.
public class PasswordMatchValidator implements ConstraintValidator<PasswordMatch, ChangePasswordRequest> {
public boolean isValid(ChangePasswordRequest req, ConstraintValidatorContext ctx) {
if (req.getNewPassword() == null) return true;
boolean match = req.getNewPassword().equals(req.getConfirmPassword());
if (!match) {
ctx.disableDefaultConstraintViolation();
ctx.buildConstraintViolationWithTemplate("Passwords do not match")
.addPropertyNode("confirmPassword").addConstraintViolation();
}
return match;
}
}Componer restricciones
Cree una metarrestricción anotando su anotación personalizada con restricciones existentes. Ambas se aplican automáticamente.
@NotBlank
@Email
@Size(max = 255)
@Constraint(validatedBy = {})
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidEmail {
String message() default "Invalid email";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}Devolver varias infracciones
Utilice ctx.disableDefaultConstraintViolation() y añada mensajes de infracción individuales para cada problema encontrado en una sola ejecución del validador.
public boolean isValid(String password, ConstraintValidatorContext ctx) {
List<String> issues = new ArrayList<>();
if (password.length() < 8) issues.add("Too short");
if (!password.matches(".*\\d.*")) issues.add("Must contain a digit");
if (issues.isEmpty()) return true;
ctx.disableDefaultConstraintViolation();
issues.forEach(msg -> ctx.buildConstraintViolationWithTemplate(msg).addConstraintViolation());
return false;
}Validadores gestionados por Spring
Como UniqueEmailValidator está anotado con @Component, Spring le inyecta los repositorios. Esto solo funciona si el validador se inicializa mediante el validador de Spring, que Spring Boot configura automáticamente.
Probar validadores personalizados
Pruebe los validadores unitariamente y de forma directa; no se necesita un contexto de Spring para la lógica de validación pura. Utilice Validation.buildDefaultValidatorFactory() para las pruebas de integración.
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
CreateUserRequest req = new CreateUserRequest("", "bad-email", null);
Set<ConstraintViolation<CreateUserRequest>> violations = validator.validate(req);
System.out.println(violations.size()); // 3Comprobación rápida
¿Qué interfaz implementa un validador de restricciones personalizado?
Resumen
Cree una @interface con @Constraint(validatedBy=...). Implemente ConstraintValidator. Utilice @Component para la inyección de Spring. Las restricciones a nivel de clase validan varios campos conjuntamente. Componga restricciones existentes para evitar duplicaciones.
Preguntas frecuentes
¿La lección «Anotaciones de restricciones personalizadas» es gratis?
Sí — el texto completo de «Anotaciones de restricciones personalizadas» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Java Academy, actualiza a CoddyKit PRO. El curso de Java Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Anotaciones de restricciones personalizadas»?
Cree una anotación @UniqueEmail personalizada con una implementación de ConstraintValidator. Practicas Java Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Java Academy?
No se requiere experiencia previa. Java Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «Anotaciones de restricciones personalizadas»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Java Academy?
Sí. Cada lección de Java Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Validación de beans: @NotNull, @Size, @Pattern
- Anotaciones de restricciones personalizadas
- Gestión global de excepciones con @ControllerAdvice
- Detalles de problemas RFC 7807 y respuestas de error coherentes