diff --git a/di-benchmarks/src/jmh/java/org/panda_lang/utilities/inject/InstanceConstructionBenchmark.java b/di-benchmarks/src/jmh/java/org/panda_lang/utilities/inject/InstanceConstructionBenchmark.java index 3c7998f..5e21365 100644 --- a/di-benchmarks/src/jmh/java/org/panda_lang/utilities/inject/InstanceConstructionBenchmark.java +++ b/di-benchmarks/src/jmh/java/org/panda_lang/utilities/inject/InstanceConstructionBenchmark.java @@ -15,12 +15,12 @@ /* JDK17 (I5-8600K OC 4.5 Ghz, 32GB RAM 3200Mhz, Windows 10) Benchmark Mode Cnt Score Error Units - InstanceConstructionBenchmark.direct thrpt 10 1786513.822 � 2208.503 ops/ms - InstanceConstructionBenchmark.injected thrpt 10 1793.499 � 10.822 ops/ms - InstanceConstructionBenchmark.injectedFast thrpt 10 2871.334 � 25.445 ops/ms - InstanceConstructionBenchmark.injectedStatic thrpt 10 2700.881 � 3.946 ops/ms - InstanceConstructionBenchmark.injectedStaticFast thrpt 10 2852.005 � 21.373 ops/ms - InstanceConstructionBenchmark.reflection thrpt 10 104001.170 � 81.311 ops/ms + InstanceConstructionBenchmark.direct thrpt 10 1789904.378 � 1122.822 ops/ms + InstanceConstructionBenchmark.injected thrpt 10 2629.149 � 66.627 ops/ms + InstanceConstructionBenchmark.injectedFast thrpt 10 2890.650 � 35.665 ops/ms + InstanceConstructionBenchmark.injectedStatic thrpt 10 2681.387 � 3.025 ops/ms + InstanceConstructionBenchmark.injectedStaticFast thrpt 10 2890.723 � 8.385 ops/ms + InstanceConstructionBenchmark.reflection thrpt 10 104444.640 � 88.579 ops/ms */ @Fork(value = 1) @Warmup(iterations = 10, time = 2) diff --git a/di-benchmarks/src/jmh/java/org/panda_lang/utilities/inject/InvokeMethodBenchmark.java b/di-benchmarks/src/jmh/java/org/panda_lang/utilities/inject/InvokeMethodBenchmark.java index f1922a6..90b0394 100644 --- a/di-benchmarks/src/jmh/java/org/panda_lang/utilities/inject/InvokeMethodBenchmark.java +++ b/di-benchmarks/src/jmh/java/org/panda_lang/utilities/inject/InvokeMethodBenchmark.java @@ -30,11 +30,11 @@ import panda.utilities.ReflectionUtils; /* JDK17 (I5-8600K OC 4.5 Ghz, 32GB RAM 3200Mhz, Windows 10) - Benchmark Mode Cnt Score Error Units - InvokeMethodBenchmark.direct thrpt 10 275095.491 � 8486.067 ops/ms - InvokeMethodBenchmark.generatedInjected thrpt 10 259745.279 � 219.899 ops/ms - InvokeMethodBenchmark.injected thrpt 10 40816.826 � 27.053 ops/ms - InvokeMethodBenchmark.reflection thrpt 10 162744.758 � 101.912 ops/ms + Benchmark Mode Cnt Score Error Units + InvokeMethodBenchmark.direct thrpt 10 492235.407 � 988.297 ops/ms + InvokeMethodBenchmark.generatedInjected thrpt 10 432266.343 � 737.527 ops/ms + InvokeMethodBenchmark.injected thrpt 10 142187.855 � 502.770 ops/ms + InvokeMethodBenchmark.reflection thrpt 10 189730.282 � 22464.101 ops/ms */ @Fork(value = 1) @Warmup(iterations = 10, time = 2) diff --git a/di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenMethodInjectorFactory.java b/di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenMethodInjectorFactory.java index c2e3a8a..14d6202 100644 --- a/di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenMethodInjectorFactory.java +++ b/di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenMethodInjectorFactory.java @@ -2,7 +2,7 @@ import java.lang.reflect.Method; -public class CodegenMethodInjectorFactory implements MethodInjectorFactory { +public final class CodegenMethodInjectorFactory implements MethodInjectorFactory { @Override public MethodInjector createMethodInjector(InjectorProcessor processor, Method method) throws Exception { diff --git a/di-codegen/src/main/java/org/panda_lang/utilities/inject/GeneratedMethodInjector.java b/di-codegen/src/main/java/org/panda_lang/utilities/inject/GeneratedMethodInjector.java index 0f6a344..4ccd15e 100644 --- a/di-codegen/src/main/java/org/panda_lang/utilities/inject/GeneratedMethodInjector.java +++ b/di-codegen/src/main/java/org/panda_lang/utilities/inject/GeneratedMethodInjector.java @@ -27,7 +27,7 @@ import net.bytebuddy.matcher.ElementMatchers; import panda.utilities.ObjectUtils; -public final class GeneratedMethodInjector implements MethodInjector { +final class GeneratedMethodInjector implements MethodInjector { private static final Object[] EMPTY = new Object[0]; @@ -50,7 +50,7 @@ public final class GeneratedMethodInjector implements MethodInjector { @SuppressWarnings("unchecked") @Override public T invoke(Object instance, Object... injectorArgs) throws Exception { - return (T) function.apply(instance, empty ? EMPTY : processor.fetchValues(cache, injectorArgs)); + return (T) this.function.apply(instance, this.empty ? EMPTY : this.processor.fetchValues(this.cache, injectorArgs)); } private static BiFunction generate(Method method) throws Exception { @@ -66,8 +66,6 @@ private static BiFunction generate(Method method) thro ByteBuddy byteBuddy = new ByteBuddy(); DynamicType.Unloaded classPackage = byteBuddy .makePackage(declaringClass.getPackage().getName()) - .innerTypeOf(declaringClass) - .asMemberType() .make(); Class loaded = byteBuddy.subclass(Object.class) diff --git a/di/src/main/java/org/panda_lang/utilities/inject/ClassCache.java b/di/src/main/java/org/panda_lang/utilities/inject/ClassCache.java index 5e442a2..3e05ac2 100644 --- a/di/src/main/java/org/panda_lang/utilities/inject/ClassCache.java +++ b/di/src/main/java/org/panda_lang/utilities/inject/ClassCache.java @@ -1,30 +1,48 @@ package org.panda_lang.utilities.inject; import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.security.InvalidParameterException; import java.util.Arrays; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.panda_lang.utilities.inject.annotations.AutoConstruct; import org.panda_lang.utilities.inject.annotations.Inject; import panda.std.Pair; +import panda.utilities.ObjectUtils; /** * Utility class for caching class data (fields, methods, etc.) to improve performance. */ final class ClassCache { + private static final Map, Constructor> CACHED_CONSTRUCTORS = new ConcurrentHashMap<>(); + private static final Map, Field[]> CACHED_FIELDS = new ConcurrentHashMap<>(); private static final Map, Field[]> INJECTOR_CACHED_FIELDS = new ConcurrentHashMap<>(); private static final Map, Class>, Method[]> CACHED_ANNOTATED_METHODS = new ConcurrentHashMap<>(); - private ClassCache() { } + private ClassCache() {} + + public static Constructor getConstructor(Class clazz) { + return ObjectUtils.cast(CACHED_CONSTRUCTORS.computeIfAbsent(clazz, key -> { + Constructor[] constructors = clazz.getDeclaredConstructors(); + if (constructors.length != 1) { + throw new InvalidParameterException("Class has to contain one and only constructor"); + } + Constructor constructor = ObjectUtils.cast(constructors[0]); + constructor.setAccessible(true); + return constructor; + })); + } /** * Get all fields of the class. * The result is cached. + * * @param clazz class to get fields from * @return array of fields */ @@ -35,6 +53,7 @@ public static Field[] getFields(Class clazz) { /** * Get all fields of the class that are annotated with {@link Inject} or {@link AutoConstruct} and make them accessible. * The result is cached. + * * @param clazz class to get fields from * @return array of fields */ @@ -60,7 +79,8 @@ private static Field[] getAllFields(Class type) { /** * Get all methods of the class that are annotated with the specified annotation. * The result is cached. - * @param clazz class to get methods from + * + * @param clazz class to get methods from * @param annotation annotation to filter methods by * @return array of methods */ diff --git a/di/src/main/java/org/panda_lang/utilities/inject/DefaultInjector.java b/di/src/main/java/org/panda_lang/utilities/inject/DefaultInjector.java index f4aa6c8..a975b97 100644 --- a/di/src/main/java/org/panda_lang/utilities/inject/DefaultInjector.java +++ b/di/src/main/java/org/panda_lang/utilities/inject/DefaultInjector.java @@ -48,13 +48,8 @@ public DefaultInjector(Resources resources) { } @Override - @SuppressWarnings("unchecked") public ConstructorInjector forConstructor(Class type) { - Constructor[] constructors = type.getDeclaredConstructors(); - if (constructors.length != 1) { - throw new InvalidParameterException("Class has to contain one and only constructor"); - } - return new ConstructorInjector<>(this.processor, (Constructor) constructors[0]); + return new ConstructorInjector<>(this.processor, ClassCache.getConstructor(type)); } @Override