Big change - changing BinderImpl to use commands/

git-svn-id: https://google-guice.googlecode.com/svn/trunk@420 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/extensions/commands/src/com/google/inject/commands/AddMessageErrorCommand.java b/extensions/commands/src/com/google/inject/commands/AddMessageErrorCommand.java
index d6f0077..2e336cd 100644
--- a/extensions/commands/src/com/google/inject/commands/AddMessageErrorCommand.java
+++ b/extensions/commands/src/com/google/inject/commands/AddMessageErrorCommand.java
@@ -16,6 +16,8 @@
 
 package com.google.inject.commands;
 
+import static com.google.inject.internal.Objects.nonNull;
+
 import java.util.Arrays;
 import static java.util.Collections.unmodifiableList;
 import java.util.List;
@@ -26,14 +28,20 @@
  * @author jessewilson@google.com (Jesse Wilson)
  */
 public final class AddMessageErrorCommand implements Command {
+  private final Object source;
   private final String message;
   private final List<Object> arguments;
 
-  AddMessageErrorCommand(String message, Object[] arguments) {
-    this.message = message;
+  AddMessageErrorCommand(Object source, String message, Object[] arguments) {
+    this.source = nonNull(source, "source");
+    this.message = nonNull(message, "message");
     this.arguments = unmodifiableList(Arrays.asList(arguments.clone()));
   }
 
+  public Object getSource() {
+    return source;
+  }
+
   public <T> T acceptVisitor(Visitor<T> visitor) {
     return visitor.visitAddMessageError(this);
   }
diff --git a/extensions/commands/src/com/google/inject/commands/AddThrowableErrorCommand.java b/extensions/commands/src/com/google/inject/commands/AddThrowableErrorCommand.java
index 38be96e..fb28038 100644
--- a/extensions/commands/src/com/google/inject/commands/AddThrowableErrorCommand.java
+++ b/extensions/commands/src/com/google/inject/commands/AddThrowableErrorCommand.java
@@ -16,16 +16,24 @@
 
 package com.google.inject.commands;
 
+import static com.google.inject.internal.Objects.nonNull;
+
 /**
  * Immutable snapshot of a request to add a throwable message.
  *
  * @author jessewilson@google.com (Jesse Wilson)
  */
 public final class AddThrowableErrorCommand implements Command {
+  private final Object source;
   private final Throwable throwable;
 
-  AddThrowableErrorCommand(Throwable throwable) {
-    this.throwable = throwable;
+  AddThrowableErrorCommand(Object source, Throwable throwable) {
+    this.source = nonNull(source, "source");
+    this.throwable = nonNull(throwable, "throwable");
+  }
+
+  public Object getSource() {
+    return source;
   }
 
   public <T> T acceptVisitor(Visitor<T> visitor) {
diff --git a/extensions/commands/src/com/google/inject/commands/BindCommand.java b/extensions/commands/src/com/google/inject/commands/BindCommand.java
index c8ef971..b666ed4 100644
--- a/extensions/commands/src/com/google/inject/commands/BindCommand.java
+++ b/extensions/commands/src/com/google/inject/commands/BindCommand.java
@@ -16,10 +16,8 @@
 
 package com.google.inject.commands;
 
-import com.google.inject.Key;
-import com.google.inject.Provider;
-import com.google.inject.Scope;
-import com.google.inject.TypeLiteral;
+import com.google.inject.*;
+import com.google.inject.spi.SourceProviders;
 import com.google.inject.binder.AnnotatedBindingBuilder;
 import com.google.inject.binder.ConstantBindingBuilder;
 import com.google.inject.binder.LinkedBindingBuilder;
@@ -34,14 +32,44 @@
  * @author jessewilson@google.com (Jesse Wilson)
  */
 public final class BindCommand<T> implements Command {
-  private Key<T> key;
-  private BindTarget<T> bindTarget;
-  private BindScoping bindScoping;
 
-  BindCommand(Key<T> key) {
+  static {
+    SourceProviders.skip(BindCommand.BindingBuilder.class);
+  }
+
+  private static final BindTarget<Object> EMPTY_BIND_TARGET = new AbstractTarget<Object>() {
+    public ScopedBindingBuilder execute(LinkedBindingBuilder<Object> linkedBindingBuilder) {
+      return linkedBindingBuilder;
+    }
+    public <V> V acceptVisitor(Visitor<Object, V> visitor) {
+      return visitor.visitUntargetted();
+    }
+  };
+
+  private static final BindScoping EMPTY_SCOPING = new AbstractScoping() {
+    public void execute(ScopedBindingBuilder scopedBindingBuilder) {
+      // do nothing
+    }
+    public <V> V acceptVisitor(Visitor<V> visitor) {
+      return visitor.visitNoScoping();
+    }
+  };
+
+  private final Object source;
+  private Key<T> key;
+  @SuppressWarnings({"unchecked"})
+  private BindTarget<T> bindTarget = (BindTarget<T>) EMPTY_BIND_TARGET;
+  private BindScoping bindScoping = EMPTY_SCOPING;
+
+  BindCommand(Object source, Key<T> key) {
+    this.source = nonNull(source, "source");
     this.key = nonNull(key, "key");
   }
 
+  public Object getSource() {
+    return source;
+  }
+
   public <V> V acceptVisitor(Visitor<V> visitor) {
     return visitor.visitBind(this);
   }
@@ -60,26 +88,25 @@
 
   @Override public String toString() {
     return "bind " + key
-        + (bindTarget == null ? "" : (" to " + bindTarget))
-        + (bindScoping == null ? "" : (" in " + bindScoping));
+        + (bindTarget == EMPTY_BIND_TARGET ? "" : (" to " + bindTarget))
+        + (bindScoping == EMPTY_SCOPING ? "" : (" in " + bindScoping));
   }
 
   private static abstract class AbstractTarget<T> implements BindTarget<T> {
     public void execute(ConstantBindingBuilder builder) {
       throw new UnsupportedOperationException();
     }
-    public T get(T defaultValue) {
-      return defaultValue;
+    public T get() {
+      return null;
     }
-    public Key<? extends Provider<? extends T>> getProviderKey(
-        Key<Provider<? extends T>> defaultValue) {
-      return defaultValue;
+    public Key<? extends Provider<? extends T>> getProviderKey() {
+      return null;
     }
-    public Provider<? extends T> getProvider(Provider<? extends T> defaultValue) {
-      return defaultValue;
+    public Provider<? extends T> getProvider() {
+      return null;
     }
-    public Key<? extends T> getKey(Key<? extends T> defaultValue) {
-      return defaultValue;
+    public Key<? extends T> getKey() {
+      return null;
     }
   }
 
@@ -87,25 +114,31 @@
     public boolean isEagerSingleton() {
       return false;
     }
-    public Scope getScope(Scope defaultValue) {
-      return defaultValue;
+    public Scope getScope() {
+      return null;
     }
-    public Class<? extends Annotation> getScopeAnnotation(
-        Class<? extends Annotation> defaultValue) {
-      return defaultValue;
+    public Class<? extends Annotation> getScopeAnnotation() {
+      return null;
     }
   }
 
-  BindingBuilder bindingBuilder() {
-    return new BindingBuilder();
+  BindingBuilder bindingBuilder(Binder binder) {
+    return new BindingBuilder(binder);
   }
 
   /**
    * Package-private write access to the internal state of this command.
    */
   class BindingBuilder implements AnnotatedBindingBuilder<T> {
+    private final Binder binder;
+
+    BindingBuilder(Binder binder) {
+      this.binder = binder;
+    }
+
     public LinkedBindingBuilder<T> annotatedWith(
         Class<? extends Annotation> annotationType) {
+      nonNull(annotationType, "annotationType");
       assertNotAnnotated();
       key = Key.get(key.getTypeLiteral(), annotationType);
       return this;
@@ -134,9 +167,12 @@
         public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
           return linkedBindingBuilder.to(targetKey);
         }
-        @Override public Key<? extends T> getKey(Key<? extends T> defaultValue) {
+        @Override public Key<? extends T> getKey() {
           return targetKey;
         }
+        public <V> V acceptVisitor(Visitor<T, V> visitor) {
+          return visitor.visitToKey(targetKey);
+        }
         @Override public String toString() {
           return String.valueOf(targetKey);
         }
@@ -145,16 +181,23 @@
     }
 
     public void toInstance(final T instance) {
-      nonNull(instance, "instance"); // might someday want to tolerate null here
+      // might someday want to tolerate null here, probably by setting up a
+      // Provider<null> rather than trying to distinguish between null and
+      // not set
+      nonNull(instance, "instance");
+
       assertNoTarget();
       bindTarget = new AbstractTarget<T>() {
         public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
           linkedBindingBuilder.toInstance(instance);
           return null;
         }
-        @Override public T get(T defaultValue) {
+        @Override public T get() {
           return instance;
         }
+        public <V> V acceptVisitor(Visitor<T, V> visitor) {
+          return visitor.visitToInstance(instance);
+        }
         @Override public String toString() {
           return "instance " + instance;
         }
@@ -168,9 +211,12 @@
         public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
           return linkedBindingBuilder.toProvider(provider);
         }
-        @Override public Provider<? extends T> getProvider(Provider<? extends T> defaultValue) {
+        @Override public Provider<? extends T> getProvider() {
           return provider;
         }
+        public <V> V acceptVisitor(Visitor<T, V> visitor) {
+          return visitor.visitToProvider(provider);
+        }
         @Override public String toString() {
           return "provider " + provider;
         }
@@ -191,10 +237,12 @@
         public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
           return linkedBindingBuilder.toProvider(providerKey);
         }
-        @Override public Key<? extends Provider<? extends T>> getProviderKey(
-            Key<Provider<? extends T>> defaultValue) {
+        @Override public Key<? extends Provider<? extends T>> getProviderKey() {
           return providerKey;
         }
+        public <V> V acceptVisitor(Visitor<T, V> visitor) {
+          return visitor.visitToProviderKey(providerKey);
+        }
         @Override public String toString() {
           return "provider " + providerKey;
         }
@@ -210,10 +258,12 @@
         public void execute(ScopedBindingBuilder scopedBindingBuilder) {
           scopedBindingBuilder.in(scopeAnnotation);
         }
-        @Override public Class<? extends Annotation> getScopeAnnotation(
-            Class<? extends Annotation> defaultValue) {
+        @Override public Class<? extends Annotation> getScopeAnnotation() {
           return scopeAnnotation;
         }
+        public <V> V acceptVisitor(Visitor<V> visitor) {
+          return visitor.visitScopeAnnotation(scopeAnnotation);
+        }
         @Override public String toString() {
           return scopeAnnotation.getName();
         }
@@ -224,12 +274,16 @@
       nonNull(scope, "scope");
       assertNoScope();
       bindScoping = new AbstractScoping() {
+
         public void execute(ScopedBindingBuilder scopedBindingBuilder) {
           scopedBindingBuilder.in(scope);
         }
-        @Override public Scope getScope(Scope defaultValue) {
+        @Override public Scope getScope() {
           return scope;
         }
+        public <V> V acceptVisitor(Visitor<V> visitor) {
+          return visitor.visitScope(scope);
+        }
         @Override public String toString() {
           return String.valueOf(scope);
         }
@@ -245,27 +299,44 @@
         @Override public boolean isEagerSingleton() {
           return true;
         }
+        public <V> V acceptVisitor(Visitor<V> visitor) {
+          return visitor.visitEagerSingleton();
+        }
         @Override public String toString() {
           return "eager singleton";
         }
       };
     }
 
+    static final String IMPLEMENTATION_ALREADY_SET
+        = "Implementation is set more than once.";
+    static final String SINGLE_INSTANCE_AND_SCOPE = "Setting the scope is not"
+        + " permitted when binding to a single instance.";
+    static final String SCOPE_ALREADY_SET = "Scope is set more than once.";
+    static final String ANNOTATION_ALREADY_SPECIFIED = "More than one annotation"
+        + " is specified for this binding.";
+
     private void assertNoTarget() {
-      if (bindTarget != null) {
-        throw new IllegalStateException("Already targetted to " + bindTarget);
+      if (bindTarget != EMPTY_BIND_TARGET) {
+        binder.addError(IMPLEMENTATION_ALREADY_SET);
       }
     }
 
     private void assertNotAnnotated() {
       if (BindCommand.this.key.getAnnotationType() != null) {
-        throw new IllegalStateException("Already annotated with " + key.getAnnotationType());
+        binder.addError(ANNOTATION_ALREADY_SPECIFIED);
       }
     }
 
     private void assertNoScope() {
-      if (bindScoping != null) {
-        throw new IllegalStateException("Already scoped by " + bindScoping);
+      // Scoping isn't allowed when we have only one instance.
+      if (bindTarget.get() != null) {
+        binder.addError(SINGLE_INSTANCE_AND_SCOPE);
+        return;
+      }
+
+      if (bindScoping != EMPTY_SCOPING) {
+        binder.addError(SCOPE_ALREADY_SET);
       }
     }
   }
diff --git a/extensions/commands/src/com/google/inject/commands/BindConstantCommand.java b/extensions/commands/src/com/google/inject/commands/BindConstantCommand.java
index 142bb19..af131ec 100644
--- a/extensions/commands/src/com/google/inject/commands/BindConstantCommand.java
+++ b/extensions/commands/src/com/google/inject/commands/BindConstantCommand.java
@@ -18,11 +18,14 @@
 
 import com.google.inject.Key;
 import com.google.inject.Provider;
+import com.google.inject.Binder;
+import com.google.inject.spi.SourceProviders;
 import com.google.inject.binder.AnnotatedConstantBindingBuilder;
 import com.google.inject.binder.ConstantBindingBuilder;
 import com.google.inject.binder.LinkedBindingBuilder;
 import com.google.inject.binder.ScopedBindingBuilder;
 import com.google.inject.internal.Objects;
+import static com.google.inject.internal.Objects.nonNull;
 
 import java.lang.annotation.Annotation;
 
@@ -32,11 +35,20 @@
  * @author jessewilson@google.com (Jesse Wilson)
  */
 public final class BindConstantCommand implements Command {
+  static {
+    SourceProviders.skip(BindingBuilder.class);
+  }
+
+  private final Object source;
   private BindingAnnotation bindingAnnotation;
   private ConstantTarget<?> target;
 
-  BindConstantCommand() {
-    // hide public constructor
+  BindConstantCommand(Object source) {
+    this.source = nonNull(source, "source");
+  }
+
+  public Object getSource() {
+    return source;
   }
 
   public <T> T acceptVisitor(Visitor<T> visitor) {
@@ -68,15 +80,17 @@
     public ScopedBindingBuilder execute(LinkedBindingBuilder linkedBindingBuilder) {
       throw new UnsupportedOperationException();
     }
-    public Provider<? extends T> getProvider(Provider<? extends T> defaultValue) {
-      return defaultValue;
+    public <V> V acceptVisitor(Visitor<T, V> visitor) {
+      return visitor.visitToInstance(get());
     }
-    public Key<? extends Provider<? extends T>> getProviderKey(
-        Key<Provider<? extends T>> defaultValue) {
-      return defaultValue;
+    public Provider<? extends T> getProvider() {
+      return null;
     }
-    public Key<? extends T> getKey(Key<? extends T> defaultValue) {
-      return defaultValue;
+    public Key<? extends Provider<? extends T>> getProviderKey() {
+      return null;
+    }
+    public Key<? extends T> getKey() {
+      return null;
     }
   }
 
@@ -88,8 +102,8 @@
     abstract <T> Key<T> getKey();
   }
 
-  BindingBuilder bindingBuilder() {
-    return new BindingBuilder();
+  BindingBuilder bindingBuilder(Binder binder) {
+    return new BindingBuilder(binder);
   }
 
   /**
@@ -97,20 +111,14 @@
    */
   class BindingBuilder
       implements AnnotatedConstantBindingBuilder, ConstantBindingBuilder {
+    private final Binder binder;
 
-    private void assertNoBindingAnnotation() {
-      if (bindingAnnotation != null) {
-        throw new IllegalStateException("Already annotated with " + bindingAnnotation);
-      }
-    }
-
-    private void assertNoTarget() {
-      if (target != null) {
-        throw new IllegalStateException("Already targetted to " + target);
-      }
+    BindingBuilder(Binder binder) {
+      this.binder = binder;
     }
 
     public ConstantBindingBuilder annotatedWith(final Class<? extends Annotation> annotationType) {
+      nonNull(annotationType, "annotationType");
       assertNoBindingAnnotation();
 
       bindingAnnotation = new BindingAnnotation() {
@@ -126,6 +134,7 @@
     }
 
     public ConstantBindingBuilder annotatedWith(final Annotation annotation) {
+      nonNull(annotation, "annotation");
       assertNoBindingAnnotation();
 
       bindingAnnotation = new BindingAnnotation() {
@@ -141,13 +150,14 @@
     }
 
     public void to(final String value) {
+      nonNull(value, "value");
       assertNoTarget();
 
       BindConstantCommand.this.target = new ConstantTarget() {
         public void execute(ConstantBindingBuilder builder) {
           builder.to(value);
         }
-        public Object get(Object defaultValue) {
+        public Object get() {
           return value;
         }
         public Class getType() {
@@ -166,11 +176,11 @@
         public void execute(ConstantBindingBuilder builder) {
           builder.to(value);
         }
-        public Object get(Object defaultValue) {
+        public Object get() {
           return value;
         }
         public Class getType() {
-          return Integer.class;
+          return int.class;
         }
         @Override public String toString() {
           return String.valueOf(value);
@@ -185,11 +195,11 @@
         public void execute(ConstantBindingBuilder builder) {
           builder.to(value);
         }
-        public Object get(Object defaultValue) {
+        public Object get() {
           return value;
         }
         public Class getType() {
-          return Long.class;
+          return long.class;
         }
         @Override public String toString() {
           return String.valueOf(value);
@@ -204,11 +214,11 @@
         public void execute(ConstantBindingBuilder builder) {
           builder.to(value);
         }
-        public Object get(Object defaultValue) {
+        public Object get() {
           return value;
         }
         public Class getType() {
-          return Boolean.class;
+          return boolean.class;
         }
         @Override public String toString() {
           return String.valueOf(value);
@@ -223,11 +233,11 @@
         public void execute(ConstantBindingBuilder builder) {
           builder.to(value);
         }
-        public Object get(Object defaultValue) {
+        public Object get() {
           return value;
         }
         public Class getType() {
-          return Double.class;
+          return double.class;
         }
         @Override public String toString() {
           return String.valueOf(value);
@@ -242,11 +252,11 @@
         public void execute(ConstantBindingBuilder builder) {
           builder.to(value);
         }
-        public Object get(Object defaultValue) {
+        public Object get() {
           return value;
         }
         public Class getType() {
-          return Float.class;
+          return float.class;
         }
         @Override public String toString() {
           return String.valueOf(value);
@@ -261,11 +271,11 @@
         public void execute(ConstantBindingBuilder builder) {
           builder.to(value);
         }
-        public Object get(Object defaultValue) {
+        public Object get() {
           return value;
         }
         public Class getType() {
-          return Short.class;
+          return short.class;
         }
         @Override public String toString() {
           return String.valueOf(value);
@@ -280,11 +290,11 @@
         public void execute(ConstantBindingBuilder builder) {
           builder.to(value);
         }
-        public Object get(Object defaultValue) {
+        public Object get() {
           return value;
         }
         public Class getType() {
-          return Character.class;
+          return char.class;
         }
         @Override public String toString() {
           return String.valueOf(value);
@@ -293,13 +303,14 @@
     }
 
     public void to(final Class<?> value) {
+      nonNull(value, "value");
       assertNoTarget();
 
       BindConstantCommand.this.target = new ConstantTarget() {
         public void execute(ConstantBindingBuilder builder) {
           builder.to(value);
         }
-        public Object get(Object defaultValue) {
+        public Object get() {
           return value;
         }
         public Class getType() {
@@ -319,7 +330,7 @@
         public void execute(ConstantBindingBuilder builder) {
           builder.to(value);
         }
-        public Object get(Object defaultValue) {
+        public Object get() {
           return value;
         }
         public Class getType() {
@@ -330,5 +341,22 @@
         }
       };
     }
+
+    static final String CONSTANT_VALUE_ALREADY_SET = "Constant value is set more"
+        + " than once.";
+    static final String ANNOTATION_ALREADY_SPECIFIED = "More than one annotation"
+        + " is specified for this binding.";
+
+    private void assertNoBindingAnnotation() {
+      if (bindingAnnotation != null) {
+        binder.addError(ANNOTATION_ALREADY_SPECIFIED);
+      }
+    }
+
+    private void assertNoTarget() {
+      if (target != null) {
+        binder.addError(CONSTANT_VALUE_ALREADY_SET);
+      }
+    }
   }
 }
diff --git a/extensions/commands/src/com/google/inject/commands/BindInterceptorCommand.java b/extensions/commands/src/com/google/inject/commands/BindInterceptorCommand.java
index ad12345..7485346 100644
--- a/extensions/commands/src/com/google/inject/commands/BindInterceptorCommand.java
+++ b/extensions/commands/src/com/google/inject/commands/BindInterceptorCommand.java
@@ -17,6 +17,7 @@
 package com.google.inject.commands;
 
 import com.google.inject.matcher.Matcher;
+import static com.google.inject.internal.Objects.nonNull;
 import org.aopalliance.intercept.MethodInterceptor;
 
 import java.lang.reflect.Method;
@@ -30,19 +31,26 @@
  * @author jessewilson@google.com (Jesse Wilson)
  */
 public final class BindInterceptorCommand implements Command {
+  private final Object source;
   private final Matcher<? super Class<?>> classMatcher;
   private final Matcher<? super Method> methodMatcher;
   private final List<MethodInterceptor> interceptors;
 
   BindInterceptorCommand(
+      Object source,
       Matcher<? super Class<?>> classMatcher,
       Matcher<? super Method> methodMatcher,
       MethodInterceptor[] interceptors) {
-    this.classMatcher = classMatcher;
-    this.methodMatcher = methodMatcher;
+    this.source = nonNull(source, "source");
+    this.classMatcher = nonNull(classMatcher, "classMatcher");
+    this.methodMatcher = nonNull(methodMatcher, "methodMatcher");
     this.interceptors = unmodifiableList(Arrays.asList(interceptors.clone()));
   }
 
+  public Object getSource() {
+    return source;
+  }
+
   public Matcher<? super Class<?>> getClassMatcher() {
     return classMatcher;
   }
diff --git a/extensions/commands/src/com/google/inject/commands/BindScopeCommand.java b/extensions/commands/src/com/google/inject/commands/BindScopeCommand.java
index 639f2c5..1c0e9f8 100644
--- a/extensions/commands/src/com/google/inject/commands/BindScopeCommand.java
+++ b/extensions/commands/src/com/google/inject/commands/BindScopeCommand.java
@@ -17,6 +17,7 @@
 package com.google.inject.commands;
 
 import com.google.inject.Scope;
+import static com.google.inject.internal.Objects.nonNull;
 
 import java.lang.annotation.Annotation;
 
@@ -26,13 +27,18 @@
  * @author jessewilson@google.com (Jesse Wilson)
  */
 public final class BindScopeCommand implements Command {
+  private final Object source;
   private final Class<? extends Annotation> annotationType;
   private final Scope scope;
 
-  BindScopeCommand(
-      Class<? extends Annotation> annotationType, Scope scope) {
-    this.annotationType = annotationType;
-    this.scope = scope;
+  BindScopeCommand(Object source, Class<? extends Annotation> annotationType, Scope scope) {
+    this.source = nonNull(source, "source");
+    this.annotationType = nonNull(annotationType, "annotationType");
+    this.scope = nonNull(scope, "scope");
+  }
+
+  public Object getSource() {
+    return source;
   }
 
   public Class<? extends Annotation> getAnnotationType() {
diff --git a/extensions/commands/src/com/google/inject/commands/BindScoping.java b/extensions/commands/src/com/google/inject/commands/BindScoping.java
index 52e162d..1eb2a83 100644
--- a/extensions/commands/src/com/google/inject/commands/BindScoping.java
+++ b/extensions/commands/src/com/google/inject/commands/BindScoping.java
@@ -30,6 +30,14 @@
 public interface BindScoping {
   void execute(ScopedBindingBuilder scopedBindingBuilder);
   boolean isEagerSingleton();
-  Scope getScope(Scope defaultValue);
-  Class<? extends Annotation> getScopeAnnotation(Class<? extends Annotation> defaultValue);
+  Scope getScope();
+  Class<? extends Annotation> getScopeAnnotation();
+  <V> V acceptVisitor(Visitor<V> visitor);
+  
+  interface Visitor<V> {
+    V visitEagerSingleton();
+    V visitScope(Scope scope);
+    V visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation);
+    V visitNoScoping();
+  }
 }
diff --git a/extensions/commands/src/com/google/inject/commands/BindTarget.java b/extensions/commands/src/com/google/inject/commands/BindTarget.java
index 99bf418..e76569d 100644
--- a/extensions/commands/src/com/google/inject/commands/BindTarget.java
+++ b/extensions/commands/src/com/google/inject/commands/BindTarget.java
@@ -41,14 +41,23 @@
   void execute(ConstantBindingBuilder builder);
 
   /**
-   * Returns the bound instance, if it exists, or {@code defaultValue}
-   * if no bound value exists.
+   * Returns the bound instance, if it exists, or {@code null} if no bound value exists.
    */
-  T get(T defaultValue);
+  T get();
 
-  Provider<? extends T> getProvider(Provider<? extends T> defaultValue);
+  Provider<? extends T> getProvider();
 
-  Key<? extends Provider<? extends T>> getProviderKey(Key<Provider<? extends T>> defaultValue);
+  Key<? extends Provider<? extends T>> getProviderKey();
 
-  Key<? extends T> getKey(Key<? extends T> defaultValue);
+  Key<? extends T> getKey();
+
+  <V> V acceptVisitor(Visitor<T, V> visitor);
+
+  interface Visitor<T, V> {
+    V visitToInstance(T instance);
+    V visitToProvider(Provider<? extends T> provider);
+    V visitToProviderKey(Key<? extends Provider<? extends T>> providerKey);
+    V visitToKey(Key<? extends T> key);
+    V visitUntargetted();
+  }
 }
diff --git a/extensions/commands/src/com/google/inject/commands/Command.java b/extensions/commands/src/com/google/inject/commands/Command.java
index 15c1729..862a77f 100644
--- a/extensions/commands/src/com/google/inject/commands/Command.java
+++ b/extensions/commands/src/com/google/inject/commands/Command.java
@@ -22,6 +22,7 @@
  * @author jessewilson@google.com (Jesse Wilson)
  */
 public interface Command {
+  Object getSource();
   <T> T acceptVisitor(Visitor<T> visitor);
 
   /**
diff --git a/extensions/commands/src/com/google/inject/commands/CommandRecorder.java b/extensions/commands/src/com/google/inject/commands/CommandRecorder.java
index 9c3ed9b..e6ce8f2 100644
--- a/extensions/commands/src/com/google/inject/commands/CommandRecorder.java
+++ b/extensions/commands/src/com/google/inject/commands/CommandRecorder.java
@@ -20,15 +20,14 @@
 import com.google.inject.binder.AnnotatedBindingBuilder;
 import com.google.inject.binder.AnnotatedConstantBindingBuilder;
 import com.google.inject.matcher.Matcher;
+import static com.google.inject.spi.SourceProviders.defaultSource;
 import com.google.inject.spi.TypeConverter;
+import com.google.inject.spi.SourceProviders;
 import org.aopalliance.intercept.MethodInterceptor;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
 
 /**
  * Records commands executed by a module so they can be inspected or
@@ -37,9 +36,13 @@
  * @author jessewilson@google.com (Jesse Wilson)
  */
 public final class CommandRecorder {
-  private final Stage stage = Stage.DEVELOPMENT;
+  private Stage currentStage = Stage.DEVELOPMENT;
   private final EarlyRequestsProvider earlyRequestsProvider;
 
+  static {
+    SourceProviders.skip(RecordingBinder.class);
+  }
+
   /**
    * @param earlyRequestsProvider satisfies requests to
    *     {@link Binder#getProvider} at module execution time. For modules that
@@ -50,6 +53,13 @@
   }
 
   /**
+   * Sets the stage reported by the binder.
+   */
+  public void setCurrentStage(Stage currentStage) {
+    this.currentStage = currentStage;
+  }
+
+  /**
    * Records the commands executed by {@code modules}.
    */
   public List<Command> recordCommands(Module... modules) {
@@ -62,49 +72,53 @@
   public List<Command> recordCommands(Iterable<Module> modules) {
     RecordingBinder binder = new RecordingBinder();
     for (Module module : modules) {
-      module.configure(binder);
+      binder.install(module);
     }
     return Collections.unmodifiableList(binder.commands);
   }
 
   private class RecordingBinder implements Binder {
+    private final Set<Module> modules = new HashSet<Module>();
     private final List<Command> commands = new ArrayList<Command>();
 
     public void bindInterceptor(
         Matcher<? super Class<?>> classMatcher,
         Matcher<? super Method> methodMatcher,
         MethodInterceptor... interceptors) {
-      commands.add(new BindInterceptorCommand(classMatcher, methodMatcher, interceptors));
+      commands.add(new BindInterceptorCommand(
+          defaultSource(), classMatcher, methodMatcher, interceptors));
     }
 
     public void bindScope(Class<? extends Annotation> annotationType, Scope scope) {
-      commands.add(new BindScopeCommand(annotationType, scope));
+      commands.add(new BindScopeCommand(defaultSource(), annotationType, scope));
     }
 
     public void requestStaticInjection(Class<?>... types) {
-      commands.add(new RequestStaticInjectionCommand(types));
+      commands.add(new RequestStaticInjectionCommand(defaultSource(), types));
     }
 
     public void install(Module module) {
-      module.configure(this);
+      if (modules.add(module)) {
+        module.configure(this);
+      }
     }
 
     public Stage currentStage() {
-      return stage;
+      return currentStage;
     }
 
     public void addError(String message, Object... arguments) {
-      commands.add(new AddMessageErrorCommand(message, arguments));
+      commands.add(new AddMessageErrorCommand(defaultSource(), message, arguments));
     }
 
     public void addError(Throwable t) {
-      commands.add(new AddThrowableErrorCommand(t));
+      commands.add(new AddThrowableErrorCommand(defaultSource(), t));
     }
 
     public <T> BindCommand<T>.BindingBuilder bind(Key<T> key) {
-      BindCommand<T> bindCommand = new BindCommand<T>(key);
+      BindCommand<T> bindCommand = new BindCommand<T>(defaultSource(), key);
       commands.add(bindCommand);
-      return bindCommand.bindingBuilder();
+      return bindCommand.bindingBuilder(RecordingBinder.this);
     }
 
     public <T> AnnotatedBindingBuilder<T> bind(TypeLiteral<T> typeLiteral) {
@@ -116,13 +130,13 @@
     }
 
     public AnnotatedConstantBindingBuilder bindConstant() {
-      BindConstantCommand bindConstantCommand = new BindConstantCommand();
+      BindConstantCommand bindConstantCommand = new BindConstantCommand(defaultSource());
       commands.add(bindConstantCommand);
-      return bindConstantCommand.bindingBuilder();
+      return bindConstantCommand.bindingBuilder(RecordingBinder.this);
     }
 
     public <T> Provider<T> getProvider(final Key<T> key) {
-      commands.add(new GetProviderCommand<T>(key, earlyRequestsProvider));
+      commands.add(new GetProviderCommand<T>(defaultSource(), key, earlyRequestsProvider));
       return new Provider<T>() {
         public T get() {
           return earlyRequestsProvider.get(key);
@@ -135,8 +149,8 @@
     }
 
     public void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
-                               TypeConverter converter) {
-      commands.add(new ConvertToTypesCommand(typeMatcher, converter));
+        TypeConverter converter) {
+      commands.add(new ConvertToTypesCommand(defaultSource(), typeMatcher, converter));
     }
   }
 }
diff --git a/extensions/commands/src/com/google/inject/commands/CommandReplayer.java b/extensions/commands/src/com/google/inject/commands/CommandReplayer.java
index 4b06890..d4326fb 100644
--- a/extensions/commands/src/com/google/inject/commands/CommandReplayer.java
+++ b/extensions/commands/src/com/google/inject/commands/CommandReplayer.java
@@ -19,6 +19,7 @@
 import com.google.inject.Binder;
 import com.google.inject.Key;
 import com.google.inject.Module;
+import com.google.inject.spi.SourceProviders;
 import com.google.inject.binder.AnnotatedConstantBindingBuilder;
 import com.google.inject.binder.ConstantBindingBuilder;
 import com.google.inject.binder.LinkedBindingBuilder;
@@ -106,59 +107,96 @@
     }
   }
 
-  public void replayAddMessageError(Binder binder, AddMessageErrorCommand command) {
-    binder.addError(command.getMessage(), command.getArguments().toArray());
+  public void replayAddMessageError(final Binder binder, final AddMessageErrorCommand command) {
+    SourceProviders.withDefault(command.getSource(), new Runnable() {
+      public void run() {
+        binder.addError(command.getMessage(), command.getArguments().toArray());
+      }
+    });
   }
 
-  public void replayAddError(Binder binder, AddThrowableErrorCommand command) {
-    binder.addError(command.getThrowable());
+  public void replayAddError(final Binder binder, final AddThrowableErrorCommand command) {
+    SourceProviders.withDefault(command.getSource(), new Runnable() {
+      public void run() {
+        binder.addError(command.getThrowable());
+      }
+    });
   }
 
-  public void replayBindInterceptor(Binder binder, BindInterceptorCommand command) {
-    List<MethodInterceptor> interceptors = command.getInterceptors();
-    binder.bindInterceptor(command.getClassMatcher(), command.getMethodMatcher(),
-        interceptors.toArray(new MethodInterceptor[interceptors.size()]));
+  public void replayBindInterceptor(final Binder binder, final BindInterceptorCommand command) {
+    SourceProviders.withDefault(command.getSource(), new Runnable() {
+      public void run() {
+        List<MethodInterceptor> interceptors = command.getInterceptors();
+        binder.bindInterceptor(command.getClassMatcher(), command.getMethodMatcher(),
+            interceptors.toArray(new MethodInterceptor[interceptors.size()]));
+      }
+    });
   }
 
-  public void replayBindScope(Binder binder, BindScopeCommand command) {
-    binder.bindScope(command.getAnnotationType(), command.getScope());
+  public void replayBindScope(final Binder binder, final BindScopeCommand command) {
+    SourceProviders.withDefault(command.getSource(), new Runnable() {
+      public void run() {
+        binder.bindScope(command.getAnnotationType(), command.getScope());
+      }
+    });
   }
 
-  public void replayRequestStaticInjection(Binder binder, RequestStaticInjectionCommand command) {
-    List<Class> types = command.getTypes();
-    binder.requestStaticInjection(types.toArray(new Class[types.size()]));
+  public void replayRequestStaticInjection(final Binder binder,
+      final RequestStaticInjectionCommand command) {
+    SourceProviders.withDefault(command.getSource(), new Runnable() {
+      public void run() {
+        List<Class> types = command.getTypes();
+        binder.requestStaticInjection(types.toArray(new Class[types.size()]));
+      }
+    });
   }
 
-  public void replayBindConstant(Binder binder, BindConstantCommand command) {
-    AnnotatedConstantBindingBuilder constantBindingBuilder = binder.bindConstant();
+  public void replayBindConstant(final Binder binder, final BindConstantCommand command) {
+    SourceProviders.withDefault(command.getSource(), new Runnable() {
+      public void run() {
+        AnnotatedConstantBindingBuilder constantBindingBuilder = binder.bindConstant();
 
-    Key<Object> key = command.getKey();
-    ConstantBindingBuilder builder = key.getAnnotation() != null
-        ? constantBindingBuilder.annotatedWith(key.getAnnotation())
-        : constantBindingBuilder.annotatedWith(key.getAnnotationType());
+        Key<Object> key = command.getKey();
+        ConstantBindingBuilder builder = key.getAnnotation() != null
+            ? constantBindingBuilder.annotatedWith(key.getAnnotation())
+            : constantBindingBuilder.annotatedWith(key.getAnnotationType());
 
-    command.getTarget().execute(builder);
+        command.getTarget().execute(builder);
+      }
+    });
   }
 
-  public void replayConvertToTypes(Binder binder, ConvertToTypesCommand command) {
-    binder.convertToTypes(command.getTypeMatcher(), command.getTypeConverter());
+  public void replayConvertToTypes(final Binder binder, final ConvertToTypesCommand command) {
+    SourceProviders.withDefault(command.getSource(), new Runnable() {
+      public void run() {
+        binder.convertToTypes(command.getTypeMatcher(), command.getTypeConverter());
+      }
+    });
   }
 
-  public <T> void replayBind(Binder binder, BindCommand<T> command) {
-    LinkedBindingBuilder<T> lbb = binder.bind(command.getKey());
+  public <T> void replayBind(final Binder binder, final BindCommand<T> command) {
+    SourceProviders.withDefault(command.getSource(), new Runnable() {
+      public void run() {
+        LinkedBindingBuilder<T> lbb = binder.bind(command.getKey());
 
-    BindTarget<T> bindTarget = command.getTarget();
-    ScopedBindingBuilder sbb = bindTarget != null
-        ? bindTarget.execute(lbb)
-        : lbb;
+        BindTarget<T> bindTarget = command.getTarget();
+        ScopedBindingBuilder sbb = bindTarget != null
+            ? bindTarget.execute(lbb)
+            : lbb;
 
-    BindScoping scoping = command.getScoping();
-    if (scoping != null) {
-      scoping.execute(sbb);
-    }
+        BindScoping scoping = command.getScoping();
+        if (scoping != null) {
+          scoping.execute(sbb);
+        }
+      }
+    });
   }
 
-  public <T> void replayGetProvider(Binder binder, GetProviderCommand<T> command) {
-    binder.getProvider(command.getKey());
+  public <T> void replayGetProvider(final Binder binder, final GetProviderCommand<T> command) {
+    SourceProviders.withDefault(command.getSource(), new Runnable() {
+      public void run() {
+        binder.getProvider(command.getKey());
+      }
+    });
   }
 }
diff --git a/extensions/commands/src/com/google/inject/commands/ConvertToTypesCommand.java b/extensions/commands/src/com/google/inject/commands/ConvertToTypesCommand.java
index a02536e..ba7a6e8 100644
--- a/extensions/commands/src/com/google/inject/commands/ConvertToTypesCommand.java
+++ b/extensions/commands/src/com/google/inject/commands/ConvertToTypesCommand.java
@@ -17,6 +17,7 @@
 package com.google.inject.commands;
 
 import com.google.inject.TypeLiteral;
+import static com.google.inject.internal.Objects.nonNull;
 import com.google.inject.matcher.Matcher;
 import com.google.inject.spi.TypeConverter;
 
@@ -27,13 +28,19 @@
  * @author jessewilson@google.com (Jesse Wilson)
  */
 public final class ConvertToTypesCommand implements Command {
+  private final Object source;
   private final Matcher<? super TypeLiteral<?>> typeMatcher;
   private final TypeConverter typeConverter;
 
-  ConvertToTypesCommand(Matcher<? super TypeLiteral<?>> typeMatcher,
+  ConvertToTypesCommand(Object source, Matcher<? super TypeLiteral<?>> typeMatcher,
       TypeConverter typeConverter) {
-    this.typeMatcher = typeMatcher;
-    this.typeConverter = typeConverter;
+    this.source = nonNull(source, "source");
+    this.typeMatcher = nonNull(typeMatcher, "typeMatcher");
+    this.typeConverter = nonNull(typeConverter, "typeConverter");
+  }
+
+  public Object getSource() {
+    return source;
   }
 
   public Matcher<? super TypeLiteral<?>> getTypeMatcher() {
diff --git a/extensions/commands/src/com/google/inject/commands/FutureInjector.java b/extensions/commands/src/com/google/inject/commands/FutureInjector.java
index e846ec1..ce1b4fa 100644
--- a/extensions/commands/src/com/google/inject/commands/FutureInjector.java
+++ b/extensions/commands/src/com/google/inject/commands/FutureInjector.java
@@ -52,7 +52,8 @@
 
   public <T> T get(Key<T> key) {
     if (injector == null) {
-      throw new IllegalStateException("Not yet initialized");
+      throw new IllegalStateException("This provider cannot be used until the"
+          + " Injector has been created.");
     }
 
     return injector.getInstance(key);
diff --git a/extensions/commands/src/com/google/inject/commands/GetProviderCommand.java b/extensions/commands/src/com/google/inject/commands/GetProviderCommand.java
index e155cd7..0839f2c 100644
--- a/extensions/commands/src/com/google/inject/commands/GetProviderCommand.java
+++ b/extensions/commands/src/com/google/inject/commands/GetProviderCommand.java
@@ -17,6 +17,7 @@
 package com.google.inject.commands;
 
 import com.google.inject.Key;
+import static com.google.inject.internal.Objects.nonNull;
 
 /**
  * Immutable snapshot of a request for a provider.
@@ -24,14 +25,20 @@
  * @author jessewilson@google.com (Jesse Wilson)
  */
 public final class GetProviderCommand<T> implements Command {
+  private final Object source;
   private final Key<T> key;
   private final EarlyRequestsProvider earlyRequestsProvider;
 
-  GetProviderCommand(Key<T> key, EarlyRequestsProvider earlyRequestsProvider) {
+  GetProviderCommand(Object source, Key<T> key, EarlyRequestsProvider earlyRequestsProvider) {
+    this.source = nonNull(source, "source");
     this.key = key;
     this.earlyRequestsProvider = earlyRequestsProvider;
   }
 
+  public Object getSource() {
+    return source;
+  }
+
   public Key<T> getKey() {
     return key;
   }
diff --git a/extensions/commands/src/com/google/inject/commands/RequestStaticInjectionCommand.java b/extensions/commands/src/com/google/inject/commands/RequestStaticInjectionCommand.java
index 677bd34..dbf3a1c 100644
--- a/extensions/commands/src/com/google/inject/commands/RequestStaticInjectionCommand.java
+++ b/extensions/commands/src/com/google/inject/commands/RequestStaticInjectionCommand.java
@@ -16,6 +16,8 @@
 
 package com.google.inject.commands;
 
+import static com.google.inject.internal.Objects.nonNull;
+
 import java.util.Arrays;
 import static java.util.Collections.unmodifiableList;
 import java.util.List;
@@ -26,12 +28,18 @@
  * @author jessewilson@google.com (Jesse Wilson)
  */
 public final class RequestStaticInjectionCommand implements Command {
+  private final Object source;
   private final List<Class> types;
 
-  RequestStaticInjectionCommand(Class[] types) {
+  RequestStaticInjectionCommand(Object source, Class[] types) {
+    this.source = nonNull(source, "source");
     this.types = unmodifiableList(Arrays.asList(types.clone()));
   }
 
+  public Object getSource() {
+    return source;
+  }
+
   public List<Class> getTypes() {
     return types;
   }
diff --git a/extensions/commands/test/com/google/inject/commands/CommandRecorderTest.java b/extensions/commands/test/com/google/inject/commands/CommandRecorderTest.java
index a6a566d..0bbc848 100644
--- a/extensions/commands/test/com/google/inject/commands/CommandRecorderTest.java
+++ b/extensions/commands/test/com/google/inject/commands/CommandRecorderTest.java
@@ -35,6 +35,7 @@
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 import java.lang.annotation.Target;
 import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * @author jessewilson@google.com (Jesse Wilson)
@@ -49,6 +50,8 @@
 
   private CommandRecorder commandRecorder = new CommandRecorder(earlyRequestProvider);
 
+  // Binder fidelity tests
+
   public void testAddMessageErrorCommand() {
     checkModule(
         new AbstractModule() {
@@ -96,7 +99,7 @@
         new FailingVisitor() {
           @Override public Void visitBindConstant(BindConstantCommand command) {
             assertEquals(Key.get(String.class, SampleAnnotation.class), command.getKey());
-            assertEquals("A", command.getTarget().get(null));
+            assertEquals("A", command.getTarget().get());
             return null;
           }
         },
@@ -104,7 +107,7 @@
         new FailingVisitor() {
           @Override public Void visitBindConstant(BindConstantCommand command) {
             assertEquals(Key.get(String.class, Names.named("Bee")), command.getKey());
-            assertEquals("B", command.getTarget().get(null));
+            assertEquals("B", command.getTarget().get());
             return null;
           }
         }
@@ -131,7 +134,7 @@
         new FailingVisitor() {
           @Override public Void visitBindConstant(BindConstantCommand command) {
             assertEquals(Key.get(String.class, Names.named("String")), command.getKey());
-            assertEquals("A", command.getTarget().get(null));
+            assertEquals("A", command.getTarget().get());
             return null;
           }
         },
@@ -139,7 +142,7 @@
         new FailingVisitor() {
           @Override public Void visitBindConstant(BindConstantCommand command) {
             assertEquals(Key.get(Integer.class, Names.named("int")), command.getKey());
-            assertEquals(2, command.getTarget().get(null));
+            assertEquals(2, command.getTarget().get());
             return null;
           }
         },
@@ -147,7 +150,7 @@
         new FailingVisitor() {
           @Override public Void visitBindConstant(BindConstantCommand command) {
             assertEquals(Key.get(Long.class, Names.named("long")), command.getKey());
-            assertEquals(3L, command.getTarget().get(null));
+            assertEquals(3L, command.getTarget().get());
             return null;
           }
         },
@@ -155,7 +158,7 @@
         new FailingVisitor() {
           @Override public Void visitBindConstant(BindConstantCommand command) {
             assertEquals(Key.get(Boolean.class, Names.named("boolean")), command.getKey());
-            assertEquals(false, command.getTarget().get(null));
+            assertEquals(false, command.getTarget().get());
             return null;
           }
         },
@@ -163,7 +166,7 @@
         new FailingVisitor() {
           @Override public Void visitBindConstant(BindConstantCommand command) {
             assertEquals(Key.get(Double.class, Names.named("double")), command.getKey());
-            assertEquals(5.0d, command.getTarget().get(null));
+            assertEquals(5.0d, command.getTarget().get());
             return null;
           }
         },
@@ -171,7 +174,7 @@
         new FailingVisitor() {
           @Override public Void visitBindConstant(BindConstantCommand command) {
             assertEquals(Key.get(Float.class, Names.named("float")), command.getKey());
-            assertEquals(6.0f, command.getTarget().get(null));
+            assertEquals(6.0f, command.getTarget().get());
             return null;
           }
         },
@@ -179,7 +182,7 @@
         new FailingVisitor() {
           @Override public Void visitBindConstant(BindConstantCommand command) {
             assertEquals(Key.get(Short.class, Names.named("short")), command.getKey());
-            assertEquals((short) 7, command.getTarget().get(null));
+            assertEquals((short) 7, command.getTarget().get());
             return null;
           }
         },
@@ -187,7 +190,7 @@
         new FailingVisitor() {
           @Override public Void visitBindConstant(BindConstantCommand command) {
             assertEquals(Key.get(Character.class, Names.named("char")), command.getKey());
-            assertEquals('h', command.getTarget().get(null));
+            assertEquals('h', command.getTarget().get());
             return null;
           }
         },
@@ -195,7 +198,7 @@
         new FailingVisitor() {
           @Override public Void visitBindConstant(BindConstantCommand command) {
             assertEquals(Key.get(Class.class, Names.named("Class")), command.getKey());
-            assertEquals(Iterator.class, command.getTarget().get(null));
+            assertEquals(Iterator.class, command.getTarget().get());
             return null;
           }
         },
@@ -203,7 +206,7 @@
         new FailingVisitor() {
           @Override public Void visitBindConstant(BindConstantCommand command) {
             assertEquals(Key.get(CoinSide.class, Names.named("Enum")), command.getKey());
-            assertEquals(CoinSide.TAILS, command.getTarget().get(null));
+            assertEquals(CoinSide.TAILS, command.getTarget().get());
             return null;
           }
         }
@@ -293,7 +296,7 @@
         new FailingVisitor() {
           @Override public <T> Void visitBind(BindCommand<T> command) {
             assertEquals(Key.get(String.class), command.getKey());
-            assertEquals("A", command.getTarget().getProvider(null).get());
+            assertEquals("A", command.getTarget().getProvider().get());
             return null;
           }
         },
@@ -301,8 +304,8 @@
         new FailingVisitor() {
           @Override public <T> Void visitBind(BindCommand<T> command) {
             assertEquals(Key.get(List.class), command.getKey());
-            assertNull(command.getTarget().get(null));
-            assertEquals(Key.get(ListProvider.class), command.getTarget().getProviderKey(null));
+            assertNull(command.getTarget().get());
+            assertEquals(Key.get(ListProvider.class), command.getTarget().getProviderKey());
             return null;
           }
         },
@@ -310,8 +313,8 @@
         new FailingVisitor() {
           @Override public <T> Void visitBind(BindCommand<T> command) {
             assertEquals(Key.get(Collection.class), command.getKey());
-            assertNull(command.getTarget().get(null));
-            assertEquals(Key.get(ListProvider.class), command.getTarget().getProviderKey(null));
+            assertNull(command.getTarget().get());
+            assertEquals(Key.get(ListProvider.class), command.getTarget().getProviderKey());
             return null;
           }
         }
@@ -331,7 +334,7 @@
         new FailingVisitor() {
           @Override public <T> Void visitBind(BindCommand<T> command) {
             assertEquals(Key.get(List.class), command.getKey());
-            assertEquals(Key.get(ArrayList.class), command.getTarget().getKey(null));
+            assertEquals(Key.get(ArrayList.class), command.getTarget().getKey());
             return null;
           }
         },
@@ -339,7 +342,7 @@
         new FailingVisitor() {
           @Override public <T> Void visitBind(BindCommand<T> command) {
             assertEquals(Key.get(Map.class), command.getKey());
-            assertEquals(Key.get(new TypeLiteral<HashMap<Integer, String>>() {}), command.getTarget().getKey(null));
+            assertEquals(Key.get(new TypeLiteral<HashMap<Integer, String>>() {}), command.getTarget().getKey());
             return null;
           }
         },
@@ -347,7 +350,7 @@
         new FailingVisitor() {
           @Override public <T> Void visitBind(BindCommand<T> command) {
             assertEquals(Key.get(Set.class), command.getKey());
-            assertEquals(Key.get(TreeSet.class, SampleAnnotation.class), command.getTarget().getKey(null));
+            assertEquals(Key.get(TreeSet.class, SampleAnnotation.class), command.getTarget().getKey());
             return null;
           }
         }
@@ -365,7 +368,7 @@
         new FailingVisitor() {
           @Override public <T> Void visitBind(BindCommand<T> command) {
             assertEquals(Key.get(String.class), command.getKey());
-            assertEquals("A", command.getTarget().get(null));
+            assertEquals("A", command.getTarget().get());
             return null;
           }
         }
@@ -385,8 +388,8 @@
         new FailingVisitor() {
           @Override public <T> Void visitBind(BindCommand<T> command) {
             assertEquals(Key.get(List.class), command.getKey());
-            assertEquals(Scopes.SINGLETON, command.getScoping().getScope(null));
-            assertNull(command.getScoping().getScopeAnnotation(null));
+            assertEquals(Scopes.SINGLETON, command.getScoping().getScope());
+            assertNull(command.getScoping().getScopeAnnotation());
             assertFalse(command.getScoping().isEagerSingleton());
             return null;
           }
@@ -395,8 +398,8 @@
         new FailingVisitor() {
           @Override public <T> Void visitBind(BindCommand<T> command) {
             assertEquals(Key.get(Map.class), command.getKey());
-            assertEquals(Singleton.class, command.getScoping().getScopeAnnotation(null));
-            assertNull(command.getScoping().getScope(null));
+            assertEquals(Singleton.class, command.getScoping().getScopeAnnotation());
+            assertNull(command.getScoping().getScope());
             assertFalse(command.getScoping().isEagerSingleton());
             return null;
           }
@@ -405,8 +408,8 @@
         new FailingVisitor() {
           @Override public <T> Void visitBind(BindCommand<T> command) {
             assertEquals(Key.get(Set.class), command.getKey());
-            assertNull(command.getScoping().getScopeAnnotation(null));
-            assertNull(command.getScoping().getScope(null));
+            assertNull(command.getScoping().getScopeAnnotation());
+            assertNull(command.getScoping().getScope());
             assertTrue(command.getScoping().isEagerSingleton());
             return null;
           }
@@ -613,6 +616,32 @@
     });
   }
 
+  // Business logic tests
+
+  public void testModulesAreInstalledAtMostOnce() {
+    final AtomicInteger aConfigureCount = new AtomicInteger(0);
+    final Module a = new AbstractModule() {
+      public void configure() {
+        aConfigureCount.incrementAndGet();
+      }
+    };
+
+    commandRecorder.recordCommands(a, a);
+    assertEquals(1, aConfigureCount.get());
+
+    aConfigureCount.set(0);
+    Module b = new AbstractModule() {
+      protected void configure() {
+        install(a);
+        install(a);
+      }
+    };
+
+    commandRecorder.recordCommands(b);
+    assertEquals(1, aConfigureCount.get());
+  }
+
+
   /**
    * Ensures the module performs the commands consistent with {@code visitors}.
    */
@@ -624,6 +653,7 @@
     for (int i = 0; i < visitors.length; i++) {
       Command.Visitor<?> visitor = visitors[i];
       Command command = commands.get(i);
+      assertTrue(command.getSource().toString().contains("CommandRecorderTest"));
       command.acceptVisitor(visitor);
     }
   }
diff --git a/extensions/commands/test/com/google/inject/commands/CommandRewriteTest.java b/extensions/commands/test/com/google/inject/commands/CommandRewriteTest.java
index 7515b4b..690c822 100644
--- a/extensions/commands/test/com/google/inject/commands/CommandRewriteTest.java
+++ b/extensions/commands/test/com/google/inject/commands/CommandRewriteTest.java
@@ -43,7 +43,7 @@
     // create a rewriter that rewrites the binding to 'Wine' with a binding to 'Beer'
     CommandReplayer rewriter = new CommandReplayer() {
       @Override public <T> void replayBind(Binder binder, BindCommand<T> command) {
-        if ("Wine".equals(command.getTarget().get(null))) {
+        if ("Wine".equals(command.getTarget().get())) {
           binder.bind(CharSequence.class).toInstance("Beer");
         } else {
           super.replayBind(binder, command);
diff --git a/guice.iml b/guice.iml
index bdec154..a451284 100644
--- a/guice.iml
+++ b/guice.iml
@@ -111,6 +111,7 @@
         <SOURCES />
       </library>
     </orderEntry>
+    <orderEntry type="module" module-name="commands" />
     <orderEntryProperties />
   </component>
 </module>
diff --git a/guice.ipr b/guice.ipr
index 71829d4..18123fd 100644
--- a/guice.ipr
+++ b/guice.ipr
@@ -356,6 +356,26 @@
     <option name="GENERATE_IIOP_STUBS" value="false" />
     <option name="ADDITIONAL_OPTIONS_STRING" value="" />
   </component>
+  <component name="SvnBranchConfigurationManager">
+    <option name="myConfigurationMap">
+      <map>
+        <entry key="$PROJECT_DIR$">
+          <value>
+            <SvnBranchConfiguration>
+              <option name="branchUrls">
+                <list>
+                  <option value="https://google-guice.googlecode.com/svn/branches" />
+                  <option value="https://google-guice.googlecode.com/svn/tags" />
+                  <option value="https://google-guice.googlecode.com/svn/wiki" />
+                </list>
+              </option>
+              <option name="trunkUrl" value="https://google-guice.googlecode.com/svn/trunk" />
+            </SvnBranchConfiguration>
+          </value>
+        </entry>
+      </map>
+    </option>
+  </component>
   <component name="VcsDirectoryMappings">
     <mapping directory="" vcs="svn" />
   </component>
diff --git a/src/com/google/inject/BindCommandProcessor.java b/src/com/google/inject/BindCommandProcessor.java
new file mode 100644
index 0000000..458e174
--- /dev/null
+++ b/src/com/google/inject/BindCommandProcessor.java
@@ -0,0 +1,356 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject;
+
+import com.google.inject.commands.BindCommand;
+import com.google.inject.commands.BindConstantCommand;
+import com.google.inject.commands.BindScoping;
+import com.google.inject.commands.BindTarget;
+import com.google.inject.internal.Annotations;
+import com.google.inject.internal.Objects;
+import com.google.inject.internal.StackTraceElements;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.*;
+import java.util.logging.Logger;
+
+/**
+ * Handles {@link Binder#bind} and {@link Binder#bindConstant} commands.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+class BindCommandProcessor extends CommandProcessor {
+
+  private final InjectorImpl injector;
+  private final Map<Class<? extends Annotation>, Scope> scopes;
+  private final List<CreationListener> creationListeners
+      = new ArrayList<CreationListener>();
+  private final List<ContextualCallable<Void>> preloaders
+      = new ArrayList<ContextualCallable<Void>>();
+  private final Stage stage;
+  private final Map<Key<?>, BindingImpl<?>> bindings;
+  private final List<MembersInjector> membersInjectors = new ArrayList<MembersInjector>();
+
+  BindCommandProcessor(InjectorImpl injector,
+      Map<Class<? extends Annotation>, Scope> scopes,
+      Stage stage,
+      Map<Key<?>, BindingImpl<?>> bindings) {
+    this.injector = injector;
+    this.scopes = scopes;
+    this.stage = stage;
+    this.bindings = bindings;
+  }
+
+  @Override public <T> Boolean visitBind(BindCommand<T> command) {
+    final Object source = command.getSource();
+
+    final Key<T> key = command.getKey();
+    Class<? super T> rawType = key.getTypeLiteral().getRawType();
+
+    if (rawType == Provider.class) {
+      addError(source, ErrorMessages.BINDING_TO_PROVIDER);
+      return true;
+    }
+
+    if (Logger.class == rawType) {
+      // TODO(jessewilson): assert this is coming from the internal module?
+      // addError(source, ErrorMessages.LOGGER_ALREADY_BOUND);
+      // return true;
+    }
+
+    validateKey(command.getSource(), command.getKey());
+
+    // TODO(jessewilson): Scope annotation on type, like @Singleton
+    final boolean shouldPreload = command.getScoping().isEagerSingleton();
+    final Scope scope = command.getScoping().acceptVisitor(new BindScoping.Visitor<Scope>() {
+      public Scope visitEagerSingleton() {
+        return Scopes.SINGLETON;
+      }
+
+      public Scope visitScope(Scope scope) {
+        return scope;
+      }
+
+      public Scope visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
+        Scope scope = scopes.get(scopeAnnotation);
+        if (scope != null) {
+          return scope;
+        } else {
+          addError(source, ErrorMessages.SCOPE_NOT_FOUND,
+              "@" + scopeAnnotation.getSimpleName());
+          return Scopes.NO_SCOPE;
+        }
+      }
+
+      public Scope visitNoScoping() {
+        return null;
+      }
+    });
+
+    command.getTarget().acceptVisitor(new BindTarget.Visitor<T, Void>() {
+      public Void visitToInstance(T instance) {
+        InternalFactory<? extends T> factory = new ConstantFactory<T>(instance);
+        registerInstanceForInjection(instance);
+        InternalFactory<? extends T> scopedFactory
+            = Scopes.scope(key, injector, factory, scope);
+        createBinding(source, shouldPreload, new InstanceBindingImpl<T>(
+            injector, key, source, scopedFactory, instance));
+        return null;
+      }
+
+      public Void visitToProvider(Provider<? extends T> provider) {
+        InternalFactory<? extends T> factory
+            = new InternalFactoryToProviderAdapter<T>(provider, source);
+        registerInstanceForInjection(provider);
+        InternalFactory<? extends T> scopedFactory
+            = Scopes.scope(key, injector, factory, scope);
+        createBinding(source, shouldPreload, new ProviderInstanceBindingImpl<T>(
+            injector, key, source, scopedFactory, scope, provider));
+        return null;
+      }
+
+      public Void visitToProviderKey(Key<? extends Provider<? extends T>> providerKey) {
+        final BoundProviderFactory<T> boundProviderFactory =
+            new BoundProviderFactory<T>(providerKey, source);
+        creationListeners.add(boundProviderFactory);
+        InternalFactory<? extends T> scopedFactory = Scopes.scope(
+            key, injector, (InternalFactory<? extends T>) boundProviderFactory, scope);
+        createBinding(source, shouldPreload, new LinkedProviderBindingImpl<T>(
+            injector, key, source, scopedFactory, scope, providerKey));
+        return null;
+      }
+
+      public Void visitToKey(Key<? extends T> targetKey) {
+        if (key.equals(targetKey)) {
+          addError(source, ErrorMessages.RECURSIVE_BINDING);
+        }
+
+        FactoryProxy<T> factory = new FactoryProxy<T>(key, targetKey, source);
+        creationListeners.add(factory);
+        InternalFactory<? extends T> scopedFactory
+            = Scopes.scope(key, injector, factory, scope);
+        createBinding(source, shouldPreload, new LinkedBindingImpl<T>(
+            injector, key, source, scopedFactory, scope, targetKey));
+        return null;
+      }
+
+      public Void visitUntargetted() {
+        Type type = key.getTypeLiteral().getType();
+
+        // Error: Missing implementation.
+        // Example: bind(Date.class).annotatedWith(Red.class);
+        // We can't assume abstract types aren't injectable. They may have an
+        // @ImplementedBy annotation or something.
+        if (key.hasAnnotationType() || !(type instanceof Class<?>)) {
+          addError(source, ErrorMessages.MISSING_IMPLEMENTATION);
+          createBinding(source, shouldPreload, invalidBinding(injector, key, source));
+          return null;
+        }
+
+        // This cast is safe after the preceeding check.
+        @SuppressWarnings("unchecked")
+        Class<T> clazz = (Class<T>) type;
+
+        BindingImpl<T> binding = injector.createBindingFromType(clazz, scope, source);
+        // TODO: Should we clean up the binding left behind in jitBindings?
+
+        if (binding == null) {
+          addError(source, ErrorMessages.CANNOT_INJECT_ABSTRACT_TYPE, clazz);
+          createBinding(source, shouldPreload, invalidBinding(injector, key, source));
+          return null;
+        }
+
+        createBinding(source, shouldPreload, binding);
+        return null;
+      }
+    });
+
+    return true;
+  }
+
+  private <T> void validateKey(Object source, Key<T> key) {
+    if (key.hasAnnotationType()) {
+      Class<? extends Annotation> annotationType = key.getAnnotationType();
+
+      if (!Annotations.isRetainedAtRuntime(annotationType)) {
+        addError(StackTraceElements.forType(annotationType),
+            ErrorMessages.MISSING_RUNTIME_RETENTION, source);
+      }
+
+      if (!Key.isBindingAnnotation(annotationType)) {
+        addError(StackTraceElements.forType(annotationType),
+            ErrorMessages.MISSING_BINDING_ANNOTATION, source);
+      }
+    }
+  }
+
+  <T> InvalidBindingImpl<T> invalidBinding(InjectorImpl injector, Key<T> key, Object source) {
+    return new InvalidBindingImpl<T>(injector, key, source);
+  }
+
+  void registerInstanceForInjection(final Object o) {
+    membersInjectors.add(new MembersInjector(o));
+  }
+
+  public void validate(InjectorImpl injector) {
+    for (MembersInjector membersInjector : membersInjectors) {
+      membersInjector.checkDependencies(injector);
+    }
+  }
+
+  public void injectMembers(InjectorImpl injector) {
+    for (MembersInjector membersInjector : membersInjectors) {
+      membersInjector.injectMembers(injector);
+    }
+  }
+
+  private static class MembersInjector {
+    final Object o;
+
+    MembersInjector(Object o) {
+      this.o = o;
+    }
+
+    void checkDependencies(InjectorImpl injector) {
+      injector.injectors.get(o.getClass());
+    }
+
+    void injectMembers(InjectorImpl injector) {
+      injector.injectMembers(o);
+    }
+  }
+
+  @Override public Boolean visitBindConstant(BindConstantCommand command) {
+    Object value = command.getTarget().get();
+    if (value == null) {
+      addError(command.getSource(), ErrorMessages.MISSING_CONSTANT_VALUE);
+    }
+
+    validateKey(command.getSource(), command.getKey());
+    ConstantFactory<Object> factory = new ConstantFactory<Object>(value);
+    putBinding(new ContantBindingImpl<Object>(
+        injector, command.getKey(), command.getSource(), factory, value));
+
+    return true;
+  }
+
+  private <T> void createBinding(Object source, boolean shouldPreload,
+      BindingImpl<T> binding) {
+    putBinding(binding);
+
+    // Register to preload if necessary.
+    if (binding.getScope() == Scopes.SINGLETON) {
+      if (stage == Stage.PRODUCTION || shouldPreload) {
+        preloaders.add(new BindingPreloader(binding.key, binding.internalFactory));
+      }
+    } else {
+      if (shouldPreload) {
+        addError(source, ErrorMessages.PRELOAD_NOT_ALLOWED);
+      }
+    }
+  }
+
+  public void runPreloaders(InjectorImpl injector) {
+    injector.callInContext(new ContextualCallable<Void>() {
+      public Void call(InternalContext context) {
+        for (ContextualCallable<Void> preloader : preloaders) {
+          preloader.call(context);
+        }
+        return null;
+      }
+    });
+  }
+
+  public void runCreationListeners(InjectorImpl injector) {
+    for (CreationListener creationListener : creationListeners) {
+      creationListener.notify(injector);
+    }
+  }
+
+  private static class BindingPreloader implements ContextualCallable<Void> {
+    private final Key<?> key;
+    private final InternalFactory<?> factory;
+
+    public BindingPreloader(Key<?> key, InternalFactory<?> factory) {
+      this.key = key;
+      this.factory = Objects.nonNull(factory, "factory");
+    }
+
+    public Void call(InternalContext context) {
+      InjectionPoint<?> injectionPoint
+          = InjectionPoint.newInstance(key, context.getInjectorImpl());
+      context.setInjectionPoint(injectionPoint);
+      try {
+        factory.get(context, injectionPoint);
+        return null;
+      } catch(ProvisionException provisionException) {
+        provisionException.addContext(injectionPoint);
+        throw provisionException;
+      } finally {
+        context.setInjectionPoint(null);
+      }
+    }
+  }
+
+  private void putBinding(BindingImpl<?> binding) {
+    Key<?> key = binding.getKey();
+    Binding<?> original = bindings.get(key);
+
+    Class<?> rawType = key.getRawType();
+    if (FORBIDDEN_TYPES.contains(rawType)) {
+      addError(binding.getSource(), ErrorMessages.CANNOT_BIND_TO_GUICE_TYPE,
+          rawType.getSimpleName());
+      return;
+    }
+
+    if (bindings.containsKey(key)) {
+      addError(binding.getSource(), ErrorMessages.BINDING_ALREADY_SET, key,
+          original.getSource());
+    } else {
+      bindings.put(key, binding);
+    }
+  }
+
+  private static Set<Class<?>> FORBIDDEN_TYPES = forbiddenTypes();
+
+  @SuppressWarnings("unchecked") // For generic array creation.
+  private static Set<Class<?>> forbiddenTypes() {
+    Set<Class<?>> set = new HashSet<Class<?>>();
+
+    Collections.addAll(set,
+
+        // It's unfortunate that we have to maintain a blacklist of specific
+        // classes, but we can't easily block the whole package because of
+        // all our unit tests.
+
+        AbstractModule.class,
+        Binder.class,
+        Binding.class,
+        Key.class,
+        Module.class,
+        Provider.class,
+        Scope.class,
+        TypeLiteral.class);
+    return Collections.unmodifiableSet(set);
+  }
+
+  interface CreationListener {
+    void notify(InjectorImpl injector);
+  }
+}
diff --git a/src/com/google/inject/BindInterceptorCommandProcessor.java b/src/com/google/inject/BindInterceptorCommandProcessor.java
new file mode 100644
index 0000000..6ae838a
--- /dev/null
+++ b/src/com/google/inject/BindInterceptorCommandProcessor.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject;
+
+import com.google.inject.commands.BindInterceptorCommand;
+
+/**
+ * Handles {@link Binder#bindInterceptor} commands.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+class BindInterceptorCommandProcessor extends CommandProcessor {
+
+  private final ProxyFactoryBuilder proxyFactoryBuilder = new ProxyFactoryBuilder();
+
+  @Override public Boolean visitBindInterceptor(BindInterceptorCommand command) {
+    proxyFactoryBuilder.intercept(
+        command.getClassMatcher(), command.getMethodMatcher(), command.getInterceptors());
+    return true;
+  }
+
+  ProxyFactory createProxyFactory() {
+    return proxyFactoryBuilder.create();
+  }
+}
diff --git a/src/com/google/inject/BindingBuilderImpl.java b/src/com/google/inject/BindingBuilderImpl.java
deleted file mode 100644
index 451f943..0000000
--- a/src/com/google/inject/BindingBuilderImpl.java
+++ /dev/null
@@ -1,398 +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.BinderImpl.CreationListener;
-import com.google.inject.BinderImpl.MembersInjector;
-import com.google.inject.binder.AnnotatedBindingBuilder;
-import com.google.inject.binder.ScopedBindingBuilder;
-import com.google.inject.internal.Annotations;
-import com.google.inject.internal.Objects;
-import com.google.inject.internal.StackTraceElements;
-import com.google.inject.internal.ToStringBuilder;
-import com.google.inject.spi.BindingVisitor;
-import com.google.inject.spi.Dependency;
-import com.google.inject.spi.InstanceBinding;
-import com.google.inject.util.Providers;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
-import java.util.logging.Logger;
-import java.util.Collection;
-
-/**
- * Binds a {@link com.google.inject.Key} to an implementation in a given scope.
- */
-class BindingBuilderImpl<T> implements AnnotatedBindingBuilder<T> {
-
-  private static final Logger logger
-      = Logger.getLogger(BindingBuilderImpl.class.getName());
-
-  final Object source;
-  Key<T> key;
-  InternalFactory<? extends T> factory;
-  Scope scope;
-  boolean preload = false;
-  private BinderImpl binder;
-
-  // These fields keep track of the raw implementation for later use in the
-  // Binding API.
-  T instance;
-  Key<? extends T> targetKey;
-  Provider<? extends T> providerInstance;
-  Key<? extends Provider<? extends T>> providerKey;
-
-  BindingBuilderImpl(BinderImpl binder, Key<T> key, Object source) {
-    this.binder = binder;
-    this.key = Objects.nonNull(key, "key");
-    this.source = source;
-  }
-
-  Object getSource() {
-    return source;
-  }
-
-  Key<T> getKey() {
-    return key;
-  }
-
-  public BindingBuilderImpl<T> annotatedWith(
-      Class<? extends Annotation> annotationType) {
-    if (this.key.hasAnnotationType()) {
-      binder.addError(source, ErrorMessages.ANNOTATION_ALREADY_SPECIFIED);
-    } else {
-      boolean retainedAtRuntime =
-          Annotations.isRetainedAtRuntime(annotationType);
-      boolean bindingAnnotation = Key.isBindingAnnotation(annotationType);
-
-      if (!retainedAtRuntime) {
-        binder.addError(StackTraceElements.forType(annotationType),
-            ErrorMessages.MISSING_RUNTIME_RETENTION, binder.source());
-      }
-
-      if (!bindingAnnotation) {
-        binder.addError(StackTraceElements.forType(annotationType),
-            ErrorMessages.MISSING_BINDING_ANNOTATION, binder.source());
-      }
-
-      if (retainedAtRuntime && bindingAnnotation) {
-        this.key = Key.get(this.key.getTypeLiteral(), annotationType);
-      }
-    }
-    return this;
-  }
-
-  public BindingBuilderImpl<T> annotatedWith(Annotation annotation) {
-    if (this.key.hasAnnotationType()) {
-      binder.addError(source, ErrorMessages.ANNOTATION_ALREADY_SPECIFIED);
-    } else {
-      Class<? extends Annotation> annotationType = annotation.annotationType();
-
-      boolean retainedAtRuntime =
-          Annotations.isRetainedAtRuntime(annotationType);
-      boolean bindingAnnotation = Key.isBindingAnnotation(annotationType);
-
-      if (!retainedAtRuntime) {
-        binder.addError(StackTraceElements.forType(annotationType),
-            ErrorMessages.MISSING_RUNTIME_RETENTION, binder.source());
-      }
-
-      if (!bindingAnnotation) {
-        binder.addError(StackTraceElements.forType(annotationType),
-            ErrorMessages.MISSING_BINDING_ANNOTATION, binder.source());
-      }
-
-      if (retainedAtRuntime && bindingAnnotation) {
-        this.key = Key.get(this.key.getTypeLiteral(), annotation);
-      }
-    }
-    return this;
-  }
-
-  public ScopedBindingBuilder to(Class<? extends T> implementation) {
-    return to(TypeLiteral.get(implementation));
-  }
-
-  public ScopedBindingBuilder to(TypeLiteral<? extends T> implementation) {
-    return to(Key.get(implementation));
-  }
-
-  public ScopedBindingBuilder to(Key<? extends T> targetKey) {
-    ensureImplementationIsNotSet();
-
-    if (key.equals(targetKey)) {
-      binder.addError(source, ErrorMessages.RECURSIVE_BINDING);
-    }
-
-    final FactoryProxy<? extends T> factoryProxy =
-        new FactoryProxy<T>(key, targetKey, source);
-    this.factory = factoryProxy;
-    binder.creationListeners.add(factoryProxy);
-    this.targetKey = targetKey;
-    return this;
-  }
-
-  public void toInstance(T instance) {
-    ensureImplementationIsNotSet();
-    this.instance = instance;
-    this.factory = new ConstantFactory<T>(instance);
-    registerInstanceForInjection(instance);
-
-    // TODO: I don't think this can happen anymore.
-    if (this.scope != null) {
-      binder.addError(source, ErrorMessages.SINGLE_INSTANCE_AND_SCOPE);
-    }
-  }
-
-  public ScopedBindingBuilder toProvider(Provider<? extends T> provider) {
-    ensureImplementationIsNotSet();
-    this.factory = new InternalFactoryToProviderAdapter<T>(provider, source);
-    this.providerInstance = provider;
-    registerInstanceForInjection(provider);
-    return this;
-  }
-
-  public BindingBuilderImpl<T> toInternalFactory(InternalFactory<T> internalFactory) {
-    ensureImplementationIsNotSet();
-    this.factory = internalFactory;
-    this.providerInstance = new Provider<T>() {
-      public T get() {
-        throw new UnsupportedOperationException("?");
-      }
-    };
-    return this;
-  }
-
-  public BindingBuilderImpl<T> toProvider(
-      Class<? extends Provider<? extends T>> providerType) {
-    return toProvider(Key.get(providerType));
-  }
-
-  public BindingBuilderImpl<T> toProvider(
-      Key<? extends Provider<? extends T>> providerKey) {
-    ensureImplementationIsNotSet();
-
-    final BoundProviderFactory<T> boundProviderFactory =
-        new BoundProviderFactory<T>(providerKey, source);
-    binder.creationListeners.add(boundProviderFactory);
-    this.factory = boundProviderFactory;
-    this.providerKey = providerKey;
-
-    return this;
-  }
-
-  /**
-   * Adds an error message if the implementation has already been bound.
-   */
-  private void ensureImplementationIsNotSet() {
-    if (factory != null) {
-      binder.addError(source, ErrorMessages.IMPLEMENTATION_ALREADY_SET);
-    }
-  }
-
-  public void in(Class<? extends Annotation> scopeAnnotation) {
-    // this method not test-covered
-    ensureScopeNotSet();
-
-    // We could defer this lookup to when we create the Injector, but this
-    // is fine for now.
-    this.scope = binder.scopes.get(
-        Objects.nonNull(scopeAnnotation, "scope annotation"));
-    if (this.scope == null) {
-      binder.addError(source, ErrorMessages.SCOPE_NOT_FOUND,
-          "@" + scopeAnnotation.getSimpleName());
-    }
-  }
-
-  public void in(Scope scope) {
-    ensureScopeNotSet();
-    this.scope = Objects.nonNull(scope, "scope");
-  }
-
-  private void ensureScopeNotSet() {
-    // Scoping isn't allowed when we have only one instance.
-    if (this.instance != null) {
-      binder.addError(source, ErrorMessages.SINGLE_INSTANCE_AND_SCOPE);
-      return;
-    }
-
-    if (this.scope != null) {
-      binder.addError(source, ErrorMessages.SCOPE_ALREADY_SET);
-    }
-  }
-
-  public void asEagerSingleton() {
-    in(Scopes.SINGLETON);
-    this.preload = true;
-  }
-
-  boolean shouldPreload() {
-    return preload;
-  }
-
-  BindingImpl<T> build(InjectorImpl injector) {
-    if (this.factory != null) {
-      Scope scope = this.scope == null ? Scopes.NO_SCOPE : this.scope;
-
-      InternalFactory<? extends T> scopedFactory
-          = Scopes.scope(this.key, injector, this.factory, scope);
-
-      // Instance binding.
-      if (instance != null) {
-        return new InstanceBindingImpl<T>(
-            injector, key, source, scopedFactory, instance);
-      }
-
-      // Linked binding.
-      if (this.targetKey != null) {
-        return new LinkedBindingImpl<T>(
-            injector, key, source, scopedFactory, scope, targetKey);
-      }
-
-      // Provider instance binding.
-      if (this.providerInstance != null) {
-        return new ProviderInstanceBindingImpl<T>(
-            injector, key, source, scopedFactory, scope, providerInstance);
-      }
-
-      // Provider binding.
-      if (this.providerKey != null) {
-        return new LinkedProviderBindingImpl<T>(
-            injector, key, source, scopedFactory, scope, providerKey);
-      }
-
-      throw new AssertionError();
-    } else {
-      // If we're here, the type we bound to is also the implementation.
-      // Example: bind(FooImpl.class).in(Scopes.SINGLETON);
-
-      Type type = key.getTypeLiteral().getType();
-
-      // Error: Missing implementation.
-      // Example: bind(Date.class).annotatedWith(Red.class);
-      // We can't assume abstract types aren't injectable. They may have an
-      // @ImplementedBy annotation or something.
-      if (key.hasAnnotationType() || !(type instanceof Class<?>)) {
-        injector.errorHandler.handle(source,
-            ErrorMessages.MISSING_IMPLEMENTATION);
-        return invalidBinding(injector);
-      }
-
-      // This cast is safe after the preceeding check.
-      @SuppressWarnings("unchecked")
-      Class<T> clazz = (Class<T>) type;
-
-      BindingImpl<T> binding = injector.createBindingFromType(
-          clazz, scope, source);
-      // TODO: Should we clean up the binding left behind in jitBindings? 
-
-      if (binding == null) {
-        injector.errorHandler.handle(source,
-            ErrorMessages.CANNOT_INJECT_ABSTRACT_TYPE, clazz);
-        return invalidBinding(injector);
-      }
-
-      return binding;
-    }
-  }
-
-  InvalidBindingImpl<T> invalidBinding(InjectorImpl injector) {
-    return new InvalidBindingImpl<T>(injector, key, source);
-  }
-
-  private static class InstanceBindingImpl<T> extends BindingImpl<T>
-      implements InstanceBinding<T> {
-
-    final T instance;
-    final Provider<T> provider;
-
-    InstanceBindingImpl(InjectorImpl injector, Key<T> key, Object source,
-        InternalFactory<? extends T> internalFactory, T instance) {
-      super(injector, key, source, internalFactory, Scopes.NO_SCOPE);
-      this.instance = instance;
-      this.provider = Providers.of(instance);
-    }
-
-    @Override
-    public Provider<T> getProvider() {
-      return this.provider;
-    }
-
-    public void accept(BindingVisitor<? super T> bindingVisitor) {
-      bindingVisitor.visit(this);
-    }
-
-    public T getInstance() {
-      return this.instance;
-    }
-
-    public Collection<Dependency<?>> getDependencies() {
-      return injector.getFieldAndMethodDependenciesFor(instance.getClass());
-    }
-
-    @Override
-    public String toString() {
-      return new ToStringBuilder(InstanceBinding.class)
-          .add("key", key)
-          .add("instance", instance)
-          .add("source", source)
-          .toString();
-    }
-  }
-
-  void registerInstanceForInjection(final Object o) {
-    binder.membersInjectors.add(new MembersInjector(o));
-  }
-
-  /**
-   * A placeholder which enables us to swap in the real factory once the
-   * container is created.
-   */
-  private static class FactoryProxy<T> implements InternalFactory<T>,
-      CreationListener {
-
-    private final Key<T> key;
-    private final Key<? extends T> targetKey;
-    private final Object source;
-
-    InternalFactory<? extends T> targetFactory;
-
-    FactoryProxy(Key<T> key, Key<? extends T> targetKey, Object source) {
-      this.key = key;
-      this.targetKey = targetKey;
-      this.source = source;
-    }
-
-    public void notify(final InjectorImpl injector) {
-      injector.withDefaultSource(source, new Runnable() {
-        public void run() {
-          targetFactory = injector.getInternalFactory(targetKey);
-        }
-      });
-    }
-
-    public T get(InternalContext context, InjectionPoint<?> injectionPoint) {
-      return targetFactory.get(context, injectionPoint);
-    }
-
-    public String toString() {
-      return new ToStringBuilder(FactoryProxy.class)
-          .add("key", key)
-          .add("provider", targetFactory)
-          .toString();
-    }
-  }
-}
diff --git a/src/com/google/inject/BoundProviderFactory.java b/src/com/google/inject/BoundProviderFactory.java
index f0708e0..77fe909 100644
--- a/src/com/google/inject/BoundProviderFactory.java
+++ b/src/com/google/inject/BoundProviderFactory.java
@@ -16,7 +16,7 @@
 
 package com.google.inject;
 
-import com.google.inject.BinderImpl.CreationListener;
+import com.google.inject.BindCommandProcessor.CreationListener;
 
 /**
  * Delegates to a custom factory which is also bound in the injector.
diff --git a/src/com/google/inject/CommandProcessor.java b/src/com/google/inject/CommandProcessor.java
new file mode 100644
index 0000000..e3f4eb7
--- /dev/null
+++ b/src/com/google/inject/CommandProcessor.java
@@ -0,0 +1,94 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject;
+
+import com.google.inject.commands.*;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Abstract base class for executing commands to creating an injector.
+ *
+ * <p>Extending classes must return {@code true} from any overridden
+ * {@code visit*()} methods, in order for the command processor to remove the
+ * handled command.
+ *
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+abstract class CommandProcessor implements Command.Visitor<Boolean> {
+
+  private ErrorHandler errorHandler;
+
+  public void processCommands(List<Command> commands, ErrorHandler errorHandler) {
+    this.errorHandler = errorHandler;
+    try {
+      for (Iterator<Command> i = commands.iterator(); i.hasNext(); ) {
+        Boolean allDone = i.next().acceptVisitor(this);
+        if (allDone) {
+          i.remove();
+        }
+      }
+    } finally {
+      this.errorHandler = null;
+    }
+  }
+
+  protected void addError(Object source, String message, Object... arguments) {
+    errorHandler.handle(source, message, arguments);
+  }
+
+  protected void addError(Object source, String message) {
+    errorHandler.handle(source, message);
+  }
+
+  public Boolean visitAddMessageError(AddMessageErrorCommand command) {
+    return false;
+  }
+
+  public Boolean visitAddError(AddThrowableErrorCommand command) {
+    return false;
+  }
+
+  public Boolean visitBindInterceptor(BindInterceptorCommand command) {
+    return false;
+  }
+
+  public Boolean visitBindScope(BindScopeCommand command) {
+    return false;
+  }
+
+  public Boolean visitRequestStaticInjection(RequestStaticInjectionCommand command) {
+    return false;
+  }
+
+  public Boolean visitBindConstant(BindConstantCommand command) {
+    return false;
+  }
+
+  public Boolean visitConvertToTypes(ConvertToTypesCommand command) {
+    return false;
+  }
+
+  public <T> Boolean visitBind(BindCommand<T> command) {
+    return false;
+  }
+
+  public <T> Boolean visitGetProvider(GetProviderCommand<T> command) {
+    return false;
+  }
+}
diff --git a/src/com/google/inject/ConfigurationException.java b/src/com/google/inject/ConfigurationException.java
index ceac337..d73ebd5 100644
--- a/src/com/google/inject/ConfigurationException.java
+++ b/src/com/google/inject/ConfigurationException.java
@@ -17,7 +17,7 @@
 package com.google.inject;
 
 /**
- * Thrown when the {@link BinderImpl} is misconfigured.
+ * Thrown when the {@link InjectorBuilder} is misconfigured.
  *
  * @author crazybob@google.com (Bob Lee)
  */
diff --git a/src/com/google/inject/ConstantBindingBuilderImpl.java b/src/com/google/inject/ConstantBindingBuilderImpl.java
deleted file mode 100644
index 6ff1037..0000000
--- a/src/com/google/inject/ConstantBindingBuilderImpl.java
+++ /dev/null
@@ -1,204 +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.Key.AnnotationStrategy;
-import com.google.inject.binder.AnnotatedConstantBindingBuilder;
-import com.google.inject.binder.ConstantBindingBuilder;
-import com.google.inject.internal.Annotations;
-import com.google.inject.internal.Objects;
-import com.google.inject.internal.StackTraceElements;
-import com.google.inject.internal.ToStringBuilder;
-import com.google.inject.spi.BindingVisitor;
-import com.google.inject.spi.ConstantBinding;
-import com.google.inject.util.Providers;
-import java.lang.annotation.Annotation;
-
-/**
- * Builds a constant binding.
- */
-class ConstantBindingBuilderImpl implements AnnotatedConstantBindingBuilder,
-    ConstantBindingBuilder {
-
-  BindingInfo<?> bindingInfo;
-  AnnotationStrategy annotationStrategy;
-  final Object source;
-  private BinderImpl binder;
-
-  ConstantBindingBuilderImpl(BinderImpl binder, Object source) {
-    this.binder = binder;
-    this.source = source;
-  }
-
-  public ConstantBindingBuilder annotatedWith(
-      Class<? extends Annotation> annotationType) {
-    Objects.nonNull(annotationType, "annotation type");
-    validateAnnotation(annotationType);
-    annotationStrategy = Key.strategyFor(annotationType);
-    return this;
-  }
-
-  public ConstantBindingBuilder annotatedWith(Annotation annotation) {
-    Objects.nonNull(annotation, "annotation");
-    validateAnnotation(annotation.annotationType());
-    annotationStrategy = Key.strategyFor(annotation);
-    return this;
-  }
-
-  void validateAnnotation(Class<? extends Annotation> annotationType) {
-    if (annotationStrategy != null) {
-      binder.addError(source, ErrorMessages.ANNOTATION_ALREADY_SPECIFIED);
-      return;
-    }
-
-    boolean retainedAtRuntime =
-        Annotations.isRetainedAtRuntime(annotationType);
-    boolean bindingAnnotation = Key.isBindingAnnotation(annotationType);
-
-    if (!retainedAtRuntime) {
-      binder.addError(StackTraceElements.forType(annotationType),
-          ErrorMessages.MISSING_RUNTIME_RETENTION, source);
-    }
-
-    if (!bindingAnnotation) {
-      binder.addError(StackTraceElements.forType(annotationType),
-          ErrorMessages.MISSING_BINDING_ANNOTATION, source);
-    }
-  }
-
-  boolean hasValue() {
-    return bindingInfo != null;
-  }
-
-  Object getSource() {
-    return source;
-  }
-
-  public void to(String value) {
-    to(String.class, value);
-  }
-
-  public void to(int value) {
-    to(int.class, value);
-  }
-
-  public void to(long value) {
-    to(long.class, value);
-  }
-
-  public void to(boolean value) {
-    to(boolean.class, value);
-  }
-
-  public void to(double value) {
-    to(double.class, value);
-  }
-
-  public void to(float value) {
-    to(float.class, value);
-  }
-
-  public void to(short value) {
-    to(short.class, value);
-  }
-
-  public void to(char value) {
-    to(char.class, value);
-  }
-
-  public void to(Class<?> value) {
-    to(Class.class, value);
-  }
-
-  public <E extends Enum<E>> void to(E value) {
-    to(value.getDeclaringClass(), value);
-  }
-
-  /**
-   * Maps a constant value to the given type and name.
-   */
-  <T> void to(final Class<T> type, final T value) {
-    if (this.bindingInfo != null) {
-      binder.addError(source, ErrorMessages.CONSTANT_VALUE_ALREADY_SET);
-    } else {
-      this.bindingInfo
-          = new BindingInfo<T>(type, value, annotationStrategy, source);
-    }
-  }
-
-  BindingImpl<?> createBinding(InjectorImpl injector) {
-    return bindingInfo.createBinding(injector);
-  }
-
-  private static class BindingInfo<T> {
-
-    final Class<T> type;
-    final T value;
-    final AnnotationStrategy annotationStrategy;
-    final Object source;
-
-    BindingInfo(Class<T> type, T value,
-        AnnotationStrategy annotationStrategy, Object source) {
-      this.type = type;
-      this.value = value;
-      this.annotationStrategy = annotationStrategy;
-      this.source = source;
-    }
-
-    BindingImpl<T> createBinding(InjectorImpl injector) {
-      Key<T> key = Key.get(type, annotationStrategy);
-        ConstantFactory<T> factory = new ConstantFactory<T>(value);
-      return new ContantBindingImpl<T>(injector, key, source, factory, value);
-    }
-  }
-
-  private static class ContantBindingImpl<T> extends BindingImpl<T>
-      implements ConstantBinding<T> {
-
-    final T value;
-    final Provider<T> provider;
-
-    ContantBindingImpl(InjectorImpl injector, Key<T> key, Object source,
-        InternalFactory<T> internalFactory, T value) {
-      super(injector, key, source, internalFactory, Scopes.NO_SCOPE);
-      this.value = value;
-      this.provider = Providers.of(value);
-    }
-
-    @Override
-    public Provider<T> getProvider() {
-      return this.provider;
-    }
-
-    public void accept(BindingVisitor<? super T> bindingVisitor) {
-      bindingVisitor.visit(this);
-    }
-
-    public T getValue() {
-      return this.value;
-    }
-
-    @Override
-    public String toString() {
-      return new ToStringBuilder(ConstantBinding.class)
-          .add("key", key)
-          .add("value", value)
-          .add("source", source)
-          .toString();
-    }
-  }
-}
diff --git a/src/com/google/inject/ContantBindingImpl.java b/src/com/google/inject/ContantBindingImpl.java
new file mode 100644
index 0000000..6157a06
--- /dev/null
+++ b/src/com/google/inject/ContantBindingImpl.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.google.inject;
+
+import com.google.inject.spi.ConstantBinding;
+import com.google.inject.spi.BindingVisitor;
+import com.google.inject.util.Providers;
+import com.google.inject.internal.ToStringBuilder;
+
+/**
+ * A constant binding.
+ */
+class ContantBindingImpl<T> extends BindingImpl<T>
+    implements ConstantBinding<T> {
+
+  final T value;
+  final Provider<T> provider;
+
+  ContantBindingImpl(InjectorImpl injector, Key<T> key, Object source,
+      InternalFactory<T> internalFactory, T value) {
+    super(injector, key, source, internalFactory, Scopes.NO_SCOPE);
+    this.value = value;
+    this.provider = Providers.of(value);
+  }
+
+  @Override
+  public Provider<T> getProvider() {
+    return this.provider;
+  }
+
+  public void accept(BindingVisitor<? super T> bindingVisitor) {
+    bindingVisitor.visit(this);
+  }
+
+  public T getValue() {
+    return this.value;
+  }
+
+  @Override
+  public String toString() {
+    return new ToStringBuilder(ConstantBinding.class)
+        .add("key", key)
+        .add("value", value)
+        .add("source", source)
+        .toString();
+  }
+}
diff --git a/src/com/google/inject/ConvertToTypesCommandProcessor.java b/src/com/google/inject/ConvertToTypesCommandProcessor.java
new file mode 100644
index 0000000..af9e592
--- /dev/null
+++ b/src/com/google/inject/ConvertToTypesCommandProcessor.java
@@ -0,0 +1,177 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.google.inject;
+
+import com.google.inject.commands.ConvertToTypesCommand;
+import com.google.inject.internal.Strings;
+import com.google.inject.matcher.AbstractMatcher;
+import com.google.inject.matcher.Matcher;
+import com.google.inject.matcher.Matchers;
+import com.google.inject.spi.SourceProviders;
+import com.google.inject.spi.TypeConverter;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.List;
+
+/**
+ * Handles {@link Binder#convertToTypes} commands.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+class ConvertToTypesCommandProcessor extends CommandProcessor {
+
+  private final List<MatcherAndConverter<?>> converters;
+
+  ConvertToTypesCommandProcessor(List<MatcherAndConverter<?>> converters) {
+    this.converters = converters;
+
+    // Configure type converters.
+    convertToPrimitiveType(int.class, Integer.class);
+    convertToPrimitiveType(long.class, Long.class);
+    convertToPrimitiveType(boolean.class, Boolean.class);
+    convertToPrimitiveType(byte.class, Byte.class);
+    convertToPrimitiveType(short.class, Short.class);
+    convertToPrimitiveType(float.class, Float.class);
+    convertToPrimitiveType(double.class, Double.class);
+
+    TypeConverter characterConverter = new TypeConverter() {
+      public Object convert(String value, TypeLiteral<?> toType) {
+        value = value.trim();
+        if (value.length() != 1) {
+          throw new RuntimeException("Length != 1.");
+        }
+        return value.charAt(0);
+      }
+
+      @Override public String toString() {
+        return "TypeConverter<Character>";
+      }
+    };
+
+    convertToClass(char.class, characterConverter);
+    convertToClass(Character.class, characterConverter);
+
+    convertToClasses(Matchers.subclassesOf(Enum.class), new TypeConverter() {
+      @SuppressWarnings("unchecked")
+      public Object convert(String value, TypeLiteral<?> toType) {
+        return Enum.valueOf((Class) toType.getRawType(), value);
+      }
+
+      @Override public String toString() {
+        return "TypeConverter<E extends Enum<E>>";
+      }
+    });
+
+    internalConvertToTypes(
+      new AbstractMatcher<TypeLiteral<?>>() {
+        public boolean matches(TypeLiteral<?> typeLiteral) {
+          return typeLiteral.getRawType() == Class.class;
+        }
+
+        @Override public String toString() {
+          return "Class<?>";
+        }
+      },
+      new TypeConverter() {
+        @SuppressWarnings("unchecked")
+        public Object convert(String value, TypeLiteral<?> toType) {
+          try {
+            return Class.forName(value);
+          }
+          catch (ClassNotFoundException e) {
+            throw new RuntimeException(e.getMessage());
+          }
+        }
+
+        @Override public String toString() {
+          return "TypeConverter<Class<?>>";
+        }
+      }
+    );
+  }
+
+  private <T> void convertToPrimitiveType(Class<T> primitiveType,
+      final Class<T> wrapperType) {
+    try {
+      final Method parser = wrapperType.getMethod(
+        "parse" + Strings.capitalize(primitiveType.getName()), String.class);
+
+      TypeConverter typeConverter = new TypeConverter() {
+        @SuppressWarnings("unchecked")
+        public Object convert(String value, TypeLiteral<?> toType) {
+          try {
+            return parser.invoke(null, value);
+          }
+          catch (IllegalAccessException e) {
+            throw new AssertionError(e);
+          }
+          catch (InvocationTargetException e) {
+            throw new RuntimeException(e.getTargetException().getMessage());
+          }
+        }
+
+        @Override public String toString() {
+          return "TypeConverter<" + wrapperType.getSimpleName() + ">";
+        }
+      };
+
+      convertToClass(primitiveType, typeConverter);
+      convertToClass(wrapperType, typeConverter);
+    }
+    catch (NoSuchMethodException e) {
+      throw new AssertionError(e);
+    }
+  }
+
+  private <T> void convertToClass(Class<T> type, TypeConverter converter) {
+    convertToClasses(Matchers.identicalTo(type), converter);
+  }
+
+  private void convertToClasses(final Matcher<? super Class<?>> typeMatcher,
+      TypeConverter converter) {
+    internalConvertToTypes(new AbstractMatcher<TypeLiteral<?>>() {
+      public boolean matches(TypeLiteral<?> typeLiteral) {
+        Type type = typeLiteral.getType();
+        if (!(type instanceof Class)) {
+          return false;
+        }
+        Class<?> clazz = (Class<?>) type;
+        return typeMatcher.matches(clazz);
+      }
+
+      public String toString() {
+        return typeMatcher.toString();
+      }
+    }, converter);
+  }
+
+  private void internalConvertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
+      TypeConverter converter) {
+    converters.add(MatcherAndConverter.newInstance(typeMatcher, converter,
+        SourceProviders.UNKNOWN_SOURCE));
+  }
+
+  @Override public Boolean visitConvertToTypes(ConvertToTypesCommand command) {
+    converters.add(MatcherAndConverter.newInstance(
+        command.getTypeMatcher(), command.getTypeConverter()));
+    return true;
+  }
+}
diff --git a/src/com/google/inject/ErrorMessages.java b/src/com/google/inject/ErrorMessages.java
index 47cddf4..321d2c8 100644
--- a/src/com/google/inject/ErrorMessages.java
+++ b/src/com/google/inject/ErrorMessages.java
@@ -123,9 +123,6 @@
 
   static final String SCOPE_NOT_FOUND = "No scope is bound to %s.";
 
-  static final String SINGLE_INSTANCE_AND_SCOPE = "Setting the scope is not"
-      + " permitted when binding to a single instance.";
-
   static final String CONSTRUCTOR_RULES = "Classes must have either one (and"
       + " only one) constructor annotated with @Inject or a zero-argument"
       + " constructor.";
@@ -149,14 +146,6 @@
       + " classes is not supported.  Please use a 'static' class (top-level or"
       + " nested) instead.";
 
-  static final String ANNOTATION_ALREADY_SPECIFIED = "More than one annotation"
-      + " is specified for this binding.";
-
-  static final String IMPLEMENTATION_ALREADY_SET = "Implementation is set more"
-      + " than once.";
-
-  static final String SCOPE_ALREADY_SET = "Scope is set more than once.";
-
   static final String DUPLICATE_BINDING_ANNOTATIONS =
       "Found more than one annotation annotated with @BindingAnnotation:"
           + " %s and %s";
@@ -164,9 +153,6 @@
   static final String DUPLICATE_SCOPE_ANNOTATIONS = "More than one scope"
       + " annotation was found: %s and %s";
 
-  static final String CONSTANT_VALUE_ALREADY_SET = "Constant value is set more"
-      + " than once.";
-
   static final String RECURSIVE_BINDING = "Binding points to itself.";
 
   static final String BINDING_ALREADY_SET = "A binding to %s was already"
diff --git a/src/com/google/inject/ErrorsCommandProcessor.java b/src/com/google/inject/ErrorsCommandProcessor.java
new file mode 100644
index 0000000..c83c59b
--- /dev/null
+++ b/src/com/google/inject/ErrorsCommandProcessor.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject;
+
+import com.google.inject.commands.AddMessageErrorCommand;
+import com.google.inject.commands.AddThrowableErrorCommand;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Handles {@link Binder#addError} commands.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+class ErrorsCommandProcessor extends CommandProcessor {
+
+  private static final Logger logger
+      = Logger.getLogger(ErrorsCommandProcessor.class.getName());
+
+  @Override public Boolean visitAddMessageError(AddMessageErrorCommand command) {
+    addError(command.getSource(), command.getMessage(), command.getArguments());
+    return true;
+  }
+
+  @Override public Boolean visitAddError(AddThrowableErrorCommand command) {
+    Object source = command.getSource();
+    String message = ErrorMessages.getRootMessage(command.getThrowable());
+    String logMessage = String.format(
+        ErrorMessages.EXCEPTION_REPORTED_BY_MODULE, message);
+    logger.log(Level.INFO, logMessage, command.getThrowable());
+    addError(source, ErrorMessages.EXCEPTION_REPORTED_BY_MODULE_SEE_LOG, message);
+    return true;
+  }
+}
diff --git a/src/com/google/inject/FactoryProxy.java b/src/com/google/inject/FactoryProxy.java
new file mode 100644
index 0000000..f6767b1
--- /dev/null
+++ b/src/com/google/inject/FactoryProxy.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.google.inject;
+
+import com.google.inject.internal.ToStringBuilder;
+
+/**
+ * A placeholder which enables us to swap in the real factory once the
+ * container is created.
+ */
+class FactoryProxy<T> implements InternalFactory<T>,
+    BindCommandProcessor.CreationListener {
+
+  private final Key<T> key;
+  private final Key<? extends T> targetKey;
+  private final Object source;
+
+  InternalFactory<? extends T> targetFactory;
+
+  FactoryProxy(Key<T> key, Key<? extends T> targetKey, Object source) {
+    this.key = key;
+    this.targetKey = targetKey;
+    this.source = source;
+  }
+
+  public void notify(final InjectorImpl injector) {
+    injector.withDefaultSource(source, new Runnable() {
+      public void run() {
+        targetFactory = injector.getInternalFactory(targetKey);
+      }
+    });
+  }
+
+  public T get(InternalContext context, InjectionPoint<?> injectionPoint) {
+    return targetFactory.get(context, injectionPoint);
+  }
+
+  public String toString() {
+    return new ToStringBuilder(FactoryProxy.class)
+        .add("key", key)
+        .add("provider", targetFactory)
+        .toString();
+  }
+}
diff --git a/src/com/google/inject/GetProviderProcessor.java b/src/com/google/inject/GetProviderProcessor.java
new file mode 100644
index 0000000..73a9a6d
--- /dev/null
+++ b/src/com/google/inject/GetProviderProcessor.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject;
+
+import com.google.inject.commands.GetProviderCommand;
+
+/**
+ * Handles {@link Binder#getProvider} commands.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+class GetProviderProcessor extends CommandProcessor {
+
+  private final InjectorImpl injector;
+
+  GetProviderProcessor(InjectorImpl injector) {
+    this.injector = injector;
+  }
+
+  @Override public <T> Boolean visitGetProvider(GetProviderCommand<T> command) {
+    try {
+      // ensure the provider can be created
+      injector.getProvider(command.getKey());
+    } catch (ConfigurationException e) {
+      ErrorMessages.handleMissingBinding(injector, command.getSource(), command.getKey());
+    }
+
+    return true;
+  }
+}
diff --git a/src/com/google/inject/Guice.java b/src/com/google/inject/Guice.java
index 900fea5..7c23e72 100644
--- a/src/com/google/inject/Guice.java
+++ b/src/com/google/inject/Guice.java
@@ -128,11 +128,11 @@
   public static Injector createInjector(
       Injector parent, Stage stage,
       Iterable<? extends Module> modules) {
-    BinderImpl binder = new BinderImpl(stage);
-    for (Module module : modules) {
-      binder.install(module);
-    }
-    return binder.createInjector(parent);
+    return new InjectorBuilder()
+        .stage(stage)
+        .parentInjector(parent)
+        .addModules(modules)
+        .build();
   }
 
 }
diff --git a/src/com/google/inject/InjectorBuilder.java b/src/com/google/inject/InjectorBuilder.java
new file mode 100644
index 0000000..cf02d38
--- /dev/null
+++ b/src/com/google/inject/InjectorBuilder.java
@@ -0,0 +1,271 @@
+/**
+ * Copyright (C) 2006 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 static com.google.inject.Scopes.SINGLETON;
+import com.google.inject.commands.Command;
+import com.google.inject.commands.CommandRecorder;
+import com.google.inject.commands.FutureInjector;
+import com.google.inject.internal.Stopwatch;
+import com.google.inject.spi.Message;
+import com.google.inject.spi.SourceProviders;
+
+import java.lang.reflect.Member;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.logging.Logger;
+
+/**
+ * Builds a dependency injection {@link Injector}.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+class InjectorBuilder {
+
+  private final Stopwatch stopwatch = new Stopwatch();
+
+  private Injector parent;
+  private Stage stage;
+  private final List<Module> modules = new LinkedList<Module>();
+
+  private final ConfigurationErrorHandler configurationErrorHandler
+      = new ConfigurationErrorHandler();
+
+  private InjectorImpl injector;
+
+  private final FutureInjector futureInjector = new FutureInjector();
+  private final List<Command> commands = new ArrayList<Command>();
+
+  private BindCommandProcessor bindCommandProcesor;
+  private RequestStaticInjectionCommandProcessor requestStaticInjectionCommandProcessor;
+
+  /**
+   * @param stage we're running in. If the stage is {@link Stage#PRODUCTION},
+   *  we will eagerly load singletons.
+   */
+  InjectorBuilder stage(Stage stage) {
+    this.stage = stage;
+    return this;
+  }
+
+  InjectorBuilder parentInjector(Injector parent) {
+    this.parent = parent;
+    return this;
+  }
+
+  InjectorBuilder addModules(Iterable<? extends Module> modules) {
+    for (Module module : modules) {
+      this.modules.add(module);
+    }
+    return this;
+  }
+
+  Injector build() {
+    if (injector != null) {
+      throw new AssertionError("Already built, builders are not reusable.");
+    }
+
+    modules.add(0, new BuiltInModule(injector, stage));
+
+    CommandRecorder commandRecorder = new CommandRecorder(futureInjector);
+    commandRecorder.setCurrentStage(stage);
+    commands.addAll(commandRecorder.recordCommands(modules));
+
+    injector = new InjectorImpl(parent);
+    injector.setErrorHandler(configurationErrorHandler);
+
+    buildCoreInjector();
+
+    validate();
+
+    injector.setErrorHandler(RuntimeErrorHandler.INSTANCE);
+
+    // If we're in the tool stage, stop here. Don't eagerly inject or load
+    // anything.
+    if (stage == Stage.TOOL) {
+      // TODO: Wrap this and prevent usage of anything besides getBindings().
+      return injector;
+    }
+
+    fulfillInjectionRequests();
+
+    if (!commands.isEmpty()) {
+      throw new AssertionError("Failed to execute " + commands);
+    }
+
+    return injector;
+  }
+
+  /**
+   * Builds the injector.
+   */
+  private void buildCoreInjector() {
+    new ErrorsCommandProcessor()
+        .processCommands(commands, configurationErrorHandler);
+
+    BindInterceptorCommandProcessor bindInterceptorCommandProcessor
+        = new BindInterceptorCommandProcessor();
+    bindInterceptorCommandProcessor.processCommands(commands, configurationErrorHandler);
+    injector.constructionProxyFactory = bindInterceptorCommandProcessor.createProxyFactory();
+    stopwatch.resetAndLog("Interceptors creation");
+
+    new ScopesCommandProcessor(injector.scopes)
+        .processCommands(commands, configurationErrorHandler);
+    stopwatch.resetAndLog("Scopes creation");
+
+    new ConvertToTypesCommandProcessor(injector.converters)
+        .processCommands(commands, configurationErrorHandler);
+    stopwatch.resetAndLog("Converters creation");
+
+    bindCommandProcesor = new BindCommandProcessor(
+        injector, injector.scopes, stage, injector.explicitBindings);
+    bindCommandProcesor.processCommands(commands, configurationErrorHandler);
+    stopwatch.resetAndLog("Binding creation");
+
+    injector.index();
+    stopwatch.resetAndLog("Binding indexing");
+
+    requestStaticInjectionCommandProcessor = new RequestStaticInjectionCommandProcessor();
+    requestStaticInjectionCommandProcessor
+        .processCommands(commands, configurationErrorHandler);
+    stopwatch.resetAndLog("Static injection");
+  }
+
+  /**
+   * Validate everything that we can validate now that the injector is ready
+   * for use.
+   */
+  private void validate() {
+    bindCommandProcesor.runCreationListeners(injector);
+    stopwatch.resetAndLog("Validation");
+
+    requestStaticInjectionCommandProcessor.validate(injector);
+    stopwatch.resetAndLog("Static validation");
+
+    bindCommandProcesor.validate(injector);
+    stopwatch.resetAndLog("Instance member validation");
+
+    new GetProviderProcessor(injector)
+        .processCommands(commands, configurationErrorHandler);
+    stopwatch.resetAndLog("Provider verification");
+
+    configurationErrorHandler.blowUpIfErrorsExist();
+  }
+
+  /**
+   * Inject everything that can be injected. This uses runtime error handling.
+   */
+  private void fulfillInjectionRequests() {
+    futureInjector.initialize(injector);
+
+    // TODO(jessewilson): sort these injections by their dependencies
+    requestStaticInjectionCommandProcessor.injectMembers(injector);
+    stopwatch.resetAndLog("Static member injection");
+    bindCommandProcesor.injectMembers(injector);
+    stopwatch.resetAndLog("Instance injection");
+
+    bindCommandProcesor.runPreloaders(injector);
+    stopwatch.resetAndLog("Preloading");
+  }
+
+  private static class BuiltInModule extends AbstractModule {
+    final Injector injector;
+    final Stage stage;
+
+    private BuiltInModule(Injector injector, Stage stage) {
+      this.injector = injector;
+      this.stage = stage;
+    }
+
+    protected void configure() {
+      SourceProviders.withDefault(SourceProviders.UNKNOWN_SOURCE, new Runnable() {
+        public void run() {
+          // TODO(jessewilson): use a real logger
+          // bind(Logger.class).toInternalFactory(new LoggerFactory());
+          bind(Logger.class).toInstance(Logger.getLogger(""));
+          bind(Stage.class).toInstance(stage);
+          bindScope(Singleton.class, SINGLETON);
+          // Create default bindings.
+          // We use toProvider() instead of toInstance() to avoid infinite recursion
+          // in toString().
+          bind(Injector.class).toProvider(new InjectorProvider(injector));
+
+        }
+      });
+    }
+
+    class InjectorProvider implements Provider<Injector> {
+      final Injector injector;
+
+      InjectorProvider(Injector injector) {
+        this.injector = injector;
+      }
+
+      public Injector get() {
+        return injector;
+      }
+
+      public String toString() {
+        return "Provider<Injector>";
+      }
+    }
+
+    class LoggerFactory implements InternalFactory<Logger> {
+      public Logger get(InternalContext context, InjectionPoint<?> injectionPoint) {
+        Member member = injectionPoint.getMember();
+        return member == null
+            ? Logger.getAnonymousLogger()
+            : Logger.getLogger(member.getDeclaringClass().getName());
+      }
+
+      public String toString() {
+        return "Provider<Logger>";
+      }
+    }
+  }
+
+  /**
+   * Handles errors while the injector is being created.
+   */
+  private class ConfigurationErrorHandler extends AbstractErrorHandler {
+    final Collection<Message> errorMessages = new ArrayList<Message>();
+
+    public void handle(Object source, String message) {
+      errorMessages.add(new Message(source, message));
+    }
+
+    void blowUpIfErrorsExist() {
+      if (!errorMessages.isEmpty()) {
+        throw new CreationException(errorMessages);
+      }
+    }
+  }
+
+  /**
+   * Handles errors after the injector is created.
+   */
+  private static class RuntimeErrorHandler extends AbstractErrorHandler {
+    static ErrorHandler INSTANCE = new RuntimeErrorHandler();
+
+    public void handle(Object source, String message) {
+      throw new ConfigurationException("Error at " + source + " " + message);
+    }
+  }
+}
diff --git a/src/com/google/inject/InjectorImpl.java b/src/com/google/inject/InjectorImpl.java
index 72bf939..ed34caf 100644
--- a/src/com/google/inject/InjectorImpl.java
+++ b/src/com/google/inject/InjectorImpl.java
@@ -53,7 +53,7 @@
  * Default {@link Injector} implementation.
  *
  * @author crazybob@google.com (Bob Lee)
- * @see BinderImpl
+ * @see InjectorBuilder
  */
 class InjectorImpl implements Injector {
 
@@ -85,27 +85,22 @@
     PRIMITIVE_COUNTERPARTS = Collections.unmodifiableMap(counterparts);
   }
 
-  final ConstructionProxyFactory constructionProxyFactory;
-  final Map<Key<?>, BindingImpl<?>> explicitBindings;
-  final BindingsMultimap bindingsMultimap = new BindingsMultimap();
-  final Map<Class<? extends Annotation>, Scope> scopes;
-  final List<MatcherAndConverter<?>> converters;
   final Injector parentInjector;
+  ConstructionProxyFactory constructionProxyFactory;
+  final Map<Key<?>, BindingImpl<?>> explicitBindings
+      = new HashMap<Key<?>, BindingImpl<?>>();
+  final BindingsMultimap bindingsMultimap = new BindingsMultimap();
+  final Map<Class<? extends Annotation>, Scope> scopes
+      = new HashMap<Class<? extends Annotation>, Scope>();
+  final List<MatcherAndConverter<?>> converters
+      = new ArrayList<MatcherAndConverter<?>>();
   final Map<Key<?>, BindingImpl<?>> parentBindings
       = new HashMap<Key<?>, BindingImpl<?>>();
 
   ErrorHandler errorHandler = new InvalidErrorHandler();
 
-  InjectorImpl(Injector parentInjector,
-      ConstructionProxyFactory constructionProxyFactory,
-      Map<Key<?>, BindingImpl<?>> bindings,
-      Map<Class<? extends Annotation>, Scope> scopes,
-      List<MatcherAndConverter<?>> converters) {
+  InjectorImpl(Injector parentInjector) {
     this.parentInjector = parentInjector;
-    this.constructionProxyFactory = constructionProxyFactory;
-    this.explicitBindings = bindings;
-    this.scopes = scopes;
-    this.converters = converters;
   }
 
   /**
diff --git a/src/com/google/inject/InstanceBindingImpl.java b/src/com/google/inject/InstanceBindingImpl.java
new file mode 100644
index 0000000..f6ab05f
--- /dev/null
+++ b/src/com/google/inject/InstanceBindingImpl.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.google.inject;
+
+import com.google.inject.spi.InstanceBinding;
+import com.google.inject.spi.BindingVisitor;
+import com.google.inject.spi.Dependency;
+import com.google.inject.util.Providers;
+import com.google.inject.internal.ToStringBuilder;
+
+import java.util.Collection;
+
+class InstanceBindingImpl<T> extends BindingImpl<T>
+    implements InstanceBinding<T> {
+
+  final T instance;
+  final Provider<T> provider;
+
+  InstanceBindingImpl(InjectorImpl injector, Key<T> key, Object source,
+      InternalFactory<? extends T> internalFactory, T instance) {
+    super(injector, key, source, internalFactory, Scopes.NO_SCOPE);
+    this.instance = instance;
+    this.provider = Providers.of(instance);
+  }
+
+  @Override
+  public Provider<T> getProvider() {
+    return this.provider;
+  }
+
+  public void accept(BindingVisitor<? super T> bindingVisitor) {
+    bindingVisitor.visit(this);
+  }
+
+  public T getInstance() {
+    return this.instance;
+  }
+
+  public Collection<Dependency<?>> getDependencies() {
+    return injector.getFieldAndMethodDependenciesFor(instance.getClass());
+  }
+
+  @Override
+  public String toString() {
+    return new ToStringBuilder(InstanceBinding.class)
+        .add("key", key)
+        .add("instance", instance)
+        .add("source", source)
+        .toString();
+  }
+}
diff --git a/src/com/google/inject/MethodAspect.java b/src/com/google/inject/MethodAspect.java
index 948aef3..26e0e75 100644
--- a/src/com/google/inject/MethodAspect.java
+++ b/src/com/google/inject/MethodAspect.java
@@ -21,6 +21,9 @@
 import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Collection;
+import java.util.ArrayList;
+
 import org.aopalliance.intercept.MethodInterceptor;
 
 /**
@@ -35,11 +38,10 @@
   final List<MethodInterceptor> interceptors;
 
   MethodAspect(Matcher<? super Class<?>> classMatcher,
-      Matcher<? super Method> methodMatcher, MethodInterceptor... interceptors) {
+      Matcher<? super Method> methodMatcher, List<MethodInterceptor> interceptors) {
     this.classMatcher = Objects.nonNull(classMatcher, "class matcher");
     this.methodMatcher = Objects.nonNull(methodMatcher, "method matcher");
-    this.interceptors
-        = Arrays.asList(Objects.nonNull(interceptors, "interceptors"));
+    this.interceptors = Objects.nonNull(interceptors, "interceptors");
   }
 
   boolean matches(Class<?> clazz) {
diff --git a/src/com/google/inject/ProxyFactoryBuilder.java b/src/com/google/inject/ProxyFactoryBuilder.java
index bb915bc..62c1cff 100644
--- a/src/com/google/inject/ProxyFactoryBuilder.java
+++ b/src/com/google/inject/ProxyFactoryBuilder.java
@@ -20,6 +20,9 @@
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Collection;
+import java.util.Arrays;
+
 import org.aopalliance.intercept.MethodInterceptor;
 
 /**
@@ -43,12 +46,17 @@
    */
   public ProxyFactoryBuilder intercept(Matcher<? super Class<?>> classMatcher,
       Matcher<? super Method> methodMatcher,
-      MethodInterceptor... interceptors) {
-    methodAspects.add(
-        new MethodAspect(classMatcher, methodMatcher, interceptors));
+      List<MethodInterceptor> interceptors) {
+    methodAspects.add(new MethodAspect(classMatcher, methodMatcher, interceptors));
     return this;
   }
 
+  public ProxyFactoryBuilder intercept(Matcher<? super Class<?>> classMatcher,
+      Matcher<? super Method> methodMatcher,
+      MethodInterceptor... interceptors) {
+    return intercept(classMatcher, methodMatcher, Arrays.asList(interceptors));
+  }
+
   /**
    * Creates a {@code ProxyFactory}.
    */
diff --git a/src/com/google/inject/RequestStaticInjectionCommandProcessor.java b/src/com/google/inject/RequestStaticInjectionCommandProcessor.java
new file mode 100644
index 0000000..401cb6a
--- /dev/null
+++ b/src/com/google/inject/RequestStaticInjectionCommandProcessor.java
@@ -0,0 +1,91 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject;
+
+import com.google.inject.commands.RequestStaticInjectionCommand;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Handles {@link Binder#requestStaticInjection} commands.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+class RequestStaticInjectionCommandProcessor extends CommandProcessor {
+
+  private final List<StaticInjection> staticInjections
+      = new ArrayList<StaticInjection>();
+
+  @Override public Boolean visitRequestStaticInjection(RequestStaticInjectionCommand command) {
+    for (Class<?> type : command.getTypes()) {
+      staticInjections.add(new StaticInjection(command.getSource(), type));
+    }
+    return true;
+  }
+
+  public void validate(InjectorImpl injector) {
+    for (StaticInjection staticInjection : staticInjections) {
+      staticInjection.validate(injector);
+    }
+  }
+
+  public void injectMembers(InjectorImpl injector) {
+    for (StaticInjection staticInjection : staticInjections) {
+      staticInjection.injectMembers(injector);
+    }
+  }
+
+  /**
+   * A requested static injection.
+   */
+  private class StaticInjection {
+    final Object source;
+    final Class<?> type;
+    final List<InjectorImpl.SingleMemberInjector> memberInjectors
+        = new ArrayList<InjectorImpl.SingleMemberInjector>();
+
+    public StaticInjection(Object source, Class type) {
+      this.source = source;
+      this.type = type;
+    }
+
+    void validate(final InjectorImpl injector) {
+      injector.withDefaultSource(source,
+          new Runnable() {
+            public void run() {
+              injector.addSingleInjectorsForFields(
+                  type.getDeclaredFields(), true, memberInjectors);
+              injector.addSingleInjectorsForMethods(
+                  type.getDeclaredMethods(), true, memberInjectors);
+            }
+          });
+    }
+
+    void injectMembers(InjectorImpl injector) {
+      injector.callInContext(new ContextualCallable<Void>() {
+        public Void call(InternalContext context) {
+          for (InjectorImpl.SingleMemberInjector injector : memberInjectors) {
+            injector.inject(context, null);
+          }
+          return null;
+        }
+      });
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/com/google/inject/ScopesCommandProcessor.java b/src/com/google/inject/ScopesCommandProcessor.java
new file mode 100644
index 0000000..317e914
--- /dev/null
+++ b/src/com/google/inject/ScopesCommandProcessor.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject;
+
+import com.google.inject.commands.BindScopeCommand;
+import com.google.inject.internal.Annotations;
+import static com.google.inject.internal.Objects.nonNull;
+import com.google.inject.internal.StackTraceElements;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+
+/**
+ * Handles {@link Binder#bindScope} commands.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+class ScopesCommandProcessor extends CommandProcessor {
+
+  private final Map<Class<? extends Annotation>, Scope> scopes;
+
+  ScopesCommandProcessor(Map<Class<? extends Annotation>, Scope> scopes) {
+    this.scopes = scopes;
+  }
+
+  @Override public Boolean visitBindScope(BindScopeCommand command) {
+    Scope scope = command.getScope();
+    Class<? extends Annotation> annotationType = command.getAnnotationType();
+
+    if (!Scopes.isScopeAnnotation(annotationType)) {
+      addError(StackTraceElements.forType(annotationType),
+          ErrorMessages.MISSING_SCOPE_ANNOTATION);
+      // Go ahead and bind anyway so we don't get collateral errors.
+    }
+
+    if (!Annotations.isRetainedAtRuntime(annotationType)) {
+      addError(StackTraceElements.forType(annotationType),
+          ErrorMessages.MISSING_RUNTIME_RETENTION, command.getSource());
+      // Go ahead and bind anyway so we don't get collateral errors.
+    }
+
+    Scope existing = scopes.get(nonNull(annotationType, "annotation type"));
+    if (existing != null) {
+      addError(command.getSource(), ErrorMessages.DUPLICATE_SCOPES, existing,
+          annotationType, scope);
+    } else {
+      scopes.put(annotationType, nonNull(scope, "scope"));
+    }
+
+    return true;
+  }
+}
\ No newline at end of file
diff --git a/src/com/google/inject/internal/Stopwatch.java b/src/com/google/inject/internal/Stopwatch.java
index 07e4b05..268f913 100644
--- a/src/com/google/inject/internal/Stopwatch.java
+++ b/src/com/google/inject/internal/Stopwatch.java
@@ -24,8 +24,9 @@
  * @author crazybob@google.com (Bob Lee)
  */
 public class Stopwatch {
+  private static final Logger logger = Logger.getLogger(Stopwatch.class.getName());
 
-  long start = System.currentTimeMillis();
+  private long start = System.currentTimeMillis();
 
   /**
    * Resets and returns elapsed time in milliseconds.
@@ -42,7 +43,7 @@
   /**
    * Resets and logs elapsed time in milliseconds.
    */
-  public void resetAndLog(Logger logger, String label) {
+  public void resetAndLog(String label) {
     logger.fine(label + ": " + reset() + "ms");
   }
 }
diff --git a/test/com/google/inject/BoundProviderTest.java b/test/com/google/inject/BoundProviderTest.java
index df20ba7..5fc46f4 100644
--- a/test/com/google/inject/BoundProviderTest.java
+++ b/test/com/google/inject/BoundProviderTest.java
@@ -24,12 +24,14 @@
 public class BoundProviderTest extends TestCase {
 
   public void testFooProvider() throws CreationException {
-    BinderImpl cb = new BinderImpl();
-    cb.bind(Foo.class).toProvider(FooProvider.class);
-    Injector c = cb.createInjector();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(Foo.class).toProvider(FooProvider.class);
+      }
+    });
 
-    Foo a = c.getInstance(Foo.class);
-    Foo b = c.getInstance(Foo.class);
+    Foo a = injector.getInstance(Foo.class);
+    Foo b = injector.getInstance(Foo.class);
 
     assertEquals(0, a.i);
     assertEquals(0, b.i);
@@ -39,12 +41,14 @@
   }
 
   public void testSingletonFooProvider() throws CreationException {
-    BinderImpl cb = new BinderImpl();
-    cb.bind(Foo.class).toProvider(SingletonFooProvider.class);
-    Injector c = cb.createInjector();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(Foo.class).toProvider(SingletonFooProvider.class);
+      }
+    });
 
-    Foo a = c.getInstance(Foo.class);
-    Foo b = c.getInstance(Foo.class);
+    Foo a = injector.getInstance(Foo.class);
+    Foo b = injector.getInstance(Foo.class);
 
     assertEquals(0, a.i);
     assertEquals(1, b.i);
diff --git a/test/com/google/inject/CircularDependencyTest.java b/test/com/google/inject/CircularDependencyTest.java
index ad50b31..018be1d 100644
--- a/test/com/google/inject/CircularDependencyTest.java
+++ b/test/com/google/inject/CircularDependencyTest.java
@@ -25,11 +25,13 @@
 
   public void testCircularlyDependentConstructors()
       throws CreationException {
-    BinderImpl builder = new BinderImpl();
-    builder.bind(A.class).to(AImpl.class);
-    builder.bind(B.class).to(BImpl.class);
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(A.class).to(AImpl.class);
+        bind(B.class).to(BImpl.class);
+      }
+    });
 
-    Injector injector = builder.createInjector();
     A a = injector.getInstance(A.class);
     assertNotNull(a.getB().getA());
   }
diff --git a/test/com/google/inject/ConstantConversionTest.java b/test/com/google/inject/ConstantConversionTest.java
index 660608d..78b3227 100644
--- a/test/com/google/inject/ConstantConversionTest.java
+++ b/test/com/google/inject/ConstantConversionTest.java
@@ -61,10 +61,13 @@
   }
 
   public void testOneConstantInjection() throws CreationException {
-    BinderImpl builder = new BinderImpl();
-    builder.bindConstant().annotatedWith(NumericValue.class).to("5");
-    builder.bind(Simple.class);
-    Injector injector = builder.createInjector();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bindConstant().annotatedWith(NumericValue.class).to("5");
+        bind(Simple.class);
+      }
+    });
+
     Simple simple = injector.getInstance(Simple.class);
     assertEquals(5, simple.i);
   }
@@ -74,13 +77,16 @@
   }
 
   public void testConstantInjection() throws CreationException {
-    BinderImpl b = new BinderImpl();
-    b.bindConstant().annotatedWith(NumericValue.class).to("5");
-    b.bindConstant().annotatedWith(BooleanValue.class).to("true");
-    b.bindConstant().annotatedWith(EnumValue.class).to("TEE");
-    b.bindConstant().annotatedWith(ClassName.class).to(Foo.class.getName());
-    Injector c = b.createInjector();
-    Foo foo = c.getInstance(Foo.class);
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bindConstant().annotatedWith(NumericValue.class).to("5");
+        bindConstant().annotatedWith(BooleanValue.class).to("true");
+        bindConstant().annotatedWith(EnumValue.class).to("TEE");
+        bindConstant().annotatedWith(ClassName.class).to(Foo.class.getName());
+      }
+    });
+
+    Foo foo = injector.getInstance(Foo.class);
 
     checkNumbers(
       foo.integerField,
@@ -108,11 +114,14 @@
   }
 
   public void testInvalidInteger() throws CreationException {
-    BinderImpl b = new BinderImpl();
-    b.bindConstant().annotatedWith(NumericValue.class).to("invalid");
-    Injector c = b.createInjector();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bindConstant().annotatedWith(NumericValue.class).to("invalid");
+      }
+    });
+
     try {
-      c.getInstance(InvalidInteger.class);
+      injector.getInstance(InvalidInteger.class);
       fail();
     } catch (ConfigurationException e) { /* expected */ }
   }
@@ -122,11 +131,14 @@
   }
 
   public void testInvalidCharacter() throws CreationException {
-    BinderImpl b = new BinderImpl();
-    b.bindConstant().annotatedWith(NumericValue.class).to("invalid");
-    Injector c = b.createInjector();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bindConstant().annotatedWith(NumericValue.class).to("invalid");
+      }
+    });
+
     try {
-      c.getInstance(InvalidCharacter.class);
+      injector.getInstance(InvalidCharacter.class);
       fail();
     } catch (ConfigurationException e) { /* expected */ }
   }
@@ -136,11 +148,14 @@
   }
 
   public void testInvalidEnum() throws CreationException {
-    BinderImpl b = new BinderImpl();
-    b.bindConstant().annotatedWith(NumericValue.class).to("invalid");
-    Injector c = b.createInjector();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bindConstant().annotatedWith(NumericValue.class).to("invalid");
+      }
+    });
+
     try {
-      c.getInstance(InvalidEnum.class);
+      injector.getInstance(InvalidEnum.class);
       fail();
     } catch (ConfigurationException e) { /* expected */ }
   }
@@ -150,10 +165,12 @@
   }
 
   public void testToInstanceIsTreatedLikeConstant() throws CreationException {
-    BinderImpl b = new BinderImpl();
-    b.bind(String.class).toInstance("5");
-    b.bind(LongHolder.class);
-    Injector c = b.createInjector();
+    Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(String.class).toInstance("5");
+        bind(LongHolder.class);
+      }
+    });
   }
 
   static class LongHolder {
diff --git a/test/com/google/inject/GenericInjectionTest.java b/test/com/google/inject/GenericInjectionTest.java
index 2281b59..74bfc0e 100644
--- a/test/com/google/inject/GenericInjectionTest.java
+++ b/test/com/google/inject/GenericInjectionTest.java
@@ -26,10 +26,14 @@
 public class GenericInjectionTest extends TestCase {
 
   public void testGenericInjection() throws CreationException {
-    List<String> names = Arrays.asList("foo", "bar", "bob");
-    BinderImpl builder = new BinderImpl();
-    builder.bind(new TypeLiteral<List<String>>() {}).toInstance(names);
-    Injector injector = builder.createInjector();
+    final List<String> names = Arrays.asList("foo", "bar", "bob");
+
+    Injector injector = Guice.createInjector((Module) new AbstractModule() {
+      protected void configure() {
+        bind(new TypeLiteral<List<String>>() {}).toInstance(names);
+      }
+    });
+
     Foo foo = injector.getInstance(Foo.class);
     assertEquals(names, foo.names);
   }
diff --git a/test/com/google/inject/InjectorTest.java b/test/com/google/inject/InjectorTest.java
index cbaa9f4..40a3dc7 100644
--- a/test/com/google/inject/InjectorTest.java
+++ b/test/com/google/inject/InjectorTest.java
@@ -41,15 +41,17 @@
   }
 
   public void testProviderMethods() throws CreationException {
-    SampleSingleton singleton = new SampleSingleton();
-    SampleSingleton other = new SampleSingleton();
+    final SampleSingleton singleton = new SampleSingleton();
+    final SampleSingleton other = new SampleSingleton();
 
-    BinderImpl builder = new BinderImpl();
-    builder.bind(SampleSingleton.class).toInstance(singleton);
-    builder.bind(SampleSingleton.class)
-        .annotatedWith(Other.class)
-        .toInstance(other);
-    Injector injector = builder.createInjector();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(SampleSingleton.class).toInstance(singleton);
+        bind(SampleSingleton.class)
+            .annotatedWith(Other.class)
+            .toInstance(other);
+      }
+    });
 
     assertSame(singleton,
         injector.getInstance(Key.get(SampleSingleton.class)));
@@ -96,9 +98,12 @@
 
   public void testIntAndIntegerAreInterchangeable()
       throws CreationException {
-    BinderImpl builder = new BinderImpl();
-    builder.bindConstant().annotatedWith(I.class).to(5);
-    Injector injector = builder.createInjector();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bindConstant().annotatedWith(I.class).to(5);
+      }
+    });
+
     IntegerWrapper iw = injector.getInstance(IntegerWrapper.class);
     assertEquals(5, (int) iw.i);
   }
@@ -175,11 +180,13 @@
   }
 
   public void testInjectStatics() throws CreationException {
-    BinderImpl builder = new BinderImpl();
-    builder.bindConstant().annotatedWith(S.class).to("test");
-    builder.bindConstant().annotatedWith(I.class).to(5);
-    builder.requestStaticInjection(Static.class);
-    builder.createInjector();
+    Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bindConstant().annotatedWith(S.class).to("test");
+        bindConstant().annotatedWith(I.class).to(5);
+        requestStaticInjection(Static.class);
+      }
+    });
 
     assertEquals("test", Static.s);
     assertEquals(5, Static.i);
diff --git a/test/com/google/inject/IntegrationTest.java b/test/com/google/inject/IntegrationTest.java
index 91923b8..2132898 100644
--- a/test/com/google/inject/IntegrationTest.java
+++ b/test/com/google/inject/IntegrationTest.java
@@ -27,12 +27,14 @@
 public class IntegrationTest extends TestCase {
 
   public void testIntegration() throws CreationException {
-    CountingInterceptor counter = new CountingInterceptor();
+    final CountingInterceptor counter = new CountingInterceptor();
 
-    BinderImpl binder = new BinderImpl();
-    binder.bind(Foo.class);
-    binder.bindInterceptor(any(), any(), counter);
-    Injector injector = binder.createInjector();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(Foo.class);
+        bindInterceptor(any(), any(), counter);
+      }
+    });
 
     Foo foo = injector.getInstance(Key.get(Foo.class));
     foo.foo();
diff --git a/test/com/google/inject/NotRequiredTest.java b/test/com/google/inject/NotRequiredTest.java
index 6cf6067..234518d 100644
--- a/test/com/google/inject/NotRequiredTest.java
+++ b/test/com/google/inject/NotRequiredTest.java
@@ -24,10 +24,13 @@
 public class NotRequiredTest extends TestCase {
 
   public void testProvided() throws CreationException {
-    BinderImpl builder = new BinderImpl();
-    builder.bind(Bar.class).to(BarImpl.class);
-    Injector c = builder.createInjector();
-    Foo foo = c.getInstance(Foo.class);
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(Bar.class).to(BarImpl.class);
+      }
+    });
+
+    Foo foo = injector.getInstance(Foo.class);
     assertNotNull(foo.bar);
     assertNotNull(foo.fromMethod);
   }
diff --git a/test/com/google/inject/PreloadingTest.java b/test/com/google/inject/PreloadingTest.java
index e8b92dc..51f618f 100644
--- a/test/com/google/inject/PreloadingTest.java
+++ b/test/com/google/inject/PreloadingTest.java
@@ -29,24 +29,24 @@
   }
 
   public void testPreloadSome() throws CreationException {
-    BinderImpl builder = createBinder(Stage.DEVELOPMENT);
-    builder.createInjector();
+    createBinder(Stage.DEVELOPMENT);
     assertEquals(1, Foo.count);
     assertEquals(0, Bar.count);
   }
 
   public void testPreloadAll() throws CreationException {
-    BinderImpl builder = createBinder(Stage.PRODUCTION);
-    builder.createInjector();
+    createBinder(Stage.PRODUCTION);
     assertEquals(1, Foo.count);
     assertEquals(1, Bar.count);
   }
 
-  private BinderImpl createBinder(Stage stage) {
-    BinderImpl builder = new BinderImpl(stage);
-    builder.bind(Foo.class).asEagerSingleton();
-    builder.bind(Bar.class);
-    return builder;
+  private Injector createBinder(Stage stage) {
+    return Guice.createInjector(stage, new AbstractModule() {
+      protected void configure() {
+        bind(Foo.class).asEagerSingleton();
+        bind(Bar.class);
+      }
+    });
   }
 
   static class Foo {
diff --git a/test/com/google/inject/ProviderInjectionTest.java b/test/com/google/inject/ProviderInjectionTest.java
index abc172c..449ba7c 100644
--- a/test/com/google/inject/ProviderInjectionTest.java
+++ b/test/com/google/inject/ProviderInjectionTest.java
@@ -27,12 +27,12 @@
 public class ProviderInjectionTest extends TestCase {
 
   public void testProviderInjection() throws CreationException {
-    BinderImpl builder = new BinderImpl();
-
-    builder.bind(Bar.class);
-    builder.bind(SampleSingleton.class).in(Scopes.SINGLETON);
-
-    Injector injector = builder.createInjector();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(Bar.class);
+        bind(SampleSingleton.class).in(Scopes.SINGLETON);
+      }
+    });
 
     Foo foo = injector.getInstance(Foo.class);
 
diff --git a/test/com/google/inject/ReflectionTest.java b/test/com/google/inject/ReflectionTest.java
index 4bd971c..0d0fa1a 100644
--- a/test/com/google/inject/ReflectionTest.java
+++ b/test/com/google/inject/ReflectionTest.java
@@ -29,10 +29,15 @@
   @BindingAnnotation @interface I {}
 
   public void testNormalBinding() throws CreationException {
-    BinderImpl builder = new BinderImpl();
-    Foo foo = new Foo();
-    builder.bind(Foo.class).toInstance(foo);
-    Injector injector = builder.createInjector();
+    InjectorBuilder builder = new InjectorBuilder();
+    final Foo foo = new Foo();
+
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(Foo.class).toInstance(foo);
+      }
+    });
+
     Binding<Foo> fooBinding = injector.getBinding(Key.get(Foo.class));
     assertSame(foo, fooBinding.getProvider().get());
     assertNotNull(fooBinding.getSource());
@@ -40,9 +45,12 @@
   }
 
   public void testConstantBinding() throws CreationException {
-    BinderImpl builder = new BinderImpl();
-    builder.bindConstant().annotatedWith(I.class).to(5);
-    Injector injector = builder.createInjector();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bindConstant().annotatedWith(I.class).to(5);
+      }
+    });
+
     Binding<?> i = injector.getBinding(Key.get(int.class, I.class));
     assertEquals(5, i.getProvider().get());
     assertNotNull(i.getSource());
@@ -50,11 +58,16 @@
   }
 
   public void testLinkedBinding() throws CreationException {
-    BinderImpl builder = new BinderImpl();
-    Bar bar = new Bar();
-    builder.bind(Bar.class).toInstance(bar);
-    builder.bind(Key.get(Foo.class)).to(Key.get(Bar.class));
-    Injector injector = builder.createInjector();
+    InjectorBuilder builder = new InjectorBuilder();
+    final Bar bar = new Bar();
+
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(Bar.class).toInstance(bar);
+        bind(Key.get(Foo.class)).to(Key.get(Bar.class));
+      }
+    });
+
     Binding<Foo> fooBinding = injector.getBinding(Key.get(Foo.class));
     assertSame(bar, fooBinding.getProvider().get());
     assertNotNull(fooBinding.getSource());
diff --git a/test/com/google/inject/ScopesTest.java b/test/com/google/inject/ScopesTest.java
index 1603b1d..29236d7 100644
--- a/test/com/google/inject/ScopesTest.java
+++ b/test/com/google/inject/ScopesTest.java
@@ -24,10 +24,12 @@
 public class ScopesTest extends TestCase {
 
   public void testSingletonAnnotation() throws CreationException {
-    BinderImpl binder = new BinderImpl();
-    BindingBuilderImpl<SampleSingleton> bindingBuilder
-        = binder.bind(SampleSingleton.class);
-    Injector injector = binder.createInjector();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(SampleSingleton.class);
+      }
+    });
+
     assertSame(
         injector.getInstance(SampleSingleton.class),
         injector.getInstance(SampleSingleton.class));
@@ -38,9 +40,12 @@
 
   public void testOverriddingAnnotation()
       throws CreationException {
-    BinderImpl binder = new BinderImpl();
-    binder.bind(SampleSingleton.class).in(Scopes.NO_SCOPE);
-    Injector injector = binder.createInjector();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(SampleSingleton.class).in(Scopes.NO_SCOPE);
+      }
+    });
+
     assertNotSame(
         injector.getInstance(SampleSingleton.class),
         injector.getInstance(SampleSingleton.class));
diff --git a/test/com/google/inject/StaticInjectionTest.java b/test/com/google/inject/StaticInjectionTest.java
index 187360d..6f1ac5f 100644
--- a/test/com/google/inject/StaticInjectionTest.java
+++ b/test/com/google/inject/StaticInjectionTest.java
@@ -32,12 +32,13 @@
   @BindingAnnotation @interface S {}
 
   public void testInjectStatics() throws CreationException {
-    BinderImpl builder = new BinderImpl();
-    builder.bindConstant().annotatedWith(S.class).to("test");
-    builder.bindConstant().annotatedWith(I.class).to(5);
-    builder.requestStaticInjection(StaticInjectionTest.Static.class);
-
-    Injector c = builder.createInjector();
+    Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bindConstant().annotatedWith(S.class).to("test");
+        bindConstant().annotatedWith(I.class).to(5);
+        requestStaticInjection(StaticInjectionTest.Static.class);
+      }
+    });
 
     assertEquals("test", StaticInjectionTest.Static.s);
     assertEquals(5, StaticInjectionTest.Static.i);
diff --git a/test/com/google/inject/SuperclassTest.java b/test/com/google/inject/SuperclassTest.java
index d39a56c..3dda585 100644
--- a/test/com/google/inject/SuperclassTest.java
+++ b/test/com/google/inject/SuperclassTest.java
@@ -24,9 +24,12 @@
 public class SuperclassTest extends TestCase {
 
   public void testSuperclassInjection() throws CreationException {
-    BinderImpl builder = new BinderImpl();
-    builder.bind(Foo.class);
-    Injector injector = builder.createInjector();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(Foo.class);
+      }
+    });
+
     Provider<Sub> creator = injector.getProvider(Sub.class);
     Sub sub = creator.get();
     sub = creator.get();