This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new d7cd102 Add support for named beans in BeanStore.
d7cd102 is described below
commit d7cd1024e967e23720ddf6b227ba9be3a7c92930
Author: JamesBognar <[email protected]>
AuthorDate: Mon Feb 22 19:43:01 2021 -0500
Add support for named beans in BeanStore.
---
.../apache/juneau/cp/BeanCreateMethodFinder.java | 4 +-
.../main/java/org/apache/juneau/cp/BeanStore.java | 124 ++++++++++++++-------
.../org/apache/juneau/utils/MethodInvoker.java | 4 +-
.../juneau/rest/springboot/SpringBeanStore.java | 9 +-
.../org/apache/juneau/rest/RestContextBuilder.java | 6 +-
.../{BeanFactory_Test.java => BeanStore_Test.java} | 73 ++++++++++--
6 files changed, 163 insertions(+), 57 deletions(-)
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanCreateMethodFinder.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanCreateMethodFinder.java
index 138b52b..d52596f 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanCreateMethodFinder.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanCreateMethodFinder.java
@@ -76,10 +76,10 @@ public class BeanCreateMethodFinder<T> {
ClassInfo ci = ClassInfo.of(resource);
for (MethodInfo m : ci.getPublicMethods()) {
if (m.isAll(NOT_DEPRECATED) &&
m.hasReturnType(beanType) && m.getSimpleName().equals(methodName) &&
(!m.hasAnnotation(BeanIgnore.class))) {
- List<ClassInfo> missing =
beanStore.getMissingParamTypes(m.getParamTypes());
+ List<ClassInfo> missing =
beanStore.getMissingParamTypes(m.getParams());
if (missing.isEmpty() &&
m.hasAllArgs(requiredParams)) {
this.method = m;
- this.args =
beanStore.getParams(m.getParamTypes());
+ this.args =
beanStore.getParams(m.getParams());
}
}
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanStore.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanStore.java
index 16e2af5..dc564e1 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanStore.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanStore.java
@@ -15,6 +15,7 @@ package org.apache.juneau.cp;
import static org.apache.juneau.internal.StringUtils.*;
import static org.apache.juneau.reflect.ReflectFlags.*;
+import java.lang.annotation.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
@@ -28,7 +29,7 @@ import org.apache.juneau.reflect.*;
/**
* Java bean store.
- *
+ *
* <p>
* Used for bean injection.
*/
@@ -39,7 +40,7 @@ public class BeanStore {
*/
public static final class Null extends BeanStore {}
- private final Map<Class<?>,Supplier<?>> beanMap = new
ConcurrentHashMap<>();
+ private final Map<String,Supplier<?>> beanMap = new
ConcurrentHashMap<>();
private final Optional<BeanStore> parent;
private final Optional<Object> outer;
@@ -92,17 +93,19 @@ public class BeanStore {
}
/**
- * Returns the bean of the specified type.
+ * Returns the named bean of the specified type.
*
* @param <T> The type of bean to return.
* @param c The type of bean to return.
+ * @param name The bean name.
* @return The bean.
*/
@SuppressWarnings("unchecked")
- public <T> Optional<T> getBean(Class<T> c) {
- Supplier<?> o = beanMap.get(c);
+ public <T> Optional<T> getBean(String name, Class<T> c) {
+ String key = name == null ? c.getName() : name;
+ Supplier<?> o = beanMap.get(key);
if (o == null && parent.isPresent())
- return parent.get().getBean(c);
+ return parent.get().getBean(name, c);
T t = (T)(o == null ? null : o.get());
return Optional.ofNullable(t);
}
@@ -110,11 +113,12 @@ public class BeanStore {
/**
* Returns the bean of the specified type.
*
+ * @param <T> The type of bean to return.
* @param c The type of bean to return.
* @return The bean.
*/
- public Optional<?> getBean(ClassInfo c) {
- return getBean(c.inner());
+ public <T> Optional<T> getBean(Class<T> c) {
+ return getBean(c.getName(), c);
}
/**
@@ -126,10 +130,22 @@ public class BeanStore {
* @return This object (for method chaining).
*/
public <T> BeanStore addBean(Class<T> c, T t) {
+ return addBean(c.getName(), t);
+ }
+
+ /**
+ * Adds a named bean of the specified type to this factory.
+ *
+ * @param <T> The class to associate this bean with.
+ * @param t The bean.
+ * @param name The bean name if this is a named bean.
+ * @return This object (for method chaining).
+ */
+ public <T> BeanStore addBean(String name, T t) {
if (t == null)
- beanMap.remove(c);
+ beanMap.remove(name);
else
- beanMap.put(c, ()->t);
+ beanMap.put(name, ()->t);
return this;
}
@@ -144,7 +160,7 @@ public class BeanStore {
@SuppressWarnings("unchecked")
public <T> BeanStore addBeans(Class<T> c, T t) {
if (t == null)
- beanMap.remove(c);
+ beanMap.remove(c.getName());
else {
addBean(c, t);
Class<T> c2 = (Class<T>)t.getClass();
@@ -164,11 +180,23 @@ public class BeanStore {
* @param t The bean supplier.
* @return This object (for method chaining).
*/
- public <T> BeanStore addBeanSupplier(Class<T> c, Supplier<T> t) {
+ public <T> BeanStore addSupplier(Class<T> c, Supplier<T> t) {
+ return addSupplier(c.getName(), t);
+ }
+
+ /**
+ * Adds a named bean supplier of the specified type to this factory.
+ *
+ * @param <T> The class to associate this bean with.
+ * @param t The bean supplier.
+ * @param name The bean name.
+ * @return This object (for method chaining).
+ */
+ public <T> BeanStore addSupplier(String name, Supplier<T> t) {
if (t == null)
- beanMap.remove(c);
+ beanMap.remove(name);
else
- beanMap.put(c, t);
+ beanMap.put(name, t);
return this;
}
@@ -179,17 +207,21 @@ public class BeanStore {
* @return <jk>true</jk> if this factory contains the specified bean
type instance.
*/
public boolean hasBean(Class<?> c) {
- return getBean(c).isPresent();
+ return hasBean(c.getName());
}
/**
- * Returns <jk>true</jk> if this factory contains the specified bean
type instance.
+ * Returns <jk>true</jk> if this factory contains a bean with the
specified name.
*
- * @param c The bean type to check.
- * @return <jk>true</jk> if this factory contains the specified bean
type instance.
+ * @param name The bean name.
+ * @return <jk>true</jk> if this factory contains a bean with the
specified name.
*/
- public final boolean hasBean(ClassInfo c) {
- return hasBean(c.inner());
+ public boolean hasBean(String name) {
+ if (getBean(name, Object.class).isPresent())
+ return true;
+ if (parent.isPresent())
+ return parent.get().hasBean(name);
+ return false;
}
/**
@@ -218,7 +250,7 @@ public class BeanStore {
if (m.isAll(STATIC, NOT_DEPRECATED) &&
m.hasReturnType(c) && (!m.hasAnnotation(BeanIgnore.class))) {
String n = m.getSimpleName();
if (isOneOf(n, "create","getInstance")) {
- List<ClassInfo> missing =
getMissingParamTypes(m.getParamTypes());
+ List<ClassInfo> missing =
getMissingParamTypes(m.getParams());
if (missing.isEmpty()) {
if (m.getParamCount() >
matchedCreatorParams) {
matchedCreatorParams =
m.getParamCount();
@@ -232,7 +264,7 @@ public class BeanStore {
}
if (matchedCreator != null)
- return matchedCreator.invoke(null,
getParams(matchedCreator.getParamTypes()));
+ return matchedCreator.invoke(null,
getParams(matchedCreator.getParams()));
if (ci.isInterface())
throw new ExecutableException("Could not instantiate
class {0}: {1}.", c.getName(), msg != null ? msg.get() : "Class is an
interface");
@@ -243,7 +275,7 @@ public class BeanStore {
int matchedConstructorParams = -1;
for (ConstructorInfo cc : ci.getPublicConstructors()) {
- List<ClassInfo> missing =
getMissingParamTypes(cc.getParamTypes());
+ List<ClassInfo> missing =
getMissingParamTypes(cc.getParams());
if (missing.isEmpty()) {
if (cc.getParamCount() >
matchedConstructorParams) {
matchedConstructorParams =
cc.getParamCount();
@@ -255,7 +287,7 @@ public class BeanStore {
}
if (matchedConstructor != null)
- return
matchedConstructor.invoke(getParams(matchedConstructor.getParamTypes()));
+ return
matchedConstructor.invoke(getParams(matchedConstructor.getParams()));
if (msg == null)
msg = () -> "Public constructor or creator not found";
@@ -324,19 +356,24 @@ public class BeanStore {
/**
* Given the list of param types, returns a list of types that are
missing from this factory.
*
- * @param paramTypes The param types to chec.
+ * @param params The param types to chec.
* @return A list of types that are missing from this factory.
*/
- public List<ClassInfo> getMissingParamTypes(List<ClassInfo> paramTypes)
{
+ public List<ClassInfo> getMissingParamTypes(List<ParamInfo> params) {
List<ClassInfo> l = AList.create();
- for (int i = 0; i < paramTypes.size(); i++) {
- ClassInfo pt = paramTypes.get(i);
+ for (int i = 0; i < params.size(); i++) {
+ ParamInfo pi = params.get(i);
+ ClassInfo pt = pi.getParameterType();
ClassInfo ptu = pt.unwrap(Optional.class);
if (i == 0 && ptu.isInstance(outer.orElse(null)))
continue;
- if (! hasBean(ptu))
- if (! pt.is(Optional.class))
- l.add(pt);
+ if (pt.is(Optional.class))
+ continue;
+ String beanName = findBeanName(pi);
+ if (beanName == null)
+ beanName = ptu.inner().getName();
+ if (! hasBean(beanName))
+ l.add(pt);
}
return l;
}
@@ -344,27 +381,36 @@ public class BeanStore {
/**
* Returns the corresponding beans in this factory for the specified
param types.
*
- * @param paramTypes The param types to get from this factory.
+ * @param params The parameters to get from this factory.
* @return The corresponding beans in this factory for the specified
param types.
*/
- public Object[] getParams(List<ClassInfo> paramTypes) {
- Object[] o = new Object[paramTypes.size()];
- for (int i = 0; i < paramTypes.size(); i++) {
- ClassInfo pt = paramTypes.get(i);
+ public Object[] getParams(List<ParamInfo> params) {
+ Object[] o = new Object[params.size()];
+ for (int i = 0; i < params.size(); i++) {
+ ParamInfo pi = params.get(i);
+ ClassInfo pt = pi.getParameterType();
ClassInfo ptu = pt.unwrap(Optional.class);
if (i == 0 && ptu.isInstance(outer.orElse(null)))
o[i] = outer.get();
else {
+ String beanName = findBeanName(pi);
if (pt.is(Optional.class)) {
- o[i] = getBean(ptu);
+ o[i] = getBean(beanName, ptu.inner());
} else {
- o[i] = getBean(ptu).get();
+ o[i] = getBean(beanName,
ptu.inner()).get();
}
}
}
return o;
}
+ private String findBeanName(ParamInfo pi) {
+ Optional<Annotation> namedAnnotation =
pi.getAnnotations(Annotation.class).stream().filter(x->x.annotationType().getSimpleName().equals("Named")).findFirst();
+ if (namedAnnotation.isPresent())
+ return AnnotationInfo.of((ClassInfo)null,
namedAnnotation.get()).getValue(String.class, "value").orElse(null);
+ return null;
+ }
+
/**
* Returns the properties defined on this bean as a simple map for
debugging purposes.
*
@@ -377,7 +423,7 @@ public class BeanStore {
return OMap
.create()
.filtered()
- .a("beanMap", beanMap.keySet().stream().map(x ->
x.getSimpleName()).collect(Collectors.toList()))
+ .a("beanMap", beanMap.keySet())
.a("outer", ObjectUtils.identity(outer))
.a("parent", parent.orElse(null));
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MethodInvoker.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MethodInvoker.java
index 47d9053..2d00a04 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MethodInvoker.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MethodInvoker.java
@@ -82,9 +82,9 @@ public class MethodInvoker {
*/
public Object invokeUsingFactory(BeanStore beanStore, Object o) throws
ExecutableException {
List<ClassInfo> missing;
- missing = beanStore.getMissingParamTypes(m.getParamTypes());
+ missing = beanStore.getMissingParamTypes(m.getParams());
if (missing.isEmpty())
- return invoke(o,
beanStore.getParams(m.getParamTypes()));
+ return invoke(o, beanStore.getParams(m.getParams()));
throw new ExecutableException("Could not find prerequisites to
invoke method ''{0}'': {1}", getFullName(),
missing.stream().map(x->x.getSimpleName()).collect(Collectors.joining(",")));
}
diff --git
a/juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/SpringBeanStore.java
b/juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/SpringBeanStore.java
index 951f50e..00fb376 100644
---
a/juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/SpringBeanStore.java
+++
b/juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/SpringBeanStore.java
@@ -37,13 +37,16 @@ public class SpringBeanStore extends BeanStore {
}
@Override
- public <T> Optional<T> getBean(Class<T> c) {
+ public <T> Optional<T> getBean(String name, Class<T> c) {
try {
- Optional<T> o = super.getBean(c);
+ Optional<T> o = super.getBean(name, c);
if (o.isPresent())
return o;
- if (appContext.isPresent())
+ if (appContext.isPresent()) {
+ if (name != null)
+ return
Optional.ofNullable(appContext.get().getBean(name, c));
return
Optional.ofNullable(appContext.get().getBean(c));
+ }
} catch (Exception e) {
e.printStackTrace();
}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
index b439667..da5f92d 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
@@ -279,14 +279,14 @@ public class RestContextBuilder extends
BeanContextBuilder implements ServletCon
}
}
for (MethodInfo m : map.values()) {
- List<ClassInfo> paramTypes = m.getParamTypes();
+ List<ParamInfo> params = m.getParams();
- List<ClassInfo> missing =
beanStore.getMissingParamTypes(paramTypes);
+ List<ClassInfo> missing =
beanStore.getMissingParamTypes(params);
if (!missing.isEmpty())
throw new RestServletException("Could not call
@RestHook(INIT) method {0}.{1}. Could not find prerequisites: {2}.",
m.getDeclaringClass().getSimpleName(), m.getSignature(),
missing.stream().map(x->x.getSimpleName()).collect(Collectors.joining(",")));
try {
- m.invoke(resource,
beanStore.getParams(paramTypes));
+ m.invoke(resource, beanStore.getParams(params));
} catch (Exception e) {
throw new RestServletException(e, "Exception
thrown from @RestHook(INIT) method {0}.{1}.",
m.getDeclaringClass().getSimpleName(), m.getSignature());
}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/cp/BeanFactory_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/cp/BeanStore_Test.java
similarity index 87%
rename from
juneau-utest/src/test/java/org/apache/juneau/cp/BeanFactory_Test.java
rename to juneau-utest/src/test/java/org/apache/juneau/cp/BeanStore_Test.java
index 748d762..956a436 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/cp/BeanFactory_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/cp/BeanStore_Test.java
@@ -17,13 +17,15 @@ import static org.junit.runners.MethodSorters.*;
import java.util.*;
+import javax.inject.*;
+
import org.apache.juneau.annotation.*;
import org.junit.*;
@FixMethodOrder(NAME_ASCENDING)
-public class BeanFactory_Test {
+public class BeanStore_Test {
- private static final String CNAME = BeanFactory_Test.class.getName();
+ private static final String CNAME = BeanStore_Test.class.getName();
//-----------------------------------------------------------------------------------------------------------------
// Basic tests.
@@ -239,6 +241,25 @@ public class BeanFactory_Test {
assertThrown(()->bs.createBean(C2d.class)).stderr().is("Could
not instantiate class "+CNAME+"$C2d: Class is abstract.");
}
+ public static class C3a {
+ public A a;
+ public static C3a create(@Named("Foo") A a) {
+ C3a x = new C3a();
+ x.a = a;
+ return x;
+ }
+ private C3a() {}
+ }
+
+ @Test
+ public void c03_createBean_create_withNamedArgs() throws Exception {
+ BeanStore bs = new BeanStore();
+ assertThrown(()->bs.createBean(C3a.class)).stderr().is("Could
not instantiate class "+CNAME+"$C3a: Static creator found but could not find
prerequisites: A.");
+ bs.addBean("Foo", new A());
+ assertObject(bs.createBean(C3a.class)).exists();
+ assertObject(bs.createBean(C3a.class)).exists();
+ }
+
//-----------------------------------------------------------------------------------------------------------------
// Construct bean - With args
//-----------------------------------------------------------------------------------------------------------------
@@ -269,7 +290,7 @@ public class BeanFactory_Test {
@Test
public void d01b_createBean_construct_withArgs_inner() throws Exception
{
BeanStore bs = new BeanStore();
- assertThrown(()->bs.createBean(D1b.class)).stderr().is("Could
not instantiate class "+CNAME+"$D1b: Public constructor found but could not
find prerequisites: BeanFactory_Test,A.");
+ assertThrown(()->bs.createBean(D1b.class)).stderr().is("Could
not instantiate class "+CNAME+"$D1b: Public constructor found but could not
find prerequisites: BeanStore_Test,A.");
BeanStore bs2 = BeanStore.of(null,this);
assertThrown(()->bs2.createBean(D1b.class)).stderr().is("Could
not instantiate class "+CNAME+"$D1b: Public constructor found but could not
find prerequisites: A.");
bs2.addBean(A.class, new A());
@@ -277,6 +298,22 @@ public class BeanFactory_Test {
assertObject(bs2.createBean(D1b.class)).exists();
}
+ public static class D2a {
+ public A a;
+ public D2a(@Named("Foo") A a) {
+ this.a = a;
+ }
+ }
+
+ @Test
+ public void d02a_createBean_construct_withNamedArgs() throws Exception {
+ BeanStore bs = new BeanStore();
+ assertThrown(()->bs.createBean(D2a.class)).stderr().is("Could
not instantiate class "+CNAME+"$D2a: Public constructor found but could not
find prerequisites: A.");
+ bs.addBean("Foo", new A());
+ assertObject(bs.createBean(D2a.class)).exists();
+ assertObject(bs.createBean(D2a.class)).exists();
+ }
+
//-----------------------------------------------------------------------------------------------------------------
// Create bean via method.
//-----------------------------------------------------------------------------------------------------------------
@@ -348,6 +385,16 @@ public class BeanFactory_Test {
e.a = a.orElse(null);
return e;
}
+
+ public static E createC4(@Named("Foo") A a) {
+ return new E();
+ }
+
+ public static E createC5(@Named("Foo") Optional<A> a) {
+ E e = new E();
+ e.a = a.orElse(null);
+ return e;
+ }
}
@Test
@@ -382,17 +429,27 @@ public class BeanFactory_Test {
assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC2").run()).exists();
assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC3").run()).exists();
assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC3").run().a).exists();
- bs.addBean(A.class, null);
+ bs.addSupplier(A.class, ()->(A)null);
assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC1").run()).doesNotExist();
assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC2").run()).doesNotExist();
assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC3").run()).exists();
assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC3").run().a).doesNotExist();
- bs.addBeanSupplier(A.class, ()->new A());
+ bs.addSupplier(A.class, ()->new A());
assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC1").run()).exists();
assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC2").run()).exists();
assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC3").run()).exists();
assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC3").run().a).exists();
- bs.addBeanSupplier(A.class, null);
+
+ assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC4").run()).doesNotExist();
+ assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC5").run()).exists();
+ assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC5").run().a).doesNotExist();
+ bs.addSupplier("Foo", ()->new A());
+ assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC4").run()).exists();
+ assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC5").run()).exists();
+ assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC5").run().a).exists();
+ bs.addSupplier("Foo", null);
+
+ bs.addSupplier(A.class, ()->null);
assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC1").run()).doesNotExist();
assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC2").run()).doesNotExist();
assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createC3").run()).exists();
@@ -404,12 +461,12 @@ public class BeanFactory_Test {
assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createA1", A.class).thenFind("createA1").run()).exists();
assertObject(bs.beanCreateMethodFinder(E.class,
x).find("createA1", A.class).withDefault(new E()).run()).exists();
- bs.addBeanSupplier(A.class, ()->new A());
+ bs.addSupplier(A.class, ()->new A());
assertObject(bs.createBean(A.class)).exists();
BeanStore bs2 = BeanStore.of(bs, null);
assertObject(bs2.beanCreateMethodFinder(E.class,
x).find("createA1").run()).exists();
- assertString(bs2.toString()).is("{parent:{beanMap:['A']}}");
+
assertString(bs2.toString()).is("{parent:{beanMap:['"+CNAME+"$A']}}");
}
}