From 3a093da04aa09d97097ea828612ee23de016080e Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 23 May 2024 17:33:04 -0500 Subject: [PATCH] #80 - Investigate use of MethodHandles instead of Constructor and Method references --- .../jandex/internal/JandexValueHelper.java | 3 +- .../internal/AnnotationUsageHelper.java | 3 +- .../internal/AttributeDescriptorImpl.java | 6 ++ .../internal/OrmAnnotationDescriptor.java | 94 +++++++++++-------- .../models/spi/AttributeDescriptor.java | 16 +++- version.txt | 2 +- 6 files changed, 75 insertions(+), 49 deletions(-) diff --git a/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexValueHelper.java b/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexValueHelper.java index 515f15c..2de6a60 100644 --- a/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexValueHelper.java +++ b/hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexValueHelper.java @@ -60,8 +60,7 @@ public static V extractOptionalValue( SourceModelBuildingContext modelContext) { final AnnotationValue value = usage.value( attributeDescriptor.getName() ); if ( value == null ) { - //noinspection unchecked - return (V) attributeDescriptor.getAttributeMethod().getDefaultValue(); + return attributeDescriptor.getDefaultValue(); } return modelContext.as( JandexModelContext.class ) diff --git a/hibernate-models/src/main/java/org/hibernate/models/internal/AnnotationUsageHelper.java b/hibernate-models/src/main/java/org/hibernate/models/internal/AnnotationUsageHelper.java index f79aa69..e8df583 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/internal/AnnotationUsageHelper.java +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/AnnotationUsageHelper.java @@ -14,7 +14,6 @@ import org.hibernate.models.spi.AnnotationDescriptor; import org.hibernate.models.spi.AttributeDescriptor; import org.hibernate.models.spi.SourceModelBuildingContext; -import org.hibernate.models.spi.SourceModelContext; /** * @see AnnotationHelper @@ -206,7 +205,7 @@ private static boolean nameMatches( AnnotationDescriptor descriptor, String matchValue, String attributeToMatch, - SourceModelContext modelContext) { + SourceModelBuildingContext modelContext) { final AttributeDescriptor attributeDescriptor = descriptor.getAttribute( attributeToMatch ); final String usageName = AnnotationHelper.extractValue( annotationUsage, attributeDescriptor ); return matchValue.equals( usageName ); diff --git a/hibernate-models/src/main/java/org/hibernate/models/internal/AttributeDescriptorImpl.java b/hibernate-models/src/main/java/org/hibernate/models/internal/AttributeDescriptorImpl.java index ecf4834..8e1e1d0 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/internal/AttributeDescriptorImpl.java +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/AttributeDescriptorImpl.java @@ -50,6 +50,12 @@ public ValueTypeDescriptor getTypeDescriptor() { return typeDescriptor; } + @Override + public T getDefaultValue() { + //noinspection unchecked + return (T) method.getDefaultValue(); + } + @Override public Method getAttributeMethod() { return method; diff --git a/hibernate-models/src/main/java/org/hibernate/models/internal/OrmAnnotationDescriptor.java b/hibernate-models/src/main/java/org/hibernate/models/internal/OrmAnnotationDescriptor.java index b98d901..57125e7 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/internal/OrmAnnotationDescriptor.java +++ b/hibernate-models/src/main/java/org/hibernate/models/internal/OrmAnnotationDescriptor.java @@ -5,12 +5,14 @@ package org.hibernate.models.internal; import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.util.Collections; import java.util.List; import java.util.Map; +import org.hibernate.models.ModelsException; import org.hibernate.models.spi.AnnotationDescriptor; import org.hibernate.models.spi.AttributeDescriptor; import org.hibernate.models.spi.MutableAnnotationDescriptor; @@ -22,6 +24,9 @@ * does not collect annotations from the annotation class as we never care about * meta-annotations in these cases. * + * @implNote There are a few cases in Hibernate ORM e.g. where we do care about meta-annotations, + * but those are handled specially there. + * * @author Steve Ebersole */ public class OrmAnnotationDescriptor @@ -30,8 +35,8 @@ public class OrmAnnotationDescriptor private final Class concreteClass; private final List> attributeDescriptors; - private DynamicCreator dynamicCreator; - private JdkCreator jdkCreator; + private final DynamicCreator dynamicCreator; + private final JdkCreator jdkCreator; private DeTypedCreator deTypedCreator; public OrmAnnotationDescriptor( @@ -53,6 +58,9 @@ public OrmAnnotationDescriptor( this.concreteClass = concreteClass; this.attributeDescriptors = AnnotationDescriptorBuilding.extractAttributeDescriptors( annotationType ); + + this.dynamicCreator = new DynamicCreator<>( annotationType, concreteClass ); + this.jdkCreator = new JdkCreator<>( annotationType, concreteClass ); } @Override @@ -69,17 +77,11 @@ public Class getMutableAnnotationType() { @Override public C createUsage(SourceModelBuildingContext context) { - if ( dynamicCreator == null ) { - dynamicCreator = new DynamicCreator<>( getAnnotationType(), concreteClass ); - } return dynamicCreator.createUsage( context ); } @Override public C createUsage(A jdkAnnotation, SourceModelBuildingContext context) { - if ( jdkCreator == null ) { - jdkCreator = new JdkCreator<>( getAnnotationType(), concreteClass ); - } return jdkCreator.createUsage( jdkAnnotation, context ); } @@ -102,93 +104,105 @@ public String toString() { } public static class DynamicCreator { - private final Constructor constructor; + private final MethodHandle constructor; + private final Class concreteClass; public DynamicCreator(Class annotationType, Class concreteClass) { - this( resolveConstructor( concreteClass ) ); + this( resolveConstructor( concreteClass ), concreteClass ); } - private static Constructor resolveConstructor(Class concreteClass) { + private static MethodHandle resolveConstructor(Class concreteClass) { try { - return concreteClass.getDeclaredConstructor( SourceModelBuildingContext.class ); + final MethodType methodType = MethodType.methodType( void.class, SourceModelBuildingContext.class ); + return MethodHandles.publicLookup().findConstructor( concreteClass, methodType ); } - catch (NoSuchMethodException e) { - throw new RuntimeException( e ); + catch (Exception e) { + throw new ModelsException( "Unable to locate default-variant constructor for `" + concreteClass.getName() + "`", e ); } } - public DynamicCreator(Constructor constructor) { + public DynamicCreator(MethodHandle constructor, Class concreteClass) { this.constructor = constructor; + this.concreteClass = concreteClass; } public C createUsage(SourceModelBuildingContext context) { try { - return constructor.newInstance( context ); + //noinspection unchecked + return (C) constructor.invoke( context ); } - catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { - throw new RuntimeException( e ); + catch (Throwable e) { + throw new ModelsException( "Unable to invoke default-variant constructor for `" + concreteClass.getName() + "`", e ); } } } public static class JdkCreator { - private final Constructor constructor; + private final MethodHandle constructor; + private final Class concreteClass; public JdkCreator(Class annotationType, Class concreteClass) { - this( resolveConstructor( annotationType, concreteClass ) ); + this( resolveConstructor( annotationType, concreteClass ), concreteClass ); } - private static Constructor resolveConstructor( + private static MethodHandle resolveConstructor( Class annotationType, Class concreteClass) { try { - return concreteClass.getDeclaredConstructor( annotationType, SourceModelBuildingContext.class ); + final MethodType methodType = MethodType.methodType( void.class, annotationType, SourceModelBuildingContext.class ); + return MethodHandles.publicLookup().findConstructor( concreteClass, methodType ); } - catch (NoSuchMethodException e) { - throw new RuntimeException( e ); + catch (Exception e) { + throw new ModelsException( "Unable to locate JDK-variant constructor for `" + concreteClass.getName() + "`", e ); } } - public JdkCreator(Constructor constructor) { + public JdkCreator(MethodHandle constructor, Class concreteClass) { this.constructor = constructor; + this.concreteClass = concreteClass; } public C createUsage(A jdkAnnotation, SourceModelBuildingContext context) { try { - return constructor.newInstance( jdkAnnotation, context ); + //noinspection unchecked + return (C) constructor.invoke( jdkAnnotation, context ); } - catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { - throw new RuntimeException( e ); + catch (Throwable e) { + throw new ModelsException( "Unable to invoke JDK-variant constructor for `" + concreteClass.getName() + "`", e ); } } } public static class DeTypedCreator { - private final Constructor constructor; + private final MethodHandle constructor; + private final Class concreteClass; public DeTypedCreator(Class annotationType, Class concreteClass) { - this( resolveConstructor( concreteClass ) ); + this( resolveConstructor( concreteClass ), concreteClass ); } - private static Constructor resolveConstructor(Class concreteClass) { + private static MethodHandle resolveConstructor(Class concreteClass) { try { - return concreteClass.getDeclaredConstructor( Map.class, SourceModelBuildingContext.class ); + final MethodType methodType = MethodType.methodType( void.class, Map.class, SourceModelBuildingContext.class ); + return MethodHandles.publicLookup().findConstructor( concreteClass, methodType ); } - catch (NoSuchMethodException e) { - throw new RuntimeException( e ); + catch (Exception e) { + throw new ModelsException( "Unable to locate Jandex-variant constructor for `" + concreteClass.getName() + "`", e ); } } - public DeTypedCreator(Constructor constructor) { + public DeTypedCreator(MethodHandle constructor, Class concreteClass) { this.constructor = constructor; + this.concreteClass = concreteClass; } public C createUsage(Map attributeValues, SourceModelBuildingContext context) { try { - return constructor.newInstance( attributeValues, context ); + //noinspection unchecked + return (C) constructor.invoke( attributeValues, context ); } - catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { - throw new RuntimeException( e ); + catch (Throwable e) { + throw new ModelsException( "Unable to invoke Jandex-variant constructor for `" + concreteClass.getName() + "`", e ); } } } diff --git a/hibernate-models/src/main/java/org/hibernate/models/spi/AttributeDescriptor.java b/hibernate-models/src/main/java/org/hibernate/models/spi/AttributeDescriptor.java index d2e32f7..6b95611 100644 --- a/hibernate-models/src/main/java/org/hibernate/models/spi/AttributeDescriptor.java +++ b/hibernate-models/src/main/java/org/hibernate/models/spi/AttributeDescriptor.java @@ -22,12 +22,20 @@ public interface AttributeDescriptor { */ ValueTypeDescriptor getTypeDescriptor(); + /** + * The attribute's type. + */ + default Class getValueType() { + return getTypeDescriptor().getValueType(); + } + + /** + * The attribute's defined default value, if one. + */ + T getDefaultValue(); + /** * The attribute method. */ Method getAttributeMethod(); - - default boolean isMultiValued() { - return getAttributeMethod().getReturnType().isArray(); - } } diff --git a/version.txt b/version.txt index 5deada8..1bc4afc 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.0.0-SNAPSHOT \ No newline at end of file +1.0.0-METHOD-HANDLE \ No newline at end of file