fix: Guice Cannot Inject Beans with Custom Annotations

SpringModule binds custom guice Providers for Spring managed beans so that Guice can inject beans from Spring Context.
Due to a bug in the Provider, Guice can only inject Spring beans that either don't have any qualifier annotations or only have Named annotation as a qualifier.

This fix enables Guice to inject beans with custom qualifier annotations as well.
Custom qualifier annotations do not need to be marker annotations (in other words, they can have attributes).

Other changes include:
Using factory method metadata of annotated bean definition rather than using custom code to retrieve factory method and its annotations

Added more test cases to validate various qualifier annotation scenarios.
This commit is contained in:
Mehmet Mustafa Yilmaz
2022-10-12 17:36:05 -07:00
committed by Dave Syer
parent e133fee483
commit ce15b8e580
2 changed files with 182 additions and 80 deletions

View File

@@ -16,7 +16,12 @@
package org.springframework.guice.module;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Qualifier;
import com.google.inject.ConfigurationException;
import com.google.inject.Guice;
@@ -35,6 +40,7 @@ import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
@@ -68,6 +74,43 @@ public class SpringModuleMetadataTests {
assertThat(injector.getInstance(Key.get(Service.class, Names.named("service")))).isNotNull();
}
@Test
public void threeServicesByQualifier() throws Exception {
Injector injector = createInjector(PrimaryConfig.class, QualifiedConfig.class);
assertThat(injector.getInstance(
Key.get(Service.class, ServiceQualifierAnnotated.class.getAnnotation(ServiceQualifier.class))))
.extracting("name").isEqualTo("emptyQualifierService");
assertThat(injector.getInstance(
Key.get(Service.class, EmptyServiceQualifierAnnotated.class.getAnnotation(ServiceQualifier.class))))
.extracting("name").isEqualTo("emptyQualifierService");
assertThat(injector.getInstance(
Key.get(Service.class, MyServiceQualifierAnnotated.class.getAnnotation(ServiceQualifier.class))))
.extracting("name").isEqualTo("myService");
assertThat(injector.getInstance(Key.get(Service.class, Names.named("namedService")))).extracting("name")
.isEqualTo("namedService");
assertThat(injector.getInstance(Key.get(Service.class, Names.named("namedServiceWithADifferentBeanName"))))
.extracting("name").isEqualTo("namedServiceWithADifferentBeanName");
assertThat(injector.getInstance(Service.class)).extracting("name").isEqualTo("primary");
// Test cases where we don't expect to find any bindings
assertThatCode(() -> injector.getInstance(Key.get(Service.class, Names.named("randomService"))))
.isInstanceOf(ConfigurationException.class);
assertThatCode(() -> injector.getInstance(
Key.get(Service.class, NoServiceQualifierAnnotated.class.getAnnotation(ServiceQualifier.class))))
.isInstanceOf(ConfigurationException.class);
assertThatCode(() -> injector.getInstance(Key.get(Service.class, UnboundServiceQualifier.class)))
.isInstanceOf(ConfigurationException.class);
}
@Test
public void includes() throws Exception {
Injector injector = createInjector(TestConfig.class, MetadataIncludesConfig.class);
@@ -92,10 +135,23 @@ public class SpringModuleMetadataTests {
interface Service {
String getName();
}
protected static class MyService implements Service {
private final String name;
protected MyService(String name) {
this.name = name;
}
@Override
public String getName() {
return this.name;
}
}
public static class Foo {
@@ -135,7 +191,7 @@ public class SpringModuleMetadataTests {
@Bean
public Service service() {
return new MyService();
return new MyService("service");
}
}
@@ -146,7 +202,7 @@ public class SpringModuleMetadataTests {
@Bean
@Primary
public Service primary() {
return new MyService();
return new MyService("primary");
}
}
@@ -156,7 +212,36 @@ public class SpringModuleMetadataTests {
@Bean
public Service more() {
return new MyService();
return new MyService("more");
}
}
@Configuration
public static class QualifiedConfig {
@Bean
@Named("namedService")
public Service namedService() {
return new MyService("namedService");
}
@Bean
@Named("namedServiceWithADifferentBeanName")
public Service anotherNamedService() {
return new MyService("namedServiceWithADifferentBeanName");
}
@Bean
@ServiceQualifier
public Service emptyQualifierService() {
return new MyService("emptyQualifierService");
}
@Bean
@ServiceQualifier(type = "myService")
public Service myService(@Named("namedService") Service service) {
return new MyService("myService");
}
}
@@ -166,4 +251,38 @@ public class SpringModuleMetadataTests {
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@interface ServiceQualifier {
String type() default "";
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@interface UnboundServiceQualifier {
}
@ServiceQualifier
interface ServiceQualifierAnnotated {
}
@ServiceQualifier(type = "")
interface EmptyServiceQualifierAnnotated {
}
@ServiceQualifier(type = "myService")
interface MyServiceQualifierAnnotated {
}
@ServiceQualifier(type = "noService")
interface NoServiceQualifierAnnotated {
}
}