| // Copyright 2006 Google Inc. All Rights Reserved. |
| |
| package com.google.inject; |
| |
| import com.google.inject.spi.ConstructionProxy; |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationTargetException; |
| |
| /** |
| * Injects constructors. |
| * |
| * @author crazybob@google.com (Bob Lee) |
| */ |
| class ConstructorInjector<T> implements Factory<T> { |
| |
| final Class<T> implementation; |
| final ContainerImpl.Injector[] injectors; |
| final ContainerImpl.ParameterInjector<?>[] parameterInjectors; |
| final ConstructionProxy<T> constructionProxy; |
| |
| private ContainerImpl container; |
| |
| ConstructorInjector(ContainerImpl container, Class<T> implementation) { |
| this.container = container; |
| this.implementation = implementation; |
| Constructor<T> constructor = findConstructorIn(implementation); |
| parameterInjectors = createParameterInjector(constructor); |
| injectors = container.injectors.get(implementation) |
| .toArray(new ContainerImpl.Injector[0]); |
| constructionProxy = container.constructionProxyFactory.get(constructor); |
| } |
| |
| ContainerImpl.ParameterInjector<?>[] createParameterInjector( |
| Constructor<T> constructor) { |
| try { |
| Inject inject = constructor.getAnnotation(Inject.class); |
| return inject == null |
| ? null // default constructor. |
| : container.getParametersInjectors( |
| constructor, |
| constructor.getParameterAnnotations(), |
| constructor.getGenericParameterTypes(), |
| inject.value() |
| ); |
| } catch (ContainerImpl.MissingDependencyException e) { |
| e.handle(container.errorHandler); |
| return null; |
| } |
| } |
| |
| @SuppressWarnings({"unchecked"}) |
| private Constructor<T> findConstructorIn(Class<T> implementation) { |
| Constructor<T> found = null; |
| for (Constructor<T> constructor |
| : implementation.getDeclaredConstructors()) { |
| if (constructor.getAnnotation(Inject.class) != null) { |
| if (found != null) { |
| container.errorHandler.handle( |
| ErrorMessages.TOO_MANY_CONSTRUCTORS, implementation); |
| return ContainerImpl.invalidConstructor(); |
| } |
| found = constructor; |
| } |
| } |
| if (found != null) { |
| return found; |
| } |
| |
| // If no annotated constructor is found, look for a no-arg constructor |
| // instead. |
| try { |
| return implementation.getDeclaredConstructor(); |
| } catch (NoSuchMethodException e) { |
| container.errorHandler.handle(ErrorMessages.MISSING_CONSTRUCTOR, implementation); |
| return ContainerImpl.invalidConstructor(); |
| } |
| } |
| |
| /** |
| * Construct an instance. Returns {@code Object} instead of {@code T} |
| * because it may return a proxy. |
| */ |
| Object construct(InternalContext context, Class<? super T> expectedType) { |
| ConstructionContext<T> constructionContext = |
| context.getConstructionContext(this); |
| |
| // We have a circular reference between constructors. Return a proxy. |
| if (constructionContext.isConstructing()) { |
| // TODO (crazybob): if we can't proxy this object, can we proxy the |
| // other object? |
| return constructionContext.createProxy(expectedType); |
| } |
| |
| // If we're re-entering this factory while injecting fields or methods, |
| // return the same instance. This prevents infinite loops. |
| T t = constructionContext.getCurrentReference(); |
| if (t != null) { |
| return t; |
| } |
| |
| try { |
| // First time through... |
| constructionContext.startConstruction(); |
| try { |
| Object[] parameters = |
| ContainerImpl.getParameters(context, parameterInjectors); |
| t = newInstance(parameters); |
| constructionContext.setProxyDelegates(t); |
| } finally { |
| constructionContext.finishConstruction(); |
| } |
| |
| // Store reference. If an injector re-enters this factory, they'll |
| // get the same reference. |
| constructionContext.setCurrentReference(t); |
| |
| // Inject fields and methods. |
| for (int i = 0; i < injectors.length; i++) { |
| injectors[i].inject(context, t); |
| } |
| |
| return t; |
| } catch (InvocationTargetException e) { |
| throw new RuntimeException(e); |
| } finally { |
| constructionContext.removeCurrentReference(); |
| } |
| } |
| |
| @SuppressWarnings({"unchecked"}) |
| private T newInstance(Object[] parameters) |
| throws InvocationTargetException { |
| return (T) constructionProxy.newInstance(parameters); |
| } |
| |
| public T get() { |
| try { |
| return container.callInContext(new ContextualCallable<T>() { |
| @SuppressWarnings({"unchecked"}) |
| public T call(InternalContext context) { |
| return (T) construct(context, implementation); |
| } |
| }); |
| } catch (RuntimeException e) { |
| throw e; |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| } |