Skip to content

Commit 7508b5c

Browse files
authored
Merge pull request quarkusio#35776 from manovotn/issue35744
Arc - Revisit BeanContainer API
2 parents 4130ff7 + 4f2aa80 commit 7508b5c

File tree

5 files changed

+62
-96
lines changed

5 files changed

+62
-96
lines changed

extensions/arc/deployment/src/test/java/io/quarkus/arc/test/unused/UnusedExclusionTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public static class TestRecorder {
7474

7575
public void test(BeanContainer beanContainer) {
7676
// This should trigger the warning - Gama was removed
77-
Gama gama = beanContainer.beanInstance(Gama.class);
77+
Gama gama = beanContainer.beanInstanceFactory(Gama.class).create().get();
7878
// Test that fallback was used - no injection was performed
7979
Assertions.assertNull(gama.beanManager);
8080
}

extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/BeanContainer.java

Lines changed: 31 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,62 @@
11
package io.quarkus.arc.runtime;
22

33
import java.lang.annotation.Annotation;
4+
import java.util.function.Supplier;
45

56
import io.quarkus.arc.ManagedContext;
67

78
/**
89
* Represents a CDI bean container.
10+
* <p/>
11+
* Extensions using this API can also leverage arbitrary methods from running {@link io.quarkus.arc.ArcContainer}
12+
* which can be obtained by invoking a static method {@link io.quarkus.arc.Arc#container()}.
913
*/
1014
public interface BeanContainer {
1115

1216
/**
13-
* Returns a bean instance for given bean type and qualifiers.
17+
* Resolves a bean instance for given bean type and qualifiers.
1418
* <p/>
15-
* This method follows standard CDI rules meaning that if there are two or more eligible beans, an ambiguous
16-
* dependency exception is thrown.
17-
* Note that the method is allowed to return {@code null} if there is no matching bean which allows
18-
* for fallback implementations.
19+
* Performs standard CDI resolution meaning it either returns a bean instance or throws a corresponding exception
20+
* if the dependency is either unsatisfied or ambiguous.
1921
*
20-
* @param type
21-
* @param qualifiers
22-
* @return a bean instance or {@code null} if no matching bean is found
22+
* @param beanType type of the bean
23+
* @param beanQualifiers bean qualifiers
24+
* @return a bean instance; never {@code null}
2325
*/
24-
default <T> T beanInstance(Class<T> type, Annotation... qualifiers) {
25-
return beanInstanceFactory(type, qualifiers).create().get();
26-
}
27-
28-
/**
29-
* This method is deprecated and will be removed in future versions.
30-
* Use {@link #beanInstance(Class, Annotation...)} instead.
31-
* </p>
32-
* As opposed to {@link #beanInstance(Class, Annotation...)}, this method does <b>NOT</b> follow CDI
33-
* resolution rules and in case of ambiguous resolution performs a choice based on the class type parameter.
34-
*
35-
* @param type
36-
* @param qualifiers
37-
* @return a bean instance or {@code null} if no matching bean is found
38-
*/
39-
@Deprecated
40-
default <T> T instance(Class<T> type, Annotation... qualifiers) {
41-
return instanceFactory(type, qualifiers).create().get();
42-
}
26+
<T> T beanInstance(Class<T> beanType, Annotation... beanQualifiers);
4327

4428
/**
4529
* Returns an instance factory for given bean type and qualifiers.
4630
* <p/>
47-
* This method follows standard CDI rules meaning that if there are two or more beans, an ambiguous dependency
48-
* exception is thrown.
49-
* Note that the factory itself is still allowed to return {@code null} if there is no matching bean which allows
50-
* for fallback implementations.
31+
* This method performs CDI ambiguous dependency resolution and throws and exception if there are two or more beans
32+
* with given type and qualifiers.
33+
* <p/>
34+
* If no matching bean is found, uses a default fallback factory that will attempt to instantiate a non-CDI object
35+
* of the given class via no-args constructor.
36+
* <p/>
37+
* If you need custom factory behavior, take a look at {@link #beanInstanceFactory(Supplier, Class, Annotation...)}
5138
*
52-
* @param type
53-
* @param qualifiers
39+
* @param type bean type
40+
* @param qualifiers bean qualifiers
5441
* @return a bean instance factory, never {@code null}
5542
*/
5643
<T> Factory<T> beanInstanceFactory(Class<T> type, Annotation... qualifiers);
5744

5845
/**
59-
* This method is deprecated and will be removed in future versions.
60-
* Use {@link #beanInstanceFactory(Class, Annotation...)} instead.
61-
* </p>
62-
* As opposed to {@link #beanInstanceFactory(Class, Annotation...)}, this method does <b>NOT</b> follow CDI
63-
* resolution rules and in case of ambiguous resolution performs a choice based on the class type parameter.
46+
* Returns an instance factory for given bean type and qualifiers.
47+
* <p/>
48+
* This method performs CDI ambiguous dependency resolution and throws and exception if there are two or more beans
49+
* with given type and qualifiers.
50+
* <p/>
51+
* If no matching bean is found, delegates all calls to the supplied factory fallback.
6452
*
65-
* @param type
66-
* @param qualifiers
53+
* @param fallbackSupplier supplier to delegate to if there is no bean
54+
* @param type bean type
55+
* @param qualifiers bean qualifiers
6756
* @return a bean instance factory, never {@code null}
6857
*/
69-
@Deprecated
70-
<T> Factory<T> instanceFactory(Class<T> type, Annotation... qualifiers);
58+
<T> Factory<T> beanInstanceFactory(Supplier<Factory<T>> fallbackSupplier, Class<T> type,
59+
Annotation... qualifiers);
7160

7261
/**
7362
* <pre>

extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/BeanContainerImpl.java

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,36 @@ class BeanContainerImpl implements BeanContainer {
2121
this.container = container;
2222
}
2323

24+
@Override
25+
public <T> T beanInstance(Class<T> beanType, Annotation... beanQualifiers) {
26+
return container.select(beanType, beanQualifiers).get();
27+
}
28+
2429
@Override
2530
public <T> Factory<T> beanInstanceFactory(Class<T> type, Annotation... qualifiers) {
2631
Supplier<InstanceHandle<T>> handleSupplier = container.beanInstanceSupplier(type, qualifiers);
27-
return createFactory(handleSupplier, type, qualifiers);
32+
return createFactory(handleSupplier, null, type, qualifiers);
2833
}
2934

3035
@Override
31-
public <T> Factory<T> instanceFactory(Class<T> type, Annotation... qualifiers) {
32-
Supplier<InstanceHandle<T>> handleSupplier = container.instanceSupplier(type, qualifiers);
33-
return createFactory(handleSupplier, type, qualifiers);
36+
public <T> Factory<T> beanInstanceFactory(Supplier<Factory<T>> fallbackSupplier, Class<T> type,
37+
Annotation... qualifiers) {
38+
Supplier<InstanceHandle<T>> handleSupplier = container.beanInstanceSupplier(type, qualifiers);
39+
return createFactory(handleSupplier, fallbackSupplier, type, qualifiers);
3440
}
3541

36-
private <T> Factory<T> createFactory(Supplier<InstanceHandle<T>> handleSupplier, Class<T> type, Annotation... qualifiers) {
42+
private <T> Factory<T> createFactory(Supplier<InstanceHandle<T>> handleSupplier, Supplier<Factory<T>> fallbackSupplier,
43+
Class<T> type, Annotation... qualifiers) {
3744
if (handleSupplier == null) {
3845
LOGGER.debugf(
3946
"No matching bean found for type %s and qualifiers %s. The bean might have been marked as unused and removed during build.",
4047
type, Arrays.toString(qualifiers));
41-
return new DefaultInstanceFactory<>(type);
48+
if (fallbackSupplier != null) {
49+
return fallbackSupplier.get();
50+
} else {
51+
// by default, if there is no bean, return factory that tries to instantiate non-cdi object
52+
return new DefaultInstanceFactory<>(type);
53+
}
4254
}
4355
return new Factory<T>() {
4456
@Override
@@ -64,7 +76,16 @@ public ManagedContext requestContext() {
6476
return container.requestContext();
6577
}
6678

67-
private static final class DefaultInstanceFactory<T> implements BeanContainer.Factory<T> {
79+
/**
80+
* A default fallback {@link Factory} implementation used by
81+
* {@link BeanContainer#beanInstanceFactory(Class, Annotation...)}.
82+
* <p/>
83+
* This factory attempts to create instances of given class by calling their no-arg constructor. Any exceptions
84+
* related to lack of such constructor of failure to invoke it are simply re-thrown.
85+
*
86+
* @param <T> represents the type that this factory can create
87+
*/
88+
private final class DefaultInstanceFactory<T> implements BeanContainer.Factory<T> {
6889

6990
private final Class<T> type;
7091

@@ -87,5 +108,4 @@ public T get() {
87108
}
88109
}
89110
}
90-
91111
}

independent-projects/arc/runtime/src/main/java/io/quarkus/arc/ArcContainer.java

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -88,34 +88,13 @@ public interface ArcContainer {
8888
/**
8989
* Returns a supplier that can be used to create new instances, or null if no matching bean can be found.
9090
*
91-
* Note that if there are multiple sub classes of the given type this will return the exact match. This means
92-
* that this can be used to directly instantiate superclasses of other beans without causing problems. This behavior differs
93-
* to standard CDI rules where an ambiguous dependency would exist.
94-
*
95-
* see https://github.com/quarkusio/quarkus/issues/3369
96-
*
9791
* @param type
9892
* @param qualifiers
9993
* @param <T>
10094
* @return
10195
*/
10296
<T> Supplier<InstanceHandle<T>> beanInstanceSupplier(Class<T> type, Annotation... qualifiers);
10397

104-
/**
105-
* This method is deprecated and will be removed in future versions.
106-
* Use {@link #beanInstanceSupplier(Class, Annotation...)} instead.
107-
* </p>
108-
* As opposed to {@link #beanInstanceSupplier(Class, Annotation...)}, this method does <b>NOT</b> follow CDI
109-
* resolution rules and in case of ambiguous resolution performs a choice based on the class type parameter.
110-
*
111-
* @param type
112-
* @param qualifiers
113-
* @return
114-
* @param <T>
115-
*/
116-
@Deprecated
117-
<T> Supplier<InstanceHandle<T>> instanceSupplier(Class<T> type, Annotation... qualifiers);
118-
11998
/**
12099
*
121100
* @param bean

independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -291,16 +291,6 @@ public <X> InstanceHandle<X> instance(Type type, Annotation... qualifiers) {
291291

292292
@Override
293293
public <T> Supplier<InstanceHandle<T>> beanInstanceSupplier(Class<T> type, Annotation... qualifiers) {
294-
return createInstanceSupplier(false, type, qualifiers);
295-
}
296-
297-
@Override
298-
public <T> Supplier<InstanceHandle<T>> instanceSupplier(Class<T> type, Annotation... qualifiers) {
299-
return createInstanceSupplier(true, type, qualifiers);
300-
}
301-
302-
private <T> Supplier<InstanceHandle<T>> createInstanceSupplier(boolean resolveAmbiguities, Class<T> type,
303-
Annotation... qualifiers) {
304294
if (qualifiers == null || qualifiers.length == 0) {
305295
qualifiers = new Annotation[] { Default.Literal.INSTANCE };
306296
}
@@ -311,20 +301,8 @@ private <T> Supplier<InstanceHandle<T>> createInstanceSupplier(boolean resolveAm
311301
}
312302
Set<InjectableBean<?>> filteredBean = resolvedBeans;
313303
if (resolvedBeans.size() > 1) {
314-
if (resolveAmbiguities) {
315-
// this is non-standard CDI behavior that we momentarily keep to retain compatibility
316-
// if there are multiple beans we look for an exact match
317-
// this method is only called with the exact type required
318-
// so ignoring subclasses is the correct behaviour
319-
filteredBean = new HashSet<>();
320-
for (InjectableBean<?> i : resolvedBeans) {
321-
if (i.getBeanClass().equals(type)) {
322-
filteredBean.add(i);
323-
}
324-
}
325-
} else {
326-
throw new AmbiguousResolutionException("Beans: " + resolvedBeans);
327-
}
304+
throw new AmbiguousResolutionException("Beans: " + resolvedBeans);
305+
328306
}
329307
@SuppressWarnings("unchecked")
330308
InjectableBean<T> bean = filteredBean.size() != 1 ? null

0 commit comments

Comments
 (0)