diff --git a/src/main/java/org/springframework/guice/module/GuiceAutowireCandidateResolver.java b/src/main/java/org/springframework/guice/module/GuiceAutowireCandidateResolver.java index 618552a..aa34115 100644 --- a/src/main/java/org/springframework/guice/module/GuiceAutowireCandidateResolver.java +++ b/src/main/java/org/springframework/guice/module/GuiceAutowireCandidateResolver.java @@ -16,6 +16,7 @@ package org.springframework.guice.module; +import java.lang.reflect.Type; import java.util.Collection; import java.util.Map; import java.util.Optional; @@ -25,12 +26,14 @@ import javax.inject.Provider; import com.google.inject.BindingAnnotation; import com.google.inject.Injector; import com.google.inject.Key; +import com.google.inject.name.Names; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.aop.TargetSource; import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver; @@ -117,8 +120,8 @@ class GuiceAutowireCandidateResolver extends ContextAnnotationAutowireCandidateR target = beanFactory.doResolveDependency(descriptor, beanName, null, null); } catch (NoSuchBeanDefinitionException ex) { - target = GuiceAutowireCandidateResolver.this.injectorProvider.get() - .getInstance(Key.get(descriptor.getResolvableType().getType())); + Key key = guiceInstanceResolverKey(); + target = GuiceAutowireCandidateResolver.this.injectorProvider.get().getInstance(key); this.isGuiceResolvable = Optional.of(true); } } @@ -129,6 +132,26 @@ class GuiceAutowireCandidateResolver extends ContextAnnotationAutowireCandidateR return target; } + private Key guiceInstanceResolverKey() { + Type type = descriptor.getResolvableType().getType(); + + Optional qualifierValue = qualifierBean(descriptor).map(Qualifier::value); + if (qualifierValue.isPresent()) { + return Key.get(type, Names.named(qualifierValue.get())); + } + return Key.get(type); + } + + private Optional qualifierBean(DependencyDescriptor descriptor) { + if (descriptor.getField() != null) { + return Optional.ofNullable(descriptor.getField().getAnnotation(Qualifier.class)); + } + if (descriptor.getMethodParameter() != null) { + return Optional.ofNullable(descriptor.getMethodParameter().getParameterAnnotation(Qualifier.class)); + } + return Optional.empty(); + } + @Override public void releaseTarget(Object target) { } diff --git a/src/test/java/org/springframework/guice/PartialInjectionTests.java b/src/test/java/org/springframework/guice/PartialInjectionTests.java new file mode 100644 index 0000000..03937a2 --- /dev/null +++ b/src/test/java/org/springframework/guice/PartialInjectionTests.java @@ -0,0 +1,154 @@ +/* + * Copyright 2013-2014 the original author or authors. + * + * 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 + * + * https://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 org.springframework.guice; + +import javax.inject.Named; +import javax.inject.Singleton; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Provides; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.guice.module.BeanFactoryProvider; +import org.springframework.guice.module.SpringModule; +import org.springframework.stereotype.Component; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PartialInjectionTests { + + @Test + void shouldResolveNamedComponentsInSpringWhenUsingSetterInjection() { + Injector injector = guiceInjectorWithSpringBean(SetterInjectionExample.class); + + SetterInjectionExample example = injector.getInstance(SetterInjectionExample.class); + + assertThat(example.getNamedMessage()).isEqualTo("banana"); + } + + @Test + void shouldResolveNamedComponentsInSpringWhenUsingConstructorInjection() { + Injector injector = guiceInjectorWithSpringBean(ConstructorInjectionExample.class); + + ConstructorInjectionExample example = injector.getInstance(ConstructorInjectionExample.class); + + assertThat(example.getNamedMessage()).isEqualTo("banana"); + } + + @Test + void shouldResolveComponentsInSpringWhenUsingSetterInjection() { + Injector injector = guiceInjectorWithSpringBean(SetterInjectionExample.class); + + SetterInjectionExample example = injector.getInstance(SetterInjectionExample.class); + + assertThat(example.getUnnamedMessage()).isEqualTo("apple"); + } + + @Test + void shouldResolveComponentsInSpringWhenUsingConstructorInjection() { + Injector injector = guiceInjectorWithSpringBean(ConstructorInjectionExample.class); + + ConstructorInjectionExample example = injector.getInstance(ConstructorInjectionExample.class); + + assertThat(example.getUnnamedMessage()).isEqualTo("apple"); + } + + private Injector guiceInjectorWithSpringBean(Class classForContext) { + Class[] components = new Class[] { classForContext }; + BeanFactoryProvider beanFactoryProvider = BeanFactoryProvider.from(components); + return Guice.createInjector(new SpringModule(beanFactoryProvider), new ExampleGuiceModule()); + } + + @Component + public static class SetterInjectionExample { + + @Autowired + @Qualifier("named") + private Dependency named; + + @Autowired + private Dependency unnamed; + + public String getNamedMessage() { + return this.named.getMessage(); + } + + public String getUnnamedMessage() { + return this.unnamed.getMessage(); + } + + } + + @Component + public static class ConstructorInjectionExample { + + private final Dependency named; + + private final Dependency unnamed; + + @Autowired + public ConstructorInjectionExample(@Qualifier("named") Dependency named, Dependency unnamed) { + this.named = named; + this.unnamed = unnamed; + } + + public String getNamedMessage() { + return this.named.getMessage(); + } + + public String getUnnamedMessage() { + return this.unnamed.getMessage(); + } + + } + + public static class ExampleGuiceModule extends AbstractModule { + + @Provides + @Singleton + @Named("named") + public Dependency namedDependencyProvider() { + return new Dependency("banana"); + } + + @Provides + @Singleton + public Dependency unnamedDependencyProvider() { + return new Dependency("apple"); + } + + } + + public static class Dependency { + + private final String message; + + public Dependency(String message) { + this.message = message; + } + + public String getMessage() { + return this.message; + } + + } + +}