Early draft of InjectionListeners.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@906 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/AbstractModule.java b/src/com/google/inject/AbstractModule.java
index 4950308..9e52f4f 100644
--- a/src/com/google/inject/AbstractModule.java
+++ b/src/com/google/inject/AbstractModule.java
@@ -24,6 +24,7 @@
 import com.google.inject.matcher.Matcher;
 import com.google.inject.spi.Message;
 import com.google.inject.spi.TypeConverter;
+import com.google.inject.spi.InjectableType;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
 
@@ -140,11 +141,11 @@
   }
 
   /**
-   * @see Binder#requestInjection(Object[])
+   * @see Binder#requestInjection(Object)
    * @since 2.0
    */
-  protected void requestInjection(Object... objects) {
-    binder.requestInjection(objects);
+  protected void requestInjection(Object instance) {
+    binder.requestInjection(instance);
   }
 
   /**
@@ -223,4 +224,30 @@
   protected Stage currentStage() {
     return binder.currentStage();
   }
+
+  /**
+   * @see Binder#getMembersInjector(Class)
+   * @since 2.0
+   */
+  protected <T> MembersInjector<T> getMembersInjector(Class<T> type) {
+    return binder.getMembersInjector(type);
+  }
+
+  /**
+   * @see Binder#getMembersInjector(TypeLiteral)
+   * @since 2.0
+   */
+  protected <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type) {
+    return binder.getMembersInjector(type);
+  }
+
+  /**
+   * @see Binder#bindListener(com.google.inject.matcher.Matcher,
+   *  com.google.inject.spi.InjectableType.Listener) 
+   * @since 2.0
+   */
+  protected void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher,
+      InjectableType.Listener listener) {
+    binder.bindListener(typeMatcher, listener);
+  }
 }
diff --git a/src/com/google/inject/AbstractProcessor.java b/src/com/google/inject/AbstractProcessor.java
index 5ea73a3..9a43034 100644
--- a/src/com/google/inject/AbstractProcessor.java
+++ b/src/com/google/inject/AbstractProcessor.java
@@ -26,6 +26,8 @@
 import com.google.inject.spi.ScopeBinding;
 import com.google.inject.spi.StaticInjectionRequest;
 import com.google.inject.spi.TypeConverterBinding;
+import com.google.inject.spi.MembersInjectorLookup;
+import com.google.inject.spi.InjectableTypeListenerBinding;
 import java.util.Iterator;
 import java.util.List;
 
@@ -71,42 +73,50 @@
     }
   }
 
-  public Boolean visitMessage(Message message) {
+  public Boolean visit(Message message) {
     return false;
   }
 
   /*if[AOP]*/
-  public Boolean visitInterceptorBinding(
+  public Boolean visit(
       com.google.inject.spi.InterceptorBinding interceptorBinding) {
     return false;
   }
   /*end[AOP]*/
 
-  public Boolean visitScopeBinding(ScopeBinding scopeBinding) {
+  public Boolean visit(ScopeBinding scopeBinding) {
     return false;
   }
 
-  public Boolean visitInjectionRequest(InjectionRequest injectionRequest) {
+  public Boolean visit(InjectionRequest injectionRequest) {
     return false;
   }
 
-  public Boolean visitStaticInjectionRequest(StaticInjectionRequest staticInjectionRequest) {
+  public Boolean visit(StaticInjectionRequest staticInjectionRequest) {
     return false;
   }
 
-  public Boolean visitTypeConverterBinding(TypeConverterBinding typeConverterBinding) {
+  public Boolean visit(TypeConverterBinding typeConverterBinding) {
     return false;
   }
 
-  public <T> Boolean visitBinding(Binding<T> binding) {
+  public <T> Boolean visit(Binding<T> binding) {
     return false;
   }
 
-  public <T> Boolean visitProviderLookup(ProviderLookup<T> providerLookup) {
+  public <T> Boolean visit(ProviderLookup<T> providerLookup) {
     return false;
   }
 
-  public Boolean visitPrivateElements(PrivateElements privateElements) {
+  public Boolean visit(PrivateElements privateElements) {
+    return false;
+  }
+
+  public <T> Boolean visit(MembersInjectorLookup<T> lookup) {
+    return false;
+  }
+
+  public Boolean visit(InjectableTypeListenerBinding binding) {
     return false;
   }
 }
diff --git a/src/com/google/inject/Binder.java b/src/com/google/inject/Binder.java
index 2331c87..22f0252 100644
--- a/src/com/google/inject/Binder.java
+++ b/src/com/google/inject/Binder.java
@@ -20,6 +20,7 @@
 import com.google.inject.binder.AnnotatedConstantBindingBuilder;
 import com.google.inject.binder.LinkedBindingBuilder;
 import com.google.inject.matcher.Matcher;
+import com.google.inject.spi.InjectableType;
 import com.google.inject.spi.Message;
 import com.google.inject.spi.TypeConverter;
 import java.lang.annotation.Annotation;
@@ -183,13 +184,21 @@
  * documentation.
  *
  * @author crazybob@google.com (Bob Lee)
+ * @author jessewilson@google.com (Jesse Wilson)
+ * @author kevinb@google.com (Kevin Bourrillion)
  */
 public interface Binder {
 
   /*if[AOP]*/
   /**
-   * Binds a method interceptor to methods matched by class and method
-   * matchers.
+   * Binds method interceptor[s] to methods matched by class and method matchers. A method is
+   * eligible for interception if:
+   *
+   * <ul>
+   *  <li>Guice created the instance the method is on</li>
+   *  <li>Neither the enclosing type nor the method is final</li>
+   *  <li>And the method is package-private, protected, or public</li>
+   * </ul>
    *
    * @param classMatcher matches classes the interceptor should apply to. For
    *     example: {@code only(Runnable.class)}.
@@ -229,12 +238,22 @@
 
   /**
    * Upon successful creation, the {@link Injector} will inject instance fields
-   * and methods of the given objects.
+   * and methods of the given object.
    *
-   * @param instances for which members will be injected
+   * @param type of instance
+   * @param instance for which members will be injected
    * @since 2.0
    */
-  void requestInjection(Object... instances);
+  <T> void requestInjection(TypeLiteral<T> type, T instance);
+
+  /**
+   * Upon successful creation, the {@link Injector} will inject instance fields
+   * and methods of the given object.
+   *
+   * @param instance for which members will be injected
+   * @since 2.0
+   */
+  void requestInjection(Object instance);
 
   /**
    * Upon successful creation, the {@link Injector} will inject static fields
@@ -290,7 +309,7 @@
 
   /**
    * Returns the provider used to obtain instances for the given injection type.
-   * The returned will not be valid until the {@link Injector} has been
+   * The returned provider will not be valid until the {@link Injector} has been
    * created. The provider will throw an {@code IllegalStateException} if you
    * try to use it beforehand.
    *
@@ -299,6 +318,28 @@
   <T> Provider<T> getProvider(Class<T> type);
 
   /**
+   * Returns the members injector used to inject dependencies into methods and fields on instances
+   * of the given type {@code T}. The returned members injector will not be valid until the main
+   * {@link Injector} has been created. The members injector will throw an {@code
+   * IllegalStateException} if you try to use it beforehand.
+   *
+   * @param typeLiteral type to get members injector for
+   * @since 2.0
+   */
+  <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral);
+
+  /**
+   * Returns the members injector used to inject dependencies into methods and fields on instances
+   * of the given type {@code T}. The returned members injector will not be valid until the main
+   * {@link Injector} has been created. The members injector will throw an {@code
+   * IllegalStateException} if you try to use it beforehand.
+   *
+   * @param type type to get members injector for
+   * @since 2.0
+   */
+  <T> MembersInjector<T> getMembersInjector(Class<T> type);
+
+  /**
    * Binds a type converter. The injector will use the given converter to
    * convert string constants to matching types as needed.
    *
@@ -306,7 +347,19 @@
    * @param converter converts values
    * @since 2.0
    */
-  void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher, TypeConverter converter);
+  void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
+      TypeConverter converter);
+
+  /**
+   * Registers a listener for injectable types. Guice will notify the listener when it encounters
+   * injectable types matched by the given type matcher.
+   *
+   * @param typeMatcher that matches injectable types the listener should be notified of
+   * @param listener for injectable types matched by typeMatcher
+   * @since 2.0
+   */
+  void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher,
+      InjectableType.Listener listener);
 
   /**
    * Returns a binder that uses {@code source} as the reference location for
diff --git a/src/com/google/inject/Binding.java b/src/com/google/inject/Binding.java
index c25d354..332237e 100644
--- a/src/com/google/inject/Binding.java
+++ b/src/com/google/inject/Binding.java
@@ -89,5 +89,4 @@
    * @since 2.0
    */
   <V> V acceptScopingVisitor(BindingScopingVisitor<V> visitor);
-
 }
diff --git a/src/com/google/inject/BindingProcessor.java b/src/com/google/inject/BindingProcessor.java
index 0d08aa0..46d4105 100644
--- a/src/com/google/inject/BindingProcessor.java
+++ b/src/com/google/inject/BindingProcessor.java
@@ -62,7 +62,7 @@
     this.initializer = initializer;
   }
 
-  @Override public <T> Boolean visitBinding(Binding<T> command) {
+  @Override public <T> Boolean visit(Binding<T> command) {
     final Object source = command.getSource();
 
     if (Void.class.equals(command.getKey().getRawType())) {
@@ -85,7 +85,7 @@
 
     command.acceptTargetVisitor(new BindingTargetVisitor<T, Void>() {
 
-      public Void visitInstance(InstanceBinding<? extends T> binding) {
+      public Void visit(InstanceBinding<? extends T> binding) {
         Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
         T instance = binding.getInstance();
         Initializable<T> ref = initializer.requestInjection(
@@ -97,7 +97,7 @@
         return null;
       }
 
-      public Void visitProviderInstance(ProviderInstanceBinding<? extends T> binding) {
+      public Void visit(ProviderInstanceBinding<? extends T> binding) {
         Provider<? extends T> provider = binding.getProviderInstance();
         Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
         Initializable<Provider<? extends T>> initializable = initializer
@@ -109,7 +109,7 @@
         return null;
       }
 
-      public Void visitProviderKey(ProviderKeyBinding<? extends T> binding) {
+      public Void visit(ProviderKeyBinding<? extends T> binding) {
         Key<? extends Provider<? extends T>> providerKey = binding.getProviderKey();
         BoundProviderFactory<T> boundProviderFactory
             = new BoundProviderFactory<T>(injector, providerKey, source);
@@ -121,7 +121,7 @@
         return null;
       }
 
-      public Void visitLinkedKey(LinkedKeyBinding<? extends T> binding) {
+      public Void visit(LinkedKeyBinding<? extends T> binding) {
         Key<? extends T> linkedKey = binding.getLinkedKey();
         if (key.equals(linkedKey)) {
           errors.recursiveBinding();
@@ -135,7 +135,7 @@
         return null;
       }
 
-      public Void visitUntargetted(UntargettedBinding<? extends T> untargetted) {
+      public Void visit(UntargettedBinding<? extends T> untargetted) {
         // Error: Missing implementation.
         // Example: bind(Date.class).annotatedWith(Red.class);
         // We can't assume abstract types aren't injectable. They may have an
@@ -171,7 +171,7 @@
         return null;
       }
 
-      public Void visitExposed(ExposedBinding<? extends T> binding) {
+      public Void visit(ExposedBinding<? extends T> binding) {
         PrivateElements privateElements = binding.getPrivateElements();
         ExposedKeyFactory<T> exposedKeyFactory = new ExposedKeyFactory<T>(key, privateElements);
         creationListeners.add(exposedKeyFactory);
@@ -180,15 +180,15 @@
         return null;
       }
 
-      public Void visitConvertedConstant(ConvertedConstantBinding<? extends T> binding) {
+      public Void visit(ConvertedConstantBinding<? extends T> binding) {
         throw new IllegalArgumentException("Cannot apply a non-module element");
       }
 
-      public Void visitConstructor(ConstructorBinding<? extends T> binding) {
+      public Void visit(ConstructorBinding<? extends T> binding) {
         throw new IllegalArgumentException("Cannot apply a non-module element");
       }
 
-      public Void visitProviderBinding(ProviderBinding<? extends T> binding) {
+      public Void visit(ProviderBinding<? extends T> binding) {
         throw new IllegalArgumentException("Cannot apply a non-module element");
       }
     });
diff --git a/src/com/google/inject/ClassBindingImpl.java b/src/com/google/inject/ClassBindingImpl.java
deleted file mode 100644
index dd83669..0000000
--- a/src/com/google/inject/ClassBindingImpl.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2007 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;
-
-import com.google.inject.InjectorImpl.LateBoundConstructor;
-import com.google.inject.internal.BindingImpl;
-import com.google.inject.internal.Errors;
-import com.google.inject.internal.ErrorsException;
-import com.google.inject.internal.ImmutableSet;
-import com.google.inject.internal.InternalFactory;
-import static com.google.inject.internal.Preconditions.checkState;
-import com.google.inject.internal.Scoping;
-import com.google.inject.internal.ToStringBuilder;
-import com.google.inject.spi.BindingTargetVisitor;
-import com.google.inject.spi.ConstructorBinding;
-import com.google.inject.spi.Dependency;
-import com.google.inject.spi.InjectionPoint;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-class ClassBindingImpl<T> extends BindingImpl<T> implements ConstructorBinding<T> {
-
-  private final LateBoundConstructor<T> lateBoundConstructor;
-  private ImmutableSet<InjectionPoint> injectionPoints;
-
-  ClassBindingImpl(Injector injector, Key<T> key, Object source,
-      InternalFactory<? extends T> internalFactory, Scoping scoping,
-      LateBoundConstructor<T> lateBoundConstructor) {
-    super(injector, key, source, internalFactory, scoping);
-    this.lateBoundConstructor = lateBoundConstructor;
-  }
-
-  @Override public void initialize(Injector injector, Errors errors) throws ErrorsException {
-    lateBoundConstructor.bind(injector, getKey().getTypeLiteral(), errors);
-    injectionPoints = lateBoundConstructor.constructorInjector.getInjectionPoints();
-  }
-
-  public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
-    checkState(injectionPoints != null, "not initialized");
-    return visitor.visitConstructor(this);
-  }
-
-  public Constructor<? extends T> getConstructor() {
-    return lateBoundConstructor.getConstructor();
-  }
-
-  public Set<InjectionPoint> getInjectionPoints() {
-    return injectionPoints;
-  }
-
-  public Set<Dependency<?>> getDependencies() {
-    return Dependency.forInjectionPoints(injectionPoints);
-  }
-
-  /*if[AOP]*/
-  public Map<Method, List<org.aopalliance.intercept.MethodInterceptor>> getMethodInterceptors() {
-    return lateBoundConstructor.constructorInjector.constructionProxy.getMethodInterceptors();
-  }
-  /*end[AOP]*/
-
-  @Override public String toString() {
-    return new ToStringBuilder(ConstructorBinding.class)
-        .add("key", getKey())
-        .add("source", getSource())
-        .add("scope", getScoping())
-        .toString();
-  }
-}
diff --git a/src/com/google/inject/ConstructionProxyFactory.java b/src/com/google/inject/ConstructionProxyFactory.java
index af404ef..de9c47e 100644
--- a/src/com/google/inject/ConstructionProxyFactory.java
+++ b/src/com/google/inject/ConstructionProxyFactory.java
@@ -16,19 +16,15 @@
 
 package com.google.inject;
 
-import com.google.inject.spi.InjectionPoint;
-
 /**
  * Creates {@link ConstructionProxy} instances.
  *
  * @author crazybob@google.com (Bob Lee)
  */
-interface ConstructionProxyFactory {
+interface ConstructionProxyFactory<T> {
 
   /**
    * Gets a construction proxy for the given constructor.
-   *
-   * @param injectionPoint an injection point whose member is a constructor of {@code T}.
    */
-  <T> ConstructionProxy<T> get(InjectionPoint injectionPoint);
+  ConstructionProxy<T> create();
 }
diff --git a/src/com/google/inject/ConstructorBindingImpl.java b/src/com/google/inject/ConstructorBindingImpl.java
new file mode 100644
index 0000000..0498902
--- /dev/null
+++ b/src/com/google/inject/ConstructorBindingImpl.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2007 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;
+
+import com.google.inject.internal.BindingImpl;
+import com.google.inject.internal.Errors;
+import com.google.inject.internal.ErrorsException;
+import com.google.inject.internal.InternalContext;
+import com.google.inject.internal.InternalFactory;
+import static com.google.inject.internal.Preconditions.checkState;
+import com.google.inject.internal.Scoping;
+import com.google.inject.internal.ToStringBuilder;
+import com.google.inject.spi.BindingTargetVisitor;
+import com.google.inject.spi.ConstructorBinding;
+import com.google.inject.spi.Dependency;
+import com.google.inject.spi.InjectableType;
+import java.lang.reflect.Constructor;
+import java.util.Set;
+
+class ConstructorBindingImpl<T> extends BindingImpl<T> implements ConstructorBinding<T> {
+
+  private final Factory<T> factory;
+  private InjectableType<T> injectableType;
+
+  private ConstructorBindingImpl(Injector injector, Key<T> key, Object source,
+      InternalFactory<? extends T> scopedFactory, Scoping scoping, Factory<T> factory) {
+    super(injector, key, source, scopedFactory, scoping);
+    this.factory = factory;
+  }
+
+  static <T> ConstructorBindingImpl<T> create(
+      InjectorImpl injector, Key<T> key, Object source, Scoping scoping) {
+    Factory<T> factoryFactory = new Factory<T>();
+    InternalFactory<? extends T> scopedFactory
+        = Scopes.scope(key, injector, factoryFactory, scoping);
+    return new ConstructorBindingImpl<T>(
+        injector, key, source, scopedFactory, scoping, factoryFactory);
+  }
+
+  @SuppressWarnings("unchecked")
+  public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
+    factory.constructorInjector = (ConstructorInjector<T>) injector.constructors.get(
+        getKey().getTypeLiteral(), errors);
+    injectableType = factory.constructorInjector.getInjectableType();
+  }
+
+  public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
+    checkState(injectableType != null, "not initialized");
+    return visitor.visit(this);
+  }
+
+  @SuppressWarnings("unchecked") // our injectable type always has a constructor of type T
+  public Constructor<? extends T> getConstructor() {
+    checkState(factory.constructorInjector != null, "Constructor is not ready");
+    return (Constructor<? extends T>) injectableType.getInjectableConstructor().getMember();
+  }
+
+  public InjectableType<T> getInjectableType() {
+    return injectableType;
+  }
+
+  public Set<Dependency<?>> getDependencies() {
+    return Dependency.forInjectableType(injectableType);
+  }
+
+  public void applyTo(Binder binder) {
+    throw new UnsupportedOperationException("This element represents a synthetic binding.");
+  }
+
+  @Override public String toString() {
+    return new ToStringBuilder(ConstructorBinding.class)
+        .add("key", getKey())
+        .add("source", getSource())
+        .add("scope", getScoping())
+        .toString();
+  }
+
+  private static class Factory<T> implements InternalFactory<T> {
+    private ConstructorInjector<T> constructorInjector;
+
+    @SuppressWarnings("unchecked")
+    public T get(Errors errors, InternalContext context, Dependency<?> dependency)
+        throws ErrorsException {
+      checkState(constructorInjector != null, "Constructor not ready");
+
+      // This may not actually be safe because it could return a super type of T (if that's all the
+      // client needs), but it should be OK in practice thanks to the wonders of erasure.
+      return (T) constructorInjector.construct(errors, context, dependency.getKey().getRawType());
+    }
+  }
+}
diff --git a/src/com/google/inject/ConstructorInjector.java b/src/com/google/inject/ConstructorInjector.java
index f911804..7176474 100644
--- a/src/com/google/inject/ConstructorInjector.java
+++ b/src/com/google/inject/ConstructorInjector.java
@@ -20,9 +20,9 @@
 import com.google.inject.internal.Errors;
 import com.google.inject.internal.ErrorsException;
 import com.google.inject.internal.ImmutableList;
-import com.google.inject.internal.ImmutableSet;
 import com.google.inject.internal.InternalContext;
-import com.google.inject.spi.InjectionPoint;
+import com.google.inject.spi.InjectableType;
+import com.google.inject.spi.InjectionListener;
 import java.lang.reflect.InvocationTargetException;
 
 /**
@@ -33,35 +33,24 @@
  */
 class ConstructorInjector<T> {
 
-  final TypeLiteral<T> implementation;
-  final InjectionPoint injectionPoint;
-  final ImmutableList<SingleMemberInjector> memberInjectors;
-  final ImmutableList<SingleParameterInjector<?>> parameterInjectors;
-  final ConstructionProxy<T> constructionProxy;
+  private final ImmutableList<SingleParameterInjector<?>> parameterInjectors;
+  private final ConstructionProxy<T> constructionProxy;
+  private final ImmutableList<SingleMemberInjector> memberInjectors;
+  private final ImmutableList<InjectionListener<? super T>> injectionListeners;
 
-  ConstructorInjector(Errors errors, InjectorImpl injector, TypeLiteral<T> implementation)
+  private final InjectableType<T> injectableType;
+
+  ConstructorInjector(ConstructionProxy<T> constructionProxy,
+      ImmutableList<SingleParameterInjector<?>> parameterInjectors,
+      ImmutableList<SingleMemberInjector> memberInjectors,
+      ImmutableList<InjectionListener<? super T>> injectionListeners,
+      InjectableType<T> injectableType)
       throws ErrorsException {
-    this.implementation = implementation;
-
-    try {
-      this.injectionPoint = InjectionPoint.forConstructorOf(implementation);
-    } catch (ConfigurationException e) {
-      throw errors.merge(e.getErrorMessages()).toException();
-    }
-
-    constructionProxy = injector.constructionProxyFactory.get(injectionPoint);
-    parameterInjectors = injector.getParametersInjectors(injectionPoint.getDependencies(), errors);
-    memberInjectors = injector.injectors.get(implementation, errors);
-  }
-
-  ImmutableSet<InjectionPoint> getInjectionPoints() {
-    InjectionPoint[] injectionPoints = new InjectionPoint[memberInjectors.size() + 1];
-    injectionPoints[0] = constructionProxy.getInjectionPoint();
-    int i = 1;
-    for (SingleMemberInjector memberInjector : memberInjectors) {
-      injectionPoints[i++] = memberInjector.getInjectionPoint();
-    }
-    return ImmutableSet.of(injectionPoints);
+    this.constructionProxy = constructionProxy;
+    this.parameterInjectors = parameterInjectors;
+    this.memberInjectors = memberInjectors;
+    this.injectionListeners = injectionListeners;
+    this.injectableType = injectableType;
   }
 
   /**
@@ -105,6 +94,10 @@
         injector.inject(errors, context, t);
       }
 
+      for (InjectionListener<? super T> injectionListener : injectionListeners) {
+        injectionListener.afterInjection(t); // TODO: handle user exceptions here
+      }
+
       return t;
     } catch (InvocationTargetException userException) {
       Throwable cause = userException.getCause() != null
@@ -116,4 +109,8 @@
       constructionContext.removeCurrentReference();
     }
   }
+
+  public InjectableType<T> getInjectableType() {
+    return injectableType;
+  }
 }
diff --git a/src/com/google/inject/ConstructorInjectorStore.java b/src/com/google/inject/ConstructorInjectorStore.java
new file mode 100644
index 0000000..ff78e6d
--- /dev/null
+++ b/src/com/google/inject/ConstructorInjectorStore.java
@@ -0,0 +1,197 @@
+/**
+ * Copyright (C) 2009 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;
+
+import com.google.inject.internal.Errors;
+import com.google.inject.internal.ErrorsException;
+import com.google.inject.internal.FailableCache;
+import com.google.inject.internal.ImmutableList;
+import com.google.inject.internal.ImmutableSet;
+import static com.google.inject.internal.Iterables.concat;
+import com.google.inject.internal.Lists;
+import com.google.inject.matcher.Matcher;
+import com.google.inject.matcher.Matchers;
+import com.google.inject.spi.InjectableType;
+import com.google.inject.spi.InjectableTypeListenerBinding;
+import com.google.inject.spi.InjectionListener;
+import com.google.inject.spi.InjectionPoint;
+import com.google.inject.spi.Message;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.aopalliance.intercept.MethodInterceptor;
+
+/**
+ * Constructor injectors by type.
+ *
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+class ConstructorInjectorStore extends FailableCache<TypeLiteral<?>, ConstructorInjector<?>> {
+
+  private final InjectorImpl injector;
+  private final ImmutableList<InjectableTypeListenerBinding> injectableTypeListenerBindings;
+
+  public ConstructorInjectorStore(InjectorImpl injector,
+      List<InjectableTypeListenerBinding> injectableTypeListenerBindings) {
+    this.injector = injector;
+    this.injectableTypeListenerBindings = ImmutableList.copyOf(injectableTypeListenerBindings);
+  }
+
+  @SuppressWarnings("unchecked")
+  protected ConstructorInjector<?> create(TypeLiteral<?> type, Errors errors)
+      throws ErrorsException {
+    try {
+      return createConstructor(type, errors);
+    } catch (ConfigurationException e) {
+      throw errors.merge(e.getErrorMessages()).toException();
+    }
+  }
+
+  private <T> ConstructorInjector<T> createConstructor(TypeLiteral<T> type, Errors errors)
+      throws ErrorsException {
+
+    InjectionPoint injectionPoint = InjectionPoint.forConstructorOf(type);
+    ImmutableList<SingleParameterInjector<?>> constructorParameterInjectors
+        = injector.getParametersInjectors(injectionPoint.getDependencies(), errors);
+    ImmutableList<SingleMemberInjector> memberInjectors = injector.injectors.get(type, errors);
+
+    ImmutableSet.Builder<InjectionPoint> injectableMembersBuilder = ImmutableSet.builder();
+    for (SingleMemberInjector memberInjector : memberInjectors) {
+      injectableMembersBuilder.add(memberInjector.getInjectionPoint());
+    }
+    ImmutableSet<InjectionPoint> injectableMembers = injectableMembersBuilder.build();
+
+    ProxyFactory<T> proxyFactory = new ProxyFactory<T>(injectionPoint, injector.methodAspects);
+    EncounterImpl<T> encounter = new EncounterImpl<T>();
+    InjectableType<T> injectableType = new InjectableTypeImpl<T>(
+        injectionPoint, type, injectableMembers, proxyFactory.getInterceptors());
+
+    for (InjectableTypeListenerBinding typeListener : injectableTypeListenerBindings) {
+      if (typeListener.getTypeMatcher().matches(type)) {
+        // TODO: wrap this user code in a better try/catch block
+        typeListener.getListener().hear(injectableType, encounter);
+      }
+    }
+
+    // rebuild the proxy factory and injectable type if new interceptors were added
+    if (encounter.hasAddedAspects()) {
+      proxyFactory = new ProxyFactory<T>(
+          injectionPoint, concat(injector.methodAspects, encounter.aspects));
+      injectableType = new InjectableTypeImpl<T>(
+          injectionPoint, type, injectableMembers, proxyFactory.getInterceptors());
+    }
+
+    return new ConstructorInjector<T>(proxyFactory.create(), constructorParameterInjectors,
+        memberInjectors, encounter.getInjectionListeners(), injectableType);
+  }
+
+  private static class EncounterImpl<T> implements InjectableType.Encounter<T> {
+    private List<InjectionListener<? super T>> injectionListeners; // lazy
+    private List<MethodAspect> aspects; // lazy
+
+    boolean hasAddedAspects() {
+      return aspects != null;
+    }
+
+    ImmutableList<InjectionListener<? super T>> getInjectionListeners() {
+      return injectionListeners == null
+          ? ImmutableList.<InjectionListener<? super T>>of()
+          : ImmutableList.copyOf(injectionListeners);
+    }
+
+    public void register(InjectionListener<? super T> injectionListener) {
+      if (injectionListeners == null) {
+        injectionListeners = Lists.newArrayList();
+      }
+
+      injectionListeners.add(injectionListener);
+    }
+
+    public void bindInterceptor(Matcher<? super Method> methodMatcher,
+        MethodInterceptor... interceptors) {
+      // make sure the applicable aspects is mutable
+      if (aspects == null) {
+        aspects = Lists.newArrayList();
+      }
+
+      aspects.add(new MethodAspect(Matchers.any(), methodMatcher, interceptors));
+    }
+
+    public void addError(String message, Object... arguments) {
+      throw new UnsupportedOperationException("TODO");
+    }
+
+    public void addError(Throwable t) {
+      throw new UnsupportedOperationException("TODO");
+    }
+
+    public void addError(Message message) {
+      throw new UnsupportedOperationException("TODO");
+    }
+
+    public <T> Provider<T> getProvider(Key<T> key) {
+      throw new UnsupportedOperationException("TODO");
+    }
+
+    public <T> Provider<T> getProvider(Class<T> type) {
+      throw new UnsupportedOperationException("TODO");
+    }
+
+    public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
+      throw new UnsupportedOperationException("TODO");
+    }
+
+    public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
+      throw new UnsupportedOperationException("TODO");
+    }
+  }
+
+  static class InjectableTypeImpl<T> implements InjectableType<T> {
+    private final InjectionPoint injectionPoint;
+    private final TypeLiteral<T> type;
+    private final Set<InjectionPoint> injectableMembers;
+    private final Map<Method, List<MethodInterceptor>> methodInterceptors;
+
+    InjectableTypeImpl(InjectionPoint injectionPoint, TypeLiteral<T> type,
+        Set<InjectionPoint> injectableMembers,
+        Map<Method, List<MethodInterceptor>> methodInterceptors) {
+      this.injectionPoint = injectionPoint;
+      this.type = type;
+      this.injectableMembers = injectableMembers;
+      this.methodInterceptors = methodInterceptors;
+    }
+
+    public TypeLiteral<T> getType() {
+      return type;
+    }
+
+    public InjectionPoint getInjectableConstructor() {
+      return injectionPoint;
+    }
+
+    public Set<InjectionPoint> getInjectableMembers() throws ConfigurationException {
+      return injectableMembers;
+    }
+
+    /*if[AOP]*/
+    public Map<Method, List<MethodInterceptor>> getMethodInterceptors() {
+      return methodInterceptors;
+    }
+    /*end[AOP]*/
+  }
+}
diff --git a/src/com/google/inject/DefaultConstructionProxyFactory.java b/src/com/google/inject/DefaultConstructionProxyFactory.java
index d56d40d..d2370d5 100644
--- a/src/com/google/inject/DefaultConstructionProxyFactory.java
+++ b/src/com/google/inject/DefaultConstructionProxyFactory.java
@@ -28,15 +28,22 @@
 import java.util.Map;
 
 /**
- * Default {@link ConstructionProxyFactory} implementation. Simply invokes the
- * constructor. Can be reused by other {@code ConstructionProxyFactory}
- * implementations.
+ * Produces construction proxies that invoke the class constructor.
  *
  * @author crazybob@google.com (Bob Lee)
  */
-class DefaultConstructionProxyFactory implements ConstructionProxyFactory {
+class DefaultConstructionProxyFactory<T> implements ConstructionProxyFactory<T> {
 
-  public <T> ConstructionProxy<T> get(final InjectionPoint injectionPoint) {
+  private final InjectionPoint injectionPoint;
+
+  /**
+   * @param injectionPoint an injection point whose member is a constructor of {@code T}.
+   */
+  DefaultConstructionProxyFactory(InjectionPoint injectionPoint) {
+    this.injectionPoint = injectionPoint;
+  }
+
+  public ConstructionProxy<T> create() {
     @SuppressWarnings("unchecked") // the injection point is for a constructor of T
     final Constructor<T> constructor = (Constructor<T>) injectionPoint.getMember();
 
diff --git a/src/com/google/inject/InheritingState.java b/src/com/google/inject/InheritingState.java
index 68f1c3d..857b163 100644
--- a/src/com/google/inject/InheritingState.java
+++ b/src/com/google/inject/InheritingState.java
@@ -22,7 +22,9 @@
 import com.google.inject.internal.Maps;
 import com.google.inject.internal.MatcherAndConverter;
 import static com.google.inject.internal.Preconditions.checkNotNull;
+import com.google.inject.spi.InjectableTypeListenerBinding;
 import java.lang.annotation.Annotation;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -43,6 +45,7 @@
   /*if[AOP]*/
   private final List<MethodAspect> methodAspects = Lists.newArrayList();
   /*end[AOP]*/
+  private final List<InjectableTypeListenerBinding> listenerBindings = Lists.newArrayList();
   private final WeakKeySet blacklistedKeys = new WeakKeySet();
   private final Object lock;
 
@@ -115,6 +118,19 @@
   }
   /*end[AOP]*/
 
+  public void addInjectableTypeListener(InjectableTypeListenerBinding listenerBinding) {
+    listenerBindings.add(listenerBinding);
+  }
+
+  public List<InjectableTypeListenerBinding> getInjectableTypeListenerBindings() {
+    List<InjectableTypeListenerBinding> parentBindings = parent.getInjectableTypeListenerBindings();
+    List<InjectableTypeListenerBinding> result
+        = new ArrayList<InjectableTypeListenerBinding>(parentBindings.size() + 1);
+    result.addAll(parentBindings);
+    result.addAll(listenerBindings);
+    return result;
+  }
+
   public void blacklist(Key<?> key) {
     parent.blacklist(key);
     blacklistedKeys.add(key);
diff --git a/src/com/google/inject/InjectableTypeListenerBindingProcessor.java b/src/com/google/inject/InjectableTypeListenerBindingProcessor.java
new file mode 100644
index 0000000..13217e5
--- /dev/null
+++ b/src/com/google/inject/InjectableTypeListenerBindingProcessor.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (C) 2009 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;
+
+import com.google.inject.internal.Errors;
+import com.google.inject.spi.InjectableTypeListenerBinding;
+
+/**
+ * Handles {@link Binder#bindListener} commands.
+ *
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+class InjectableTypeListenerBindingProcessor extends AbstractProcessor {
+
+  InjectableTypeListenerBindingProcessor(Errors errors) {
+    super(errors);
+  }
+
+  @Override public Boolean visit(InjectableTypeListenerBinding binding) {
+    injector.state.addInjectableTypeListener(binding);
+    return true;
+  }
+}
\ No newline at end of file
diff --git a/src/com/google/inject/InjectionRequestProcessor.java b/src/com/google/inject/InjectionRequestProcessor.java
index 2862496..03d5e29 100644
--- a/src/com/google/inject/InjectionRequestProcessor.java
+++ b/src/com/google/inject/InjectionRequestProcessor.java
@@ -44,12 +44,12 @@
     this.initializer = initializer;
   }
 
-  @Override public Boolean visitStaticInjectionRequest(StaticInjectionRequest request) {
+  @Override public Boolean visit(StaticInjectionRequest request) {
     staticInjections.add(new StaticInjection(injector, request));
     return true;
   }
 
-  @Override public Boolean visitInjectionRequest(InjectionRequest request) {
+  @Override public Boolean visit(InjectionRequest request) {
     Set<InjectionPoint> injectionPoints;
     try {
       injectionPoints = request.getInjectionPoints();
diff --git a/src/com/google/inject/Injector.java b/src/com/google/inject/Injector.java
index 8ff26b7..e49540c 100644
--- a/src/com/google/inject/Injector.java
+++ b/src/com/google/inject/Injector.java
@@ -47,6 +47,7 @@
  * enables tools and extensions to operate on an injector reflectively.
  *
  * @author crazybob@google.com (Bob Lee)
+ * @author jessewilson@google.com (Jesse Wilson)
  */
 public interface Injector {
 
@@ -57,10 +58,38 @@
    * <p>Whenever Guice creates an instance, it performs this injection automatically (after first
    * performing constructor injection), so if you're able to let Guice create all your objects for
    * you, you'll never need to use this method.
+   *
+   * @param instance to inject members on
+   *
+   * @see Binder#getMembersInjector(Class) for a preferred alternative that supports checks before
+   *  run time
    */
   void injectMembers(Object instance);
 
   /**
+   * Returns the members injector used to inject dependencies into methods and fields on instances
+   * of the given type {@code T}.
+   *
+   * @param typeLiteral type to get members injector for
+   * @see Binder#getMembersInjector(TypeLiteral) for an alternative that offers up front error
+   *  detection
+   * @since 2.0
+   */
+  <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral);
+
+  /**
+   * Returns the members injector used to inject dependencies into methods and fields on instances
+   * of the given type {@code T}. When feasible, use {@link Binder#getMembersInjector(TypeLiteral)}
+   * instead to get increased up front error detection.
+   *
+   * @param type type to get members injector for
+   * @see Binder#getMembersInjector(Class) for an alternative that offers up front error
+   *  detection
+   * @since 2.0
+   */
+  <T> MembersInjector<T> getMembersInjector(Class<T> type);
+
+  /**
    * Returns all explicit bindings.
    *
    * <p>The returned map does not include bindings inherited from a {@link #getParent() parent
@@ -69,7 +98,6 @@
    * the order in which bindings appear in user Modules.
    *
    * <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
-   *
    */
   Map<Key<?>, Binding<?>> getBindings();
 
@@ -108,6 +136,7 @@
    * using this method, in favor of having Guice inject your dependencies ahead of time.
    *
    * @throws ConfigurationException if this injector cannot find or create the provider.
+   * @see Binder#getProvider(Key) for an alternative that offers up front error detection
    */
   <T> Provider<T> getProvider(Key<T> key);
 
@@ -116,6 +145,7 @@
    * using this method, in favor of having Guice inject your dependencies ahead of time.
    *
    * @throws ConfigurationException if this injector cannot find or create the provider.
+   * @see Binder#getProvider(Class) for an alternative that offers up front error detection
    */
   <T> Provider<T> getProvider(Class<T> type);
 
diff --git a/src/com/google/inject/InjectorBuilder.java b/src/com/google/inject/InjectorBuilder.java
index ad3777a..eeb3529 100644
--- a/src/com/google/inject/InjectorBuilder.java
+++ b/src/com/google/inject/InjectorBuilder.java
@@ -251,6 +251,14 @@
       throw new UnsupportedOperationException(
         "Injector.getProvider(Class<T>) is not supported in Stage.TOOL");
     }
+    public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
+      throw new UnsupportedOperationException(
+        "Injector.getMembersInjector(TypeLiteral<T>) is not supported in Stage.TOOL");
+    }
+    public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
+      throw new UnsupportedOperationException(
+        "Injector.getMembersInjector(Class<T>) is not supported in Stage.TOOL");
+    }
     public <T> T getInstance(Key<T> key) {
       throw new UnsupportedOperationException(
         "Injector.getInstance(Key<T>) is not supported in Stage.TOOL");
diff --git a/src/com/google/inject/InjectorImpl.java b/src/com/google/inject/InjectorImpl.java
index d0a60e1..a17fb40 100644
--- a/src/com/google/inject/InjectorImpl.java
+++ b/src/com/google/inject/InjectorImpl.java
@@ -34,7 +34,6 @@
 import com.google.inject.internal.Maps;
 import com.google.inject.internal.MatcherAndConverter;
 import com.google.inject.internal.Nullable;
-import static com.google.inject.internal.Preconditions.checkState;
 import com.google.inject.internal.Scoping;
 import com.google.inject.internal.SourceProvider;
 import com.google.inject.internal.ToStringBuilder;
@@ -47,7 +46,6 @@
 import com.google.inject.util.Providers;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.AnnotatedElement;
-import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.InvocationTargetException;
@@ -71,7 +69,7 @@
   final InjectorImpl parent;
   final BindingsMultimap bindingsMultimap = new BindingsMultimap();
   final Initializer initializer;
-  ConstructionProxyFactory constructionProxyFactory;
+  ImmutableList<MethodAspect> methodAspects;
 
   /** Just-in-time binding cache. Guarded by state.lock() */
   final Map<Key<?>, BindingImpl<?>> jitBindings = Maps.newHashMap();
@@ -226,7 +224,11 @@
     }
 
     public <V> V acceptTargetVisitor(BindingTargetVisitor<? super Provider<T>, V> visitor) {
-      return visitor.visitProviderBinding(this);
+      return visitor.visit(this);
+    }
+
+    public void applyTo(Binder binder) {
+      throw new UnsupportedOperationException("This element represents a synthetic binding.");
     }
 
     @Override public String toString() {
@@ -308,7 +310,7 @@
     }
 
     public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
-      return visitor.visitConvertedConstant(this);
+      return visitor.visit(this);
     }
 
     public T getValue() {
@@ -323,6 +325,10 @@
       return ImmutableSet.<Dependency<?>>of(Dependency.get(getSourceKey()));
     }
 
+    public void applyTo(Binder binder) {
+      throw new UnsupportedOperationException("This element represents a synthetic binding.");
+    }
+
     @Override public String toString() {
       return new ToStringBuilder(ConvertedConstantBinding.class)
           .add("key", getKey())
@@ -336,13 +342,13 @@
     // Put the partially constructed binding in the map a little early. This enables us to handle
     // circular dependencies. Example: FooImpl -> BarImpl -> FooImpl.
     // Note: We don't need to synchronize on state.lock() during injector creation.
-    if (binding instanceof ClassBindingImpl<?>) {
+    // TODO: for the above example, remove the binding for BarImpl if the binding for FooImpl fails
+    if (binding instanceof ConstructorBindingImpl<?>) {
       Key<T> key = binding.getKey();
       jitBindings.put(key, binding);
       boolean successful = false;
       try {
-        // TODO: does this put the binding in JIT bindings?
-        binding.initialize(this, errors);
+        ((ConstructorBindingImpl) binding).initialize(this, errors);
         successful = true;
       } finally {
         if (!successful) {
@@ -399,8 +405,6 @@
       throw errors.cannotInjectInnerClass(rawType).toException();
     }
 
-    LateBoundConstructor<T> lateBoundConstructor = new LateBoundConstructor<T>();
-
     if (!scoping.isExplicitlyScoped()) {
       Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, rawType);
       if (scopeAnnotation != null) {
@@ -409,10 +413,7 @@
       }
     }
 
-    InternalFactory<? extends T> scopedFactory
-        = Scopes.scope(key, this, lateBoundConstructor, scoping);
-    return new ClassBindingImpl<T>(
-        this, key, source, scopedFactory, scoping, lateBoundConstructor);
+    return ConstructorBindingImpl.create(this, key, source, scoping);
   }
 
   /**
@@ -445,34 +446,6 @@
         factory, ImmutableSet.<InjectionPoint>of(), value);
   }
 
-  static class LateBoundConstructor<T> implements InternalFactory<T> {
-    ConstructorInjector<T> constructorInjector;
-
-    @SuppressWarnings("unchecked") // the constructor T is the same as the implementation T
-    void bind(Injector injector, TypeLiteral<T> implementation, Errors errors)
-        throws ErrorsException {
-      InjectorImpl injectorImpl = (InjectorImpl) injector;
-      constructorInjector
-          = (ConstructorInjector<T>) injectorImpl.constructors.get(implementation, errors);
-    }
-
-    public Constructor<T> getConstructor() {
-      checkState(constructorInjector != null, "Constructor is not ready");
-      return constructorInjector.constructionProxy.getConstructor();
-    }
-
-    @SuppressWarnings("unchecked")
-    public T get(Errors errors, InternalContext context, Dependency<?> dependency)
-        throws ErrorsException {
-      checkState(constructorInjector != null, "Construct before bind, " + constructorInjector);
-
-      // This may not actually be safe because it could return a super type of T (if that's all the
-      // client needs), but it should be OK in practice thanks to the wonders of erasure.
-      return (T) constructorInjector.construct(
-          errors, context, dependency.getKey().getRawType());
-    }
-  }
-
   /** Creates a binding for a type annotated with @ProvidedBy. */
   <T> BindingImpl<T> createProvidedByBinding(Key<T> key, Scoping scoping,
       ProvidedBy providedBy, Errors errors) throws ErrorsException {
@@ -751,14 +724,7 @@
   }
 
   /** Cached constructor injectors for each type */
-  final FailableCache<TypeLiteral<?>, ConstructorInjector<?>> constructors
-      = new FailableCache<TypeLiteral<?>, ConstructorInjector<?>>() {
-    @SuppressWarnings("unchecked")
-    protected ConstructorInjector<?> create(TypeLiteral<?> type, Errors errors)
-        throws ErrorsException {
-      return new ConstructorInjector(errors, InjectorImpl.this, type);
-    }
-  };
+  FailableCache<TypeLiteral<?>, ConstructorInjector<?>> constructors;
 
   void injectMembers(Errors errors, Object o, InternalContext context,
       List<SingleMemberInjector> injectors)
@@ -790,6 +756,14 @@
     errors.throwProvisionExceptionIfErrorsExist();
   }
 
+  public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
+    throw new UnsupportedOperationException("TODO");
+  }
+
+  public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
+    throw new UnsupportedOperationException("TODO");
+  }
+
   public void injectMembersOrThrow(final Errors errors, final Object o,
       final List<SingleMemberInjector> injectors)
       throws ErrorsException {
diff --git a/src/com/google/inject/InjectorShell.java b/src/com/google/inject/InjectorShell.java
index 69c4b74..7176729 100644
--- a/src/com/google/inject/InjectorShell.java
+++ b/src/com/google/inject/InjectorShell.java
@@ -146,6 +146,11 @@
       injector.constructionProxyFactory = new DefaultConstructionProxyFactory();
       end[NO_AOP]*/
 
+      new InjectableTypeListenerBindingProcessor(errors).process(injector, elements);
+      stopwatch.resetAndLog("InjectableType listeners creation");
+      injector.constructors = new ConstructorInjectorStore(
+          injector, injector.state.getInjectableTypeListenerBindings());
+
       new ScopeBindingProcessor(errors).process(injector, elements);
       stopwatch.resetAndLog("Scopes creation");
 
diff --git a/src/com/google/inject/InterceptorBindingProcessor.java b/src/com/google/inject/InterceptorBindingProcessor.java
index 3967355..da1419f 100644
--- a/src/com/google/inject/InterceptorBindingProcessor.java
+++ b/src/com/google/inject/InterceptorBindingProcessor.java
@@ -17,6 +17,7 @@
 package com.google.inject;
 
 import com.google.inject.internal.Errors;
+import com.google.inject.internal.ImmutableList;
 import com.google.inject.spi.InterceptorBinding;
 
 /**
@@ -31,13 +32,13 @@
     super(errors);
   }
 
-  @Override public Boolean visitInterceptorBinding(InterceptorBinding command) {
+  @Override public Boolean visit(InterceptorBinding command) {
     injector.state.addMethodAspect(new MethodAspect(
         command.getClassMatcher(), command.getMethodMatcher(), command.getInterceptors()));
     return true;
   }
 
   void setupProxyFactory(InjectorImpl injector) {
-    injector.constructionProxyFactory = new ProxyFactory(injector.state.getMethodAspects());
+    injector.methodAspects = ImmutableList.copyOf(injector.state.getMethodAspects());
   }
 }
diff --git a/src/com/google/inject/MembersInjector.java b/src/com/google/inject/MembersInjector.java
new file mode 100644
index 0000000..d0c8125
--- /dev/null
+++ b/src/com/google/inject/MembersInjector.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2009 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;
+
+/**
+ * Injects dependencies into the fields and methods on instances of type {@code T}. Ignores the
+ * presence or absence of an injectable constructor.
+ *
+ * @param <T> type to inject members of
+ *
+ * @author crazybob@google.com (Bob Lee)
+ * @author jessewilson@google.com (Jesse Wilson)
+ * @since 2.0
+ */
+public interface MembersInjector<T> {
+
+  /**
+   * Injects dependencies into the fields and methods of {@code instance}. Ignores the presence or
+   * absence of an injectable constructor.
+   *
+   * <p>Whenever Guice creates an instance, it performs this injection automatically (after first
+   * performing constructor injection), so if you're able to let Guice create all your objects for
+   * you, you'll never need to use this method.
+   *
+   * @param instance to inject members on
+   */
+  void injectMembers(T instance);
+}
diff --git a/src/com/google/inject/MessageProcessor.java b/src/com/google/inject/MessageProcessor.java
index 1cf79eb..99b3d09 100644
--- a/src/com/google/inject/MessageProcessor.java
+++ b/src/com/google/inject/MessageProcessor.java
@@ -35,7 +35,7 @@
     super(errors);
   }
 
-  @Override public Boolean visitMessage(Message message) {
+  @Override public Boolean visit(Message message) {
     if (message.getCause() != null) {
       String rootMessage = getRootMessage(message.getCause());
       logger.log(Level.INFO,
diff --git a/src/com/google/inject/PrivateElementProcessor.java b/src/com/google/inject/PrivateElementProcessor.java
index 44b331f..a767b9f 100644
--- a/src/com/google/inject/PrivateElementProcessor.java
+++ b/src/com/google/inject/PrivateElementProcessor.java
@@ -36,7 +36,7 @@
     this.stage = stage;
   }
 
-  @Override public Boolean visitPrivateElements(PrivateElements privateElements) {
+  @Override public Boolean visit(PrivateElements privateElements) {
     InjectorShell.Builder builder = new InjectorShell.Builder()
         .parent(injector)
         .stage(stage)
diff --git a/src/com/google/inject/PrivateModule.java b/src/com/google/inject/PrivateModule.java
index 68dcecb..25cda90 100644
--- a/src/com/google/inject/PrivateModule.java
+++ b/src/com/google/inject/PrivateModule.java
@@ -24,6 +24,7 @@
 import com.google.inject.matcher.Matcher;
 import com.google.inject.spi.Message;
 import com.google.inject.spi.TypeConverter;
+import com.google.inject.spi.InjectableType;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
 
@@ -130,55 +131,94 @@
 
   // everything below is copied from AbstractModule
 
+  /**
+   * Returns the current binder.
+   */
   protected final PrivateBinder binder() {
     return binder;
   }
 
+  /**
+   * @see Binder#bindScope(Class, Scope)
+   */
   protected final void bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope) {
     binder.bindScope(scopeAnnotation, scope);
   }
 
+  /**
+   * @see Binder#bind(Key)
+   */
   protected final <T> LinkedBindingBuilder<T> bind(Key<T> key) {
     return binder.bind(key);
   }
 
+  /**
+   * @see Binder#bind(TypeLiteral)
+   */
   protected final <T> AnnotatedBindingBuilder<T> bind(TypeLiteral<T> typeLiteral) {
     return binder.bind(typeLiteral);
   }
 
+  /**
+   * @see Binder#bind(Class)  
+   */
   protected final <T> AnnotatedBindingBuilder<T> bind(Class<T> clazz) {
     return binder.bind(clazz);
   }
 
+  /**
+   * @see Binder#bindConstant()
+   */
   protected final AnnotatedConstantBindingBuilder bindConstant() {
     return binder.bindConstant();
   }
 
+  /**
+   * @see Binder#install(Module)
+   */
   protected final void install(Module module) {
     binder.install(module);
   }
 
+  /**
+   * @see Binder#addError(String, Object[])
+   */
   protected final void addError(String message, Object... arguments) {
     binder.addError(message, arguments);
   }
 
+  /**
+   * @see Binder#addError(Throwable)
+   */
   protected final void addError(Throwable t) {
     binder.addError(t);
   }
 
+  /**
+   * @see Binder#addError(Message)
+   */
   protected final void addError(Message message) {
     binder.addError(message);
   }
 
-  protected final void requestInjection(Object... objects) {
-    binder.requestInjection(objects);
+  /**
+   * @see Binder#requestInjection(Object)
+   */
+  protected final void requestInjection(Object instance) {
+    binder.requestInjection(instance);
   }
 
+  /**
+   * @see Binder#requestStaticInjection(Class[])
+   */
   protected final void requestStaticInjection(Class<?>... types) {
     binder.requestStaticInjection(types);
   }
 
   /*if[AOP]*/
+  /**
+   * @see Binder#bindInterceptor(com.google.inject.matcher.Matcher, com.google.inject.matcher.Matcher, org.aopalliance.intercept.MethodInterceptor[])
+   */
   protected final void bindInterceptor(Matcher<? super Class<?>> classMatcher,
       Matcher<? super Method> methodMatcher,
       org.aopalliance.intercept.MethodInterceptor... interceptors) {
@@ -186,28 +226,68 @@
   }
   /*end[AOP]*/
 
+  /**
+   * Instructs Guice to require a binding to the given key.
+   */
   protected final void requireBinding(Key<?> key) {
     binder.getProvider(key);
   }
 
+  /**
+   * Instructs Guice to require a binding to the given type.
+   */
   protected final void requireBinding(Class<?> type) {
     binder.getProvider(type);
   }
 
+  /**
+   * @see Binder#getProvider(Key)
+   */
   protected final <T> Provider<T> getProvider(Key<T> key) {
     return binder.getProvider(key);
   }
-
+  
+  /**
+   * @see Binder#getProvider(Class)
+   */
   protected final <T> Provider<T> getProvider(Class<T> type) {
     return binder.getProvider(type);
   }
 
+  /**
+   * @see Binder#convertToTypes(com.google.inject.matcher.Matcher, com.google.inject.spi.TypeConverter)
+   */
   protected final void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
       TypeConverter converter) {
     binder.convertToTypes(typeMatcher, converter);
   }
 
+  /**
+   * @see Binder#currentStage()
+   */
   protected final Stage currentStage() {
     return binder.currentStage();
   }
+
+  /**
+   * @see Binder#getMembersInjector(Class)
+   */
+  protected <T> MembersInjector<T> getMembersInjector(Class<T> type) {
+    return binder.getMembersInjector(type);
+  }
+
+  /**
+   * @see Binder#getMembersInjector(TypeLiteral)
+   */
+  protected <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type) {
+    return binder.getMembersInjector(type);
+  }
+
+  /**
+   * @see Binder#bindListener(com.google.inject.matcher.Matcher, com.google.inject.spi.InjectableType.Listener)
+   */
+  protected void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher,
+      InjectableType.Listener listener) {
+    binder.bindListener(typeMatcher, listener);
+  }
 }
diff --git a/src/com/google/inject/ProviderLookupProcessor.java b/src/com/google/inject/ProviderLookupProcessor.java
index 8c06228..689adde 100644
--- a/src/com/google/inject/ProviderLookupProcessor.java
+++ b/src/com/google/inject/ProviderLookupProcessor.java
@@ -32,11 +32,11 @@
     super(errors);
   }
 
-  @Override public <T> Boolean visitProviderLookup(ProviderLookup<T> command) {
+  @Override public <T> Boolean visit(ProviderLookup<T> command) {
     // ensure the provider can be created
     try {
       Provider<T> provider = injector.getProviderOrThrow(command.getKey(), errors);
-      command.initDelegate(provider);
+      command.initializeDelegate(provider);
     } catch (ErrorsException e) {
       errors.merge(e.getErrors()); // TODO: source
     }
diff --git a/src/com/google/inject/ProxyFactory.java b/src/com/google/inject/ProxyFactory.java
index 0143e2e..bac5382 100644
--- a/src/com/google/inject/ProxyFactory.java
+++ b/src/com/google/inject/ProxyFactory.java
@@ -1,5 +1,5 @@
 /**
- * Copyright (C) 2006 Google Inc.
+ * Copyright (C) 2009 Google Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,13 +17,10 @@
 package com.google.inject;
 
 import com.google.inject.internal.BytecodeGen;
-import com.google.inject.internal.BytecodeGen.Visibility;
-import static com.google.inject.internal.BytecodeGen.newEnhancer;
-import com.google.inject.internal.Function;
+import static com.google.inject.internal.BytecodeGen.newFastClass;
 import com.google.inject.internal.ImmutableList;
 import com.google.inject.internal.ImmutableMap;
 import com.google.inject.internal.Lists;
-import com.google.inject.internal.MapMaker;
 import com.google.inject.internal.Maps;
 import com.google.inject.spi.InjectionPoint;
 import java.lang.reflect.Constructor;
@@ -41,92 +38,12 @@
 import org.aopalliance.intercept.MethodInterceptor;
 
 /**
- * Proxies classes applying interceptors to methods.
+ * Builds a construction proxy that can participate in AOP. This class manages applying type and
+ * method matchers to come up with the set of intercepted methods.
  *
- * @author crazybob@google.com (Bob Lee)
+ * @author jessewilson@google.com (Jesse Wilson)
  */
-class ProxyFactory implements ConstructionProxyFactory {
-
-  final List<MethodAspect> methodAspects;
-  final ConstructionProxyFactory defaultFactory;
-
-  ProxyFactory(List<MethodAspect> methodAspects) {
-    this.methodAspects = methodAspects;
-    defaultFactory = new DefaultConstructionProxyFactory();
-  }
-
-  /** Cached construction proxies for each injection point */
-  Map<InjectionPoint, ConstructionProxy> constructionProxies = new MapMaker().makeComputingMap(
-      new Function<InjectionPoint, ConstructionProxy>() {
-    public ConstructionProxy apply(InjectionPoint key) {
-      return createConstructionProxy(key);
-    }
-  });
-
-  @SuppressWarnings("unchecked") // the constructed T is the same as the injection point's T
-  public <T> ConstructionProxy<T> get(InjectionPoint injectionPoint) {
-    return (ConstructionProxy<T>) constructionProxies.get(injectionPoint);
-  }
-
-  // TODO: This isn't safe.
-  <T> ConstructionProxy<T> createConstructionProxy(InjectionPoint injectionPoint) {
-    @SuppressWarnings("unchecked") // the member of injectionPoint is always a Constructor<T>
-    Constructor<T> constructor = (Constructor<T>) injectionPoint.getMember();
-    Class<T> declaringClass = constructor.getDeclaringClass();
-
-    // Find applicable aspects. Bow out if none are applicable to this class.
-    List<MethodAspect> applicableAspects = Lists.newArrayList();
-    for (MethodAspect methodAspect : methodAspects) {
-      if (methodAspect.matches(declaringClass)) {
-        applicableAspects.add(methodAspect);
-      }
-    }
-    if (applicableAspects.isEmpty()) {
-      return defaultFactory.get(injectionPoint);
-    }
-
-    // Get list of methods from cglib.
-    List<Method> methods = Lists.newArrayList();
-    Enhancer.getMethods(declaringClass, null, methods);
-
-    // Create method/interceptor holders and record indices.
-    List<MethodInterceptorsPair> methodInterceptorsPairs = Lists.newArrayList();
-    for (Method method : methods) {
-      methodInterceptorsPairs.add(new MethodInterceptorsPair(method));
-    }
-
-    // PUBLIC if all the methods we're intercepting are public. This impacts which classloader we
-    // should use for loading the enhanced class
-    Visibility visibility = Visibility.PUBLIC;
-
-    // Iterate over aspects and add interceptors for the methods they apply to
-    boolean anyMatched = false;
-    for (MethodAspect methodAspect : applicableAspects) {
-      for (MethodInterceptorsPair pair : methodInterceptorsPairs) {
-        if (methodAspect.matches(pair.method)) {
-          visibility = visibility.and(Visibility.forMember(pair.method));
-          pair.addAll(methodAspect.interceptors());
-          anyMatched = true;
-        }
-      }
-    }
-    if (!anyMatched) {
-      // not test-covered
-      return defaultFactory.get(injectionPoint);
-    }
-
-    @SuppressWarnings("unchecked")
-    Class<? extends Callback>[] callbackTypes = new Class[methods.size()];
-    Arrays.fill(callbackTypes, net.sf.cglib.proxy.MethodInterceptor.class);
-
-    // Create the proxied class. We're careful to ensure that all enhancer state is not-specific to
-    // this injector. Otherwise, the proxies for each injector will waste Permgen memory
-    Enhancer enhancer = newEnhancer(declaringClass, visibility);
-    enhancer.setCallbackFilter(new IndicesCallbackFilter(declaringClass, methods));
-    enhancer.setCallbackTypes(callbackTypes);
-
-    return new ProxyConstructor<T>(methods, methodInterceptorsPairs, enhancer, injectionPoint);
-  }
+class ProxyFactory<T> implements ConstructionProxyFactory<T> {
 
   private static final net.sf.cglib.proxy.MethodInterceptor NO_OP_METHOD_INTERCEPTOR
       = new net.sf.cglib.proxy.MethodInterceptor() {
@@ -137,78 +54,122 @@
     }
   };
 
+  private final InjectionPoint injectionPoint;
+  private final ImmutableMap<Method, List<MethodInterceptor>> interceptors;
+  private final Class<T> declaringClass;
+  private final List<Method> methods;
+  private final Callback[] callbacks;
+
   /**
-   * Constructs instances that participate in AOP.
+   * PUBLIC is default; it's used if all the methods we're intercepting are public. This impacts
+   * which classloader we should use for loading the enhanced class
    */
-  private static class ProxyConstructor<T> implements ConstructionProxy<T> {
-    private final Class<?> enhanced;
-    private final InjectionPoint injectionPoint;
-    private final Constructor<T> constructor;
+  private BytecodeGen.Visibility visibility = BytecodeGen.Visibility.PUBLIC;
 
-    private final Callback[] callbacks;
-    private final FastConstructor fastConstructor;
-    private final ImmutableMap<Method, List<MethodInterceptor>> methodInterceptors;
+  ProxyFactory(InjectionPoint injectionPoint, Iterable<MethodAspect> methodAspects) {
+    this.injectionPoint = injectionPoint;
 
-    @SuppressWarnings("unchecked") // the constructor promises to construct 'T's
-    ProxyConstructor(List<Method> methods, List<MethodInterceptorsPair> methodInterceptorsPairs,
-        Enhancer enhancer, InjectionPoint injectionPoint) {
-      this.enhanced = enhancer.createClass(); // this returns a cached class if possible
-      this.injectionPoint = injectionPoint;
-      this.constructor = (Constructor<T>) injectionPoint.getMember();
+    @SuppressWarnings("unchecked") // the member of injectionPoint is always a Constructor<T>
+        Constructor<T> constructor = (Constructor<T>) injectionPoint.getMember();
+    declaringClass = constructor.getDeclaringClass();
 
-      ImmutableMap.Builder<Method, List<MethodInterceptor>> interceptorsMapBuilder = null; // lazy
-
-      this.callbacks = new Callback[methods.size()];
-      for (int i = 0; i < methods.size(); i++) {
-        MethodInterceptorsPair pair = methodInterceptorsPairs.get(i);
-
-        if (!pair.hasInterceptors()) {
-          callbacks[i] = NO_OP_METHOD_INTERCEPTOR;
-          continue;
-        }
-
-        if (interceptorsMapBuilder == null) {
-          interceptorsMapBuilder = ImmutableMap.builder();
-        }
-        interceptorsMapBuilder.put(pair.method, ImmutableList.copyOf(pair.interceptors));
-        callbacks[i] = new InterceptorStackCallback(pair.method, pair.interceptors);
-      }
-
-      FastClass fastClass = BytecodeGen.newFastClass(enhanced, Visibility.forMember(constructor));
-      this.fastConstructor = fastClass.getConstructor(constructor.getParameterTypes());
-      this.methodInterceptors = interceptorsMapBuilder != null
-          ? interceptorsMapBuilder.build()
-          : ImmutableMap.<Method, List<MethodInterceptor>>of();
-    }
-
-    @SuppressWarnings("unchecked") // the constructor promises to produce 'T's
-    public T newInstance(Object[] arguments) throws InvocationTargetException {
-      Enhancer.registerCallbacks(enhanced, callbacks);
-      try {
-        return (T) fastConstructor.newInstance(arguments);
-      } finally {
-        Enhancer.registerCallbacks(enhanced, null);
+    // Find applicable aspects. Bow out if none are applicable to this class.
+    List<MethodAspect> applicableAspects = Lists.newArrayList();
+    for (MethodAspect methodAspect : methodAspects) {
+      if (methodAspect.matches(declaringClass)) {
+        applicableAspects.add(methodAspect);
       }
     }
 
-    public InjectionPoint getInjectionPoint() {
-      return injectionPoint;
+    if (applicableAspects.isEmpty()) {
+      interceptors = ImmutableMap.of();
+      methods = ImmutableList.of();
+      callbacks = null;
+      return;
     }
 
-    public Constructor<T> getConstructor() {
-      return constructor;
+    // Get list of methods from cglib.
+    methods = Lists.newArrayList();
+    Enhancer.getMethods(declaringClass, null, methods);
+
+    // Create method/interceptor holders and record indices.
+    List<MethodInterceptorsPair> methodInterceptorsPairs = Lists.newArrayList();
+    for (Method method : methods) {
+      methodInterceptorsPairs.add(new MethodInterceptorsPair(method));
     }
 
-    public Map<Method, List<MethodInterceptor>> getMethodInterceptors() {
-      return methodInterceptors;
+    // Iterate over aspects and add interceptors for the methods they apply to
+    boolean anyMatched = false;
+    for (MethodAspect methodAspect : applicableAspects) {
+      for (MethodInterceptorsPair pair : methodInterceptorsPairs) {
+        if (methodAspect.matches(pair.method)) {
+          visibility = visibility.and(BytecodeGen.Visibility.forMember(pair.method));
+          pair.addAll(methodAspect.interceptors());
+          anyMatched = true;
+        }
+      }
     }
+
+    if (!anyMatched) {
+      interceptors = ImmutableMap.of();
+      callbacks = null;
+      return;
+    }
+
+    ImmutableMap.Builder<Method, List<MethodInterceptor>> interceptorsMapBuilder = null; // lazy
+
+    callbacks = new Callback[methods.size()];
+    for (int i = 0; i < methods.size(); i++) {
+      MethodInterceptorsPair pair = methodInterceptorsPairs.get(i);
+
+      if (!pair.hasInterceptors()) {
+        callbacks[i] = NO_OP_METHOD_INTERCEPTOR;
+        continue;
+      }
+
+      if (interceptorsMapBuilder == null) {
+        interceptorsMapBuilder = ImmutableMap.builder();
+      }
+
+      interceptorsMapBuilder.put(pair.method, ImmutableList.copyOf(pair.interceptors));
+      callbacks[i] = new InterceptorStackCallback(pair.method, pair.interceptors);
+    }
+
+    interceptors = interceptorsMapBuilder != null
+        ? interceptorsMapBuilder.build()
+        : ImmutableMap.<Method, List<MethodInterceptor>>of();
+  }
+
+  /**
+   * Returns the interceptors that apply to the constructed type. When InjectableType.Listeners
+   * add additional interceptors, this builder will be thrown out and another created.n
+   */
+  public ImmutableMap<Method, List<MethodInterceptor>> getInterceptors() {
+    return interceptors;
+  }
+
+  public ConstructionProxy<T> create() {
+    if (interceptors.isEmpty()) {
+      return new DefaultConstructionProxyFactory<T>(injectionPoint).create();
+    }
+
+    @SuppressWarnings("unchecked")
+    Class<? extends Callback>[] callbackTypes = new Class[methods.size()];
+    Arrays.fill(callbackTypes, net.sf.cglib.proxy.MethodInterceptor.class);
+
+    // Create the proxied class. We're careful to ensure that all enhancer state is not-specific
+    // to this injector. Otherwise, the proxies for each injector will waste PermGen memory
+    Enhancer enhancer = BytecodeGen.newEnhancer(declaringClass, visibility);
+    enhancer.setCallbackFilter(new IndicesCallbackFilter(declaringClass, methods));
+    enhancer.setCallbackTypes(callbackTypes);
+    return new ProxyConstructor<T>(enhancer, injectionPoint, callbacks, interceptors);
   }
 
   private static class MethodInterceptorsPair {
     final Method method;
-    List<MethodInterceptor> interceptors;
+    List<MethodInterceptor> interceptors; // lazy
 
-    public MethodInterceptorsPair(Method method) {
+    MethodInterceptorsPair(Method method) {
       this.method = method;
     }
 
@@ -232,7 +193,7 @@
     final Class<?> declaringClass;
     final Map<Method, Integer> indices;
 
-    public IndicesCallbackFilter(Class<?> declaringClass, List<Method> methods) {
+    IndicesCallbackFilter(Class<?> declaringClass, List<Method> methods) {
       this.declaringClass = declaringClass;
       final Map<Method, Integer> indices = Maps.newHashMap();
       for (int i = 0; i < methods.size(); i++) {
@@ -256,4 +217,52 @@
       return declaringClass.hashCode();
     }
   }
+
+  /**
+   * Constructs instances that participate in AOP.
+   */
+  private static class ProxyConstructor<T> implements ConstructionProxy<T> {
+    final Class<?> enhanced;
+    final InjectionPoint injectionPoint;
+    final Constructor<T> constructor;
+    final Callback[] callbacks;
+
+    final FastConstructor fastConstructor;
+    final ImmutableMap<Method, List<MethodInterceptor>> methodInterceptors;
+
+    @SuppressWarnings("unchecked") // the constructor promises to construct 'T's
+    ProxyConstructor(Enhancer enhancer, InjectionPoint injectionPoint, Callback[] callbacks,
+        ImmutableMap<Method, List<MethodInterceptor>> methodInterceptors) {
+      this.enhanced = enhancer.createClass(); // this returns a cached class if possible
+      this.injectionPoint = injectionPoint;
+      this.constructor = (Constructor<T>) injectionPoint.getMember();
+      this.callbacks = callbacks;
+      this.methodInterceptors = methodInterceptors;
+
+      FastClass fastClass = newFastClass(enhanced, BytecodeGen.Visibility.forMember(constructor));
+      this.fastConstructor = fastClass.getConstructor(constructor.getParameterTypes());
+    }
+
+    @SuppressWarnings("unchecked") // the constructor promises to produce 'T's
+    public T newInstance(Object[] arguments) throws InvocationTargetException {
+      Enhancer.registerCallbacks(enhanced, callbacks);
+      try {
+        return (T) fastConstructor.newInstance(arguments);
+      } finally {
+        Enhancer.registerCallbacks(enhanced, null);
+      }
+    }
+
+    public InjectionPoint getInjectionPoint() {
+      return injectionPoint;
+    }
+
+    public Constructor<T> getConstructor() {
+      return constructor;
+    }
+
+    public Map<Method, List<MethodInterceptor>> getMethodInterceptors() {
+      return methodInterceptors;
+    }
+  }
 }
diff --git a/src/com/google/inject/ScopeBindingProcessor.java b/src/com/google/inject/ScopeBindingProcessor.java
index 4dcbd3d..79bce81 100644
--- a/src/com/google/inject/ScopeBindingProcessor.java
+++ b/src/com/google/inject/ScopeBindingProcessor.java
@@ -34,7 +34,7 @@
     super(errors);
   }
 
-  @Override public Boolean visitScopeBinding(ScopeBinding command) {
+  @Override public Boolean visit(ScopeBinding command) {
     Scope scope = command.getScope();
     Class<? extends Annotation> annotationType = command.getAnnotationType();
 
diff --git a/src/com/google/inject/State.java b/src/com/google/inject/State.java
index 058e229..7411aee 100644
--- a/src/com/google/inject/State.java
+++ b/src/com/google/inject/State.java
@@ -21,6 +21,7 @@
 import com.google.inject.internal.ImmutableList;
 import com.google.inject.internal.ImmutableSet;
 import com.google.inject.internal.MatcherAndConverter;
+import com.google.inject.spi.InjectableTypeListenerBinding;
 import java.lang.annotation.Annotation;
 import java.util.List;
 import java.util.Map;
@@ -81,6 +82,15 @@
     }
     /*end[AOP]*/
 
+    public void addInjectableTypeListener(
+        InjectableTypeListenerBinding injectableTypeListenerBinding) {
+      throw new UnsupportedOperationException();
+    }
+
+    public List<InjectableTypeListenerBinding> getInjectableTypeListenerBindings() {
+      return ImmutableList.of();
+    }
+
     public void blacklist(Key<?> key) {
     }
 
@@ -123,6 +133,10 @@
   List<MethodAspect> getMethodAspects();
   /*end[AOP]*/
 
+  void addInjectableTypeListener(InjectableTypeListenerBinding injectableTypeListenerBinding);
+
+  List<InjectableTypeListenerBinding> getInjectableTypeListenerBindings();
+
   /**
    * Forbids the corresponding injector from creating a binding to {@code key}. Child injectors
    * blacklist their bound keys on their parent injectors to prevent just-in-time bindings on the
diff --git a/src/com/google/inject/TypeConverterBindingProcessor.java b/src/com/google/inject/TypeConverterBindingProcessor.java
index b2e5885..967ec97 100644
--- a/src/com/google/inject/TypeConverterBindingProcessor.java
+++ b/src/com/google/inject/TypeConverterBindingProcessor.java
@@ -166,7 +166,7 @@
         new MatcherAndConverter(typeMatcher, converter, SourceProvider.UNKNOWN_SOURCE));
   }
 
-  @Override public Boolean visitTypeConverterBinding(TypeConverterBinding command) {
+  @Override public Boolean visit(TypeConverterBinding command) {
     injector.state.addConverter(new MatcherAndConverter(
         command.getTypeMatcher(), command.getTypeConverter(), command.getSource()));
     return true;
diff --git a/src/com/google/inject/internal/BindingImpl.java b/src/com/google/inject/internal/BindingImpl.java
index 625b9ee..471dae3 100644
--- a/src/com/google/inject/internal/BindingImpl.java
+++ b/src/com/google/inject/internal/BindingImpl.java
@@ -90,18 +90,13 @@
   }
 
   public <V> V acceptVisitor(ElementVisitor<V> visitor) {
-    return visitor.visitBinding(this);
+    return visitor.visit(this);
   }
 
   public <V> V acceptScopingVisitor(BindingScopingVisitor<V> visitor) {
     return scoping.acceptVisitor(visitor);
   }
 
-  /**
-   * Perform any post-creation initialization, that could require construction of other bindings.
-   */
-  public void initialize(Injector injector, Errors errors) throws ErrorsException {}
-
   protected BindingImpl<T> withScoping(Scoping scoping) {
     throw new AssertionError();
   }
diff --git a/src/com/google/inject/internal/ExposedBindingImpl.java b/src/com/google/inject/internal/ExposedBindingImpl.java
index 2518cab..9e59a5c 100644
--- a/src/com/google/inject/internal/ExposedBindingImpl.java
+++ b/src/com/google/inject/internal/ExposedBindingImpl.java
@@ -16,6 +16,7 @@
 
 package com.google.inject.internal;
 
+import com.google.inject.Binder;
 import com.google.inject.Injector;
 import com.google.inject.Key;
 import com.google.inject.spi.BindingTargetVisitor;
@@ -41,7 +42,7 @@
   }
 
   public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
-    return visitor.visitExposed(this);
+    return visitor.visit(this);
   }
 
   public Set<Dependency<?>> getDependencies() {
@@ -67,4 +68,8 @@
         .add("privateElements", privateElements)
         .toString();
   }
+
+  public void applyTo(Binder binder) {
+    throw new UnsupportedOperationException("This element represents a synthetic binding.");
+  }
 }
diff --git a/src/com/google/inject/internal/InstanceBindingImpl.java b/src/com/google/inject/internal/InstanceBindingImpl.java
index 92dc559..efa3f12 100644
--- a/src/com/google/inject/internal/InstanceBindingImpl.java
+++ b/src/com/google/inject/internal/InstanceBindingImpl.java
@@ -19,6 +19,7 @@
 import com.google.inject.Injector;
 import com.google.inject.Key;
 import com.google.inject.Provider;
+import com.google.inject.Binder;
 import com.google.inject.spi.BindingTargetVisitor;
 import com.google.inject.spi.Dependency;
 import com.google.inject.spi.HasDependencies;
@@ -55,7 +56,7 @@
   }
 
   public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
-    return visitor.visitInstance(this);
+    return visitor.visit(this);
   }
 
   public T getInstance() {
@@ -80,6 +81,11 @@
     return new InstanceBindingImpl<T>(getSource(), key, getScoping(), injectionPoints, instance);
   }
 
+  public void applyTo(Binder binder) {
+    // instance bindings aren't scoped
+    binder.withSource(getSource()).bind(getKey()).toInstance(instance);
+  }
+
   @Override public String toString() {
     return new ToStringBuilder(InstanceBinding.class)
         .add("key", getKey())
diff --git a/src/com/google/inject/internal/LinkedBindingImpl.java b/src/com/google/inject/internal/LinkedBindingImpl.java
index 01485ba..0b29e0a 100644
--- a/src/com/google/inject/internal/LinkedBindingImpl.java
+++ b/src/com/google/inject/internal/LinkedBindingImpl.java
@@ -18,6 +18,7 @@
 
 import com.google.inject.Injector;
 import com.google.inject.Key;
+import com.google.inject.Binder;
 import com.google.inject.spi.BindingTargetVisitor;
 import com.google.inject.spi.LinkedKeyBinding;
 
@@ -38,7 +39,7 @@
   }
 
   public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
-    return visitor.visitLinkedKey(this);
+    return visitor.visit(this);
   }
 
   public Key<? extends T> getLinkedKey() {
@@ -53,6 +54,10 @@
     return new LinkedBindingImpl<T>(getSource(), key, getScoping(), targetKey);
   }
 
+  public void applyTo(Binder binder) {
+    getScoping().applyTo(binder.withSource(getSource()).bind(getKey()).to(getLinkedKey()));
+  }
+
   @Override public String toString() {
     return new ToStringBuilder(LinkedKeyBinding.class)
         .add("key", getKey())
diff --git a/src/com/google/inject/internal/LinkedProviderBindingImpl.java b/src/com/google/inject/internal/LinkedProviderBindingImpl.java
index fe3a39f..38393dd 100644
--- a/src/com/google/inject/internal/LinkedProviderBindingImpl.java
+++ b/src/com/google/inject/internal/LinkedProviderBindingImpl.java
@@ -19,6 +19,7 @@
 import com.google.inject.Injector;
 import com.google.inject.Key;
 import com.google.inject.Provider;
+import com.google.inject.Binder;
 import com.google.inject.spi.BindingTargetVisitor;
 import com.google.inject.spi.ProviderKeyBinding;
 
@@ -41,7 +42,7 @@
   }
 
   public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
-    return visitor.visitProviderKey(this);
+    return visitor.visit(this);
   }
 
   public Key<? extends Provider<? extends T>> getProviderKey() {
@@ -56,6 +57,10 @@
     return new LinkedProviderBindingImpl<T>(getSource(), key, getScoping(), providerKey);
   }
 
+  public void applyTo(Binder binder) {
+    getScoping().applyTo(binder.bind(getKey()).toProvider(getProviderKey()));
+  }
+
   @Override public String toString() {
     return new ToStringBuilder(ProviderKeyBinding.class)
         .add("key", getKey())
diff --git a/src/com/google/inject/internal/PrivateElementsImpl.java b/src/com/google/inject/internal/PrivateElementsImpl.java
index 0888309..92990d3 100644
--- a/src/com/google/inject/internal/PrivateElementsImpl.java
+++ b/src/com/google/inject/internal/PrivateElementsImpl.java
@@ -18,6 +18,7 @@
 
 import com.google.inject.Injector;
 import com.google.inject.Key;
+import com.google.inject.Binder;
 import com.google.inject.internal.BindingBuilder.ExposureBuilder;
 import static com.google.inject.internal.Preconditions.checkNotNull;
 import static com.google.inject.internal.Preconditions.checkState;
@@ -90,7 +91,7 @@
   }
 
   public <T> T acceptVisitor(ElementVisitor<T> visitor) {
-    return visitor.visitPrivateElements(this);
+    return visitor.visit(this);
   }
 
   public List<Element> getElementsMutable() {
@@ -100,4 +101,8 @@
   public void addExposureBuilder(ExposureBuilder<?> exposureBuilder) {
     exposureBuilders.add(exposureBuilder);
   }
+
+  public void applyTo(Binder binder) {
+    throw new UnsupportedOperationException("TODO");
+  }
 }
diff --git a/src/com/google/inject/internal/ProviderInstanceBindingImpl.java b/src/com/google/inject/internal/ProviderInstanceBindingImpl.java
index 4dc38f9..907e000 100644
--- a/src/com/google/inject/internal/ProviderInstanceBindingImpl.java
+++ b/src/com/google/inject/internal/ProviderInstanceBindingImpl.java
@@ -19,6 +19,7 @@
 import com.google.inject.Injector;
 import com.google.inject.Key;
 import com.google.inject.Provider;
+import com.google.inject.Binder;
 import com.google.inject.spi.BindingTargetVisitor;
 import com.google.inject.spi.Dependency;
 import com.google.inject.spi.HasDependencies;
@@ -49,7 +50,7 @@
   }
 
   public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
-    return visitor.visitProviderInstance(this);
+    return visitor.visit(this);
   }
 
   public Provider<? extends T> getProviderInstance() {
@@ -76,6 +77,11 @@
         getSource(), key, getScoping(), injectionPoints, providerInstance);
   }
 
+  public void applyTo(Binder binder) {
+    getScoping().applyTo(
+        binder.withSource(getSource()).bind(getKey()).toProvider(getProviderInstance()));
+  }
+
   @Override
   public String toString() {
     return new ToStringBuilder(ProviderInstanceBinding.class)
diff --git a/src/com/google/inject/internal/Scoping.java b/src/com/google/inject/internal/Scoping.java
index d39b4ba..a0b3583 100644
--- a/src/com/google/inject/internal/Scoping.java
+++ b/src/com/google/inject/internal/Scoping.java
@@ -20,6 +20,7 @@
 import com.google.inject.Scopes;
 import com.google.inject.Singleton;
 import com.google.inject.Stage;
+import com.google.inject.binder.ScopedBindingBuilder;
 import com.google.inject.spi.BindingScopingVisitor;
 import java.lang.annotation.Annotation;
 
@@ -47,6 +48,10 @@
     @Override public String toString() {
       return Scopes.NO_SCOPE.toString();
     }
+
+    public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
+      // do nothing
+    }
   };
 
   public static final Scoping SINGLETON_ANNOTATION = new Scoping() {
@@ -61,6 +66,10 @@
     @Override public String toString() {
       return Singleton.class.getName();
     }
+
+    public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
+      scopedBindingBuilder.in(Singleton.class);
+    }
   };
 
   public static final Scoping SINGLETON_INSTANCE = new Scoping() {
@@ -75,6 +84,10 @@
     @Override public String toString() {
       return Scopes.SINGLETON.toString();
     }
+
+    public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
+      scopedBindingBuilder.in(Scopes.SINGLETON);
+    }
   };
 
   public static final Scoping EAGER_SINGLETON = new Scoping() {
@@ -89,6 +102,10 @@
     @Override public String toString() {
       return "eager singleton";
     }
+
+    public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
+      scopedBindingBuilder.asEagerSingleton();
+    }
   };
 
   public static Scoping forAnnotation(final Class<? extends Annotation> scopingAnnotation) {
@@ -108,6 +125,10 @@
       @Override public String toString() {
         return scopingAnnotation.getName();
       }
+
+      public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
+        scopedBindingBuilder.in(scopingAnnotation);
+      }
     };
   }
 
@@ -128,6 +149,10 @@
       @Override public String toString() {
         return scope.toString();
       }
+
+      public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
+        scopedBindingBuilder.in(scope);
+      }
     };
   }
 
@@ -178,5 +203,7 @@
 
   public abstract <V> V acceptVisitor(BindingScopingVisitor<V> visitor);
 
+  public abstract void applyTo(ScopedBindingBuilder scopedBindingBuilder);
+
   private Scoping() {}
 }
diff --git a/src/com/google/inject/internal/UntargettedBindingImpl.java b/src/com/google/inject/internal/UntargettedBindingImpl.java
index 90d7796..3e410e2 100644
--- a/src/com/google/inject/internal/UntargettedBindingImpl.java
+++ b/src/com/google/inject/internal/UntargettedBindingImpl.java
@@ -18,6 +18,7 @@
 
 import com.google.inject.Injector;
 import com.google.inject.Key;
+import com.google.inject.Binder;
 import com.google.inject.spi.BindingTargetVisitor;
 import com.google.inject.spi.Dependency;
 import com.google.inject.spi.UntargettedBinding;
@@ -37,7 +38,7 @@
   }
 
   public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
-    return visitor.visitUntargetted(this);
+    return visitor.visit(this);
   }
 
   public BindingImpl<T> withScoping(Scoping scoping) {
@@ -48,6 +49,10 @@
     return new UntargettedBindingImpl<T>(getSource(), key, getScoping());
   }
 
+  public void applyTo(Binder binder) {
+    getScoping().applyTo(binder.withSource(getSource()).bind(getKey()));
+  }
+
   @Override public String toString() {
     return new ToStringBuilder(UntargettedBinding.class)
         .add("key", getKey())
diff --git a/src/com/google/inject/spi/BindingTargetVisitor.java b/src/com/google/inject/spi/BindingTargetVisitor.java
index d69506b..cfb57d7 100644
--- a/src/com/google/inject/spi/BindingTargetVisitor.java
+++ b/src/com/google/inject/spi/BindingTargetVisitor.java
@@ -29,55 +29,55 @@
    * Visit a instance binding. The same instance is returned for every injection. This target is
    * found in both module and injector bindings.
    */
-  V visitInstance(InstanceBinding<? extends T> binding);
+  V visit(InstanceBinding<? extends T> binding);
 
   /**
    * Visit a provider instance binding. The provider's {@code get} method is invoked to resolve
    * injections. This target is found in both module and injector bindings.
    */
-  V visitProviderInstance(ProviderInstanceBinding<? extends T> binding);
+  V visit(ProviderInstanceBinding<? extends T> binding);
 
   /**
    * Visit a provider key binding. To resolve injections, the provider key is first resolved, then
    * that provider's {@code get} method is invoked. This target is found in both module and injector
    * bindings.
    */
-  V visitProviderKey(ProviderKeyBinding<? extends T> binding);
+  V visit(ProviderKeyBinding<? extends T> binding);
 
   /**
    * Visit a linked key binding. The other key's binding is used to resolve injections. This
    * target is found in both module and injector bindings.
    */
-  V visitLinkedKey(LinkedKeyBinding<? extends T> binding);
+  V visit(LinkedKeyBinding<? extends T> binding);
 
   /**
    * Visit a binding to a key exposed from an enclosed private environment. This target is found in
    * both module and injector bindings.
    */
-  V visitExposed(ExposedBinding<? extends T> binding);
+  V visit(ExposedBinding<? extends T> binding);
 
   /**
    * Visit an untargetted binding. This target is found only on module bindings. It indicates
    * that the injector should use its implicit binding strategies to resolve injections.
    */
-  V visitUntargetted(UntargettedBinding<? extends T> binding);
+  V visit(UntargettedBinding<? extends T> binding);
 
   /**
    * Visit a constructor binding. To resolve injections, an instance is instantiated by invoking
    * {@code constructor}. This target is found only on injector bindings.
    */
-  V visitConstructor(ConstructorBinding<? extends T> binding);
+  V visit(ConstructorBinding<? extends T> binding);
 
   /**
    * Visit a binding created from converting a bound instance to a new type. The source binding
    * has the same binding annotation but a different type. This target is found only on injector
    * bindings.
    */
-  V visitConvertedConstant(ConvertedConstantBinding<? extends T> binding);
+  V visit(ConvertedConstantBinding<? extends T> binding);
 
   /**
    * Visit a binding to a {@link com.google.inject.Provider} that delegates to the binding for the
    * provided type. This target is found only on injector bindings.
    */
-  V visitProviderBinding(ProviderBinding<? extends T> binding);
+  V visit(ProviderBinding<? extends T> binding);
 }
diff --git a/src/com/google/inject/spi/ConstructorBinding.java b/src/com/google/inject/spi/ConstructorBinding.java
index e7dd163..7bc3e84 100644
--- a/src/com/google/inject/spi/ConstructorBinding.java
+++ b/src/com/google/inject/spi/ConstructorBinding.java
@@ -18,10 +18,6 @@
 
 import com.google.inject.Binding;
 import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 /**
  * A binding to the constructor of a concrete clss. To resolve injections, an instance is
@@ -33,24 +29,12 @@
 public interface ConstructorBinding<T> extends Binding<T>, HasDependencies {
 
   /**
-   * Returns the {@link com.google.inject.Inject annotated} or default constructor that is invoked
-   * for creating values.
+   * Returns the injected type.
    */
-  Constructor<? extends T> getConstructor();
+  InjectableType<T> getInjectableType();
 
   /**
-   * Returns the constructor, field and method injection points to create and populate a new
-   * instance. The set contains exactly one constructor injection point.
+   * Gets the constructor this binding injects.
    */
-  Set<InjectionPoint> getInjectionPoints();
-
-  /*if[AOP]*/
-  /**
-   * Returns the interceptors applied to each method, in the order that they will be applied.
-   *
-   * @return a possibly empty map
-   */
-  Map<Method, List<org.aopalliance.intercept.MethodInterceptor>> getMethodInterceptors();
-  /*end[AOP]*/
-
+  Constructor<?> getConstructor();
 }
\ No newline at end of file
diff --git a/src/com/google/inject/spi/DefaultBindingScopingVisitor.java b/src/com/google/inject/spi/DefaultBindingScopingVisitor.java
index 0f59e88..f034187 100644
--- a/src/com/google/inject/spi/DefaultBindingScopingVisitor.java
+++ b/src/com/google/inject/spi/DefaultBindingScopingVisitor.java
@@ -31,6 +31,9 @@
  */
 public class DefaultBindingScopingVisitor<V> implements BindingScopingVisitor<V> {
 
+  /**
+   * Default visit implementation. Returns {@code null}.
+   */
   protected V visitOther() {
     return null;
   }
diff --git a/src/com/google/inject/spi/DefaultBindingTargetVisitor.java b/src/com/google/inject/spi/DefaultBindingTargetVisitor.java
index 638f1ff..e5cfd4d 100644
--- a/src/com/google/inject/spi/DefaultBindingTargetVisitor.java
+++ b/src/com/google/inject/spi/DefaultBindingTargetVisitor.java
@@ -30,45 +30,48 @@
  */
 public abstract class DefaultBindingTargetVisitor<T, V> implements BindingTargetVisitor<T, V> {
 
+  /**
+   * Default visit implementation. Returns {@code null}.
+   */
   protected V visitOther(Binding<? extends T> binding) {
     return null;
   }
 
-  public V visitInstance(InstanceBinding<? extends T> instanceBinding) {
+  public V visit(InstanceBinding<? extends T> instanceBinding) {
     return visitOther(instanceBinding);
   }
 
-  public V visitProviderInstance(ProviderInstanceBinding<? extends T> providerInstanceBinding) {
+  public V visit(ProviderInstanceBinding<? extends T> providerInstanceBinding) {
     return visitOther(providerInstanceBinding);
   }
 
-  public V visitProviderKey(ProviderKeyBinding<? extends T> providerKeyBinding) {
+  public V visit(ProviderKeyBinding<? extends T> providerKeyBinding) {
     return visitOther(providerKeyBinding);
   }
 
-  public V visitLinkedKey(LinkedKeyBinding<? extends T> linkedKeyBinding) {
+  public V visit(LinkedKeyBinding<? extends T> linkedKeyBinding) {
     return visitOther(linkedKeyBinding);
   }
 
-  public V visitExposed(ExposedBinding<? extends T> exposedBinding) {
+  public V visit(ExposedBinding<? extends T> exposedBinding) {
     return visitOther(exposedBinding);
   }
 
-  public V visitUntargetted(UntargettedBinding<? extends T> untargettedBinding) {
+  public V visit(UntargettedBinding<? extends T> untargettedBinding) {
     return visitOther(untargettedBinding);
   }
 
-  public V visitConstructor(ConstructorBinding<? extends T> constructorBinding) {
+  public V visit(ConstructorBinding<? extends T> constructorBinding) {
     return visitOther(constructorBinding);
   }
 
-  public V visitConvertedConstant(ConvertedConstantBinding<? extends T> convertedConstantBinding) {
+  public V visit(ConvertedConstantBinding<? extends T> convertedConstantBinding) {
     return visitOther(convertedConstantBinding);
   }
 
    // javac says it's an error to cast ProviderBinding<? extends T> to Binding<? extends T>
   @SuppressWarnings("unchecked")
-  public V visitProviderBinding(ProviderBinding<? extends T> providerBinding) {
+  public V visit(ProviderBinding<? extends T> providerBinding) {
     return visitOther((Binding) providerBinding);
   }
 }
diff --git a/src/com/google/inject/spi/DefaultElementVisitor.java b/src/com/google/inject/spi/DefaultElementVisitor.java
index 5afb7f0..77c0e1d 100644
--- a/src/com/google/inject/spi/DefaultElementVisitor.java
+++ b/src/com/google/inject/spi/DefaultElementVisitor.java
@@ -20,7 +20,7 @@
 
 /**
  * No-op visitor for subclassing. All interface methods simply delegate to
- * {@link #visitElement(Element)}, returning its result.
+ * {@link #visitOther(Element)}, returning its result.
  *
  * @param <V> any type to be returned by the visit method. Use {@link Void} with
  *     {@code return null} if no return type is needed.
@@ -31,47 +31,55 @@
 public abstract class DefaultElementVisitor<V> implements ElementVisitor<V> {
 
   /**
-   * Visit {@code element} and return a result.
+   * Default visit implementation. Returns {@code null}.
    */
-  protected V visitElement(Element element) {
+  protected V visitOther(Element element) {
     return null;
   }
 
-  public V visitMessage(Message message) {
-    return visitElement(message);
+  public V visit(Message message) {
+    return visitOther(message);
   }
 
-  public <T> V visitBinding(Binding<T> binding) {
-    return visitElement(binding);
+  public <T> V visit(Binding<T> binding) {
+    return visitOther(binding);
   }
 
   /*if[AOP]*/
-  public V visitInterceptorBinding(InterceptorBinding interceptorBinding) {
-    return visitElement(interceptorBinding);
+  public V visit(InterceptorBinding interceptorBinding) {
+    return visitOther(interceptorBinding);
   }
   /*end[AOP]*/
 
-  public V visitScopeBinding(ScopeBinding scopeBinding) {
-    return visitElement(scopeBinding);
+  public V visit(ScopeBinding scopeBinding) {
+    return visitOther(scopeBinding);
   }
 
-  public V visitTypeConverterBinding(TypeConverterBinding typeConverterBinding) {
-    return visitElement(typeConverterBinding);
+  public V visit(TypeConverterBinding typeConverterBinding) {
+    return visitOther(typeConverterBinding);
   }
 
-  public <T> V visitProviderLookup(ProviderLookup<T> providerLookup) {
-    return visitElement(providerLookup);
+  public <T> V visit(ProviderLookup<T> providerLookup) {
+    return visitOther(providerLookup);
   }
 
-  public V visitInjectionRequest(InjectionRequest injectionRequest) {
-    return visitElement(injectionRequest);
+  public V visit(InjectionRequest injectionRequest) {
+    return visitOther(injectionRequest);
   }
 
-  public V visitStaticInjectionRequest(StaticInjectionRequest staticInjectionRequest) {
-    return visitElement(staticInjectionRequest);
+  public V visit(StaticInjectionRequest staticInjectionRequest) {
+    return visitOther(staticInjectionRequest);
   }
 
-  public V visitPrivateElements(PrivateElements privateElements) {
-    return visitElement(privateElements);
+  public V visit(PrivateElements privateElements) {
+    return visitOther(privateElements);
+  }
+
+  public <T> V visit(MembersInjectorLookup<T> lookup) {
+    return visitOther(lookup);
+  }
+
+  public V visit(InjectableTypeListenerBinding binding) {
+    return visitOther(binding);
   }
 }
diff --git a/src/com/google/inject/spi/Dependency.java b/src/com/google/inject/spi/Dependency.java
index 5c8e5be..c4de6bc 100644
--- a/src/com/google/inject/spi/Dependency.java
+++ b/src/com/google/inject/spi/Dependency.java
@@ -68,6 +68,20 @@
   }
 
   /**
+   * Returns the dependencies from the given injectable type.
+   */
+  public static Set<Dependency<?>> forInjectableType(InjectableType<?> injectableType) {
+    List<Dependency<?>> dependencies = Lists.newArrayList();
+    if (injectableType.getInjectableConstructor() != null) {
+      dependencies.addAll(injectableType.getInjectableConstructor().getDependencies());
+    }
+    for (InjectionPoint injectionPoint : injectableType.getInjectableMembers()) {
+      dependencies.addAll(injectionPoint.getDependencies());
+    }
+    return ImmutableSet.copyOf(dependencies);
+  }
+
+  /**
    * Returns the key to the binding that satisfies this dependency.
    */
   public Key<T> getKey() {
diff --git a/src/com/google/inject/spi/Element.java b/src/com/google/inject/spi/Element.java
index 882f774..8fca6eb 100644
--- a/src/com/google/inject/spi/Element.java
+++ b/src/com/google/inject/spi/Element.java
@@ -16,6 +16,8 @@
 
 package com.google.inject.spi;
 
+import com.google.inject.Binder;
+
 /**
  * A core component of a module or injector.
  *
@@ -28,6 +30,7 @@
  * com.google.inject.Injector#getBindings Injector.getBindings()} to reflect on Guice injectors.
  *
  * @author jessewilson@google.com (Jesse Wilson)
+ * @author crazybob@google.com (Bob Lee)
  * @since 2.0
  */
 public interface Element {
@@ -49,4 +52,13 @@
    */
   <T> T acceptVisitor(ElementVisitor<T> visitor);
 
+  /**
+   * Writes this module element to the given binder (optional operation).
+   *
+   * @param binder to apply configuration element to
+   * @throws UnsupportedOperationException if the {@code applyTo} method is not supported by this
+   *     element.
+   */
+  void applyTo(Binder binder);
+
 }
diff --git a/src/com/google/inject/spi/ElementVisitor.java b/src/com/google/inject/spi/ElementVisitor.java
index 9f6df6c..29bf111 100644
--- a/src/com/google/inject/spi/ElementVisitor.java
+++ b/src/com/google/inject/spi/ElementVisitor.java
@@ -32,47 +32,58 @@
    * Visit a mapping from a key (type and optional annotation) to the strategy for getting
    * instances of the type.
    */
-  <T> V visitBinding(Binding<T> binding);
+  <T> V visit(Binding<T> binding);
 
   /*if[AOP]*/
   /**
    * Visit a registration of interceptors for matching methods of matching classes.
    */
-  V visitInterceptorBinding(InterceptorBinding interceptorBinding);
+  V visit(InterceptorBinding binding);
   /*end[AOP]*/
 
   /**
    * Visit a registration of a scope annotation with the scope that implements it.
    */
-  V visitScopeBinding(ScopeBinding scopeBinding);
+  V visit(ScopeBinding binding);
 
   /**
    * Visit a registration of type converters for matching target types.
    */
-  V visitTypeConverterBinding(TypeConverterBinding typeConverterBinding);
+  V visit(TypeConverterBinding binding);
 
   /**
    * Visit a request to inject the instance fields and methods of an instance.
    */
-  V visitInjectionRequest(InjectionRequest injectionRequest);
+  V visit(InjectionRequest request);
 
   /**
    * Visit a request to inject the static fields and methods of type.
    */
-  V visitStaticInjectionRequest(StaticInjectionRequest staticInjectionRequest);
+  V visit(StaticInjectionRequest request);
 
   /**
    * Visit a lookup of the provider for a type.
    */
-  <T> V visitProviderLookup(ProviderLookup<T> providerLookup);
+  <T> V visit(ProviderLookup<T> lookup);
+
+  /**
+   * Visit a lookup of the members injector.
+   */
+  <T> V visit(MembersInjectorLookup<T> lookup);
 
   /**
    * Visit an error message and the context in which it occured.
    */
-  V visitMessage(Message message);
+  V visit(Message message);
 
   /**
-   * Visit a collection of configuration elements for a private environment.
+   * Visit a collection of configuration elements for a {@linkplain com.google.inject.PrivateBinder
+   * private binder}.
    */
-  V visitPrivateElements(PrivateElements privateElements);
+  V visit(PrivateElements elements);
+
+  /**
+   * Visit an injectable type listener binding.
+   */
+  V visit(InjectableTypeListenerBinding binding);
 }
diff --git a/src/com/google/inject/spi/Elements.java b/src/com/google/inject/spi/Elements.java
index 44084d7..efe4c9f 100644
--- a/src/com/google/inject/spi/Elements.java
+++ b/src/com/google/inject/spi/Elements.java
@@ -27,6 +27,7 @@
 import com.google.inject.Scope;
 import com.google.inject.Stage;
 import com.google.inject.TypeLiteral;
+import com.google.inject.MembersInjector;
 import com.google.inject.binder.AnnotatedBindingBuilder;
 import com.google.inject.binder.AnnotatedConstantBindingBuilder;
 import com.google.inject.binder.AnnotatedElementBuilder;
@@ -37,7 +38,6 @@
 import com.google.inject.internal.ImmutableList;
 import com.google.inject.internal.Lists;
 import static com.google.inject.internal.Preconditions.checkArgument;
-import static com.google.inject.internal.Preconditions.checkNotNull;
 import static com.google.inject.internal.Preconditions.checkState;
 import com.google.inject.internal.PrivateElementsImpl;
 import com.google.inject.internal.ProviderMethodsModule;
@@ -62,7 +62,7 @@
 public final class Elements {
   private static final BindingTargetVisitor<Object, Object> GET_INSTANCE_VISITOR
       = new DefaultBindingTargetVisitor<Object, Object>() {
-    @Override public Object visitInstance(InstanceBinding<?> binding) {
+    @Override public Object visit(InstanceBinding<?> binding) {
       return binding.getInstance();
     }
 
@@ -169,10 +169,26 @@
       elements.add(new ScopeBinding(getSource(), annotationType, scope));
     }
 
-    public void requestInjection(Object... instances) {
-      for (Object instance : instances) {
-        elements.add(new InjectionRequest(getSource(), instance));
-      }
+    @SuppressWarnings("unchecked") // it is safe to use the type literal for the raw type
+    public void requestInjection(Object instance) {
+      requestInjection((TypeLiteral) TypeLiteral.get(instance.getClass()), instance);
+    }
+
+    public <T> void requestInjection(TypeLiteral<T> type, T instance) {
+      elements.add(new InjectionRequest<T>(getSource(), type, instance));
+    }
+
+    public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
+      throw new UnsupportedOperationException("TODO");
+    }
+
+    public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
+      throw new UnsupportedOperationException("TODO");
+    }
+
+    public void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher,
+        InjectableType.Listener listener) {
+      elements.add(new InjectableTypeListenerBinding(getSource(), listener, typeMatcher));
     }
 
     public void requestStaticInjection(Class<?>... types) {
@@ -182,8 +198,6 @@
     }
 
     public void install(Module module) {
-      checkNotNull(module, "module");
-
       if (modules.add(module)) {
         Binder binder = this;
         if (module instanceof PrivateModule) {
diff --git a/src/com/google/inject/spi/ExposedBinding.java b/src/com/google/inject/spi/ExposedBinding.java
index fe26071..66f0536 100644
--- a/src/com/google/inject/spi/ExposedBinding.java
+++ b/src/com/google/inject/spi/ExposedBinding.java
@@ -17,6 +17,7 @@
 package com.google.inject.spi;
 
 import com.google.inject.Binding;
+import com.google.inject.Binder;
 
 /**
  * A binding to a key exposed from an enclosed private environment.
@@ -31,4 +32,8 @@
    */
   PrivateElements getPrivateElements();
 
+  /**
+   * Unsupported. Always throws {@link UnsupportedOperationException}.
+   */
+  void applyTo(Binder binder);
 }
\ No newline at end of file
diff --git a/src/com/google/inject/spi/InjectableType.java b/src/com/google/inject/spi/InjectableType.java
new file mode 100644
index 0000000..00e7f44
--- /dev/null
+++ b/src/com/google/inject/spi/InjectableType.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2009 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.spi;
+
+import com.google.inject.ConfigurationException;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+import com.google.inject.MembersInjector;
+import com.google.inject.matcher.Matcher;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.aopalliance.intercept.MethodInterceptor;
+
+/**
+ * Represents an injectable type. A type is injectable if:
+ *
+ * <ol type="a">
+ *   <li>Guice can inject its constructor, or</li>
+ *   <li>Guice can inject the methods and fields of a pre-existing instance of the type</li>
+ * </ol>
+ *
+ * <p>Note: Despite generic type erasure, Guice keeps track of full types, so it can and does treat
+ * {@code Foo<String>} and {@code Foo<Integer>} as distinct injectable types.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ * @author jessewilson@google.com (Jesse Wilson)
+ * @since 2.0
+ */
+public interface InjectableType<I> {
+
+  /**
+   * Gets the type literal.
+   */
+  TypeLiteral<I> getType();
+
+  /**
+   * Returns the injection point representing the contructor or {@code null} if no injectable
+   * constructor is present
+   */
+  InjectionPoint getInjectableConstructor();
+
+  /**
+   * Returns the instance methods and fields of {@code T} that can be injected.
+   *
+   * @return a possibly empty set of injection points. The set has a specified iteration order.
+   *  All fields are returned and then all methods. Within the fields, supertype fields are
+   *  returned before subtype fields. Similarly, supertype methods are returned before subtype
+   *  methods.
+   * @throws com.google.inject.ConfigurationException if there is a malformed injection point on
+   *  the class of {@code instance}, such as a field with multiple binding annotations. The
+   *  exception's {@link com.google.inject.ConfigurationException#getPartialValue() partial value}
+   *  is a {@code Set<InjectionPoint>} of the valid injection points.
+   */
+  Set<InjectionPoint> getInjectableMembers() throws ConfigurationException;
+
+  /*if[AOP]*/
+  /**
+   * Returns the interceptors applied to each method, in the order that they will be applied.
+   * These only apply when Guice instantiates {@code I}.
+   *
+   * @return a possibly empty map
+   */
+  Map<Method, List<MethodInterceptor>> getMethodInterceptors();
+  /*end[AOP]*/
+
+  /**
+   * Listens for Guice to encounter injectable types. If a given type has its constructor injected
+   * in one situation but only its methods and fields injected in another, Guice will notify
+   * this listener once.
+   *
+   * <p>Useful for extra type checking, {@linkplain Encounter#register(InjectionListener)
+   * registering injection listeners}, and {@linkplain Encounter#bindInterceptor(
+   * com.google.inject.matcher.Matcher, org.aopalliance.intercept.MethodInterceptor[])
+   * binding method interceptors}.
+   */
+  public interface Listener {
+
+    /**
+     * Invoked when Guice encounters a new type eligible for constructor or members injection.
+     * Called during {@link Injector} creation (or afterwords if Guice encounters a type at run
+     * time and creates a JIT binding).
+     *
+     * @param injectableType encountered by Guice
+     * @param encounter context of this encounter, enables reporting errors, registering injection
+     *  listeners and binding method interceptors for injectableType
+     *
+     * @param <I> the injectable type
+     */
+    <I> void hear(InjectableType<I> injectableType, Encounter<I> encounter);
+
+  }
+
+  /**
+   * Context of the injectable type encounter. Enables reporting errors, registering injection
+   * listeners and binding method interceptors for injectable type {@code I}.
+   *
+   * @param <I> the injectable type encountered
+   */
+  public interface Encounter<I> {
+
+    /**
+     * Records an error message for type {@code I} which will be presented to the user at a later
+     * time. Unlike throwing an exception, this enable us to continue configuring the Injector and
+     * discover more errors. Uses {@link String#format(String, Object[])} to insert the arguments
+     * into the message.
+     */
+    void addError(String message, Object... arguments);
+
+    /**
+     * Records an exception for type {@code I}, the full details of which will be logged, and the
+     * message of which will be presented to the user at a later time. If your
+     * InjectableTypeListener calls something that you worry may fail, you should catch the
+     * exception and pass it to this method.
+     */
+    void addError(Throwable t);
+
+    /**
+     * Records an error message to be presented to the user at a later time.
+     */
+    void addError(Message message);
+
+    /**
+     * Returns the provider used to obtain instances for the given injection key. The returned
+     * provider will not be valid until the {@link Injector} has been created. The provider will
+     * throw an {@code IllegalStateException} if you try to use it beforehand.
+     */
+    <T> Provider<T> getProvider(Key<T> key);
+
+    /**
+     * Returns the provider used to obtain instances for the given injection type. The returned
+     * provider will not be valid until the {@link Injector} has been created. The provider will
+     * throw an {@code IllegalStateException} if you try to use it beforehand.
+     */
+    <T> Provider<T> getProvider(Class<T> type);
+
+    /**
+     * Returns the members injector used to inject dependencies into methods and fields on instances
+     * of the given type {@code T}. The returned members injector will not be valid until the main
+     * {@link Injector} has been created. The members injector will throw an {@code
+     * IllegalStateException} if you try to use it beforehand.
+     *
+     * @param typeLiteral type to get members injector for
+     */
+    <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral);
+
+    /**
+     * Returns the members injector used to inject dependencies into methods and fields on instances
+     * of the given type {@code T}. The returned members injector will not be valid until the main
+     * {@link Injector} has been created. The members injector will throw an {@code
+     * IllegalStateException} if you try to use it beforehand.
+     *
+     * @param type type to get members injector for
+     */
+    <T> MembersInjector<T> getMembersInjector(Class<T> type);
+
+    /**
+     * Registers an injection listener for type {@code I}. Guice will notify the listener after
+     * injecting an instance of {@code I}. The order in which Guice will invoke listeners is
+     * unspecified.
+     * 
+     * @param listener for injections into instances of type {@code I}
+     */
+    void register(InjectionListener<? super I> listener);
+
+    /*if[AOP]*/
+    /**
+     * Binds method interceptor[s] to methods matched in type {@code I} and its supertypes. A
+     * method is eligible for interception if:
+     *
+     * <ul>
+     *  <li>Guice created the instance the method is on</li>
+     *  <li>Neither the enclosing type nor the method is final</li>
+     *  <li>And the method is package-private or more accessible</li>
+     * </ul>
+     *
+     * @param methodMatcher matches methods the interceptor should apply to. For
+     *     example: {@code annotatedWith(Transactional.class)}.
+     * @param interceptors to bind
+     */
+    void bindInterceptor(Matcher<? super Method> methodMatcher,
+        org.aopalliance.intercept.MethodInterceptor... interceptors);
+    /*end[AOP]*/
+  }
+}
\ No newline at end of file
diff --git a/src/com/google/inject/spi/InjectableTypeListenerBinding.java b/src/com/google/inject/spi/InjectableTypeListenerBinding.java
new file mode 100644
index 0000000..9381e10
--- /dev/null
+++ b/src/com/google/inject/spi/InjectableTypeListenerBinding.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (C) 2009 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.spi;
+
+import com.google.inject.Binder;
+import com.google.inject.TypeLiteral;
+import com.google.inject.matcher.Matcher;
+
+/**
+ * Binds injectable types (picked using a Matcher) to an injectable type listener.
+ * Registrations are created explicitly in a module using {@link
+ * com.google.inject.Binder#bindListener(com.google.inject.matcher.Matcher,
+ * com.google.inject.spi.InjectableType.Listener)} statements:
+ *
+ * <pre>
+ *     register(any(), listener);</pre>
+ */
+public final class InjectableTypeListenerBinding implements Element {
+
+  final Object source;
+  final Matcher<? super TypeLiteral<?>> typeMatcher;
+  final InjectableType.Listener listener;
+
+  InjectableTypeListenerBinding(Object source, InjectableType.Listener listener,
+      Matcher<? super TypeLiteral<?>> typeMatcher) {
+    this.source = source;
+    this.listener = listener;
+    this.typeMatcher = typeMatcher;
+  }
+
+  /** Returns the registered listener. */
+  public InjectableType.Listener getListener() {
+    return listener;
+  }
+
+  /** Returns the type matcher which chooses which types the listener should be notified of. */
+  public Matcher<? super TypeLiteral<?>> getTypeMatcher() {
+    return typeMatcher;
+  }
+
+  public Object getSource() {
+    return source;
+  }
+
+  public <T> T acceptVisitor(ElementVisitor<T> visitor) {
+    return visitor.visit(this);
+  }
+
+  public void applyTo(Binder binder) {
+    binder.withSource(getSource()).bindListener(typeMatcher, listener);
+  }
+}
diff --git a/src/com/google/inject/spi/InjectionListener.java b/src/com/google/inject/spi/InjectionListener.java
new file mode 100644
index 0000000..14cf00d
--- /dev/null
+++ b/src/com/google/inject/spi/InjectionListener.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2009 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.spi;
+
+/**
+ * Listens for injections into instances of type {@code I}. Useful for performing further
+ * injections, post-injection initialization, and more.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ * @author jessewilson@google.com (Jesse Wilson)
+ * @since 2.0
+ */
+public interface InjectionListener<I> {
+
+  /**
+   * Invoked by Guice after it injects the fields and methods of instance.
+   *
+   * @param injectee instance that Guice injected dependencies into
+   */
+  void afterInjection(I injectee);
+}
diff --git a/src/com/google/inject/spi/InjectionRequest.java b/src/com/google/inject/spi/InjectionRequest.java
index e1407af..d058dda 100644
--- a/src/com/google/inject/spi/InjectionRequest.java
+++ b/src/com/google/inject/spi/InjectionRequest.java
@@ -16,13 +16,15 @@
 
 package com.google.inject.spi;
 
+import com.google.inject.Binder;
 import com.google.inject.ConfigurationException;
+import com.google.inject.TypeLiteral;
 import static com.google.inject.internal.Preconditions.checkNotNull;
 import java.util.Set;
 
 /**
  * A request to inject the instance fields and methods of an instance. Requests are created
- * explicitly in a module using {@link com.google.inject.Binder#requestInjection(Object[])
+ * explicitly in a module using {@link com.google.inject.Binder#requestInjection(Object)
  * requestInjection()} statements:
  * <pre>
  *     requestInjection(serviceInstance);</pre>
@@ -30,12 +32,15 @@
  * @author mikeward@google.com (Mike Ward)
  * @since 2.0
  */
-public final class InjectionRequest implements Element {
-  private Object source;
-  private Object instance;
+public final class InjectionRequest<T> implements Element {
 
-  public InjectionRequest(Object source, Object instance) {
+  private final Object source;
+  private final TypeLiteral<T> type;
+  private final T instance;
+
+  public InjectionRequest(Object source, TypeLiteral<T> type, T instance) {
     this.source = checkNotNull(source, "source");
+    this.type = checkNotNull(type, "type");
     this.instance = checkNotNull(instance, "instance");
   }
 
@@ -43,10 +48,14 @@
     return source;
   }
 
-  public Object getInstance() {
+  public T getInstance() {
     return instance;
   }
 
+  public TypeLiteral<T> getType() {
+    return type;
+  }
+
   /**
    * Returns the instance methods and fields of {@code instance} that will be injected to fulfill
    * this request.
@@ -63,7 +72,11 @@
     return InjectionPoint.forInstanceMethodsAndFields(instance.getClass());
   }
 
-  public <T> T acceptVisitor(ElementVisitor<T> visitor) {
-    return visitor.visitInjectionRequest(this);
+  public <R> R acceptVisitor(ElementVisitor<R> visitor) {
+    return visitor.visit(this);
+  }
+
+  public void applyTo(Binder binder) {
+    binder.withSource(getSource()).requestInjection(type, instance);
   }
 }
diff --git a/src/com/google/inject/spi/InterceptorBinding.java b/src/com/google/inject/spi/InterceptorBinding.java
index 7006d74..1b1171b 100644
--- a/src/com/google/inject/spi/InterceptorBinding.java
+++ b/src/com/google/inject/spi/InterceptorBinding.java
@@ -16,11 +16,11 @@
 
 package com.google.inject.spi;
 
+import com.google.inject.Binder;
+import com.google.inject.internal.ImmutableList;
 import static com.google.inject.internal.Preconditions.checkNotNull;
 import com.google.inject.matcher.Matcher;
 import java.lang.reflect.Method;
-import java.util.Arrays;
-import static java.util.Collections.unmodifiableList;
 import java.util.List;
 import org.aopalliance.intercept.MethodInterceptor;
 
@@ -33,6 +33,10 @@
  *         Matchers.annotatedWith(Transactional.class),
  *         new MyTransactionInterceptor());</pre>
  *
+ * or from an injectable type listener using
+ * {@link com.google.inject.spi.InjectableType.Encounter#bindInterceptor(com.google.inject.matcher.Matcher, org.aopalliance.intercept.MethodInterceptor[])
+ * InjectableType.Encounter.bindInterceptor()}.  
+ *
  * @author jessewilson@google.com (Jesse Wilson)
  * @since 2.0
  */
@@ -40,7 +44,7 @@
   private final Object source;
   private final Matcher<? super Class<?>> classMatcher;
   private final Matcher<? super Method> methodMatcher;
-  private final List<MethodInterceptor> interceptors;
+  private final ImmutableList<MethodInterceptor> interceptors;
 
   InterceptorBinding(
       Object source,
@@ -50,7 +54,7 @@
     this.source = checkNotNull(source, "source");
     this.classMatcher = checkNotNull(classMatcher, "classMatcher");
     this.methodMatcher = checkNotNull(methodMatcher, "methodMatcher");
-    this.interceptors = unmodifiableList(Arrays.asList(interceptors.clone()));
+    this.interceptors = ImmutableList.of(interceptors);
   }
 
   public Object getSource() {
@@ -70,6 +74,11 @@
   }
 
   public <T> T acceptVisitor(ElementVisitor<T> visitor) {
-    return visitor.visitInterceptorBinding(this);
+    return visitor.visit(this);
+  }
+
+  public void applyTo(Binder binder) {
+    binder.withSource(getSource()).bindInterceptor(classMatcher, methodMatcher,
+        interceptors.toArray(new MethodInterceptor[interceptors.size()]));
   }
 }
diff --git a/src/com/google/inject/spi/MembersInjectorLookup.java b/src/com/google/inject/spi/MembersInjectorLookup.java
new file mode 100644
index 0000000..67eb601
--- /dev/null
+++ b/src/com/google/inject/spi/MembersInjectorLookup.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright (C) 2009 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.spi;
+
+import com.google.inject.MembersInjector;
+import com.google.inject.Binder;
+import static com.google.inject.internal.Preconditions.checkNotNull;
+import static com.google.inject.internal.Preconditions.checkState;
+
+/**
+ * A lookup of the members injector for a type. Lookups are created explicitly in a module using
+ * {@link com.google.inject.Binder#getMembersInjector(Class) getMembersInjector()} statements:
+ * <pre>
+ *     MembersInjector&lt;PaymentService&gt; membersInjector
+ *         = getMembersInjector(PaymentService.class);</pre>
+ *
+ * @author crazybob@google.com (Bob Lee)
+ * @since 2.0
+ */
+public final class MembersInjectorLookup<T> implements Element {
+
+  private final Object source;
+  private final InjectableType<T> injectableType;
+  private MembersInjector<T> delegate;
+
+  MembersInjectorLookup(Object source, InjectableType<T> injectableType) {
+    this.source = checkNotNull(source, "source");
+    this.injectableType = checkNotNull(injectableType, "injectableType");
+  }
+
+  public Object getSource() {
+    return source;
+  }
+
+  /**
+   * Gets the injectable type containing the members to be injected.
+   */
+  public InjectableType<T> getInjectableType() {
+    return injectableType;
+  }
+
+  public <T> T acceptVisitor(ElementVisitor<T> visitor) {
+    return visitor.visit(this);
+  }
+
+  /**
+   * Sets the actual members injector.
+   *
+   * @param delegate members injector
+   * @throws IllegalStateException if the delegate is already set
+   * @throws NullPointerException if the delegate is null
+   */
+  public void initializeDelegate(MembersInjector<T> delegate) {
+    checkState(this.delegate == null, "delegate already initialized");
+    checkNotNull(delegate, "delegate");
+    this.delegate = delegate;
+  }
+
+  public void applyTo(Binder binder) {
+    binder.withSource(getSource()).getMembersInjector(injectableType.getType());
+  }
+
+  /**
+   * Returns the delegate members injector, or {@code null} if it has not yet been initialized.
+   * The delegate will be initialized when this element is processed, or otherwise used to create
+   * an injector.
+   */
+  public MembersInjector<T> getDelegate() {
+    return delegate;
+  }
+}
\ No newline at end of file
diff --git a/src/com/google/inject/spi/Message.java b/src/com/google/inject/spi/Message.java
index ce4c615..3eb9395 100644
--- a/src/com/google/inject/spi/Message.java
+++ b/src/com/google/inject/spi/Message.java
@@ -21,6 +21,7 @@
 import com.google.inject.internal.Objects;
 import static com.google.inject.internal.Preconditions.checkNotNull;
 import com.google.inject.internal.SourceProvider;
+import com.google.inject.Binder;
 import java.io.Serializable;
 import java.util.List;
 
@@ -81,7 +82,7 @@
 
   /** @since 2.0 */
   public <T> T acceptVisitor(ElementVisitor<T> visitor) {
-    return visitor.visitMessage(this);
+    return visitor.visit(this);
   }
 
   /**
@@ -109,4 +110,8 @@
     Message e = (Message) o;
     return message.equals(e.message) && Objects.equal(cause, e.cause) && sources.equals(e.sources);
   }
+
+  public void applyTo(Binder binder) {
+    binder.withSource(getSource()).addError(this);
+  }
 }
\ No newline at end of file
diff --git a/src/com/google/inject/spi/ModuleWriter.java b/src/com/google/inject/spi/ModuleWriter.java
index d99aacb..466bf0d 100644
--- a/src/com/google/inject/spi/ModuleWriter.java
+++ b/src/com/google/inject/spi/ModuleWriter.java
@@ -61,49 +61,57 @@
 
     ElementVisitor<Void> visitor = new ElementVisitor<Void>() {
 
-      public Void visitMessage(Message message) {
+      public Void visit(Message message) {
         writeMessage(binder, message);
         return null;
       }
 
       /*if[AOP]*/
-      public Void visitInterceptorBinding(InterceptorBinding element) {
+      public Void visit(InterceptorBinding element) {
         writeBindInterceptor(binder, element);
         return null;
       }
       /*end[AOP]*/
 
-      public Void visitScopeBinding(ScopeBinding element) {
+      public Void visit(ScopeBinding element) {
         writeBindScope(binder, element);
         return null;
       }
 
-      public Void visitInjectionRequest(InjectionRequest element) {
+      public Void visit(InjectionRequest element) {
         writeRequestInjection(binder, element);
         return null;
       }
 
-      public Void visitStaticInjectionRequest(StaticInjectionRequest element) {
+      public Void visit(StaticInjectionRequest element) {
         writeRequestStaticInjection(binder, element);
         return null;
       }
 
-      public Void visitTypeConverterBinding(TypeConverterBinding element) {
+      public Void visit(TypeConverterBinding element) {
         writeConvertToTypes(binder, element);
         return null;
       }
 
-      public <T> Void visitBinding(Binding<T> element) {
+      public <T> Void visit(Binding<T> element) {
         writeBind(binder, element);
         return null;
       }
 
-      public <T> Void visitProviderLookup(ProviderLookup<T> element) {
+      public <T> Void visit(ProviderLookup<T> element) {
         writeGetProvider(binder, element);
         return null;
       }
 
-      public Void visitPrivateElements(PrivateElements privateElements) {
+      public <T> Void visit(MembersInjectorLookup<T> lookup) {
+        throw new UnsupportedOperationException("TODO");
+      }
+
+      public Void visit(InjectableTypeListenerBinding binding) {
+        throw new UnsupportedOperationException("TODO");
+      }
+
+      public Void visit(PrivateElements privateElements) {
         writePrivateElements(binder, privateElements);
         return null;
       }
@@ -168,44 +176,44 @@
   protected <T> ScopedBindingBuilder bindKeyToTarget(
       final Binding<T> binding, final Binder binder, final Key<T> key) {
     return binding.acceptTargetVisitor(new BindingTargetVisitor<T, ScopedBindingBuilder>() {
-      public ScopedBindingBuilder visitInstance(InstanceBinding<? extends T> binding) {
+      public ScopedBindingBuilder visit(InstanceBinding<? extends T> binding) {
         binder.bind(key).toInstance(binding.getInstance());
         return null;
       }
 
-      public ScopedBindingBuilder visitProviderInstance(
+      public ScopedBindingBuilder visit(
           ProviderInstanceBinding<? extends T> binding) {
         return binder.bind(key).toProvider(binding.getProviderInstance());
       }
 
-      public ScopedBindingBuilder visitProviderKey(ProviderKeyBinding<? extends T> binding) {
+      public ScopedBindingBuilder visit(ProviderKeyBinding<? extends T> binding) {
         return binder.bind(key).toProvider(binding.getProviderKey());
       }
 
-      public ScopedBindingBuilder visitLinkedKey(LinkedKeyBinding<? extends T> binding) {
+      public ScopedBindingBuilder visit(LinkedKeyBinding<? extends T> binding) {
         return binder.bind(key).to(binding.getLinkedKey());
       }
 
-      public ScopedBindingBuilder visitUntargetted(UntargettedBinding<? extends T> binding) {
+      public ScopedBindingBuilder visit(UntargettedBinding<? extends T> binding) {
         return binder.bind(key);
       }
 
-      public ScopedBindingBuilder visitExposed(ExposedBinding<? extends T> binding) {
+      public ScopedBindingBuilder visit(ExposedBinding<? extends T> binding) {
         PrivateBinder privateBinder = getPrivateBinder(binding.getPrivateElements());
         privateBinder.withSource(binding.getSource()).expose(key);
         return null;
       }
 
-      public ScopedBindingBuilder visitConvertedConstant(
+      public ScopedBindingBuilder visit(
           ConvertedConstantBinding<? extends T> binding) {
         throw new IllegalArgumentException("Non-module element");
       }
 
-      public ScopedBindingBuilder visitConstructor(ConstructorBinding<? extends T> binding) {
+      public ScopedBindingBuilder visit(ConstructorBinding<? extends T> binding) {
         throw new IllegalArgumentException("Non-module element");
       }
 
-      public ScopedBindingBuilder visitProviderBinding(ProviderBinding<? extends T> binding) {
+      public ScopedBindingBuilder visit(ProviderBinding<? extends T> binding) {
         throw new IllegalArgumentException("Non-module element");
       }
     });
@@ -254,6 +262,6 @@
 
   protected <T> void writeGetProvider(Binder binder, ProviderLookup<T> element) {
     Provider<T> provider = binder.withSource(element.getSource()).getProvider(element.getKey());
-    element.initDelegate(provider);
+    element.initializeDelegate(provider);
   }
 }
diff --git a/src/com/google/inject/spi/ProviderLookup.java b/src/com/google/inject/spi/ProviderLookup.java
index 205e778..20160d4 100644
--- a/src/com/google/inject/spi/ProviderLookup.java
+++ b/src/com/google/inject/spi/ProviderLookup.java
@@ -18,6 +18,7 @@
 
 import com.google.inject.Key;
 import com.google.inject.Provider;
+import com.google.inject.Binder;
 import static com.google.inject.internal.Preconditions.checkNotNull;
 import static com.google.inject.internal.Preconditions.checkState;
 
@@ -50,15 +51,26 @@
   }
 
   public <T> T acceptVisitor(ElementVisitor<T> visitor) {
-    return visitor.visitProviderLookup(this);
+    return visitor.visit(this);
   }
 
-  public void initDelegate(Provider<T> delegate) {
+  /**
+   * Sets the actual provider.
+   *
+   * @param delegate provider
+   * @throws IllegalStateException if the delegate is already set
+   * @throws NullPointerException if the delegate is null
+   */
+  public void initializeDelegate(Provider<T> delegate) {
     checkState(this.delegate == null, "delegate already initialized");
     checkNotNull(delegate, "delegate");
     this.delegate = delegate;
   }
 
+  public void applyTo(Binder binder) {
+    binder.withSource(getSource()).getProvider(key);
+  }
+
   /**
    * Returns the delegate provider, or {@code null} if it has not yet been initialized. The delegate
    * will be initialized when this element is processed, or otherwise used to create an injector.
diff --git a/src/com/google/inject/spi/ScopeBinding.java b/src/com/google/inject/spi/ScopeBinding.java
index b3fb9d8..5f7b675 100644
--- a/src/com/google/inject/spi/ScopeBinding.java
+++ b/src/com/google/inject/spi/ScopeBinding.java
@@ -17,6 +17,7 @@
 package com.google.inject.spi;
 
 import com.google.inject.Scope;
+import com.google.inject.Binder;
 import static com.google.inject.internal.Preconditions.checkNotNull;
 import java.lang.annotation.Annotation;
 
@@ -55,6 +56,10 @@
   }
 
   public <T> T acceptVisitor(ElementVisitor<T> visitor) {
-    return visitor.visitScopeBinding(this);
+    return visitor.visit(this);
+  }
+
+  public void applyTo(Binder binder) {
+    binder.withSource(getSource()).bindScope(annotationType, scope);
   }
 }
diff --git a/src/com/google/inject/spi/StaticInjectionRequest.java b/src/com/google/inject/spi/StaticInjectionRequest.java
index 47b52fb..94e4a0e 100644
--- a/src/com/google/inject/spi/StaticInjectionRequest.java
+++ b/src/com/google/inject/spi/StaticInjectionRequest.java
@@ -17,6 +17,7 @@
 package com.google.inject.spi;
 
 import com.google.inject.ConfigurationException;
+import com.google.inject.Binder;
 import static com.google.inject.internal.Preconditions.checkNotNull;
 import java.util.Set;
 
@@ -63,7 +64,11 @@
     return InjectionPoint.forStaticMethodsAndFields(type);
   }
 
+  public void applyTo(Binder binder) {
+    binder.withSource(getSource()).requestStaticInjection(type);
+  }
+
   public <T> T acceptVisitor(ElementVisitor<T> visitor) {
-    return visitor.visitStaticInjectionRequest(this);
+    return visitor.visit(this);
   }
 }
diff --git a/src/com/google/inject/spi/TypeConverterBinding.java b/src/com/google/inject/spi/TypeConverterBinding.java
index 32d6f72..7a141d1 100644
--- a/src/com/google/inject/spi/TypeConverterBinding.java
+++ b/src/com/google/inject/spi/TypeConverterBinding.java
@@ -17,6 +17,7 @@
 package com.google.inject.spi;
 
 import com.google.inject.TypeLiteral;
+import com.google.inject.Binder;
 import static com.google.inject.internal.Preconditions.checkNotNull;
 import com.google.inject.matcher.Matcher;
 
@@ -55,6 +56,10 @@
   }
 
   public <T> T acceptVisitor(ElementVisitor<T> visitor) {
-    return visitor.visitTypeConverterBinding(this);
+    return visitor.visit(this);
+  }
+
+  public void applyTo(Binder binder) {
+    binder.withSource(getSource()).convertToTypes(typeMatcher, typeConverter);
   }
 }