Skip to content

Commit d12a937

Browse files
committed
#95 - Add support for supplying qualifer name bean via withBean() - withBean(name, type, suppliedImplementation)
1 parent 160e6f4 commit d12a937

File tree

12 files changed

+119
-23
lines changed

12 files changed

+119
-23
lines changed

inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,9 @@ boolean isExtraInjectionRequired() {
358358

359359
void buildAddFor(Append writer) {
360360
writer.append(" if (builder.isAddBeanFor(");
361+
if (name != null) {
362+
writer.append("\"%s\", ", name);
363+
}
361364
if (addForType != null) {
362365
writer.append(addForType).append(".class, ");
363366
}

inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,10 @@ void addImports(Set<String> importTypes) {
177177
}
178178

179179
void buildAddFor(Append writer) {
180-
181180
writer.append(" if (builder.isAddBeanFor(");
181+
if (name != null) {
182+
writer.append("\"%s\", ", name);
183+
}
182184
if (addForType != null) {
183185
writer.append(addForType).append(".class, ");
184186
}

inject-test/src/test/java/org/example/coffee/qualifier/StoreManagerWithSetterQualifierTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,31 @@ void redStore() {
1515
assertThat(manager.greenStore()).isEqualTo("green");
1616
}
1717
}
18+
19+
@Test
20+
void namedTestDouble() {
21+
try (BeanContext context = BeanContext.newBuilder()
22+
.withBean("Blue", SomeStore.class, () -> "TD Blue")
23+
.withBean("Green", SomeStore.class, () -> "TD Green")
24+
.build()) {
25+
26+
StoreManagerWithSetterQualifier manager = context.getBean(StoreManagerWithSetterQualifier.class);
27+
assertThat(manager.blueStore()).isEqualTo("TD Blue");
28+
assertThat(manager.greenStore()).isEqualTo("TD Green");
29+
}
30+
}
31+
32+
@Test
33+
void namedTestDouble_expect_otherNamedStillWired() {
34+
try (BeanContext context = BeanContext.newBuilder()
35+
.withBean("Blue", SomeStore.class, () -> "TD Blue Only")
36+
// with GreenStore still wired
37+
.build()) {
38+
39+
StoreManagerWithSetterQualifier manager = context.getBean(StoreManagerWithSetterQualifier.class);
40+
assertThat(manager.blueStore()).isEqualTo("TD Blue Only");
41+
assertThat(manager.greenStore()).isEqualTo("green");
42+
}
43+
}
44+
1845
}

inject/src/main/java/io/avaje/inject/BeanContextBuilder.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,15 @@ public interface BeanContextBuilder {
168168
*/
169169
<D> BeanContextBuilder withBean(Class<D> type, D bean);
170170

171+
/**
172+
* Add a supplied bean instance with the given name and injection type.
173+
*
174+
* @param name The name qualifier
175+
* @param type The dependency injection type this bean is target for
176+
* @param bean The supplied bean instance to use (typically a test mock)
177+
*/
178+
<D> BeanContextBuilder withBean(String name, Class<D> type, D bean);
179+
171180
/**
172181
* Use a mockito mock when injecting this bean type.
173182
*

inject/src/main/java/io/avaje/inject/DBeanContextBuilder.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,19 @@ public BeanContextBuilder withIgnoreMissingModuleDependencies() {
6767
@SuppressWarnings({"unchecked", "rawtypes"})
6868
public BeanContextBuilder withBeans(Object... beans) {
6969
for (Object bean : beans) {
70-
suppliedBeans.add(new SuppliedBean(suppliedType(bean.getClass()), bean));
70+
suppliedBeans.add(new SuppliedBean(null, suppliedType(bean.getClass()), bean));
7171
}
7272
return this;
7373
}
7474

7575
@Override
76-
public <D> DBeanContextBuilder withBean(Class<D> type, D bean) {
77-
suppliedBeans.add(new SuppliedBean<>(type, bean));
76+
public <D> BeanContextBuilder withBean(Class<D> type, D bean) {
77+
return withBean(null, type, bean);
78+
}
79+
80+
@Override
81+
public <D> BeanContextBuilder withBean(String name, Class<D> type, D bean) {
82+
suppliedBeans.add(new SuppliedBean<>(name, type, bean));
7883
return this;
7984
}
8085

@@ -85,7 +90,7 @@ public BeanContextBuilder withMock(Class<?> type) {
8590

8691
@Override
8792
public <D> DBeanContextBuilder withMock(Class<D> type, Consumer<D> consumer) {
88-
suppliedBeans.add(new SuppliedBean<>(type, null, consumer));
93+
suppliedBeans.add(new SuppliedBean<>(null, type, null, consumer));
8994
return this;
9095
}
9196

inject/src/main/java/io/avaje/inject/spi/Builder.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ static Builder newBuilder(String name, String[] provides, String[] dependsOn) {
6363
/**
6464
* Return true if the bean should be created and registered with the context.
6565
* <p/>
66-
* Returning false means there has been a (test double) bean already registered and
66+
* Returning false means there has been a supplied bean already registered and
6767
* that we should skip the creation and registration for this bean.
6868
*
6969
* @param addForType The interface that the bean implements and provides
@@ -74,13 +74,36 @@ static Builder newBuilder(String name, String[] provides, String[] dependsOn) {
7474
/**
7575
* Return true if the bean should be created and registered with the context.
7676
* <p/>
77-
* Returning false means there has been a (test double) bean already registered and
77+
* Returning false means there has been a supplied bean already registered and
78+
* that we should skip the creation and registration for this bean.
79+
*
80+
* @param name The qualifier name
81+
* @param addForType The interface that the bean implements and provides
82+
* @param injectTarget The actual bean type we are looking to create and register
83+
*/
84+
boolean isAddBeanFor(String name, Class<?> addForType, Class<?> injectTarget);
85+
86+
/**
87+
* Return true if the bean should be created and registered with the context.
88+
* <p/>
89+
* Returning false means there has been a supplied bean already registered and
7890
* that we should skip the creation and registration for this bean.
7991
*
8092
* @param injectTarget The actual bean type we are looking to create and register
8193
*/
8294
boolean isAddBeanFor(Class<?> injectTarget);
8395

96+
/**
97+
* Return true if the bean should be created and registered with the context.
98+
* <p/>
99+
* Returning false means there has been a supplied bean already registered and
100+
* that we should skip the creation and registration for this bean.
101+
*
102+
* @param name The qualifier name
103+
* @param injectTarget The actual bean type we are looking to create and register
104+
*/
105+
boolean isAddBeanFor(String name, Class<?> injectTarget);
106+
84107
/**
85108
* Register the bean instance into the context.
86109
* <p>

inject/src/main/java/io/avaje/inject/spi/DBeanMap.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,7 @@ void add(List<SuppliedBean> suppliedBeans) {
3939
@SuppressWarnings("rawtypes")
4040
private void addSuppliedBean(SuppliedBean supplied) {
4141
Class<?> suppliedType = supplied.getType();
42-
Named annotation = suppliedType.getAnnotation(Named.class);
43-
String name = (annotation == null) ? null : annotation.value();
44-
45-
DContextEntryBean entryBean = DContextEntryBean.of(supplied.getBean(), name, SUPPLIED);
42+
DContextEntryBean entryBean = DContextEntryBean.of(supplied.getBean(), supplied.name(), SUPPLIED);
4643
beans.computeIfAbsent(suppliedType.getCanonicalName(), s -> new DContextEntry()).add(entryBean);
4744
for (Class<?> anInterface : suppliedType.getInterfaces()) {
4845
beans.computeIfAbsent(anInterface.getCanonicalName(), s -> new DContextEntry()).add(entryBean);
@@ -93,8 +90,8 @@ void addAll(Class type, List list) {
9390
/**
9491
* Return true if there is a supplied bean for this type.
9592
*/
96-
boolean isSupplied(String canonicalName) {
93+
boolean isSupplied(String canonicalName, String qualifierName) {
9794
DContextEntry entry = beans.get(canonicalName);
98-
return entry != null && entry.isSupplied();
95+
return entry != null && entry.isSupplied(qualifierName);
9996
}
10097
}

inject/src/main/java/io/avaje/inject/spi/DBuilder.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,18 +103,28 @@ public void setParent(Builder parent) {
103103

104104
@Override
105105
public boolean isAddBeanFor(Class<?> addForType, Class<?> injectTarget) {
106+
return isAddBeanFor(null, addForType, injectTarget);
107+
}
108+
109+
@Override
110+
public boolean isAddBeanFor(String name, Class<?> addForType, Class<?> injectTarget) {
106111
if (parent == null) {
107112
return true;
108113
}
109114
this.injectTarget = injectTarget;
110-
return parent.isAddBeanFor(addForType);
115+
return parent.isAddBeanFor(name, addForType, injectTarget);
111116
}
112117

113118
@Override
114119
public boolean isAddBeanFor(Class<?> injectTarget) {
115120
return isAddBeanFor(injectTarget, injectTarget);
116121
}
117122

123+
@Override
124+
public boolean isAddBeanFor(String name, Class<?> injectTarget) {
125+
return isAddBeanFor(name, injectTarget, injectTarget);
126+
}
127+
118128
@Override
119129
public <T> Set<T> getSet(Class<T> interfaceType) {
120130
return new LinkedHashSet<>(getList(interfaceType));

inject/src/main/java/io/avaje/inject/spi/DBuilderExtn.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,18 @@ class DBuilderExtn extends DBuilder {
2828
}
2929
}
3030

31+
@Override
32+
public boolean isAddBeanFor(Class<?> addForType, Class<?> injectTarget) {
33+
return isAddBeanFor(null, addForType, injectTarget);
34+
}
35+
3136
/**
3237
* Checking for supplied beans (mocks).
3338
*/
3439
@Override
35-
public boolean isAddBeanFor(Class<?> addForType, Class<?> injectTarget) {
40+
public boolean isAddBeanFor(String qualifierName, Class<?> addForType, Class<?> injectTarget) {
3641
if (hasSuppliedBeans) {
37-
return !beanMap.isSupplied(addForType.getCanonicalName());
42+
return !beanMap.isSupplied(addForType.getCanonicalName(), qualifierName);
3843
}
3944
return true;
4045
}

inject/src/main/java/io/avaje/inject/spi/DContextEntry.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ void add(DContextEntryBean entryBean) {
4545
/**
4646
* Return true if a supplied bean is one of the entries.
4747
*/
48-
boolean isSupplied() {
48+
boolean isSupplied(String qualifierName) {
4949
for (DContextEntryBean entry : entries) {
50-
if (entry.isSupplied()) {
50+
if (entry.isSupplied(qualifierName)) {
5151
return true;
5252
}
5353
}

inject/src/main/java/io/avaje/inject/spi/DContextEntryBean.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ boolean isSecondary() {
5858
return flag == BeanEntry.SECONDARY;
5959
}
6060

61-
boolean isSupplied() {
62-
return flag == BeanEntry.SUPPLIED;
61+
boolean isSupplied(String qualifierName) {
62+
return flag == BeanEntry.SUPPLIED && (qualifierName == null || qualifierName.equals(this.name));
6363
}
6464

6565
@SuppressWarnings({"unchecked", "rawtypes"})

inject/src/main/java/io/avaje/inject/spi/SuppliedBean.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.mockito.Mockito;
44

5+
import javax.inject.Named;
56
import java.util.function.Consumer;
67

78
/**
@@ -14,6 +15,8 @@
1415
*/
1516
public class SuppliedBean<B> {
1617

18+
private final String name;
19+
1720
private final Class<B> type;
1821

1922
private final Consumer<B> consumer;
@@ -23,14 +26,15 @@ public class SuppliedBean<B> {
2326
/**
2427
* Create with a given target type and bean instance.
2528
*/
26-
public SuppliedBean(Class<B> type, B bean) {
27-
this(type, bean, null);
29+
public SuppliedBean(String name, Class<B> type, B bean) {
30+
this(name, type, bean, null);
2831
}
2932

3033
/**
3134
* Create with a consumer to setup the mock.
3235
*/
33-
public SuppliedBean(Class<B> type, B bean, Consumer<B> consumer) {
36+
public SuppliedBean(String name, Class<B> type, B bean, Consumer<B> consumer) {
37+
this.name = name;
3438
this.type = type;
3539
this.bean = bean;
3640
this.consumer = consumer;
@@ -56,4 +60,15 @@ public B getBean() {
5660
}
5761
return bean;
5862
}
63+
64+
/**
65+
* Return the qualifier name of the supplied bean.
66+
*/
67+
public String name() {
68+
if (name != null) {
69+
return name;
70+
}
71+
Named annotation = type.getAnnotation(Named.class);
72+
return (annotation == null) ? null : annotation.value();
73+
}
5974
}

0 commit comments

Comments
 (0)