aboutsummaryrefslogtreecommitdiff
path: root/core/src/com/google/inject
diff options
context:
space:
mode:
authorSam Berlin <sameb@google.com>2014-05-10 10:34:16 -0400
committerSam Berlin <sameb@google.com>2014-05-10 10:34:16 -0400
commit409e0f578b620c38f6c8626dee78783219d2956e (patch)
treeb884da0ec0f1c4566558e6da54b9a09188944fa4 /core/src/com/google/inject
parent4daa205dc4fe1d339f91155dde65f3941cbd144c (diff)
downloadguice-409e0f578b620c38f6c8626dee78783219d2956e.tar.gz
Try to use cglibs FastClass to invoke @Provides methods, it's faster!
Here is a caliper benchmark to demonstrate the difference. https://caliper.googleplex.com/runs/2d349fec-2742-45e1-b6e5-16997c522387#r:scenario.benchmarkSpec.methodName,scenario.benchmarkSpec.parameters.strategy This should save about 200-300 ns per method invocation and about 224 bytes (over 4 objects) of allocations for each invocation. The cost of this (of course) is greater permgen usage and potentially slower startup/injector creation due to the class generation. ------------- Created by MOE: http://code.google.com/p/moe-java MOE_MIGRATED_REVID=66364901
Diffstat (limited to 'core/src/com/google/inject')
-rw-r--r--core/src/com/google/inject/internal/ProviderMethod.java109
-rw-r--r--core/src/com/google/inject/internal/ProviderMethodsModule.java2
2 files changed, 102 insertions, 9 deletions
diff --git a/core/src/com/google/inject/internal/ProviderMethod.java b/core/src/com/google/inject/internal/ProviderMethod.java
index d8bc18b1..8746ae10 100644
--- a/core/src/com/google/inject/internal/ProviderMethod.java
+++ b/core/src/com/google/inject/internal/ProviderMethod.java
@@ -17,12 +17,14 @@
package com.google.inject.internal;
import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Binder;
import com.google.inject.Exposed;
import com.google.inject.Key;
import com.google.inject.PrivateBinder;
import com.google.inject.Provider;
+import com.google.inject.internal.BytecodeGen.Visibility;
import com.google.inject.internal.util.StackTraceElements;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
@@ -35,6 +37,7 @@ import com.google.inject.spi.ProvidesMethodTargetVisitor;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Set;
@@ -43,12 +46,44 @@ import java.util.Set;
*
* @author jessewilson@google.com (Jesse Wilson)
*/
-public class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDependencies,
+public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDependencies,
ProvidesMethodBinding<T> {
+
+ /**
+ * Creates a {@link ProviderMethod}.
+ *
+ * <p>Ideally, we will use {@link FastClass} to invoke the actual method, since it is
+ * significantly faster. However, this will fail if the method is {@code private} or
+ * {@code protected}, since fastclass is subject to java access policies.
+ */
+ static <T> ProviderMethod<T> create(Key<T> key, Method method, Object instance,
+ ImmutableSet<Dependency<?>> dependencies, List<Provider<?>> parameterProviders,
+ Class<? extends Annotation> scopeAnnotation) {
+ int modifiers = method.getModifiers();
+ /*if[AOP]*/
+ if (!Modifier.isPrivate(modifiers) && !Modifier.isProtected(modifiers)) {
+ try {
+ // We use an index instead of FastMethod to save a stack frame.
+ return new FastClassProviderMethod<T>(
+ key, method, instance, dependencies, parameterProviders, scopeAnnotation);
+ } catch (net.sf.cglib.core.CodeGenerationException e) {/* fall-through */}
+ }
+ /*end[AOP]*/
+
+ if (!Modifier.isPublic(modifiers) ||
+ !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
+ method.setAccessible(true);
+ }
+
+ return new ReflectionProviderMethod<T>(
+ key, method, instance, dependencies, parameterProviders, scopeAnnotation);
+ }
+
+ protected final Object instance;
+ protected final Method method;
+
private final Key<T> key;
private final Class<? extends Annotation> scopeAnnotation;
- private final Object instance;
- private final Method method;
private final ImmutableSet<Dependency<?>> dependencies;
private final List<Provider<?>> parameterProviders;
private final boolean exposed;
@@ -56,7 +91,7 @@ public class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDe
/**
* @param method the method to invoke. It's return type must be the same type as {@code key}.
*/
- ProviderMethod(Key<T> key, Method method, Object instance,
+ private ProviderMethod(Key<T> key, Method method, Object instance,
ImmutableSet<Dependency<?>> dependencies, List<Provider<?>> parameterProviders,
Class<? extends Annotation> scopeAnnotation) {
this.key = key;
@@ -66,8 +101,6 @@ public class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDe
this.method = method;
this.parameterProviders = parameterProviders;
this.exposed = method.isAnnotationPresent(Exposed.class);
-
- method.setAccessible(true);
}
public Key<T> getKey() {
@@ -110,9 +143,8 @@ public class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDe
}
try {
- // We know this cast is safe becase T is the method's return type.
@SuppressWarnings({ "unchecked", "UnnecessaryLocalVariable" })
- T result = (T) method.invoke(instance, parameters);
+ T result = (T) doProvision(parameters);
return result;
} catch (IllegalAccessException e) {
throw new AssertionError(e);
@@ -121,6 +153,10 @@ public class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDe
}
}
+ /** Extension point for our subclasses to implement the provisioning strategy. */
+ abstract Object doProvision(Object[] parameters)
+ throws IllegalAccessException, InvocationTargetException;
+
public Set<Dependency<?>> getDependencies() {
return dependencies;
}
@@ -156,4 +192,61 @@ public class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDe
// (We need to call equals, so we do. But we can avoid hashCode.)
return Objects.hashCode(method);
}
+
+ /*if[AOP]*/
+ /**
+ * A {@link ProviderMethod} implementation that uses {@link net.sf.cglib.reflect.FastClass#invoke}
+ * to invoke the provider method.
+ */
+ private static final class FastClassProviderMethod<T> extends ProviderMethod<T> {
+ final net.sf.cglib.reflect.FastClass fastClass;
+ final int methodIndex;
+
+ FastClassProviderMethod(Key<T> key,
+ Method method,
+ Object instance,
+ ImmutableSet<Dependency<?>> dependencies,
+ List<Provider<?>> parameterProviders,
+ Class<? extends Annotation> scopeAnnotation) {
+ super(key, method, instance, dependencies, parameterProviders, scopeAnnotation);
+ // We need to generate a FastClass for the method's class, not the object's class.
+ this.fastClass =
+ BytecodeGen.newFastClass(method.getDeclaringClass(), Visibility.forMember(method));
+ // Use the Signature overload of getIndex because it properly uses return types to identify
+ // particular methods. This is normally irrelevant, except in the case of covariant overrides
+ // which java implements with a compiler generated bridge method to implement the override.
+ this.methodIndex = fastClass.getIndex(
+ new net.sf.cglib.core.Signature(
+ method.getName(), org.objectweb.asm.Type.getMethodDescriptor(method)));
+ Preconditions.checkArgument(this.methodIndex >= 0,
+ "Could not find method %s in fast class for class %s",
+ method,
+ method.getDeclaringClass());
+ }
+
+ @Override public Object doProvision(Object[] parameters)
+ throws IllegalAccessException, InvocationTargetException {
+ return fastClass.invoke(methodIndex, instance, parameters);
+ }
+ }
+ /*end[AOP]*/
+
+ /**
+ * A {@link ProviderMethod} implementation that invokes the method using normal java reflection.
+ */
+ private static final class ReflectionProviderMethod<T> extends ProviderMethod<T> {
+ ReflectionProviderMethod(Key<T> key,
+ Method method,
+ Object instance,
+ ImmutableSet<Dependency<?>> dependencies,
+ List<Provider<?>> parameterProviders,
+ Class<? extends Annotation> scopeAnnotation) {
+ super(key, method, instance, dependencies, parameterProviders, scopeAnnotation);
+ }
+
+ @Override Object doProvision(Object[] parameters) throws IllegalAccessException,
+ InvocationTargetException {
+ return method.invoke(instance, parameters);
+ }
+ }
}
diff --git a/core/src/com/google/inject/internal/ProviderMethodsModule.java b/core/src/com/google/inject/internal/ProviderMethodsModule.java
index 7ec3075a..062653d2 100644
--- a/core/src/com/google/inject/internal/ProviderMethodsModule.java
+++ b/core/src/com/google/inject/internal/ProviderMethodsModule.java
@@ -123,7 +123,7 @@ public final class ProviderMethodsModule implements Module {
binder.addError(message);
}
- return new ProviderMethod<T>(key, method, delegate, ImmutableSet.copyOf(dependencies),
+ return ProviderMethod.create(key, method, delegate, ImmutableSet.copyOf(dependencies),
parameterProviders, scopeAnnotation);
}