All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.inject.internal.ProviderMethod Maven / Gradle / Ivy

There is a newer version: 7.0.0
Show newest version
/**
 * Copyright (C) 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

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.Provides;
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;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.spi.ProvidesMethodBinding;
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;

/**
 * A provider that invokes a method and returns its result.
 *
 * @author [email protected] (Jesse Wilson)
 */
public abstract class ProviderMethod implements ProviderWithExtensionVisitor, HasDependencies,
    ProvidesMethodBinding {

  /**
   * Creates a {@link ProviderMethod}.
   *
   * 

Unless {@code skipFastClassGeneration} is set, this will use * {@link net.sf.cglib.reflect.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 ProviderMethod create(Key key, Method method, Object instance, ImmutableSet> dependencies, List> parameterProviders, Class scopeAnnotation, boolean skipFastClassGeneration, Annotation annotation) { int modifiers = method.getModifiers(); /*if[AOP]*/ if (!skipFastClassGeneration && !Modifier.isPrivate(modifiers) && !Modifier.isProtected(modifiers)) { try { // We use an index instead of FastMethod to save a stack frame. return new FastClassProviderMethod(key, method, instance, dependencies, parameterProviders, scopeAnnotation, annotation); } 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(key, method, instance, dependencies, parameterProviders, scopeAnnotation, annotation); } protected final Object instance; protected final Method method; private final Key key; private final Class scopeAnnotation; private final ImmutableSet> dependencies; private final List> parameterProviders; private final boolean exposed; private final Annotation annotation; /** * @param method the method to invoke. It's return type must be the same type as {@code key}. */ private ProviderMethod(Key key, Method method, Object instance, ImmutableSet> dependencies, List> parameterProviders, Class scopeAnnotation, Annotation annotation) { this.key = key; this.scopeAnnotation = scopeAnnotation; this.instance = instance; this.dependencies = dependencies; this.method = method; this.parameterProviders = parameterProviders; this.exposed = method.isAnnotationPresent(Exposed.class); this.annotation = annotation; } @Override public Key getKey() { return key; } @Override public Method getMethod() { return method; } // exposed for GIN public Object getInstance() { return instance; } @Override public Object getEnclosingInstance() { return instance; } @Override public Annotation getAnnotation() { return annotation; } public void configure(Binder binder) { binder = binder.withSource(method); if (scopeAnnotation != null) { binder.bind(key).toProvider(this).in(scopeAnnotation); } else { binder.bind(key).toProvider(this); } if (exposed) { // the cast is safe 'cause the only binder we have implements PrivateBinder. If there's a // misplaced @Exposed, calling this will add an error to the binder's error queue ((PrivateBinder) binder).expose(key); } } @Override public T get() { Object[] parameters = new Object[parameterProviders.size()]; for (int i = 0; i < parameters.length; i++) { parameters[i] = parameterProviders.get(i).get(); } try { @SuppressWarnings({ "unchecked", "UnnecessaryLocalVariable" }) T result = (T) doProvision(parameters); return result; } catch (IllegalAccessException e) { throw new AssertionError(e); } catch (InvocationTargetException e) { throw Exceptions.rethrowCause(e); } } /** Extension point for our subclasses to implement the provisioning strategy. */ abstract Object doProvision(Object[] parameters) throws IllegalAccessException, InvocationTargetException; @Override public Set> getDependencies() { return dependencies; } @Override @SuppressWarnings("unchecked") public V acceptExtensionVisitor(BindingTargetVisitor visitor, ProviderInstanceBinding binding) { if (visitor instanceof ProvidesMethodTargetVisitor) { return ((ProvidesMethodTargetVisitor)visitor).visit(this); } return visitor.visit(binding); } @Override public String toString() { String annotationString = annotation.toString(); // Show @Provides w/o the com.google.inject prefix. if (annotation.annotationType() == Provides.class) { annotationString = "@Provides"; } else if (annotationString.endsWith("()")) { // Remove the common "()" suffix if there are no values. annotationString = annotationString.substring(0, annotationString.length() - 2); } return annotationString + " " + StackTraceElements.forMember(method); } @Override public boolean equals(Object obj) { if (obj instanceof ProviderMethod) { ProviderMethod o = (ProviderMethod) obj; return method.equals(o.method) && instance.equals(o.instance) && annotation.equals(o.annotation); } else { return false; } } @Override public int hashCode() { // Avoid calling hashCode on 'instance', which is a user-object // that might not be expecting it. // (We need to call equals, so we do. But we can avoid hashCode.) return Objects.hashCode(method, annotation); } /*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 extends ProviderMethod { final net.sf.cglib.reflect.FastClass fastClass; final int methodIndex; FastClassProviderMethod(Key key, Method method, Object instance, ImmutableSet> dependencies, List> parameterProviders, Class scopeAnnotation, Annotation annotation) { super(key, method, instance, dependencies, parameterProviders, scopeAnnotation, annotation); // 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 extends ProviderMethod { ReflectionProviderMethod(Key key, Method method, Object instance, ImmutableSet> dependencies, List> parameterProviders, Class scopeAnnotation, Annotation annotation) { super(key, method, instance, dependencies, parameterProviders, scopeAnnotation, annotation); } @Override Object doProvision(Object[] parameters) throws IllegalAccessException, InvocationTargetException { return method.invoke(instance, parameters); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy