or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdconstraint-validation.mdconstraints.mdindex.mdmessage-interpolation.mdpath-navigation.mdprogrammatic-config.mdresource-loading.mdspi.md

spi.mddocs/

0

# Service Provider Interfaces

1

2

Comprehensive SPI for extending Hibernate Validator including constraint mapping contributors, dynamic group sequence providers, custom locale resolvers, property node name providers, getter property selection strategies, resource bundle locators, and script evaluator factories.

3

4

## Capabilities

5

6

### Constraint Mapping Contributor SPI

7

8

Contribute constraint mappings programmatically during validator factory creation.

9

10

```java { .api }

11

package org.hibernate.validator.spi.cfg;

12

13

import org.hibernate.validator.cfg.ConstraintMapping;

14

15

/**

16

* Contributes constraint mappings to validator configuration.

17

* Implementations are discovered via ServiceLoader or configured explicitly.

18

*/

19

interface ConstraintMappingContributor {

20

/**

21

* Create and add constraint mappings to builder.

22

*

23

* @param builder constraint mapping builder

24

*/

25

void createConstraintMappings(ConstraintMappingBuilder builder);

26

27

/**

28

* Builder for adding constraint mappings.

29

*/

30

interface ConstraintMappingBuilder {

31

/**

32

* Add new constraint mapping to configuration.

33

*

34

* @return newly created constraint mapping for fluent configuration

35

*/

36

ConstraintMapping addConstraintMapping();

37

}

38

}

39

```

40

41

**Usage Example:**

42

43

```java

44

import org.hibernate.validator.spi.cfg.ConstraintMappingContributor;

45

import org.hibernate.validator.cfg.ConstraintMapping;

46

import org.hibernate.validator.cfg.defs.*;

47

import static java.lang.annotation.ElementType.*;

48

49

/**

50

* Contributor that adds validation constraints for all User entities.

51

*/

52

public class UserConstraintContributor implements ConstraintMappingContributor {

53

54

@Override

55

public void createConstraintMappings(ConstraintMappingBuilder builder) {

56

// Add user constraints

57

builder.addConstraintMapping()

58

.type(User.class)

59

.field("username")

60

.constraint(new NotBlankDef())

61

.constraint(new LengthDef().min(3).max(50))

62

.field("email")

63

.constraint(new NotBlankDef())

64

.constraint(new EmailDef())

65

.field("age")

66

.constraint(new MinDef().value(18))

67

.constraint(new MaxDef().value(120));

68

69

// Add address constraints

70

builder.addConstraintMapping()

71

.type(Address.class)

72

.field("street")

73

.constraint(new NotBlankDef())

74

.field("zipCode")

75

.constraint(new NotBlankDef())

76

.constraint(new PatternDef().regexp("[0-9]{5}"));

77

}

78

}

79

80

// Register via ServiceLoader:

81

// Create META-INF/services/org.hibernate.validator.spi.cfg.ConstraintMappingContributor

82

// Add line: com.example.UserConstraintContributor

83

84

// Or configure explicitly:

85

ValidatorFactory factory = Validation.byProvider(HibernateValidator.class)

86

.configure()

87

.addProperty(

88

BaseHibernateValidatorConfiguration.CONSTRAINT_MAPPING_CONTRIBUTORS,

89

"com.example.UserConstraintContributor"

90

)

91

.buildValidatorFactory();

92

```

93

94

### Group Sequence Provider SPI

95

96

Provide default group sequence dynamically at validation time.

97

98

```java { .api }

99

package org.hibernate.validator.spi.group;

100

101

import java.util.List;

102

103

/**

104

* Provides default group sequence for given type dynamically.

105

* Allows group sequence to depend on object state.

106

*

107

* @param <T> type for which to provide default group sequence

108

*/

109

interface DefaultGroupSequenceProvider<T> {

110

/**

111

* Returns validation groups for given object.

112

* The Default group will be replaced by returned groups.

113

*

114

* @param object object being validated

115

* @return list of validation groups

116

*/

117

List<Class<?>> getValidationGroups(T object);

118

}

119

120

/**

121

* Annotation that specifies the DefaultGroupSequenceProvider implementation

122

* for dynamic group sequence resolution.

123

*

124

* Applied to bean types to enable state-dependent validation group ordering.

125

* Cannot be used together with @GroupSequence.

126

*

127

* This is a Hibernate Validator specific annotation (not portable).

128

*

129

* @see DefaultGroupSequenceProvider

130

* @see jakarta.validation.GroupSequence

131

*/

132

package org.hibernate.validator.group;

133

134

import java.lang.annotation.*;

135

import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;

136

137

@Target({ElementType.TYPE})

138

@Retention(RetentionPolicy.RUNTIME)

139

@interface GroupSequenceProvider {

140

/**

141

* The DefaultGroupSequenceProvider implementation to use.

142

*

143

* @return default group sequence provider class

144

*/

145

Class<? extends DefaultGroupSequenceProvider<?>> value();

146

}

147

```

148

149

**Usage Example:**

150

151

```java

152

import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;

153

import org.hibernate.validator.group.GroupSequenceProvider;

154

import jakarta.validation.constraints.*;

155

import java.util.*;

156

157

// Define validation groups

158

interface BasicChecks {}

159

interface PremiumChecks {}

160

interface AdminChecks {}

161

162

// Entity with dynamic group sequence

163

@GroupSequenceProvider(UserGroupSequenceProvider.class)

164

class User {

165

@NotNull

166

private String username;

167

168

@NotNull(groups = BasicChecks.class)

169

@Email(groups = BasicChecks.class)

170

private String email;

171

172

@NotNull(groups = PremiumChecks.class)

173

@Pattern(regexp = "\\d{3}-\\d{3}-\\d{4}", groups = PremiumChecks.class)

174

private String phoneNumber;

175

176

@NotNull(groups = AdminChecks.class)

177

private String adminToken;

178

179

private UserType userType;

180

181

// getters/setters...

182

}

183

184

enum UserType {

185

BASIC, PREMIUM, ADMIN

186

}

187

188

/**

189

* Provides dynamic group sequence based on user type.

190

*/

191

class UserGroupSequenceProvider implements DefaultGroupSequenceProvider<User> {

192

193

@Override

194

public List<Class<?>> getValidationGroups(User user) {

195

List<Class<?>> groups = new ArrayList<>();

196

197

// Always include Default group

198

groups.add(User.class);

199

200

if (user == null) {

201

return groups;

202

}

203

204

// Add groups based on user type

205

switch (user.getUserType()) {

206

case BASIC:

207

groups.add(BasicChecks.class);

208

break;

209

case PREMIUM:

210

groups.add(BasicChecks.class);

211

groups.add(PremiumChecks.class);

212

break;

213

case ADMIN:

214

groups.add(BasicChecks.class);

215

groups.add(PremiumChecks.class);

216

groups.add(AdminChecks.class);

217

break;

218

}

219

220

return groups;

221

}

222

}

223

224

// Usage

225

Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

226

227

// Basic user - only basic checks

228

User basicUser = new User();

229

basicUser.setUserType(UserType.BASIC);

230

Set<ConstraintViolation<User>> violations = validator.validate(basicUser);

231

// Validates: username, email

232

233

// Premium user - basic + premium checks

234

User premiumUser = new User();

235

premiumUser.setUserType(UserType.PREMIUM);

236

violations = validator.validate(premiumUser);

237

// Validates: username, email, phoneNumber

238

239

// Admin user - all checks

240

User adminUser = new User();

241

adminUser.setUserType(UserType.ADMIN);

242

violations = validator.validate(adminUser);

243

// Validates: username, email, phoneNumber, adminToken

244

```

245

246

### Locale Resolver SPI

247

248

Resolve locale dynamically for message interpolation.

249

250

```java { .api }

251

package org.hibernate.validator.spi.messageinterpolation;

252

253

import java.util.Locale;

254

255

/**

256

* Resolves locale for message interpolation.

257

* Allows dynamic locale selection based on context.

258

*

259

* @since 6.1.1

260

*/

261

@Incubating

262

interface LocaleResolver {

263

/**

264

* Resolve locale for given context.

265

*

266

* @param context locale resolution context

267

* @return resolved locale

268

*/

269

Locale resolve(LocaleResolverContext context);

270

}

271

272

/**

273

* Context for locale resolution.

274

*

275

* @since 6.1.1

276

*/

277

@Incubating

278

interface LocaleResolverContext {

279

/**

280

* Get default locale configured for validator factory.

281

*

282

* @return default locale

283

*/

284

Locale getDefaultLocale();

285

286

/**

287

* Get set of supported locales configured for validator factory.

288

*

289

* @return supported locales

290

*/

291

java.util.Set<Locale> getSupportedLocales();

292

}

293

```

