Skip to content

Support for ConstraintValidator#initialize - #89

Draft
pfichtner wants to merge 4 commits into
mainfrom
feat/ConstraintValidator#initialize
Draft

Support for ConstraintValidator#initialize#89
pfichtner wants to merge 4 commits into
mainfrom
feat/ConstraintValidator#initialize

Conversation

@pfichtner

@pfichtner pfichtner commented May 24, 2026

Copy link
Copy Markdown
Owner

This PR adds support for the ConstraintValidator#initialize lifecycle method in custom validators. This ensures that validators can correctly access annotation attributes (like min, max, or custom regex patterns) at runtime, fulfilling JSR 380 requirements while strictly adhering to the project's constraint of not creating additional class files.

Problem

Previously, vaadoo only supported validators that didn't require initialization. For validators implementing initialize(A annotation), the method was never called. This led to uninitialized fields within the validator instances, resulting in incorrect validation logic (e.g., a MinValidator always using a default threshold of 0 instead of the value specified in the annotation).

Solution: Static Validator Cache with Weaved Proxies

The implementation leverages a "self-contained" bytecode strategy that keeps the output classes 1:1 with the input classes:

  1. Static Fields: For each unique validator/annotation combination, a private static final field is weaved into the target class.
  2. Static Initialization (): The class's static initializer is intercepted (or created) to perform
    the following once during class loading:
    • Instantiate the validator.
    • Create a dynamic proxy for the annotation type using java.lang.reflect.Proxy.
    • Implement the InvocationHandler via INVOKEDYNAMIC: To avoid creating a separate .class file for the handler, we use LambdaMetafactory to point to a private static $handler method weaved into the same class. This method returns the annotation values known at compile-time.
    • Call validator.initialize(proxy).
    • Store the initialized instance in the static field.
  3. Deduplication: A fieldNameResolver ensures that identical validators (same class, same annotation, same attributes) share the same static field across different parameters or constructors.
  4. Optimized Validation Site: CustomAnnotations now replaces the NEW/DUP/INVOKESPECIAL sequence with a GETSTATIC call for validators that require initialization.

Key Changes

  • CustomAnnotationValidatorClassVisitor: A new ASM visitor that handles the weaving of static fields, handler methods, and the initialization logic in .
  • VaadooImplementor: Updated to coordinate validator collection and integrate the new visitor into the ByteBuddy transformation chain.
  • CustomAnnotations: Enhanced to detect initialize overrides and resolve the appropriate injection strategy (new instance vs. cached instance).
  • CustomValidatorInfo: Metadata holder used for deduplication and field mapping.

Verification

  • Reproduction Test: Added ConstraintValidatorInitializeTest which confirms the failure state (initialization not called) and verifies the fix.
  • Regression Testing: Validated that existing regex optimizations and standard JSR 380 validations remain unaffected.
  • Architectural Integrity: Confirmed via build that no extra .class files are generated; all logic is self-contained within the transformed classes.

Technical Details

  • Uses INVOKEDYNAMIC for InvocationHandler implementation to satisfy the "no extra classes" rule.
  • Handles various attribute types including Primitives, Strings, Enums, and Classes.
  • Implements deep equality for annotation values to ensure correct deduplication.

pfichtner added 4 commits May 24, 2026 11:25
Implement a static validator cache and weave initialization logic into classes
using dynamic proxies and INVOKEDYNAMIC to avoid creating extra .class files.
… initialization

Improve array handling in validator initialization and ensure deterministic field ordering.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant