diff --git a/src/main/java/org/springframework/guice/annotation/ModuleRegistryConfiguration.java b/src/main/java/org/springframework/guice/annotation/ModuleRegistryConfiguration.java index 118db66..b3f995a 100644 --- a/src/main/java/org/springframework/guice/annotation/ModuleRegistryConfiguration.java +++ b/src/main/java/org/springframework/guice/annotation/ModuleRegistryConfiguration.java @@ -29,6 +29,7 @@ import com.google.inject.name.Named; import com.google.inject.spi.Element; import com.google.inject.spi.ElementSource; import com.google.inject.spi.Elements; +import com.google.inject.spi.PrivateElements; import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; @@ -87,7 +88,7 @@ class ModuleRegistryConfiguration implements BeanDefinitionRegistryPostProcessor if (injector == null) { injector = Guice.createInjector(modules); } - beanFactory.registerResolvableDependency(Injector.class, injector); + beanFactory.registerSingleton("injector", injector); } private void mapBindings(Map, Binding> bindings, @@ -138,6 +139,8 @@ class ModuleRegistryConfiguration implements BeanDefinitionRegistryPostProcessor if (e instanceof Binding) { Binding binding = (Binding) e; bindings.put(binding.getKey(), binding); + } else if (e instanceof PrivateElements) { + extractPrivateElements(bindings, (PrivateElements) e); } } mapBindings(bindings, registry); @@ -148,6 +151,18 @@ class ModuleRegistryConfiguration implements BeanDefinitionRegistryPostProcessor applicationContext.publishEvent(new CreateInjectorSignalEvent()); } + private void extractPrivateElements(Map, Binding> bindings, PrivateElements privateElements) { + List elements = privateElements.getElements(); + for (Element e : elements) { + if (e instanceof Binding && privateElements.getExposedKeys().contains(((Binding) e).getKey())) { + Binding binding = (Binding) e; + bindings.put(binding.getKey(), binding); + } else if (e instanceof PrivateElements) { + extractPrivateElements(bindings, (PrivateElements) e); + } + } + } + @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { diff --git a/src/test/java/org/springframework/guice/PrivateModuleTests.java b/src/test/java/org/springframework/guice/PrivateModuleTests.java new file mode 100644 index 0000000..329341d --- /dev/null +++ b/src/test/java/org/springframework/guice/PrivateModuleTests.java @@ -0,0 +1,99 @@ +package org.springframework.guice; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.guice.PrivateModuleTests.SomeInterface; +import org.springframework.guice.PrivateModuleTests.SomePrivateModule; +import org.springframework.guice.annotation.EnableGuiceModules; + +import com.google.inject.AbstractModule; +import com.google.inject.ConfigurationException; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.PrivateModule; +import com.google.inject.name.Names; + +public class PrivateModuleTests { + + private static AnnotationConfigApplicationContext context; + + @BeforeClass + public static void init() { + context = new AnnotationConfigApplicationContext(PrivateModuleTestConfig.class); + } + + @AfterClass + public static void cleanup() { + if(context != null) { + context.close(); + } + } + + @Test + public void verifyPrivateModulesCanExposeBindings() { + Injector injector = context.getBean(Injector.class); + SomeInterface injectorProvidedPrivateBinding = injector.getInstance(Key.get(SomeInterface.class, Names.named("exposed"))); + assertNotNull(injectorProvidedPrivateBinding); + SomeInterface springProvidedPrivateBinding = context.getBean(SomeInterface.class); + assertNotNull(springProvidedPrivateBinding); + SomeInterface namedPrivateBinding = context.getBean("exposed",SomeInterface.class); + assertNotNull(namedPrivateBinding); + assertEquals(injectorProvidedPrivateBinding, springProvidedPrivateBinding); + assertEquals(injectorProvidedPrivateBinding, namedPrivateBinding); + String beanDependingOnPrivateBinding = context.getBean("somethingThatWantsAPrivateBinding", String.class); + assertNotNull(beanDependingOnPrivateBinding); + assertEquals("foo", beanDependingOnPrivateBinding); + } + + @Test(expected=ConfigurationException.class) + public void verifyPrivateModulesPrivateBindingsAreNotExposedViaInjector() { + Injector injector = context.getBean(Injector.class); + injector.getInstance(Key.get(SomeInterface.class, Names.named("notexposed"))); + } + + @Test(expected=NoSuchBeanDefinitionException.class) + public void verifyPrivateModulesPrivateBindingsAreNotExposedViaSpring() { + context.getBean("notexposed",SomeInterface.class); + } + + public static interface SomeInterface {} + public static class SomePrivateBinding implements SomeInterface {} + + public static class SomePrivateModule extends PrivateModule { + @Override + protected void configure() { + bind(SomeInterface.class).annotatedWith(Names.named("exposed")).to(SomePrivateBinding.class).asEagerSingleton(); + bind(SomeInterface.class).annotatedWith(Names.named("notexposed")).to(SomePrivateBinding.class).asEagerSingleton(); + expose(SomeInterface.class).annotatedWith(Names.named("exposed")); + } + } +} + +@EnableGuiceModules +@Configuration +class PrivateModuleTestConfig { + + @Bean + public String somethingThatWantsAPrivateBinding(SomeInterface privateBinding) { + return "foo"; + } + + @Bean + public Module module() { + return new AbstractModule() { + @Override + protected void configure() { + install(new SomePrivateModule()); + } + }; + } +} \ No newline at end of file