Skip to content

Commit 22fd3d5

Browse files
committed
HV-1697 Reduce memory allocation in ValidationContexts
1 parent 57dda43 commit 22fd3d5

File tree

3 files changed

+101
-45
lines changed

3 files changed

+101
-45
lines changed

engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,6 @@ public final <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... grou
161161

162162
BaseBeanValidationContext<T> validationContext = getValidationContextBuilder().forValidate( rootBeanClass, rootBeanMetaData, object );
163163

164-
if ( !validationContext.getRootBeanMetaData().hasConstraints() ) {
165-
return Collections.emptySet();
166-
}
167-
168164
ValidationOrder validationOrder = determineGroupValidationOrder( groups );
169165
BeanValueContext<?, Object> valueContext = ValueContexts.getLocalExecutionContextForBean(
170166
validatorScopedContext.getParameterNameProvider(),
@@ -270,6 +266,10 @@ private <T> Set<ConstraintViolation<T>> validateParameters(T object, Executable
270266
Class<T> rootBeanClass = object != null ? (Class<T>) object.getClass() : (Class<T>) executable.getDeclaringClass();
271267
BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass );
272268

269+
if ( !rootBeanMetaData.hasConstraints() ) {
270+
return Collections.emptySet();
271+
}
272+
273273
ExecutableValidationContext<T> validationContext = getValidationContextBuilder().forValidateParameters(
274274
rootBeanClass,
275275
rootBeanMetaData,
@@ -278,10 +278,6 @@ private <T> Set<ConstraintViolation<T>> validateParameters(T object, Executable
278278
parameterValues
279279
);
280280

281-
if ( !validationContext.getRootBeanMetaData().hasConstraints() ) {
282-
return Collections.emptySet();
283-
}
284-
285281
ValidationOrder validationOrder = determineGroupValidationOrder( groups );
286282

287283
validateParametersInContext( validationContext, parameterValues, validationOrder );
@@ -296,6 +292,9 @@ private <T> Set<ConstraintViolation<T>> validateReturnValue(T object, Executable
296292
Class<T> rootBeanClass = object != null ? (Class<T>) object.getClass() : (Class<T>) executable.getDeclaringClass();
297293
BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass );
298294

295+
if ( !rootBeanMetaData.hasConstraints() ) {
296+
return Collections.emptySet();
297+
}
299298

300299
ExecutableValidationContext<T> validationContext = getValidationContextBuilder().forValidateReturnValue(
301300
rootBeanClass,
@@ -305,10 +304,6 @@ private <T> Set<ConstraintViolation<T>> validateReturnValue(T object, Executable
305304
returnValue
306305
);
307306

308-
if ( !validationContext.getRootBeanMetaData().hasConstraints() ) {
309-
return Collections.emptySet();
310-
}
311-
312307
ValidationOrder validationOrder = determineGroupValidationOrder( groups );
313308

314309
validateReturnValueInContext( validationContext, object, returnValue, validationOrder );

engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/AbstractValidationContext.java

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@
66
*/
77
package org.hibernate.validator.internal.engine.validationcontext;
88

9-
import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;
10-
119
import java.lang.invoke.MethodHandles;
10+
import java.util.Collections;
1211
import java.util.HashSet;
1312
import java.util.IdentityHashMap;
1413
import java.util.Iterator;
@@ -35,6 +34,7 @@
3534
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
3635
import org.hibernate.validator.internal.util.logging.Log;
3736
import org.hibernate.validator.internal.util.logging.LoggerFactory;
37+
import org.hibernate.validator.internal.util.stereotypes.Lazy;
3838

3939
/**
4040
* Context object keeping track of all required data for a validation call.
@@ -73,49 +73,53 @@ abstract class AbstractValidationContext<T> implements BaseBeanValidationContext
7373
private final BeanMetaData<T> rootBeanMetaData;
7474

7575
/**
76-
* The set of already processed meta constraints per bean - path ({@link BeanPathMetaConstraintProcessedUnit}).
76+
* The constraint factory which should be used in this context.
7777
*/
78-
private final Set<BeanPathMetaConstraintProcessedUnit> processedPathUnits;
78+
private final ConstraintValidatorFactory constraintValidatorFactory;
7979

8080
/**
81-
* The set of already processed groups per bean ({@link BeanGroupProcessedUnit}).
81+
* Context containing all {@link Validator} level helpers and configuration properties.
8282
*/
83-
private final Set<BeanGroupProcessedUnit> processedGroupUnits;
83+
protected final ValidatorScopedContext validatorScopedContext;
8484

8585
/**
86-
* Maps an object to a list of paths in which it has been validated. The objects are the bean instances.
86+
* Allows a JPA provider to decide whether a property should be validated.
8787
*/
88-
private final Map<Object, Set<PathImpl>> processedPathsPerBean;
88+
private final TraversableResolver traversableResolver;
8989

9090
/**
91-
* Contains all failing constraints so far.
91+
* The constraint validator initialization context.
9292
*/
93-
private final Set<ConstraintViolation<T>> failingConstraintViolations;
93+
private final HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext;
9494

9595
/**
96-
* The constraint factory which should be used in this context.
96+
* Indicates if the tracking of already validated bean should be disabled.
9797
*/
98-
private final ConstraintValidatorFactory constraintValidatorFactory;
98+
private final boolean disableAlreadyValidatedBeanTracking;
9999

100100
/**
101-
* Context containing all {@link Validator} level helpers and configuration properties.
101+
* The set of already processed meta constraints per bean - path ({@link BeanPathMetaConstraintProcessedUnit}).
102102
*/
103-
protected final ValidatorScopedContext validatorScopedContext;
103+
@Lazy
104+
private Set<BeanPathMetaConstraintProcessedUnit> processedPathUnits;
104105

105106
/**
106-
* Allows a JPA provider to decide whether a property should be validated.
107+
* The set of already processed groups per bean ({@link BeanGroupProcessedUnit}).
107108
*/
108-
private final TraversableResolver traversableResolver;
109+
@Lazy
110+
private Set<BeanGroupProcessedUnit> processedGroupUnits;
109111

110112
/**
111-
* The constraint validator initialization context.
113+
* Maps an object to a list of paths in which it has been validated. The objects are the bean instances.
112114
*/
113-
private final HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext;
115+
@Lazy
116+
private Map<Object, Set<PathImpl>> processedPathsPerBean;
114117

115118
/**
116-
* Indicates if the tracking of already validated bean should be disabled.
119+
* Contains all failing constraints so far.
117120
*/
118-
private final boolean disableAlreadyValidatedBeanTracking;
121+
@Lazy
122+
private Set<ConstraintViolation<T>> failingConstraintViolations;
119123

120124
protected AbstractValidationContext(
121125
ConstraintValidatorManager constraintValidatorManager,
@@ -138,11 +142,6 @@ protected AbstractValidationContext(
138142
this.rootBeanClass = rootBeanClass;
139143
this.rootBeanMetaData = rootBeanMetaData;
140144

141-
this.processedGroupUnits = new HashSet<>();
142-
this.processedPathUnits = new HashSet<>();
143-
this.processedPathsPerBean = new IdentityHashMap<>();
144-
this.failingConstraintViolations = newHashSet();
145-
146145
this.disableAlreadyValidatedBeanTracking = disableAlreadyValidatedBeanTracking;
147146
}
148147

@@ -214,6 +213,10 @@ public void markCurrentBeanAsProcessed(ValueContext<?, ?> valueContext) {
214213

215214
@Override
216215
public Set<ConstraintViolation<T>> getFailingConstraints() {
216+
if ( failingConstraintViolations == null ) {
217+
return Collections.emptySet();
218+
}
219+
217220
return failingConstraintViolations;
218221
}
219222

@@ -235,7 +238,7 @@ public void addConstraintFailure(
235238
// at this point we make a copy of the path to avoid side effects
236239
Path path = PathImpl.createCopy( constraintViolationCreationContext.getPath() );
237240

238-
this.failingConstraintViolations.add(
241+
getInitializedFailingConstraintViolations().add(
239242
createConstraintViolation(
240243
messageTemplate,
241244
interpolatedMessage,
@@ -263,7 +266,7 @@ public boolean hasMetaConstraintBeenProcessed(Object bean, Path path, MetaConstr
263266
return false;
264267
}
265268

266-
return processedPathUnits.contains( new BeanPathMetaConstraintProcessedUnit( bean, path, metaConstraint ) );
269+
return getInitializedProcessedPathUnits().contains( new BeanPathMetaConstraintProcessedUnit( bean, path, metaConstraint ) );
267270
}
268271

269272
@Override
@@ -274,7 +277,7 @@ public void markConstraintProcessed(Object bean, Path path, MetaConstraint<?> me
274277
return;
275278
}
276279

277-
processedPathUnits.add( new BeanPathMetaConstraintProcessedUnit( bean, path, metaConstraint ) );
280+
getInitializedProcessedPathUnits().add( new BeanPathMetaConstraintProcessedUnit( bean, path, metaConstraint ) );
278281
}
279282

280283
@Override
@@ -321,7 +324,7 @@ private String interpolate(
321324
}
322325

323326
private boolean isAlreadyValidatedForPath(Object value, PathImpl path) {
324-
Set<PathImpl> pathSet = processedPathsPerBean.get( value );
327+
Set<PathImpl> pathSet = getInitializedProcessedPathsPerBean().get( value );
325328
if ( pathSet == null ) {
326329
return false;
327330
}
@@ -352,17 +355,52 @@ private boolean isSubPathOf(Path p1, Path p2) {
352355
}
353356

354357
private boolean isAlreadyValidatedForCurrentGroup(Object value, Class<?> group) {
355-
return processedGroupUnits.contains( new BeanGroupProcessedUnit( value, group ) );
358+
return getInitializedProcessedGroupUnits().contains( new BeanGroupProcessedUnit( value, group ) );
356359
}
357360

358361
private void markCurrentBeanAsProcessedForCurrentPath(Object bean, PathImpl path) {
359362
// HV-1031 The path object is mutated as we traverse the object tree, hence copy it before saving it
360-
processedPathsPerBean.computeIfAbsent( bean, b -> new HashSet<>() )
361-
.add( PathImpl.createCopy( path ) );
363+
Map<Object, Set<PathImpl>> processedPathsPerBean = getInitializedProcessedPathsPerBean();
364+
365+
Set<PathImpl> processedPaths = processedPathsPerBean.get( bean );
366+
if ( processedPaths == null ) {
367+
processedPaths = new HashSet<>();
368+
processedPathsPerBean.put( bean, processedPaths );
369+
}
370+
371+
processedPaths.add( PathImpl.createCopy( path ) );
362372
}
363373

364374
private void markCurrentBeanAsProcessedForCurrentGroup(Object bean, Class<?> group) {
365-
processedGroupUnits.add( new BeanGroupProcessedUnit( bean, group ) );
375+
getInitializedProcessedGroupUnits().add( new BeanGroupProcessedUnit( bean, group ) );
376+
}
377+
378+
private Set<BeanPathMetaConstraintProcessedUnit> getInitializedProcessedPathUnits() {
379+
if ( processedPathUnits == null ) {
380+
processedPathUnits = new HashSet<>();
381+
}
382+
return processedPathUnits;
383+
}
384+
385+
private Set<BeanGroupProcessedUnit> getInitializedProcessedGroupUnits() {
386+
if ( processedGroupUnits == null ) {
387+
processedGroupUnits = new HashSet<>();
388+
}
389+
return processedGroupUnits;
390+
}
391+
392+
private Map<Object, Set<PathImpl>> getInitializedProcessedPathsPerBean() {
393+
if ( processedPathsPerBean == null ) {
394+
processedPathsPerBean = new IdentityHashMap<>();
395+
}
396+
return processedPathsPerBean;
397+
}
398+
399+
private Set<ConstraintViolation<T>> getInitializedFailingConstraintViolations() {
400+
if ( failingConstraintViolations == null ) {
401+
failingConstraintViolations = new HashSet<>();
402+
}
403+
return failingConstraintViolations;
366404
}
367405

368406
private static final class BeanPathMetaConstraintProcessedUnit {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Hibernate Validator, declare and validate application constraints
3+
*
4+
* License: Apache License, Version 2.0
5+
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6+
*/
7+
package org.hibernate.validator.internal.util.stereotypes;
8+
9+
import java.lang.annotation.ElementType;
10+
import java.lang.annotation.Retention;
11+
import java.lang.annotation.RetentionPolicy;
12+
import java.lang.annotation.Target;
13+
14+
/**
15+
* Documents that the value assigned to the annotated field is lazily initialized and shouldn't be accessed directly but
16+
* via a getter.
17+
*
18+
* @author Guillaume Smet
19+
*/
20+
@Retention(RetentionPolicy.SOURCE)
21+
@Target(ElementType.FIELD)
22+
public @interface Lazy {
23+
}

0 commit comments

Comments
 (0)