Added support for automatically binding Spring beans.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@273 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/spring/src/com/google/inject/spring/SpringIntegration.java b/spring/src/com/google/inject/spring/SpringIntegration.java
index f791ae8..3a67147 100644
--- a/spring/src/com/google/inject/spring/SpringIntegration.java
+++ b/spring/src/com/google/inject/spring/SpringIntegration.java
@@ -16,30 +16,73 @@
 
 package com.google.inject.spring;
 
+import static com.google.inject.util.Objects.nonNull;
 import com.google.inject.Provider;
 import com.google.inject.Inject;
+import com.google.inject.Binder;
+import com.google.inject.name.Names;
+import com.google.inject.spi.SourceProviders;
+import com.google.inject.util.Objects;
 import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.ListableBeanFactory;
 
 /**
- * Integrates Guice with Spring. Requires a binding to
- * {@link org.springframework.beans.factory.BeanFactory}.
+ * Integrates Guice with Spring.
  *
  * @author crazybob@google.com (Bob Lee)
  */
 public class SpringIntegration {
 
+  static {
+    SourceProviders.skip(SpringIntegration.class);
+  }
+
   private SpringIntegration() {}
 
   /**
    * Creates a provider which looks up objects from Spring using the given name.
-   * Example usage:
+   * Expects a binding to {@link
+   * org.springframework.beans.factory.BeanFactory}. Example usage:
    *
    * <pre>
-   * bind(DataSource.class).toProvider(fromSpring(DataSource.class, "dataSource"));
+   * bind(DataSource.class)
+   *   .toProvider(fromSpring(DataSource.class, "dataSource"));
    * </pre>
    */
   public static <T> Provider<T> fromSpring(Class<T> type, String name) {
-    return new SpringProvider<T>(type, name);
+    return new InjectableSpringProvider<T>(type, name);
+  }
+
+  /**
+   * Binds all Spring beans from the given factory by name. For a Spring bean
+   * named "foo", this method creates a binding to the bean's type and
+   * {@code @Named("foo")}.
+   *
+   * @see com.google.inject.name.Named
+   * @see com.google.inject.name.Names#named(String) 
+   */
+  public static void bindAll(Binder binder, ListableBeanFactory beanFactory) {
+    for (String name : beanFactory.getBeanDefinitionNames()) {
+      Class<?> type = beanFactory.getType(name);
+      bindBean(binder, beanFactory, name, type);
+    }
+  }
+
+  static <T> void bindBean(Binder binder, ListableBeanFactory beanFactory,
+      String name, Class<T> type) {
+    SpringProvider<T> provider
+        = SpringProvider.newInstance(type, name);
+    try {
+      provider.initialize(beanFactory);
+    }
+    catch (Exception e) {
+      binder.addError(e);
+      return;
+    }
+
+    binder.bind(type)
+        .annotatedWith(Names.named(name))
+        .toProvider(provider);
   }
 
   static class SpringProvider<T> implements Provider<T> {
@@ -50,11 +93,14 @@
     final String name;
 
     public SpringProvider(Class<T> type, String name) {
-      this.type = type;
-      this.name = name;
+      this.type = nonNull(type, "type");
+      this.name = nonNull(name, "name");
     }
 
-    @Inject
+    static <T> SpringProvider<T> newInstance(Class<T> type, String name) {
+      return new SpringProvider<T>(type, name);
+    }
+
     void initialize(BeanFactory beanFactory) {
       this.beanFactory = beanFactory;
       if (!beanFactory.isTypeMatch(name, type)) {
@@ -77,4 +123,17 @@
       return instance;
     }
   }
+
+  static class InjectableSpringProvider<T> extends SpringProvider<T> {
+
+    InjectableSpringProvider(Class<T> type, String name) {
+      super(type, name);
+    }
+
+    @Inject
+    @Override
+    void initialize(BeanFactory beanFactory) {
+      super.initialize(beanFactory);
+    }
+  }
 }
diff --git a/spring/test/com/google/inject/spring/SpringIntegrationTest.java b/spring/test/com/google/inject/spring/SpringIntegrationTest.java
index 048c050..c110ca4 100644
--- a/spring/test/com/google/inject/spring/SpringIntegrationTest.java
+++ b/spring/test/com/google/inject/spring/SpringIntegrationTest.java
@@ -26,6 +26,8 @@
 import com.google.inject.Guice;
 import com.google.inject.AbstractModule;
 import com.google.inject.CreationException;
+import com.google.inject.Key;
+import com.google.inject.name.Names;
 import static com.google.inject.spring.SpringIntegration.*;
 
 /**
@@ -33,7 +35,7 @@
  */
 public class SpringIntegrationTest extends TestCase {
 
-  public void testSpringIntegration() throws CreationException {
+  public void testBindFromSpring() throws CreationException {
     final DefaultListableBeanFactory beanFactory
         = new DefaultListableBeanFactory();
 
@@ -64,6 +66,38 @@
         injector.getInstance(Prototype.class));
   }
 
+  public void testBindAll() throws CreationException {
+    final DefaultListableBeanFactory beanFactory
+        = new DefaultListableBeanFactory();
+
+    RootBeanDefinition singleton
+        = new RootBeanDefinition(Singleton.class);
+    beanFactory.registerBeanDefinition("singleton", singleton);
+
+    RootBeanDefinition prototype
+        = new RootBeanDefinition(Prototype.class, false);
+    beanFactory.registerBeanDefinition("prototype", prototype);
+
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        SpringIntegration.bindAll(binder(), beanFactory);
+      }
+    });
+
+    Key<Singleton> singletonKey
+        = Key.get(Singleton.class, Names.named("singleton"));
+    Key<Prototype> prototypeKey
+        = Key.get(Prototype.class, Names.named("prototype"));
+
+    assertNotNull(injector.getInstance(singletonKey));
+    assertSame(injector.getInstance(singletonKey),
+        injector.getInstance(singletonKey));
+
+    assertNotNull(injector.getInstance(prototypeKey));
+    assertNotSame(injector.getInstance(prototypeKey),
+        injector.getInstance(prototypeKey));
+  }
+
   static class Singleton {}
   static class Prototype {}
 }