Prevent infinite recursion in BuilderCustomizer
Summary
Fixes #278 by automatically applying RecursionGuard to BuilderCustomizer to prevent StackOverflowError when generating objects with self-referencing fields.
Problem
When using @Customization(BuilderCustomizer.class) with Lombok @Builder classes that have self-referencing fields (e.g., Category parent or Set<Category> children), the builder would enter an infinite recursion loop and throw StackOverflowError.
The issue occurred because:
BuilderInvoker.setProperty()callscontext.resolve()for each builder property- For self-referencing types, this creates an infinite loop: Category → parent (Category) → parent (Category) → ...
- The default
RecursionGuardinDefaultObjectGeneratorwas bypassed whenBuilderCustomizerwas applied via@Customization
Solution
Modified BuilderCustomizer.customize() to automatically wrap the generator with RecursionGuard:
@Override
public ObjectGenerator customize(ObjectGenerator generator) {
return new RecursionGuard().customize(invoker.customize(generator));
}
This ensures:
- Recursion depth is limited to 1 by default
- Self-referencing fields are set to
nullto break the recursion - No
StackOverflowErroroccurs
Changes
- BuilderCustomizer.java: Apply
RecursionGuardautomatically incustomize()method - SpecsForBuilderCustomizer.java: Add test cases for self-referencing types:
Categorywithparentandchildrenfields (from issue #278)Nodewithleftandrightfields (binary tree structure)
Test Plan
- [x] All existing tests pass
- [x] New tests verify self-referencing types don't cause
StackOverflowError - [x] Full build succeeds
- [x] Commit message validation passes
Documentation
Updated BuilderCustomizer Javadoc to document the automatic recursion guard behavior and its implications for self-referencing types.
Fixed the nullable annotation issues in 10578ee:
- Changed
childrenparameter from@NonNullto@Nullablein bothHierarchyEntityandCategory - Added null check before calling
children.addAll()to safely handle null values
This ensures the test code properly handles cases where RecursionGuard sets the children field to null.