294

295

**Usage Example:**

296

297

```java

298

import org.hibernate.validator.spi.messageinterpolation.LocaleResolver;

299

import org.hibernate.validator.spi.messageinterpolation.LocaleResolverContext;

300

import org.hibernate.validator.HibernateValidator;

301

import jakarta.validation.*;

302

import java.util.*;

303

304

/**

305

* Locale resolver that uses thread-local context.

306

*/

307

class ThreadLocalLocaleResolver implements LocaleResolver {

308

309

private static final ThreadLocal<Locale> LOCALE_HOLDER = new ThreadLocal<>();

310

311

public static void setLocale(Locale locale) {

312

LOCALE_HOLDER.set(locale);

313

}

314

315

public static void clearLocale() {

316

LOCALE_HOLDER.remove();

317

}

318

319

@Override

320

public Locale resolve(LocaleResolverContext context) {

321

Locale locale = LOCALE_HOLDER.get();

322

323

if (locale != null && context.getSupportedLocales().contains(locale)) {

324

return locale;

325

}

326

327

// Fall back to default locale

328

return context.getDefaultLocale();

329

}

330

}

331

332

// Configure validator

333

LocaleResolver localeResolver = new ThreadLocalLocaleResolver();

334

335

ValidatorFactory factory = Validation.byProvider(HibernateValidator.class)

336

.configure()

337

.locales(Locale.US, Locale.FRANCE, Locale.GERMANY)

338

.defaultLocale(Locale.US)

339

.localeResolver(localeResolver)

340

.buildValidatorFactory();

341

342

Validator validator = factory.getValidator();

343

344

// Use with different locales per thread

345

ThreadLocalLocaleResolver.setLocale(Locale.FRANCE);

346

Set<ConstraintViolation<User>> violations = validator.validate(user);

347

// Messages interpolated in French

348

349

ThreadLocalLocaleResolver.setLocale(Locale.GERMANY);

350

violations = validator.validate(user);

351

// Messages interpolated in German

352

353

ThreadLocalLocaleResolver.clearLocale();

354

```

355

356

### Property Node Name Provider SPI

357

358

Resolve property node names dynamically in validation paths.

359

360

```java { .api }

361

package org.hibernate.validator.spi.nodenameprovider;

362

363

/**

364

* Resolves property node name when creating property path.

365

* Allows customizing how property names appear in constraint violations.

366

*

367

* @since 6.1.0

368

*/

369

@Incubating

370

interface PropertyNodeNameProvider {

371

/**

372

* Get name for property.

373

*

374

* @param property property to resolve name for

375

* @return property name

376

*/

377

String getName(Property property);

378

}

379

380

/**

381

* Represents a property for name resolution.

382

*/

383

@Incubating

384

interface Property {

385

/**

386

* Get property name.

387

*

388

* @return property name

389

*/

390

String getName();

391

}

392

393

/**

394

* Represents JavaBean property with additional metadata.

395

*/

396

@Incubating

397

interface JavaBeanProperty extends Property {

398

/**

399

* Get declaring class.

400

*

401

* @return class declaring this property

402

*/

403

Class<?> getDeclaringClass();

404

405

/**

406

* Get property type.

407

*

408

* @return property type

409

*/

410

Class<?> getType();

411

}

412

```

413

414

**Usage Example:**

415

416

```java

417

import org.hibernate.validator.spi.nodenameprovider.*;

418

import org.hibernate.validator.HibernateValidator;

419

import jakarta.validation.*;

420

import java.lang.reflect.Field;

421

422

/**

423

* Property node name provider using @JsonProperty annotation names.

424

*/

425

class JsonPropertyNodeNameProvider implements PropertyNodeNameProvider {

426

427

@Override

428

public String getName(Property property) {

429

if (property instanceof JavaBeanProperty) {

430

JavaBeanProperty javaProperty = (JavaBeanProperty) property;

431

432

// Try to find @JsonProperty annotation

433

try {

434

Field field = javaProperty.getDeclaringClass()

435

.getDeclaredField(property.getName());

436

437

JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class);

438

if (jsonProperty != null && !jsonProperty.value().isEmpty()) {

439

return jsonProperty.value();

440

}

441

} catch (NoSuchFieldException e) {

442

// Fall through to default name

443

}

444

}

445

446

// Use default property name

447

return property.getName();

448

}

449

}

450

451

// Example annotation

452

@interface JsonProperty {

453

String value();

454

}

455

456

// Example entity

457

class ApiRequest {

458

@JsonProperty("user_name")

459

@NotBlank

460

private String userName;

461

462

@JsonProperty("email_address")

463

@Email

464

private String emailAddress;

465

466

// getters/setters...

467

}

468

469

// Configure validator

470

PropertyNodeNameProvider nameProvider = new JsonPropertyNodeNameProvider();

471

472

ValidatorFactory factory = Validation.byProvider(HibernateValidator.class)

473

.configure()

474

.propertyNodeNameProvider(nameProvider)

475

.buildValidatorFactory();

476

477

Validator validator = factory.getValidator();

478

479

ApiRequest request = new ApiRequest();

480

Set<ConstraintViolation<ApiRequest>> violations = validator.validate(request);

481

482

for (ConstraintViolation<ApiRequest> violation : violations) {

483

// Property path uses JSON names: "user_name", "email_address"

484

System.out.println(violation.getPropertyPath());

485

}

486

```

487

488

### Getter Property Selection Strategy SPI

489

490

Define strategy for detecting JavaBean getters.

491

492

```java { .api }

493

package org.hibernate.validator.spi.properties;

494

495

import java.util.List;

496

import java.util.Optional;

497

498

/**

499

* Defines strategy for detecting getters of a bean.

500

* Determines which methods are considered JavaBean getters.

501

*

502

* @since 6.1.0

503

*/

504

@Incubating

505

interface GetterPropertySelectionStrategy {

506

/**

507

* Determine if method is a getter and return property name.

508

*

509

* @param executable method to check

510

* @return Optional containing property name if method is getter, empty otherwise

511

*/

512

Optional<String> getProperty(ConstrainableExecutable executable);

513

514

/**

515

* Get possible getter method names for property name.

516

*

517

* @param propertyName property name

518

* @return list of possible getter method names

519

*/

520

List<String> getGetterMethodNameCandidates(String propertyName);

521

}

522

523

/**

524

* Represents an executable (method or constructor) that can be constrained.

525

*/

526

@Incubating

527

interface ConstrainableExecutable {

528

/**

529

* Get method/constructor name.

530

*

531

* @return executable name

532

*/

533

String getName();

534

535

/**

536

* Get return type.

537

*

538

* @return return type

539

*/

540

Class<?> getReturnType();

541

542

/**

543

* Get parameter types.

544

*

545

* @return parameter types

546

*/

547

Class<?>[] getParameterTypes();

548

549

/**

550

* Check if this is a method.

551

*

552

* @return true if method, false if constructor

553

*/

554

boolean isMethod();

555

}

556

```

557

558

**Usage Example:**

559

560

```java

561

import org.hibernate.validator.spi.properties.*;

562

import org.hibernate.validator.HibernateValidator;

563

import jakarta.validation.*;

564

import java.util.*;

565

566

/**

567

* Custom getter selection strategy supporting "read" prefix.

568

* Recognizes: get, is, has, read

569

*/

570

class ExtendedGetterSelectionStrategy implements GetterPropertySelectionStrategy {

571

572

@Override

573

public Optional<String> getProperty(ConstrainableExecutable executable) {

574

if (!executable.isMethod()) {

575

return Optional.empty();

576

}

577

578

String methodName = executable.getName();

579

Class<?> returnType = executable.getReturnType();

580

Class<?>[] paramTypes = executable.getParameterTypes();

581

582

// Must have no parameters

583

if (paramTypes.length != 0) {

584

return Optional.empty();

585

}

586

587

// Must have return value

588

if (returnType == void.class) {

589

return Optional.empty();

590

}

591

592

// Check standard patterns

593

if (methodName.startsWith("get") && methodName.length() > 3) {

594

return Optional.of(decapitalize(methodName.substring(3)));

595

}

596

597

if (methodName.startsWith("is") && methodName.length() > 2 &&

598

(returnType == boolean.class || returnType == Boolean.class)) {

599

return Optional.of(decapitalize(methodName.substring(2)));

600

}

601

602

if (methodName.startsWith("has") && methodName.length() > 3) {

603

return Optional.of(decapitalize(methodName.substring(3)));

604

}

605

606

// Custom: support "read" prefix

607

if (methodName.startsWith("read") && methodName.length() > 4) {

608

return Optional.of(decapitalize(methodName.substring(4)));

609

}

610

611

return Optional.empty();

612

}

613

614

@Override

615

public List<String> getGetterMethodNameCandidates(String propertyName) {

616

String capitalized = capitalize(propertyName);

617

618

return Arrays.asList(

619

"get" + capitalized,

620

"is" + capitalized,

621

"has" + capitalized,

622

"read" + capitalized // Custom

623

);

624

}

625

626

private String decapitalize(String string) {

627

if (string.isEmpty()) {

628

return string;

629

}

630

return Character.toLowerCase(string.charAt(0)) + string.substring(1);

631

}

632

633

private String capitalize(String string) {

634

if (string.isEmpty()) {

635

return string;

636

}

637

return Character.toUpperCase(string.charAt(0)) + string.substring(1);

638

}

639

}

640

641

// Configure validator

642

GetterPropertySelectionStrategy strategy = new ExtendedGetterSelectionStrategy();

643

644

ValidatorFactory factory = Validation.byProvider(HibernateValidator.class)

645

.configure()

646

.getterPropertySelectionStrategy(strategy)

647

.buildValidatorFactory();

648

649

// Now methods like readProperty() are recognized as getters

650

class Document {

651

private String content;

652

653

@NotBlank // Will validate the property via readContent()

654

public String readContent() {

655

return content;

656

}

657

}

658

```

