diff --git a/blackbox-test-inject/src/main/java/org/example/myapp/config/BFace.java b/blackbox-test-inject/src/main/java/org/example/myapp/config/BFace.java new file mode 100644 index 00000000..4b3e0683 --- /dev/null +++ b/blackbox-test-inject/src/main/java/org/example/myapp/config/BFace.java @@ -0,0 +1,6 @@ +package org.example.myapp.config; + +public interface BFace { + + String hi(); +} diff --git a/blackbox-test-inject/src/main/java/org/example/myapp/config/BFactory.java b/blackbox-test-inject/src/main/java/org/example/myapp/config/BFactory.java new file mode 100644 index 00000000..1c3c95b7 --- /dev/null +++ b/blackbox-test-inject/src/main/java/org/example/myapp/config/BFactory.java @@ -0,0 +1,44 @@ +package org.example.myapp.config; + +import io.avaje.inject.Bean; +import io.avaje.inject.Factory; +import jakarta.inject.Named; + +import java.util.Optional; + +@Factory +public class BFactory { + + @Named + @Bean + BFace one() { + return new TheBFace("one"); + } + + @Named + @Bean + BFace two() { + return new TheBFace("two"); + } + + @Named + @Bean + Optional three() { + return Optional.of(new TheBFace("three")); + } + + + static class TheBFace implements BFace { + + private final String msg; + + TheBFace(String msg) { + this.msg = msg; + } + + @Override + public String hi() { + return msg; + } + } +} diff --git a/blackbox-test-inject/src/test/java/org/example/myapp/config/BFactoryTest.java b/blackbox-test-inject/src/test/java/org/example/myapp/config/BFactoryTest.java new file mode 100644 index 00000000..578ea8d9 --- /dev/null +++ b/blackbox-test-inject/src/test/java/org/example/myapp/config/BFactoryTest.java @@ -0,0 +1,31 @@ +package org.example.myapp.config; + +import io.avaje.inject.BeanScope; +import io.avaje.inject.test.TestBeanScope; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; + +class BFactoryTest { + + @Test + void beanMethodsOfSameType() { + try (BeanScope scope = TestBeanScope.builder().build()) { + BFace one = scope.get(BFace.class, "one"); + BFace two = scope.get(BFace.class, "two"); + BFace three = scope.get(BFace.class, "three"); + assertThat(one).isNotNull(); + assertThat(two).isNotNull(); + assertThat(three).isNotNull(); + + List list = scope.list(BFace.class); + assertThat(list).hasSize(3); + + List hi = list.stream().map(BFace::hi).collect(Collectors.toList()); + assertThat(hi).containsOnly("one", "two", "three"); + } + } +} diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java b/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java index 4c14b8c1..02ca5cd7 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java @@ -499,6 +499,14 @@ MethodParam observeParam() { return observeParameter; } + String qualifiedKey() { + return name + ':' + genericType.full(); + } + + boolean isVoid() { + return isVoid; + } + static class MethodParam { private final VariableElement element; diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/TypeExtendsInjection.java b/inject-generator/src/main/java/io/avaje/inject/generator/TypeExtendsInjection.java index 612f47b7..621cb5f7 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/TypeExtendsInjection.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/TypeExtendsInjection.java @@ -253,8 +253,24 @@ DestroyMethods.DestroyMethod matchPreDestroy(String returnTypeRaw) { } void validate() { + validateFactoryMethodDuplicates(); for (DestroyMethods.DestroyMethod destroyMethod : factoryPreDestroyMethods.unmatched()) { logError(destroyMethod.element(), "Unused @PreDestroy method, no matching @Bean method for type " + destroyMethod.matchType()); } } + + void validateFactoryMethodDuplicates() { + var map = new HashMap(); + for (MethodReader method : factoryMethods) { + if (!method.isVoid()) { + var clashMethod = map.put(method.qualifiedKey(), method); + if (clashMethod != null) { + var msg = String.format("@Bean method %s() returns the same type as with method %s() without a unique name qualifier." + + " Add @Named or a qualifier annotation to allow both @Bean methods.", + method.name(), clashMethod.name()); + logError(method.element(), msg); + } + } + } + } } diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/Util.java b/inject-generator/src/main/java/io/avaje/inject/generator/Util.java index f919350c..de9b34a9 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/Util.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/Util.java @@ -294,7 +294,12 @@ static String commonParent(String currentTop, String aPackage) { static String named(Element p) { final NamedPrism named = NamedPrism.getInstanceOn(p); if (named != null) { - return named.value().replace("\"", "\\\""); + String raw = named.value(); + if (raw.isEmpty()) { + // default to the method name + raw = p.getSimpleName().toString(); + } + return raw.replace("\"", "\\\""); } for (final AnnotationMirror annotationMirror : p.getAnnotationMirrors()) { final DeclaredType annotationType = annotationMirror.getAnnotationType();