Hibernate Validator is the reference implementation of Jakarta Validation 3.1 providing annotation-based validation for JavaBeans and method parameters
Multiple resource bundle locator implementations supporting platform resource bundles, aggregation of multiple bundles, caching, and delegation patterns for flexible message source configuration.
Default platform-based resource bundle locator using Java's standard ResourceBundle mechanism.
package org.hibernate.validator.resourceloading;
import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
import java.util.Locale;
import java.util.ResourceBundle;
/**
* Default implementation using platform's ResourceBundle mechanism.
* Loads resource bundles using standard Java resource bundle loading.
*/
class PlatformResourceBundleLocator implements ResourceBundleLocator {
/**
* Create locator for specified bundle name.
*
* @param bundleName resource bundle name (without .properties extension)
*/
PlatformResourceBundleLocator(String bundleName);
/**
* Create locator with custom class loader.
*
* @param bundleName resource bundle name
* @param classLoader class loader for loading bundles
*/
PlatformResourceBundleLocator(String bundleName, ClassLoader classLoader);
/**
* Create locator with class loader and control.
*
* @param bundleName resource bundle name
* @param classLoader class loader for loading bundles
* @param control resource bundle control for loading strategy
*/
PlatformResourceBundleLocator(
String bundleName,
ClassLoader classLoader,
ResourceBundle.Control control);
/**
* Get resource bundle for specified locale.
*
* @param locale locale for bundle
* @return resource bundle
*/
@Override
ResourceBundle getResourceBundle(Locale locale);
}Usage Example:
import org.hibernate.validator.resourceloading.PlatformResourceBundleLocator;
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
import jakarta.validation.*;
import java.util.*;
// Create locator for custom bundle
ResourceBundleLocator locator = new PlatformResourceBundleLocator("MyValidationMessages");
// Use with message interpolator
MessageInterpolator interpolator = new ResourceBundleMessageInterpolator(locator);
ValidatorFactory factory = Validation.byDefaultProvider()
.configure()
.messageInterpolator(interpolator)
.buildValidatorFactory();
// MyValidationMessages.properties:
// user.name.required=User name is required
// user.email.invalid=Invalid email format
// user.age.range=Age must be between {min} and {max}
// MyValidationMessages_fr.properties:
// user.name.required=Le nom d'utilisateur est requis
// user.email.invalid=Format d'email invalide
// user.age.range=L'âge doit être entre {min} et {max}Aggregates multiple resource bundle locators into a single combined bundle.
package org.hibernate.validator.resourceloading;
import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
/**
* Aggregates multiple ResourceBundleLocators.
* Combines resource bundles from multiple sources into single bundle.
* Messages are looked up in order of locators; first match wins.
*/
class AggregateResourceBundleLocator implements ResourceBundleLocator {
/**
* Create aggregating locator.
*
* @param resourceBundleLocators list of locators to aggregate
*/
AggregateResourceBundleLocator(List<ResourceBundleLocator> resourceBundleLocators);
/**
* Get aggregated resource bundle for specified locale.
* Returns AggregateResourceBundle combining all locators.
*
* @param locale locale for bundle
* @return aggregated resource bundle
*/
@Override
ResourceBundle getResourceBundle(Locale locale);
}Usage Example:
import org.hibernate.validator.resourceloading.*;
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
import java.util.*;
// Create multiple resource bundle locators
ResourceBundleLocator userMessages =
new PlatformResourceBundleLocator("UserMessages");
ResourceBundleLocator productMessages =
new PlatformResourceBundleLocator("ProductMessages");
ResourceBundleLocator orderMessages =
new PlatformResourceBundleLocator("OrderMessages");
// Aggregate them
AggregateResourceBundleLocator aggregateLocator =
new AggregateResourceBundleLocator(Arrays.asList(
userMessages,
productMessages,
orderMessages
));
// Use aggregated locator
MessageInterpolator interpolator = new ResourceBundleMessageInterpolator(aggregateLocator);
// Message lookup order:
// 1. UserMessages.properties
// 2. ProductMessages.properties
// 3. OrderMessages.properties
// First matching key winsResource bundle that aggregates multiple resource bundles.
package org.hibernate.validator.resourceloading;
import java.util.Enumeration;
import java.util.Locale;
import java.util.ResourceBundle;
/**
* ResourceBundle aggregating multiple resource bundles.
* Provides unified view over multiple resource bundle sources.
*/
class AggregateResourceBundle extends ResourceBundle {
/**
* Create aggregate bundle.
*
* @param locale locale for bundle
* @param bundlesToAggregate bundles to aggregate
*/
AggregateResourceBundle(Locale locale, Iterable<ResourceBundle> bundlesToAggregate);
/**
* Get object for key from aggregated bundles.
*
* @param key message key
* @return message value from first bundle containing key
*/
@Override
protected Object handleGetObject(String key);
/**
* Get enumeration of all keys from all aggregated bundles.
*
* @return enumeration of all keys
*/
@Override
Enumeration<String> getKeys();
}Wraps another resource bundle locator with caching capability.
package org.hibernate.validator.resourceloading;
import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
import java.util.Locale;
import java.util.ResourceBundle;
/**
* ResourceBundleLocator with caching support.
* Caches resource bundles by locale to avoid repeated loading.
*/
class CachingResourceBundleLocator implements ResourceBundleLocator {
/**
* Create caching locator wrapping delegate.
*
* @param delegate underlying locator to cache
*/
CachingResourceBundleLocator(ResourceBundleLocator delegate);
/**
* Get cached resource bundle for locale.
* Loads from delegate and caches on first request.
*
* @param locale locale for bundle
* @return cached resource bundle
*/
@Override
ResourceBundle getResourceBundle(Locale locale);
}Usage Example:
import org.hibernate.validator.resourceloading.*;
// Create base locator
ResourceBundleLocator baseLocator =
new PlatformResourceBundleLocator("ValidationMessages");
// Wrap with caching
CachingResourceBundleLocator cachingLocator =
new CachingResourceBundleLocator(baseLocator);
// Use caching locator
// Subsequent calls with same locale will return cached bundle
ResourceBundle bundle1 = cachingLocator.getResourceBundle(Locale.US);
ResourceBundle bundle2 = cachingLocator.getResourceBundle(Locale.US);
// bundle1 and bundle2 are the same instance (cached)Delegates resource bundle loading to another locator.
package org.hibernate.validator.resourceloading;
import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
import java.util.Locale;
import java.util.ResourceBundle;
/**
* ResourceBundleLocator that delegates to another locator.
* Base class for decorating locators with additional behavior.
*/
class DelegatingResourceBundleLocator implements ResourceBundleLocator {
/**
* Create delegating locator.
*
* @param delegate locator to delegate to
*/
DelegatingResourceBundleLocator(ResourceBundleLocator delegate);
/**
* Get resource bundle by delegating to wrapped locator.
*
* @param locale locale for bundle
* @return resource bundle from delegate
*/
@Override
ResourceBundle getResourceBundle(Locale locale);
}import org.hibernate.validator.resourceloading.*;
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
import jakarta.validation.*;
import java.util.*;
/**
* Configure complex resource bundle hierarchy:
* 1. Application-specific messages
* 2. Module-specific messages
* 3. Contributor messages
* 4. Default ValidationMessages
*/
class ResourceBundleConfiguration {
public ValidatorFactory createValidatorFactory() {
// Create individual locators
ResourceBundleLocator appMessages =
new PlatformResourceBundleLocator("ApplicationMessages");
ResourceBundleLocator userModuleMessages =
new PlatformResourceBundleLocator("UserModuleMessages");
ResourceBundleLocator productModuleMessages =
new PlatformResourceBundleLocator("ProductModuleMessages");
ResourceBundleLocator contributorMessages =
new PlatformResourceBundleLocator("ContributorMessages");
ResourceBundleLocator defaultMessages =
new PlatformResourceBundleLocator("ValidationMessages");
// Aggregate module messages
AggregateResourceBundleLocator moduleMessages =
new AggregateResourceBundleLocator(Arrays.asList(
userModuleMessages,
productModuleMessages
));
// Create final hierarchy
AggregateResourceBundleLocator allMessages =
new AggregateResourceBundleLocator(Arrays.asList(
appMessages, // Highest priority
moduleMessages,
contributorMessages,
defaultMessages // Lowest priority
));
// Add caching for performance
CachingResourceBundleLocator cachedMessages =
new CachingResourceBundleLocator(allMessages);
// Create message interpolator
ResourceBundleMessageInterpolator interpolator =
new ResourceBundleMessageInterpolator(cachedMessages);
// Build validator factory
return Validation.byDefaultProvider()
.configure()
.messageInterpolator(interpolator)
.buildValidatorFactory();
}
}
// Message lookup order:
// 1. ApplicationMessages.properties (app-specific overrides)
// 2. UserModuleMessages.properties (user module messages)
// 3. ProductModuleMessages.properties (product module messages)
// 4. ContributorMessages.properties (contributor messages)
// 5. ValidationMessages.properties (defaults)import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
import java.util.*;
/**
* Custom resource bundle locator loading messages from database.
*/
class DatabaseResourceBundleLocator implements ResourceBundleLocator {
private final MessageRepository messageRepository;
public DatabaseResourceBundleLocator(MessageRepository messageRepository) {
this.messageRepository = messageRepository;
}
@Override
public ResourceBundle getResourceBundle(Locale locale) {
// Load messages from database
Map<String, String> messages = messageRepository.findMessagesByLocale(locale);
// Create resource bundle from database messages
return new DatabaseResourceBundle(messages);
}
}
/**
* ResourceBundle backed by database messages.
*/
class DatabaseResourceBundle extends ResourceBundle {
private final Map<String, String> messages;
public DatabaseResourceBundle(Map<String, String> messages) {
this.messages = messages;
}
@Override
protected Object handleGetObject(String key) {
return messages.get(key);
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(messages.keySet());
}
}
// Usage
interface MessageRepository {
Map<String, String> findMessagesByLocale(Locale locale);
}
class MessageRepositoryImpl implements MessageRepository {
@Override
public Map<String, String> findMessagesByLocale(Locale locale) {
// Query database for messages
Map<String, String> messages = new HashMap<>();
// ... load from database ...
return messages;
}
}
// Configure validator with database messages
MessageRepository repository = new MessageRepositoryImpl();
ResourceBundleLocator dbLocator = new DatabaseResourceBundleLocator(repository);
// Combine database messages with file-based messages
ResourceBundleLocator fileLocator =
new PlatformResourceBundleLocator("ValidationMessages");
AggregateResourceBundleLocator combinedLocator =
new AggregateResourceBundleLocator(Arrays.asList(
dbLocator, // Database messages (higher priority)
fileLocator // File messages (fallback)
));
MessageInterpolator interpolator =
new ResourceBundleMessageInterpolator(combinedLocator);
ValidatorFactory factory = Validation.byDefaultProvider()
.configure()
.messageInterpolator(interpolator)
.buildValidatorFactory();/**
* Best practices for organizing validation messages:
*
* 1. Hierarchical structure:
* - ValidationMessages.properties (defaults from Jakarta Validation & Hibernate Validator)
* - ApplicationMessages.properties (application-wide overrides)
* - ModuleMessages.properties (module-specific messages)
* - CustomMessages.properties (custom constraint messages)
*
* 2. Message key conventions:
* - Standard constraints: Use default keys (e.g., jakarta.validation.constraints.NotNull.message)
* - Custom constraints: Use fully qualified names (e.g., com.example.constraints.CustomConstraint.message)
* - Domain-specific: Use domain prefixes (e.g., user.email.invalid, product.price.negative)
*
* 3. Localization:
* - Create locale-specific files: ValidationMessages_fr.properties, ValidationMessages_de.properties
* - Keep keys consistent across all locales
* - Provide fallback messages in default bundle
*
* 4. Parameter naming:
* - Use descriptive parameter names: {min}, {max}, {value}, {regexp}
* - Document expected parameters in comments
*
* 5. Performance:
* - Use caching locators for frequently accessed bundles
* - Preload bundles for known locales at startup
* - Avoid complex bundle hierarchies for simple applications
*/
// Example bundle structure:
// ValidationMessages.properties (defaults)
// jakarta.validation.constraints.NotNull.message=must not be null
// jakarta.validation.constraints.Size.message=size must be between {min} and {max}
// ApplicationMessages.properties (overrides)
// jakarta.validation.constraints.NotNull.message=This field is required
// user.email.invalid=Please provide a valid email address
// user.password.weak=Password must contain at least 8 characters with letters and numbers
// ModuleMessages.properties (module-specific)
// order.total.negative=Order total cannot be negative
// order.items.empty=Order must contain at least one item
// order.discount.invalid=Discount percentage must be between 0 and 100
// ValidationMessages_fr.properties (French)
// jakarta.validation.constraints.NotNull.message=ne doit pas être null
// user.email.invalid=Veuillez fournir une adresse email valide
// Usage
ResourceBundleLocator appLocator = new PlatformResourceBundleLocator("ApplicationMessages");
ResourceBundleLocator moduleLocator = new PlatformResourceBundleLocator("ModuleMessages");
ResourceBundleLocator defaultLocator = new PlatformResourceBundleLocator("ValidationMessages");
AggregateResourceBundleLocator aggregateLocator = new AggregateResourceBundleLocator(
Arrays.asList(appLocator, moduleLocator, defaultLocator)
);
CachingResourceBundleLocator cachingLocator = new CachingResourceBundleLocator(aggregateLocator);
MessageInterpolator interpolator = new ResourceBundleMessageInterpolator(cachingLocator);Install with Tessl CLI
npx tessl i tessl/maven-org-hibernate-validator--hibernate-validator