diff --git a/pom.xml b/pom.xml
index da2170b..3172c8a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,7 +30,7 @@
com.google.inject
guice
- 3.0
+ 4.0
compile
diff --git a/src/main/java/org/springframework/guice/injector/GuiceAutowireCandidateResolver.java b/src/main/java/org/springframework/guice/injector/GuiceAutowireCandidateResolver.java
index 145108b..38ce73e 100644
--- a/src/main/java/org/springframework/guice/injector/GuiceAutowireCandidateResolver.java
+++ b/src/main/java/org/springframework/guice/injector/GuiceAutowireCandidateResolver.java
@@ -13,36 +13,91 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.springframework.guice.injector;
-import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import javax.inject.Provider;
+
+import org.springframework.aop.TargetSource;
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.DependencyDescriptor;
-import org.springframework.beans.factory.support.AutowireCandidateResolver;
-import org.springframework.core.ResolvableType;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
+import org.springframework.util.Assert;
+
+import com.google.inject.Injector;
/**
* @author Dave Syer
+ * @author Taylor Wicksell
*
*/
-public class GuiceAutowireCandidateResolver implements AutowireCandidateResolver {
+public class GuiceAutowireCandidateResolver extends ContextAnnotationAutowireCandidateResolver {
+
+ private Provider injectorProvider;
- @Override
- public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder,
- DependencyDescriptor descriptor) {
- return false;
- }
+ public GuiceAutowireCandidateResolver(Provider injectorProvider) {
+ this.injectorProvider = injectorProvider;
+ }
- @Override
- public Object getSuggestedValue(DependencyDescriptor descriptor) {
- ResolvableType resolvable = descriptor.getResolvableType();
- return null;
- }
+ @Override
+ public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
+ return (isLazy(descriptor, beanName) ? buildLazyResolutionProxy(descriptor, beanName) : null);
+ }
- @Override
- public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor,
- String beanName) {
- return null;
- }
+ protected boolean isLazy(DependencyDescriptor descriptor, String beanName) {
+ Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory,
+ "BeanFactory needs to be a DefaultListableBeanFactory");
+ final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
+ try {
+ beanFactory.doResolveDependency(descriptor, beanName, null, null);
+ } catch (NoSuchBeanDefinitionException e) {
+ return true;
+ }
+ return super.isLazy(descriptor);
+ }
+
+ protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final String beanName) {
+ Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory,
+ "BeanFactory needs to be a DefaultListableBeanFactory");
+ final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
+ TargetSource ts = new TargetSource() {
+ @Override
+ public Class> getTargetClass() {
+ return descriptor.getDependencyType();
+ }
+
+ @Override
+ public boolean isStatic() {
+ return false;
+ }
+
+ @Override
+ public Object getTarget() {
+ Object target = null;
+ try {
+ target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
+ } catch (NoSuchBeanDefinitionException e) {
+ target = injectorProvider.get().getInstance(descriptor.getDependencyType());
+ }
+ if (target == null) {
+ throw new NoSuchBeanDefinitionException(descriptor.getDependencyType(),
+ "Optional dependency not present for lazy injection point");
+ }
+ return target;
+ }
+
+ @Override
+ public void releaseTarget(Object target) {
+ }
+ };
+ ProxyFactory pf = new ProxyFactory();
+ pf.setTargetSource(ts);
+ Class> dependencyType = descriptor.getDependencyType();
+ if (dependencyType.isInterface()) {
+ pf.addInterface(dependencyType);
+ }
+ return pf.getProxy(beanFactory.getBeanClassLoader());
+ }
}
diff --git a/src/main/java/org/springframework/guice/module/SpringModule.java b/src/main/java/org/springframework/guice/module/SpringModule.java
index 6561969..0aefd82 100644
--- a/src/main/java/org/springframework/guice/module/SpringModule.java
+++ b/src/main/java/org/springframework/guice/module/SpringModule.java
@@ -20,64 +20,96 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import javax.inject.Provider;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Binder;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.ProvisionException;
+import com.google.inject.Stage;
+import com.google.inject.TypeLiteral;
+import com.google.inject.matcher.Matchers;
+import com.google.inject.name.Names;
+import com.google.inject.spi.ProvisionListener;
+
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
+import org.springframework.guice.injector.GuiceAutowireCandidateResolver;
import org.springframework.util.ClassUtils;
-import com.google.inject.Binder;
-import com.google.inject.Module;
-import com.google.inject.Key;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.TypeLiteral;
-import com.google.inject.name.Names;
-
/**
* @author Dave Syer
*
*/
-public class SpringModule implements Module {
-
- private DefaultListableBeanFactory beanFactory;
+public class SpringModule extends AbstractModule {
private BindingTypeMatcher matcher = new GuiceModuleMetadata();
private Map> bound = new HashMap>();
+ private ConfigurableListableBeanFactory beanFactory;
+
+ private Provider beanFactoryProvider;
+
public SpringModule(ApplicationContext context) {
- this((DefaultListableBeanFactory) context.getAutowireCapableBeanFactory());
+ this((ConfigurableListableBeanFactory) context.getAutowireCapableBeanFactory());
}
- public SpringModule(DefaultListableBeanFactory beanFactory) {
+ public SpringModule(ConfigurableListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
- if (beanFactory.getBeanNamesForType(GuiceModuleMetadata.class).length > 0) {
- this.matcher = new CompositeTypeMatcher(
- beanFactory.getBeansOfType(GuiceModuleMetadata.class).values());
- }
}
- public void configure(Binder binder) {
- for (String name : this.beanFactory.getBeanDefinitionNames()) {
- BeanDefinition definition = this.beanFactory.getBeanDefinition(name);
+ public SpringModule(Provider beanFactoryProvider) {
+ this.beanFactoryProvider = beanFactoryProvider;
+ }
+
+ @Override
+ public void configure() {
+ if (binder().currentStage() != Stage.TOOL) {
+ if (beanFactory == null) {
+ beanFactory = beanFactoryProvider.get();
+ }
+ if (beanFactory.getBeanNamesForType(ProvisionListener.class).length > 0) {
+ binder().bindListener(Matchers.any(),
+ beanFactory.getBeansOfType(ProvisionListener.class).values()
+ .toArray(new ProvisionListener[0]));
+ }
+ if (beanFactory instanceof DefaultListableBeanFactory) {
+ ((DefaultListableBeanFactory) beanFactory)
+ .setAutowireCandidateResolver(new GuiceAutowireCandidateResolver(
+ binder().getProvider(Injector.class)));
+ }
+ if (beanFactory.getBeanNamesForType(GuiceModuleMetadata.class).length > 0) {
+ this.matcher = new CompositeTypeMatcher(
+ beanFactory.getBeansOfType(GuiceModuleMetadata.class).values());
+ }
+ }
+ bind(beanFactory);
+ }
+
+ private void bind(ConfigurableListableBeanFactory beanFactory) {
+ for (String name : beanFactory.getBeanDefinitionNames()) {
+ BeanDefinition definition = beanFactory.getBeanDefinition(name);
if (definition.isAutowireCandidate()
&& definition.getRole() == AbstractBeanDefinition.ROLE_APPLICATION) {
- Class> type = this.beanFactory.getType(name);
+ Class> type = beanFactory.getType(name);
final String beanName = name;
- Provider> typeProvider = BeanFactoryProvider.typed(this.beanFactory,
- type);
- Provider> namedProvider = BeanFactoryProvider.named(this.beanFactory,
+ Provider> typeProvider = BeanFactoryProvider.typed(beanFactory, type);
+ Provider> namedProvider = BeanFactoryProvider.named(beanFactory,
beanName, type);
if (!type.isInterface() && !ClassUtils.isCglibProxyClass(type)) {
- bindConditionally(binder, name, type, typeProvider, namedProvider);
+ bindConditionally(binder(), name, type, typeProvider, namedProvider);
}
for (Class> iface : ClassUtils.getAllInterfacesForClass(type)) {
- bindConditionally(binder, name, iface, typeProvider, namedProvider);
+ bindConditionally(binder(), name, iface, typeProvider, namedProvider);
}
for (Type iface : type.getGenericInterfaces()) {
- bindConditionally(binder, name, iface, typeProvider, namedProvider);
+ bindConditionally(binder(), name, iface, typeProvider, namedProvider);
}
}
}
@@ -106,7 +138,7 @@ public class SpringModule implements Module {
private static class BeanFactoryProvider implements Provider {
- private DefaultListableBeanFactory beanFactory;
+ private ConfigurableListableBeanFactory beanFactory;
private String name;
@@ -114,19 +146,19 @@ public class SpringModule implements Module {
private T result;
- private BeanFactoryProvider(DefaultListableBeanFactory beanFactory, String name,
- Class type) {
+ private BeanFactoryProvider(ConfigurableListableBeanFactory beanFactory,
+ String name, Class type) {
this.beanFactory = beanFactory;
this.name = name;
this.type = type;
}
- public static Provider named(DefaultListableBeanFactory beanFactory,
+ public static Provider named(ConfigurableListableBeanFactory beanFactory,
String name, Class type) {
return new BeanFactoryProvider(beanFactory, name, type);
}
- public static Provider typed(DefaultListableBeanFactory beanFactory,
+ public static Provider typed(ConfigurableListableBeanFactory beanFactory,
Class type) {
return new BeanFactoryProvider(beanFactory, null, type);
}
@@ -184,5 +216,4 @@ public class SpringModule implements Module {
return false;
}
}
-
}
diff --git a/src/test/java/org/springframework/guice/annotation/GuiceModuleAnnotationTests.java b/src/test/java/org/springframework/guice/annotation/GuiceModuleAnnotationTests.java
index b25dbba..53b50f7 100644
--- a/src/test/java/org/springframework/guice/annotation/GuiceModuleAnnotationTests.java
+++ b/src/test/java/org/springframework/guice/annotation/GuiceModuleAnnotationTests.java
@@ -25,7 +25,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
-import org.springframework.guice.annotation.GuiceModule;
import org.springframework.guice.module.SpringModule;
import com.google.inject.ConfigurationException;
@@ -73,10 +72,7 @@ public class GuiceModuleAnnotationTests {
}
private Injector createInjector(Class>... config) {
- AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
- context.register(config);
- context.refresh();
- Injector injector = Guice.createInjector(new SpringModule(context));
+ Injector injector = Guice.createInjector(new SpringModule(new AnnotationConfigApplicationContext(config)));
return injector;
}
diff --git a/src/test/java/org/springframework/guice/module/SpringModuleGuiceBindingAwareTest.java b/src/test/java/org/springframework/guice/module/SpringModuleGuiceBindingAwareTest.java
new file mode 100644
index 0000000..fec4d9d
--- /dev/null
+++ b/src/test/java/org/springframework/guice/module/SpringModuleGuiceBindingAwareTest.java
@@ -0,0 +1,201 @@
+package org.springframework.guice.module;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Scopes;
+import com.google.inject.spi.ProvisionListener;
+import com.google.inject.util.Providers;
+
+import org.junit.Test;
+
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.guice.injector.GuiceAutowireCandidateResolver;
+import org.springframework.test.util.AopTestUtils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+
+public class SpringModuleGuiceBindingAwareTest {
+
+ @Test
+ public void testAllDependenciesInjectedAndLifeycleMethodsCalledOnce() {
+ Injector injector = Guice.createInjector(new SimpleGuiceModule(),
+ new SpringModule(new Provider() {
+ @Override
+ public ConfigurableListableBeanFactory get() {
+ PartiallyRefreshableApplicationContext context = new PartiallyRefreshableApplicationContext();
+ context.register(
+ GuiceProjectWithSpringLibraryTestSpringConfig.class);
+ context.getBeanFactory().registerSingleton("refreshListener",
+ new ContextRefreshingProvisionListener(context));
+ context.partialRefresh();
+ return context.getBeanFactory();
+ }
+ }));
+
+ // check guice provided bindings
+ assertNotNull(injector.getInstance(GuiceDependency1.class));
+ assertNotNull(injector.getInstance(IGuiceDependency1.class));
+
+ // check spring bindings as interface
+ ISpringBean springBean = injector.getInstance(ISpringBean.class);
+ assertNotNull(springBean);
+ assertNotNull(springBean.getDep1());
+ assertNotNull(springBean.getDep2());
+ assertNotNull(springBean.getDep3());
+
+ // invoke a method to make sure we aren't dealing with a lazy proxy
+ assertEquals("done", springBean.getDep1().doWork());
+
+ // check binding equality
+ assertSame(injector.getInstance(IGuiceDependency1.class),
+ AopTestUtils.getTargetObject(springBean.getDep1()));
+ assertSame(injector.getInstance(IGuiceDependency2.class),
+ AopTestUtils.getTargetObject(springBean.getDep2()));
+ assertSame(injector.getInstance(IGuiceDependency3.class),
+ AopTestUtils.getTargetObject(springBean.getDep3()));
+ }
+
+ static class SimpleGuiceModule extends AbstractModule {
+
+ @Override
+ protected void configure() {
+ bind(IGuiceDependency1.class).to(GuiceDependency1.class).in(Scopes.SINGLETON); // test
+ // normal
+ // binding
+ bind(IGuiceDependency2.class).toInstance(new IGuiceDependency2() {
+ }); // test instance binding
+ bind(IGuiceDependency3.class)
+ .toProvider(Providers.of(new IGuiceDependency3() {
+ })); // test provider binding
+ }
+ }
+
+ @Configuration
+ static class GuiceProjectWithSpringLibraryTestSpringConfig {
+
+ @Bean
+ public ISpringBean springDefinedSomething(IGuiceDependency1 dependency) {
+ return new SpringBean(dependency);
+ }
+
+ @Bean
+ public ApplicationListener eventListener(
+ final IGuiceDependency1 dependency) {
+ return new ApplicationListener() {
+ @Override
+ public void onApplicationEvent(ApplicationEvent event) {
+ dependency.doWork();
+ }
+ };
+ }
+ }
+
+ static interface IGuiceDependency1 {
+ String doWork();
+ }
+
+ static interface IGuiceDependency2 {
+ }
+
+ static interface IGuiceDependency3 {
+ }
+
+ static class GuiceDependency1 implements IGuiceDependency1 {
+ public String doWork() {
+ return "done";
+ }
+ }
+
+ static interface ISpringBean {
+ IGuiceDependency1 getDep1();
+
+ IGuiceDependency2 getDep2();
+
+ IGuiceDependency3 getDep3();
+ }
+
+ static class SpringBean implements ISpringBean {
+
+ private final IGuiceDependency1 dep1;
+ @Inject
+ private IGuiceDependency2 dep2;
+ @Inject
+ private IGuiceDependency3 dep3;
+
+ @Inject
+ public SpringBean(IGuiceDependency1 dependency) {
+ this.dep1 = dependency;
+ }
+
+ @Override
+ public IGuiceDependency1 getDep1() {
+ return dep1;
+ }
+
+ @Override
+ public IGuiceDependency2 getDep2() {
+ return dep2;
+ }
+
+ @Override
+ public IGuiceDependency3 getDep3() {
+ return dep3;
+ }
+ }
+
+ private static final class PartiallyRefreshableApplicationContext
+ extends AnnotationConfigApplicationContext {
+
+ private final AtomicBoolean partiallyRefreshed = new AtomicBoolean(false);
+
+ /*
+ * Initializes beanFactoryPostProcessors only to ensure that all BeanDefinition's
+ * are available
+ */
+ void partialRefresh() {
+ invokeBeanFactoryPostProcessors(getBeanFactory());
+ }
+
+ @Override
+ protected void invokeBeanFactoryPostProcessors(
+ ConfigurableListableBeanFactory beanFactory) {
+ if (partiallyRefreshed.compareAndSet(false, true)) {
+ super.invokeBeanFactoryPostProcessors(beanFactory);
+ }
+ }
+ }
+
+ private static final class ContextRefreshingProvisionListener
+ implements ProvisionListener {
+ private final ConfigurableApplicationContext context;
+ private final AtomicBoolean initialized = new AtomicBoolean(false);
+
+ private ContextRefreshingProvisionListener(
+ ConfigurableApplicationContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public void onProvision(ProvisionInvocation provision) {
+ if (!initialized.getAndSet(true) && !context.isActive()) {
+ context.refresh();
+ }
+ provision.provision();
+ }
+ }
+
+}
diff --git a/src/test/java/org/springframework/guice/module/SpringModuleWiringTests.java b/src/test/java/org/springframework/guice/module/SpringModuleWiringTests.java
index 69e0f66..58b8d35 100644
--- a/src/test/java/org/springframework/guice/module/SpringModuleWiringTests.java
+++ b/src/test/java/org/springframework/guice/module/SpringModuleWiringTests.java
@@ -15,6 +15,7 @@ package org.springframework.guice.module;
import org.junit.Rule;
import org.junit.rules.ExpectedException;
+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -34,10 +35,8 @@ public class SpringModuleWiringTests extends AbstractCompleteWiringTests {
@Override
protected Injector createInjector() {
- AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
- context.register(TestConfig.class);
- context.refresh();
- return Guice.createInjector(new SpringModule(context));
+ return Guice.createInjector(new SpringModule(
+ new AnnotationConfigApplicationContext(TestConfig.class)));
}
@Configuration
@@ -61,10 +60,11 @@ public class SpringModuleWiringTests extends AbstractCompleteWiringTests {
public Thing that() {
return new Thing();
}
-
+
@Bean
public Parameterized parameterizedBean() {
- return new Parameterized(){};
+ return new Parameterized() {
+ };
}
}
}