659

660

### Resource Bundle Locator SPI

661

662

SPI for custom resource bundle loading strategies.

663

664

```java { .api }

665

package org.hibernate.validator.spi.resourceloading;

666

667

import java.util.Locale;

668

import java.util.ResourceBundle;

669

670

/**

671

* Locates resource bundles for message interpolation.

672

* Implement to provide custom resource bundle loading.

673

*/

674

interface ResourceBundleLocator {

675

/**

676

* Get resource bundle for specified locale.

677

*

678

* @param locale locale for bundle

679

* @return resource bundle

680

*/

681

ResourceBundle getResourceBundle(Locale locale);

682

}

683

```

684

685

**Usage Example:** See [Resource Bundle Loading](./resource-loading.md) for detailed examples.

686

687

### Script Evaluator Factory SPI

688

689

Factory for creating script evaluators for @ScriptAssert constraints.

690

691

```java { .api }

692

package org.hibernate.validator.spi.scripting;

693

694

/**

695

* Factory for creating ScriptEvaluators.

696

* Implement to provide custom script evaluation.

697

*

698

* @since 6.0.3

699

*/

700

@Incubating

701

interface ScriptEvaluatorFactory {

702

/**

703

* Get script evaluator for specified language.

704

*

705

* @param languageName script language name

706

* @return script evaluator

707

* @throws ScriptEvaluatorNotFoundException if language not supported

708

*/

709

ScriptEvaluator getScriptEvaluatorByLanguageName(String languageName);

710

711

/**

712

* Clear factory state (caches, etc.).

713

*/

714

void clear();

715

}

716

717

/**

718

* Evaluates script expressions.

719

*/

720

@Incubating

721

interface ScriptEvaluator {

722

/**

723

* Evaluate script expression.

724

*

725

* @param script script to evaluate

726

* @param bindings variables available in script

727

* @return evaluation result

728

* @throws ScriptEvaluationException if evaluation fails

729

*/

730

Object evaluate(String script, java.util.Map<String, Object> bindings)

731

throws ScriptEvaluationException;

732

}

733

734

/**

735

* Base class for script evaluator factories with caching.

736

*/

737

@Incubating

738

abstract class AbstractCachingScriptEvaluatorFactory implements ScriptEvaluatorFactory {

739

/**

740

* Get or create cached script evaluator.

741

*

742

* @param languageName language name

743

* @return cached script evaluator

744

*/

745

protected abstract ScriptEvaluator createScriptEvaluator(String languageName);

746

747

@Override

748

public final ScriptEvaluator getScriptEvaluatorByLanguageName(String languageName) {

749

// Caching implementation

750

}

751

752

@Override

753

public void clear() {

754

// Clear cache

755

}

756

}

757

758

/**

759

* ScriptEvaluator using javax.script.ScriptEngine.

760

*/

761

@Incubating

762

class ScriptEngineScriptEvaluator implements ScriptEvaluator {

763

/**

764

* Create evaluator for script engine.

765

*

766

* @param scriptEngine JSR 223 script engine

767

*/

768

ScriptEngineScriptEvaluator(javax.script.ScriptEngine scriptEngine);

769

770

@Override

771

Object evaluate(String script, java.util.Map<String, Object> bindings);

772

}

773

774

// Exception classes

775

class ScriptEvaluationException extends RuntimeException {

776

ScriptEvaluationException(String message);

777

ScriptEvaluationException(String message, Throwable cause);

778

}

779

780

class ScriptEvaluatorNotFoundException extends RuntimeException {

781

ScriptEvaluatorNotFoundException(String message);

782

}

783

```

784

785

**Usage Example:**

786

787

```java

788

import org.hibernate.validator.spi.scripting.*;

789

import org.hibernate.validator.HibernateValidator;

790

import jakarta.validation.*;

791

import javax.script.*;

792

import java.util.*;

793

794

/**

795

* Custom script evaluator factory with security restrictions.

796

*/

797

class SecureScriptEvaluatorFactory extends AbstractCachingScriptEvaluatorFactory {

798

799

@Override

800

protected ScriptEvaluator createScriptEvaluator(String languageName) {

801

if (!"javascript".equalsIgnoreCase(languageName)) {

802

throw new ScriptEvaluatorNotFoundException(

803

"Only JavaScript is supported");

804

}

805

806

// Create sandboxed JavaScript engine

807

ScriptEngineManager manager = new ScriptEngineManager();

808

ScriptEngine engine = manager.getEngineByName("javascript");

809

810

if (engine == null) {

811

throw new ScriptEvaluatorNotFoundException(

812

"JavaScript engine not available");

813

}

814

815

// Apply security restrictions

816

configureSecurityRestrictions(engine);

817

818

return new ScriptEngineScriptEvaluator(engine);

819

}

820

821

private void configureSecurityRestrictions(ScriptEngine engine) {

822

// Restrict access to dangerous classes/methods

823

// Implementation depends on script engine

824

}

825

}

826

827

// Configure validator

828

ScriptEvaluatorFactory scriptFactory = new SecureScriptEvaluatorFactory();

829

830

ValidatorFactory factory = Validation.byProvider(HibernateValidator.class)

831

.configure()

832

.scriptEvaluatorFactory(scriptFactory)

833

.buildValidatorFactory();

834

835

// Use @ScriptAssert constraints

836

@ScriptAssert(

837

lang = "javascript",

838

script = "_this.endDate > _this.startDate",

839

message = "End date must be after start date"

840

)

841

class DateRange {

842

private Date startDate;

843

private Date endDate;

844

// getters/setters...

845

}

846

```

847

848

### Parameter Name Provider Implementation

849

850

Hibernate Validator provides an alternative parameter name provider implementation using the ParaNamer library.

851

852

```java { .api }

853

package org.hibernate.validator.parameternameprovider;

854

855

import jakarta.validation.ParameterNameProvider;

856

import com.thoughtworks.paranamer.Paranamer;

857

import java.lang.reflect.Constructor;

858

import java.lang.reflect.Method;

859

import java.util.List;

860

861

/**

862

* ParameterNameProvider implementation backed by the ParaNamer library.

863

* Extracts parameter names from debug information or other sources.

864

*

865

* The ParaNamer implementation can be customized via constructor.

866

* By default uses AdaptiveParanamer wrapped in CachingParanamer.

867

* Falls back to default parameter names if ParaNamer cannot retrieve names.

868

*

869

* Requires ParaNamer library on classpath.

870

*

871

* @see <a href="http://paranamer.codehaus.org/">ParaNamer web site</a>

872

*/

873

class ParanamerParameterNameProvider implements ParameterNameProvider {

874

/**

875

* Create provider with default Paranamer (AdaptiveParanamer with caching).

876

*/

877

ParanamerParameterNameProvider();

878

879

/**

880

* Create provider with custom Paranamer implementation.

881

*

882

* @param paranamer custom paranamer implementation, null for default

883

*/

884

ParanamerParameterNameProvider(Paranamer paranamer);

885

886

/**

887

* Get parameter names for constructor.

888

* Falls back to default names if ParaNamer cannot retrieve them.

889

*

890

* @param constructor the constructor

891

* @return list of parameter names

892

*/

893

List<String> getParameterNames(Constructor<?> constructor);

894

895

/**

896

* Get parameter names for method.

897

* Falls back to default names if ParaNamer cannot retrieve them.

898

*

899

* @param method the method

900

* @return list of parameter names

901

*/

902

List<String> getParameterNames(Method method);

903

}

904

```

