Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HV-1226 Explore usage of method handles instead of core reflection in ReflectionHelper#getValue() #975

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
package org.hibernate.validator;

import java.lang.invoke.MethodHandles.Lookup;
import java.time.Duration;
import java.util.Set;

Expand Down Expand Up @@ -311,4 +312,37 @@ public interface HibernateValidatorConfiguration extends Configuration<Hibernate
*/
@Incubating
HibernateValidatorConfiguration constraintValidatorPayload(Object constraintValidatorPayload);

/**
* Allows to set the property access kind. Could be either {@link PropertyAccessKind#REFLECTION}, which is
* a classic and default way to access properties, or it can also be {@link PropertyAccessKind#METHOD_HANDLES},
* which is a new incubating way. Usage of method handle access should provide better performance but also requires
* users to set {@link Lookup} through
*
* @param propertyAccessKind the desired property access kind
*
* @return {@code this} following the chaining method pattern
*
* @since 6.1.0
*/
@Incubating
HibernateValidatorConfiguration propertyAccessKind(PropertyAccessKind propertyAccessKind);

/**
* Allows to set the {@link Lookup} object that will be used when accessing properies using {@link PropertyAccessKind#METHOD_HANDLES}.
* if is not configured Hibernate Validator will internally call {@code MethodHandles.lookup()}.
*
* @param lookup the lookup object to use
*
* @return {@code this} following the chaining method pattern
*
* @since 6.1.0
*/
@Incubating
HibernateValidatorConfiguration externalLookup(Lookup lookup);

@Incubating
enum PropertyAccessKind {
REFLECTION, METHOD_HANDLES,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
import org.hibernate.validator.internal.metadata.raw.BeanConfiguration;
import org.hibernate.validator.internal.properties.javabean.accessors.JavaBeanPropertyAccessorFactory;
import org.hibernate.validator.internal.util.Contracts;
import org.hibernate.validator.internal.util.TypeResolutionHelper;
import org.hibernate.validator.internal.util.logging.Log;
Expand All @@ -45,13 +46,15 @@ public class DefaultConstraintMapping implements ConstraintMapping {
private final Set<TypeConstraintMappingContextImpl<?>> typeContexts;
private final Set<Class<?>> definedConstraints;
private final Set<ConstraintDefinitionContextImpl<?>> constraintContexts;
private final JavaBeanPropertyAccessorFactory propertyAccessorFactory;

public DefaultConstraintMapping() {
public DefaultConstraintMapping(JavaBeanPropertyAccessorFactory propertyAccessorFactory) {
this.annotationProcessingOptions = new AnnotationProcessingOptionsImpl();
this.configuredTypes = newHashSet();
this.typeContexts = newHashSet();
this.definedConstraints = newHashSet();
this.constraintContexts = newHashSet();
this.propertyAccessorFactory = propertyAccessorFactory;
}

@Override
Expand Down Expand Up @@ -124,4 +127,8 @@ public Set<ConstraintDefinitionContribution<?>> getConstraintDefinitionContribut

return contributions;
}

JavaBeanPropertyAccessorFactory getPropertyAccessorFactory() {
return propertyAccessorFactory;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ public MethodConstraintMappingContext method(String name, Class<?>... parameterT
throw LOG.getBeanDoesNotContainMethodException( beanClass, name, parameterTypes );
}

JavaBeanMethod javaBeanMethod = JavaBeanExecutable.of( method );
JavaBeanMethod javaBeanMethod = JavaBeanExecutable.of( mapping.getPropertyAccessorFactory(), method );

if ( configuredMembers.contains( javaBeanMethod ) ) {
throw LOG.getMethodHasAlreadyBeenConfiguredViaProgrammaticApiException(
Expand Down Expand Up @@ -289,7 +289,7 @@ private JavaBeanField getFieldProperty(Class<?> clazz, String property) {
Contracts.assertNotNull( clazz, MESSAGES.classCannotBeNull() );

Field field = run( GetDeclaredField.action( clazz, property ) );
return field == null ? null : new JavaBeanField( field );
return field == null ? null : new JavaBeanField( mapping.getPropertyAccessorFactory(), field );
}

private JavaBeanGetter getGetterProperty(Class<?> clazz, String property) {
Expand All @@ -303,7 +303,7 @@ private JavaBeanGetter getGetterProperty(Class<?> clazz, String property) {
break;
}
}
return method == null ? null : new JavaBeanGetter( method );
return method == null ? null : new JavaBeanGetter( mapping.getPropertyAccessorFactory(), method );
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.hibernate.validator.internal.engine.resolver.TraversableResolvers;
import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor;
import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager;
import org.hibernate.validator.internal.properties.javabean.accessors.JavaBeanPropertyAccessorFactory;
import org.hibernate.validator.internal.util.Contracts;
import org.hibernate.validator.internal.util.Version;
import org.hibernate.validator.internal.util.logging.Log;
Expand Down Expand Up @@ -103,6 +104,8 @@ public class ConfigurationImpl implements HibernateValidatorConfiguration, Confi
private ScriptEvaluatorFactory scriptEvaluatorFactory;
private Duration temporalValidationTolerance;
private Object constraintValidatorPayload;
private PropertyAccessKind propertyAccessKind;
private MethodHandles.Lookup lookup;

public ConfigurationImpl(BootstrapState state) {
this();
Expand Down Expand Up @@ -133,6 +136,10 @@ private ConfigurationImpl() {
this.defaultConstraintValidatorFactory = new ConstraintValidatorFactoryImpl();
this.defaultParameterNameProvider = new DefaultParameterNameProvider();
this.defaultClockProvider = DefaultClockProvider.INSTANCE;
// we default to classical reflection access
this.propertyAccessKind = PropertyAccessKind.REFLECTION;
// set default lookup value to be used unless it will be redefined by users.
this.lookup = MethodHandles.lookup();
}

@Override
Expand Down Expand Up @@ -294,6 +301,22 @@ public HibernateValidatorConfiguration constraintValidatorPayload(Object constra
return this;
}

@Override
public HibernateValidatorConfiguration propertyAccessKind(PropertyAccessKind propertyAccessKind) {
Contracts.assertNotNull( propertyAccessKind, MESSAGES.parameterMustNotBeNull( "propertyAccessKind" ) );

this.propertyAccessKind = propertyAccessKind;
return this;
}

@Override
public HibernateValidatorConfiguration externalLookup(MethodHandles.Lookup lookup) {
Contracts.assertNotNull( lookup, MESSAGES.parameterMustNotBeNull( "lookup" ) );

this.lookup = lookup;
return this;
}

public boolean isAllowParallelMethodsDefineParameterConstraints() {
return this.methodValidationConfigurationBuilder.isAllowParallelMethodsDefineParameterConstraints();
}
Expand All @@ -304,7 +327,7 @@ public MethodValidationConfiguration getMethodValidationConfiguration() {

@Override
public final DefaultConstraintMapping createConstraintMapping() {
return new DefaultConstraintMapping();
return new DefaultConstraintMapping( JavaBeanPropertyAccessorFactory.of( this ) );
}

@Override
Expand Down Expand Up @@ -450,6 +473,14 @@ public Object getConstraintValidatorPayload() {
return constraintValidatorPayload;
}

public PropertyAccessKind getPropertyAccessKind() {
return propertyAccessKind;
}

public MethodHandles.Lookup getLookup() {
return lookup;
}

@Override
public Set<ValueExtractor<?>> getValueExtractors() {
return validationBootstrapParameters.getValueExtractorDescriptors()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.hibernate.validator.internal.metadata.provider.MetaDataProvider;
import org.hibernate.validator.internal.metadata.provider.ProgrammaticMetaDataProvider;
import org.hibernate.validator.internal.metadata.provider.XmlMetaDataProvider;
import org.hibernate.validator.internal.properties.javabean.accessors.JavaBeanPropertyAccessorFactory;
import org.hibernate.validator.internal.util.Contracts;
import org.hibernate.validator.internal.util.ExecutableHelper;
import org.hibernate.validator.internal.util.ExecutableParameterNameProvider;
Expand Down Expand Up @@ -134,6 +135,8 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory {

private final ValidationOrderGenerator validationOrderGenerator;

private final JavaBeanPropertyAccessorFactory propertyAccessorFactory;

public ValidatorFactoryImpl(ConfigurationState configurationState) {
ClassLoader externalClassLoader = getExternalClassLoader( configurationState );

Expand All @@ -147,19 +150,21 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) {
if ( configurationState instanceof ConfigurationImpl ) {
hibernateSpecificConfig = (ConfigurationImpl) configurationState;
}
this.propertyAccessorFactory = JavaBeanPropertyAccessorFactory.of( hibernateSpecificConfig );

// HV-302; don't load XmlMappingParser if not necessary
if ( configurationState.getMappingStreams().isEmpty() ) {
this.xmlMetaDataProvider = null;
}
else {
this.xmlMetaDataProvider = new XmlMetaDataProvider(
constraintHelper, typeResolutionHelper, valueExtractorManager, configurationState.getMappingStreams(), externalClassLoader
constraintHelper, typeResolutionHelper, valueExtractorManager, propertyAccessorFactory, configurationState.getMappingStreams(), externalClassLoader
);
}

this.constraintMappings = Collections.unmodifiableSet(
getConstraintMappings(
propertyAccessorFactory,
typeResolutionHelper,
configurationState,
externalClassLoader
Expand Down Expand Up @@ -207,7 +212,7 @@ private static ClassLoader getExternalClassLoader(ConfigurationState configurati
return ( configurationState instanceof ConfigurationImpl ) ? ( (ConfigurationImpl) configurationState ).getExternalClassLoader() : null;
}

private static Set<DefaultConstraintMapping> getConstraintMappings(TypeResolutionHelper typeResolutionHelper,
private static Set<DefaultConstraintMapping> getConstraintMappings(JavaBeanPropertyAccessorFactory propertyAccessorFactory, TypeResolutionHelper typeResolutionHelper,
ConfigurationState configurationState, ClassLoader externalClassLoader) {
Set<DefaultConstraintMapping> constraintMappings = newHashSet();

Expand All @@ -224,7 +229,7 @@ private static Set<DefaultConstraintMapping> getConstraintMappings(TypeResolutio
ConstraintMappingContributor serviceLoaderBasedContributor = new ServiceLoaderBasedConstraintMappingContributor(
typeResolutionHelper,
externalClassLoader != null ? externalClassLoader : run( GetClassLoader.fromContext() ) );
DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder( constraintMappings );
DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder( propertyAccessorFactory, constraintMappings );
serviceLoaderBasedContributor.createConstraintMappings( builder );
}

Expand All @@ -233,7 +238,7 @@ private static Set<DefaultConstraintMapping> getConstraintMappings(TypeResolutio
externalClassLoader );

for ( ConstraintMappingContributor contributor : contributors ) {
DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder( constraintMappings );
DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder( propertyAccessorFactory, constraintMappings );
contributor.createConstraintMappings( builder );
}

Expand Down Expand Up @@ -349,7 +354,8 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory,
valueExtractorManager,
validationOrderGenerator,
buildDataProviders(),
methodValidationConfiguration
methodValidationConfiguration,
propertyAccessorFactory
)
);

Expand Down Expand Up @@ -598,16 +604,18 @@ private static <T> T run(PrivilegedAction<T> action) {
*/
private static class DefaultConstraintMappingBuilder
implements ConstraintMappingContributor.ConstraintMappingBuilder {
private final JavaBeanPropertyAccessorFactory propertyAccessorFactory;
private final Set<DefaultConstraintMapping> mappings;

public DefaultConstraintMappingBuilder(Set<DefaultConstraintMapping> mappings) {
public DefaultConstraintMappingBuilder(JavaBeanPropertyAccessorFactory propertyAccessorFactory, Set<DefaultConstraintMapping> mappings) {
super();
this.propertyAccessorFactory = propertyAccessorFactory;
this.mappings = mappings;
}

@Override
public ConstraintMapping addConstraintMapping() {
DefaultConstraintMapping mapping = new DefaultConstraintMapping();
DefaultConstraintMapping mapping = new DefaultConstraintMapping( propertyAccessorFactory );
mappings.add( mapping );
return mapping;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider;
import org.hibernate.validator.internal.metadata.provider.MetaDataProvider;
import org.hibernate.validator.internal.metadata.raw.BeanConfiguration;
import org.hibernate.validator.internal.properties.javabean.accessors.JavaBeanPropertyAccessorFactory;
import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.internal.util.ConcurrentReferenceHashMap;
import org.hibernate.validator.internal.util.Contracts;
Expand Down Expand Up @@ -123,7 +124,8 @@ public BeanMetaDataManager(ConstraintHelper constraintHelper,
ValueExtractorManager valueExtractorManager,
ValidationOrderGenerator validationOrderGenerator,
List<MetaDataProvider> optionalMetaDataProviders,
MethodValidationConfiguration methodValidationConfiguration) {
MethodValidationConfiguration methodValidationConfiguration,
JavaBeanPropertyAccessorFactory propertyAccessorFactory) {
this.constraintHelper = constraintHelper;
this.executableHelper = executableHelper;
this.typeResolutionHelper = typeResolutionHelper;
Expand All @@ -147,7 +149,8 @@ public BeanMetaDataManager(ConstraintHelper constraintHelper,
constraintHelper,
typeResolutionHelper,
valueExtractorManager,
annotationProcessingOptions
annotationProcessingOptions,
propertyAccessorFactory
);
List<MetaDataProvider> tmpMetaDataProviders = new ArrayList<>( optionalMetaDataProviders.size() + 1 );
// We add the annotation based metadata provider at the first position so that the entire metadata model is assembled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable;
import org.hibernate.validator.internal.properties.javabean.JavaBeanField;
import org.hibernate.validator.internal.properties.javabean.JavaBeanParameter;
import org.hibernate.validator.internal.properties.javabean.accessors.JavaBeanPropertyAccessorFactory;
import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.internal.util.ReflectionHelper;
import org.hibernate.validator.internal.util.TypeResolutionHelper;
Expand All @@ -92,19 +93,22 @@ public class AnnotationMetaDataProvider implements MetaDataProvider {

private final ConstraintHelper constraintHelper;
private final TypeResolutionHelper typeResolutionHelper;
private final AnnotationProcessingOptions annotationProcessingOptions;
private final ValueExtractorManager valueExtractorManager;
private final AnnotationProcessingOptions annotationProcessingOptions;
private final JavaBeanPropertyAccessorFactory propertyAccessorFactory;

private final BeanConfiguration<Object> objectBeanConfiguration;

public AnnotationMetaDataProvider(ConstraintHelper constraintHelper,
TypeResolutionHelper typeResolutionHelper,
ValueExtractorManager valueExtractorManager,
AnnotationProcessingOptions annotationProcessingOptions) {
AnnotationProcessingOptions annotationProcessingOptions,
JavaBeanPropertyAccessorFactory propertyAccessorFactory) {
this.constraintHelper = constraintHelper;
this.typeResolutionHelper = typeResolutionHelper;
this.valueExtractorManager = valueExtractorManager;
this.annotationProcessingOptions = annotationProcessingOptions;
this.propertyAccessorFactory = propertyAccessorFactory;

this.objectBeanConfiguration = retrieveBeanConfiguration( Object.class );
}
Expand Down Expand Up @@ -220,7 +224,7 @@ private Set<ConstrainedElement> getFieldMetaData(Class<?> beanClass) {
continue;
}

JavaBeanField javaBeanField = new JavaBeanField( field );
JavaBeanField javaBeanField = new JavaBeanField( propertyAccessorFactory, field );

if ( annotationProcessingOptions.areMemberConstraintsIgnoredFor( javaBeanField ) ) {
continue;
Expand Down Expand Up @@ -301,7 +305,7 @@ private Set<ConstrainedExecutable> getMetaData(Executable[] executableElements)
* given element.
*/
private ConstrainedExecutable findExecutableMetaData(Executable executable) {
JavaBeanExecutable<?> javaBeanExecutable = JavaBeanExecutable.of( executable );
JavaBeanExecutable<?> javaBeanExecutable = JavaBeanExecutable.of( propertyAccessorFactory, executable );
List<ConstrainedParameter> parameterConstraints = getParameterMetaData( javaBeanExecutable );

Map<ConstraintType, List<ConstraintDescriptorImpl<?>>> executableConstraints = findConstraints(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.hibernate.validator.internal.metadata.raw.BeanConfiguration;
import org.hibernate.validator.internal.metadata.raw.ConfigurationSource;
import org.hibernate.validator.internal.metadata.raw.ConstrainedElement;
import org.hibernate.validator.internal.properties.javabean.accessors.JavaBeanPropertyAccessorFactory;
import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.internal.util.TypeResolutionHelper;
import org.hibernate.validator.internal.util.stereotypes.Immutable;
Expand All @@ -40,11 +41,12 @@ public class XmlMetaDataProvider implements MetaDataProvider {
public XmlMetaDataProvider(ConstraintHelper constraintHelper,
TypeResolutionHelper typeResolutionHelper,
ValueExtractorManager valueExtractorManager,
JavaBeanPropertyAccessorFactory propertyAccessorFactory,
Set<InputStream> mappingStreams,
ClassLoader externalClassLoader) {

MappingXmlParser mappingParser = new MappingXmlParser( constraintHelper, typeResolutionHelper, valueExtractorManager,
externalClassLoader );
propertyAccessorFactory, externalClassLoader );
mappingParser.parse( mappingStreams );

configuredBeans = CollectionHelper.toImmutableMap( createBeanConfigurations( mappingParser ) );
Expand Down
Loading