905

906

**Usage Example:**

907

908

```java

909

import org.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider;

910

import jakarta.validation.*;

911

import jakarta.validation.constraints.*;

912

import jakarta.validation.executable.*;

913

914

// 1. Configure validator factory with ParanamerParameterNameProvider

915

ValidatorFactory factory = Validation.byDefaultProvider()

916

.configure()

917

.parameterNameProvider(new ParanamerParameterNameProvider())

918

.buildValidatorFactory();

919

920

Validator validator = factory.getValidator();

921

ExecutableValidator executableValidator = validator.forExecutables();

922

923

// 2. Bean with validated method

924

class UserService {

925

/**

926

* Create new user with validation.

927

* When compiled with debug information (-g flag), parameter names

928

* are preserved and used in validation messages.

929

*/

930

public User createUser(

931

@NotNull @Size(min = 3, max = 50) String username,

932

@NotNull @Email String email,

933

@Min(18) int age

934

) {

935

return new User(username, email, age);

936

}

937

}

938

939

// 3. Validate method parameters

940

UserService service = new UserService();

941

Method method = UserService.class.getMethod("createUser", String.class, String.class, int.class);

942

943

Object[] params = {"jo", "invalid", 15}; // All parameters invalid

944

945

Set<ConstraintViolation<UserService>> violations = executableValidator.validateParameters(

946

service,

947

method,

948

params

949

);

950

951

// With ParaNamer, violation messages use actual parameter names:

952

// - "createUser.username: size must be between 3 and 50"

953

// - "createUser.email: must be a well-formed email address"

954

// - "createUser.age: must be greater than or equal to 18"

955

//

956

// Without ParaNamer, generic names would be used:

957

// - "createUser.arg0: size must be between 3 and 50"

958

// - "createUser.arg1: must be a well-formed email address"

959

// - "createUser.arg2: must be greater than or equal to 18"

960

```

961

962

## Complete SPI Integration Example

963

964

```java

965

import org.hibernate.validator.*;

966

import org.hibernate.validator.spi.cfg.ConstraintMappingContributor;

967

import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;

968

import org.hibernate.validator.spi.messageinterpolation.*;

969

import org.hibernate.validator.spi.nodenameprovider.*;

970

import org.hibernate.validator.spi.properties.*;

971

import org.hibernate.validator.spi.scripting.*;

972

import jakarta.validation.*;

973

import java.util.*;

974

975

/**

976

* Complete custom validation configuration using all SPIs.

977

*/

978

class CustomValidationConfiguration {

979

980

public ValidatorFactory createValidatorFactory() {

981

// Create SPI implementations

982

ConstraintMappingContributor mappingContributor = new CustomMappingContributor();

983

LocaleResolver localeResolver = new ThreadLocalLocaleResolver();

984

PropertyNodeNameProvider nodeNameProvider = new JsonPropertyNodeNameProvider();

985

GetterPropertySelectionStrategy getterStrategy = new ExtendedGetterSelectionStrategy();

986

ScriptEvaluatorFactory scriptFactory = new SecureScriptEvaluatorFactory();

987

988

// Configure validator factory

989

return Validation.byProvider(HibernateValidator.class)

990

.configure()

991

// Add constraint mapping contributor

992

.addProperty(

993

BaseHibernateValidatorConfiguration.CONSTRAINT_MAPPING_CONTRIBUTORS,

994

CustomMappingContributor.class.getName()

995

)

996

// Configure locale resolution

997

.locales(Locale.US, Locale.FRANCE, Locale.GERMANY)

998

.defaultLocale(Locale.US)

999

.localeResolver(localeResolver)

1000

// Configure property name resolution

1001

.propertyNodeNameProvider(nodeNameProvider)

1002

// Configure getter detection

1003

.getterPropertySelectionStrategy(getterStrategy)

1004

// Configure script evaluation

1005

.scriptEvaluatorFactory(scriptFactory)

1006

// Other settings

1007

.failFast(false)

1008

.constraintExpressionLanguageFeatureLevel(

1009

ExpressionLanguageFeatureLevel.BEAN_PROPERTIES)

1010

.buildValidatorFactory();

1011

}

1012

}

1013

```

1014