You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2018/11/21 20:10:43 UTC

[juneau] branch master updated: Allow RestResourceResolver to resolve all beans in RestServlet.

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 9d2c50f  Allow RestResourceResolver to resolve all beans in RestServlet.
9d2c50f is described below

commit 9d2c50ff9e95ce5bbf74d692dd2a774c31ec6bd8
Author: JamesBognar <ja...@apache.org>
AuthorDate: Wed Nov 21 15:10:30 2018 -0500

    Allow RestResourceResolver to resolve all beans in RestServlet.
---
 .../org/apache/juneau/BasicResourceResolver.java   | 28 +++------
 .../src/main/java/org/apache/juneau/Context.java   | 40 +++++--------
 .../org/apache/juneau/FuzzyResourceResolver.java   | 28 +++------
 .../main/java/org/apache/juneau/PropertyStore.java | 69 ++++++++++++----------
 .../java/org/apache/juneau/ResourceResolver.java   | 51 +++++++++-------
 juneau-doc/docs/ReleaseNotes/8.0.0.html            |  3 +
 .../springboot/SpringRestResourceResolver.java     |  6 +-
 .../org/apache/juneau/rest/client/RestClient.java  |  4 +-
 .../juneau/rest/BasicRestResourceResolver.java     | 14 ++---
 .../java/org/apache/juneau/rest/RestContext.java   | 30 +++++-----
 .../org/apache/juneau/rest/RestContextBuilder.java |  2 +-
 .../apache/juneau/rest/RestResourceResolver.java   |  6 +-
 12 files changed, 132 insertions(+), 149 deletions(-)

diff --git a/juneau-examples/juneau-examples-rest-springboot/src/main/java/org/apache/juneau/examples/rest/springboot/SpringRestResourceResolver.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicResourceResolver.java
similarity index 68%
copy from juneau-examples/juneau-examples-rest-springboot/src/main/java/org/apache/juneau/examples/rest/springboot/SpringRestResourceResolver.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicResourceResolver.java
index 029ef85..d3839ec 100644
--- a/juneau-examples/juneau-examples-rest-springboot/src/main/java/org/apache/juneau/examples/rest/springboot/SpringRestResourceResolver.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicResourceResolver.java
@@ -10,31 +10,21 @@
 // * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
 // * specific language governing permissions and limitations under the License.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau.examples.rest.springboot;
+package org.apache.juneau;
 
-import org.apache.juneau.rest.*;
-import org.springframework.context.ApplicationContext;
+import org.apache.juneau.internal.*;
 
 /**
- * Implementation of a {@link RestResourceResolver} for resolving REST resources using Spring.
+ * Basic implementation of a resource resolver.
  */
-public class SpringRestResourceResolver extends BasicRestResourceResolver {
+public class BasicResourceResolver implements ResourceResolver {
 
-	private ApplicationContext appContext;
-
-	public SpringRestResourceResolver(ApplicationContext appContext) {
-		this.appContext = appContext;
-	}
-
-	@Override
-	public Object resolve(Object parent, Class<?> type, RestContextBuilder builder) throws Exception {
+	@Override /* ResourceResolver */
+	public <T> T resolve(Object parent, Class<T> c, Object...args) {
 		try {
-			Object o = appContext.getBean(type);
-			if (o != null)
-				return o;
-		} catch(Exception e) {
-			// Ignore
+			return ClassUtils.newInstanceFromOuter(parent, c, c, false, args);
+		} catch (Exception e) {
+			throw new BeanRuntimeException(e, c, "Could not instantiate resource class");
 		}
-		return super.resolve(parent, type, builder);
 	}
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java
index b8eab62..73dc315 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java
@@ -337,17 +337,15 @@ public abstract class Context {
 	 * @param def
 	 * 	The default value if the property doesn't exist.
 	 * 	<br>Can either be an instance of <code>T</code>, or a <code>Class&lt;? <jk>extends</jk> T&gt;</code>.
-	 * @param fuzzyArgs
-	 * 	Use fuzzy constructor arg matching.
-	 * 	<br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
-	 * 	<br>No-arg constructors are also used if no other constructors are found.
+	 * @param resolver
+	 * 	The resolver to use for instantiating objects.
 	 * @param args
 	 * 	Arguments to pass to the constructor.
 	 * 	Constructors matching the arguments are always used before no-arg constructors.
 	 * @return A new property instance.
 	 */
-	public <T> T getInstanceProperty(String key, Class<T> type, Object def, boolean fuzzyArgs, Object...args) {
-		return propertyStore.getInstanceProperty(key, type, def, fuzzyArgs, args);
+	public <T> T getInstanceProperty(String key, Class<T> type, Object def, ResourceResolver resolver, Object...args) {
+		return propertyStore.getInstanceProperty(key, type, def, resolver, args);
 	}
 
 	/**
@@ -359,17 +357,15 @@ public abstract class Context {
 	 * @param def
 	 * 	The default value if the property doesn't exist.
 	 * 	<br>Can either be an instance of <code>T</code>, or a <code>Class&lt;? <jk>extends</jk> T&gt;</code>.
-	 * @param fuzzyArgs
-	 * 	Use fuzzy constructor arg matching.
-	 * 	<br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
-	 * 	<br>No-arg constructors are also used if no other constructors are found.
+	 * @param resolver
+	 * 	The resolver to use for instantiating objects.
 	 * @param args
 	 * 	Arguments to pass to the constructor.
 	 * 	Constructors matching the arguments are always used before no-arg constructors.
 	 * @return A new property instance.
 	 */
-	public <T> T getInstanceProperty(String key, Object outer, Class<T> type, Object def, boolean fuzzyArgs, Object...args) {
-		return propertyStore.getInstanceProperty(key, outer, type, def, fuzzyArgs, args);
+	public <T> T getInstanceProperty(String key, Object outer, Class<T> type, Object def, ResourceResolver resolver, Object...args) {
+		return propertyStore.getInstanceProperty(key, outer, type, def, resolver, args);
 	}
 
 	/**
@@ -390,17 +386,15 @@ public abstract class Context {
 	 * @param key The property name.
 	 * @param type The class type of the property.
 	 * @param def The default object to return if the property doesn't exist.
-	 * @param fuzzyArgs
-	 * 	Use fuzzy constructor arg matching.
-	 * 	<br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
-	 * 	<br>No-arg constructors are also used if no other constructors are found.
+	 * @param resolver
+	 * 	The resolver to use for instantiating objects.
 	 * @param args
 	 * 	Arguments to pass to the constructor.
 	 * 	Constructors matching the arguments are always used before no-arg constructors.
 	 * @return A new property instance.
 	 */
-	public <T> T[] getInstanceArrayProperty(String key, Class<T> type, T[] def, boolean fuzzyArgs, Object...args) {
-		return propertyStore.getInstanceArrayProperty(key, type, def, fuzzyArgs, args);
+	public <T> T[] getInstanceArrayProperty(String key, Class<T> type, T[] def, ResourceResolver resolver, Object...args) {
+		return propertyStore.getInstanceArrayProperty(key, type, def, resolver, args);
 	}
 
 	/**
@@ -410,17 +404,15 @@ public abstract class Context {
 	 * @param outer The outer object if the class we're instantiating is an inner class.
 	 * @param type The class type of the property.
 	 * @param def The default object to return if the property doesn't exist.
-	 * @param fuzzyArgs
-	 * 	Use fuzzy constructor arg matching.
-	 * 	<br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
-	 * 	<br>No-arg constructors are also used if no other constructors are found.
+	 * @param resolver
+	 * 	The resolver to use for instantiating objects.
 	 * @param args
 	 * 	Arguments to pass to the constructor.
 	 * 	Constructors matching the arguments are always used before no-arg constructors.
 	 * @return A new property instance.
 	 */
-	public <T> T[] getInstanceArrayProperty(String key, Object outer, Class<T> type, T[] def, boolean fuzzyArgs, Object...args) {
-		return propertyStore.getInstanceArrayProperty(key, outer, type, def, fuzzyArgs, args);
+	public <T> T[] getInstanceArrayProperty(String key, Object outer, Class<T> type, T[] def, ResourceResolver resolver, Object...args) {
+		return propertyStore.getInstanceArrayProperty(key, outer, type, def, resolver, args);
 	}
 
 	/**
diff --git a/juneau-examples/juneau-examples-rest-springboot/src/main/java/org/apache/juneau/examples/rest/springboot/SpringRestResourceResolver.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/FuzzyResourceResolver.java
similarity index 68%
copy from juneau-examples/juneau-examples-rest-springboot/src/main/java/org/apache/juneau/examples/rest/springboot/SpringRestResourceResolver.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/FuzzyResourceResolver.java
index 029ef85..c5abed3 100644
--- a/juneau-examples/juneau-examples-rest-springboot/src/main/java/org/apache/juneau/examples/rest/springboot/SpringRestResourceResolver.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/FuzzyResourceResolver.java
@@ -10,31 +10,21 @@
 // * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
 // * specific language governing permissions and limitations under the License.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau.examples.rest.springboot;
+package org.apache.juneau;
 
-import org.apache.juneau.rest.*;
-import org.springframework.context.ApplicationContext;
+import org.apache.juneau.internal.*;
 
 /**
- * Implementation of a {@link RestResourceResolver} for resolving REST resources using Spring.
+ * Basic implementation of a resource resolver.
  */
-public class SpringRestResourceResolver extends BasicRestResourceResolver {
+public class FuzzyResourceResolver implements ResourceResolver {
 
-	private ApplicationContext appContext;
-
-	public SpringRestResourceResolver(ApplicationContext appContext) {
-		this.appContext = appContext;
-	}
-
-	@Override
-	public Object resolve(Object parent, Class<?> type, RestContextBuilder builder) throws Exception {
+	@Override /* ResourceResolver */
+	public <T> T resolve(Object parent, Class<T> c, Object...args) {
 		try {
-			Object o = appContext.getBean(type);
-			if (o != null)
-				return o;
-		} catch(Exception e) {
-			// Ignore
+			return ClassUtils.newInstanceFromOuter(parent, c, c, true, args);
+		} catch (Exception e) {
+			throw new BeanRuntimeException(e, c, "Could not instantiate resource class ''{0}''");
 		}
-		return super.resolve(parent, type, builder);
 	}
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStore.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStore.java
index 354736a..7189e8f 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStore.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStore.java
@@ -489,7 +489,7 @@ public final class PropertyStore {
 	 * @return A new property instance.
 	 */
 	public <T> T getInstanceProperty(String key, Class<T> type, Object def) {
-		return getInstanceProperty(key, type, def, false);
+		return getInstanceProperty(key, type, def, ResourceResolver.BASIC);
 	}
 
 	/**
@@ -500,17 +500,15 @@ public final class PropertyStore {
 	 * @param def
 	 * 	The default value if the property doesn't exist.
 	 * 	<br>Can either be an instance of <code>T</code>, or a <code>Class&lt;? <jk>extends</jk> T&gt;</code>.
-	 * @param fuzzyArgs
-	 * 	Use fuzzy constructor arg matching.
-	 * 	<br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
-	 * 	<br>No-arg constructors are also used if no other constructors are found.
+	 * @param resolver
+	 * 	The resolver to use for instantiating objects.
 	 * @param args
 	 * 	Arguments to pass to the constructor.
 	 * 	Constructors matching the arguments are always used before no-arg constructors.
 	 * @return A new property instance.
 	 */
-	public <T> T getInstanceProperty(String key, Class<T> type, Object def, boolean fuzzyArgs, Object...args) {
-		return getInstanceProperty(key, null, type, def, fuzzyArgs, args);
+	public <T> T getInstanceProperty(String key, Class<T> type, Object def, ResourceResolver resolver, Object...args) {
+		return getInstanceProperty(key, null, type, def, resolver, args);
 	}
 
 	/**
@@ -522,23 +520,21 @@ public final class PropertyStore {
 	 * @param def
 	 * 	The default value if the property doesn't exist.
 	 * 	<br>Can either be an instance of <code>T</code>, or a <code>Class&lt;? <jk>extends</jk> T&gt;</code>.
-	 * @param fuzzyArgs
-	 * 	Use fuzzy constructor arg matching.
-	 * 	<br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
-	 * 	<br>No-arg constructors are also used if no other constructors are found.
+	 * @param resolver
+	 * 	The resolver to use for instantiating objects.
 	 * @param args
 	 * 	Arguments to pass to the constructor.
 	 * 	Constructors matching the arguments are always used before no-arg constructors.
 	 * @return A new property instance.
 	 */
-	public <T> T getInstanceProperty(String key, Object outer, Class<T> type, Object def, boolean fuzzyArgs, Object...args) {
+	public <T> T getInstanceProperty(String key, Object outer, Class<T> type, Object def, ResourceResolver resolver, Object...args) {
 		Property p = findProperty(key);
 		if (p != null)
-			return p.asInstance(outer, type, fuzzyArgs, args);
+			return p.asInstance(outer, type, resolver, args);
 		if (def == null)
 			return null;
 		if (def instanceof Class)
-			return ClassUtils.newInstance(type, def, fuzzyArgs, args);
+			return resolver.resolve(outer, (Class<T>)def, args);
 		if (type.isInstance(def))
 			return (T)def;
 		throw new ConfigException("Could not instantiate property ''{0}'' as type ''{1}'' with default value ''{2}''", key, type, def);
@@ -553,7 +549,7 @@ public final class PropertyStore {
 	 * @return A new property instance.
 	 */
 	public <T> T[] getInstanceArrayProperty(String key, Class<T> type, T[] def) {
-		return getInstanceArrayProperty(key, type, def, false);
+		return getInstanceArrayProperty(key, type, def, ResourceResolver.BASIC);
 	}
 
 	/**
@@ -562,17 +558,15 @@ public final class PropertyStore {
 	 * @param key The property name.
 	 * @param type The class type of the property.
 	 * @param def The default object to return if the property doesn't exist.
-	 * @param fuzzyArgs
-	 * 	Use fuzzy constructor arg matching.
-	 * 	<br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
-	 * 	<br>No-arg constructors are also used if no other constructors are found.
+	 * @param resolver
+	 * 	The resolver to use for instantiating objects.
 	 * @param args
 	 * 	Arguments to pass to the constructor.
 	 * 	Constructors matching the arguments are always used before no-arg constructors.
 	 * @return A new property instance.
 	 */
-	public <T> T[] getInstanceArrayProperty(String key, Class<T> type, T[] def, boolean fuzzyArgs, Object...args) {
-		return getInstanceArrayProperty(key, null, type, def, fuzzyArgs, args);
+	public <T> T[] getInstanceArrayProperty(String key, Class<T> type, T[] def, ResourceResolver resolver, Object...args) {
+		return getInstanceArrayProperty(key, null, type, def, resolver, args);
 	}
 
 	/**
@@ -582,18 +576,16 @@ public final class PropertyStore {
 	 * @param outer The outer object if the class we're instantiating is an inner class.
 	 * @param type The class type of the property.
 	 * @param def The default object to return if the property doesn't exist.
-	 * @param fuzzyArgs
-	 * 	Use fuzzy constructor arg matching.
-	 * 	<br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
-	 * 	<br>No-arg constructors are also used if no other constructors are found.
+	 * @param resolver
+	 * 	The resolver to use for instantiating objects.
 	 * @param args
 	 * 	Arguments to pass to the constructor.
 	 * 	Constructors matching the arguments are always used before no-arg constructors.
 	 * @return A new property instance.
 	 */
-	public <T> T[] getInstanceArrayProperty(String key, Object outer, Class<T> type, T[] def, boolean fuzzyArgs, Object...args) {
+	public <T> T[] getInstanceArrayProperty(String key, Object outer, Class<T> type, T[] def, ResourceResolver resolver, Object...args) {
 		Property p = findProperty(key);
-		return p == null ? def : p.asInstanceArray(outer, type, fuzzyArgs, args);
+		return p == null ? def : p.asInstanceArray(outer, type, resolver, args);
 	}
 
 	/**
@@ -859,15 +851,20 @@ public final class PropertyStore {
 			}
 		}
 
-		public <T> T asInstance(Object outer, Class<T> iType, boolean fuzzyArgs, Object...args) {
+		public <T> T asInstance(Object outer, Class<T> iType, ResourceResolver resolver, Object...args) {
+			if (value == null)
+				return null;
 			if (type == STRING)
 				return ClassUtils.fromString(iType, value.toString());
-			else if (type == OBJECT || type == CLASS)
-				return ClassUtils.newInstanceFromOuter(outer, iType, value, fuzzyArgs, args);
+			else if (type == OBJECT || type == CLASS) {
+				T t = instantiate(resolver, outer, iType, value, args);
+				if (t != null)
+					return t;
+			}
 			throw new ConfigException("Invalid property instantiation ''{0}'' to ''{1}'' on property ''{2}''", type, iType, name);
 		}
 
-		public <T> T[] asInstanceArray(Object outer, Class<T> eType, boolean fuzzyArgs, Object...args) {
+		public <T> T[] asInstanceArray(Object outer, Class<T> eType, ResourceResolver resolver, Object...args) {
 			if (value instanceof Collection) {
 				Collection<?> l = (Collection<?>)value;
 				Object t = Array.newInstance(eType, l.size());
@@ -879,7 +876,7 @@ public final class PropertyStore {
 					else if (type == SET_STRING || type == LIST_STRING)
 						o2 = ClassUtils.fromString(eType, o.toString());
 					else if (type == SET_CLASS || type == LIST_CLASS || type == LIST_OBJECT)
-						o2 = ClassUtils.newInstanceFromOuter(outer, eType, o, fuzzyArgs, args);
+						o2 = instantiate(resolver, outer, eType, o, args);
 					if (o2 == null)
 						throw new ConfigException("Invalid property conversion ''{0}'' to ''{1}[]'' on property ''{2}''", type, eType, name);
 					Array.set(t, i++, o2);
@@ -910,6 +907,14 @@ public final class PropertyStore {
 	// Utility methods
 	//-------------------------------------------------------------------------------------------------------------------
 
+	static <T> T instantiate(ResourceResolver resolver, Object outer, Class<T> c, Object value, Object...args) {
+		if (ClassUtils.isParentClass(c, value.getClass()))
+			return (T)value;
+		if (ClassUtils.isParentClass(Class.class, value.getClass()))
+			return resolver.resolve(outer, (Class<T>)value, args);
+		return null;
+	}
+
 	private static String group(String key) {
 		if (key == null || key.indexOf('.') == -1 || key.charAt(key.length()-1) == '.')
 			throw new ConfigException("Invalid property name specified: ''{0}''", key);
diff --git a/juneau-examples/juneau-examples-rest-springboot/src/main/java/org/apache/juneau/examples/rest/springboot/SpringRestResourceResolver.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ResourceResolver.java
similarity index 59%
copy from juneau-examples/juneau-examples-rest-springboot/src/main/java/org/apache/juneau/examples/rest/springboot/SpringRestResourceResolver.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ResourceResolver.java
index 029ef85..a96c864 100644
--- a/juneau-examples/juneau-examples-rest-springboot/src/main/java/org/apache/juneau/examples/rest/springboot/SpringRestResourceResolver.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ResourceResolver.java
@@ -10,31 +10,38 @@
 // * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
 // * specific language governing permissions and limitations under the License.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau.examples.rest.springboot;
-
-import org.apache.juneau.rest.*;
-import org.springframework.context.ApplicationContext;
+package org.apache.juneau;
 
 /**
- * Implementation of a {@link RestResourceResolver} for resolving REST resources using Spring.
+ * Class used to resolve {@link Class} objects to instances.
  */
-public class SpringRestResourceResolver extends BasicRestResourceResolver {
-
-	private ApplicationContext appContext;
+public interface ResourceResolver {
+	
+	/**
+	 * Look for constructors where the arguments passed in must match exactly.
+	 */
+	public static final ResourceResolver BASIC = new BasicResourceResolver();
 
-	public SpringRestResourceResolver(ApplicationContext appContext) {
-		this.appContext = appContext;
-	}
+	/**
+	 * Look for constructors where arguments may or may not exist in any order.
+	 */
+	public static final ResourceResolver FUZZY = new FuzzyResourceResolver();
 
-	@Override
-	public Object resolve(Object parent, Class<?> type, RestContextBuilder builder) throws Exception {
-		try {
-			Object o = appContext.getBean(type);
-			if (o != null)
-				return o;
-		} catch(Exception e) {
-			// Ignore
-		}
-		return super.resolve(parent, type, builder);
-	}
+	/**
+	 * Resolves the specified class to a resource object.
+	 *
+	 * <p>
+	 * Subclasses can override this method to provide their own custom resolution.
+	 *
+	 * <p>
+	 * The default implementation simply creates a new class instance using {@link Class#newInstance()}.
+	 *
+	 * @param parent
+	 * 	The parent resource.
+	 * @param c The class to resolve.
+	 * @param builder The initialization configuration for the resource.
+	 * @param args Optional arguments to pass to constructor
+	 * @return The instance of that class.
+	 */
+	<T> T resolve(Object parent, Class<T> c, Object...args);
 }
diff --git a/juneau-doc/docs/ReleaseNotes/8.0.0.html b/juneau-doc/docs/ReleaseNotes/8.0.0.html
index 0f19fb2..e3a314d 100644
--- a/juneau-doc/docs/ReleaseNotes/8.0.0.html
+++ b/juneau-doc/docs/ReleaseNotes/8.0.0.html
@@ -39,6 +39,9 @@
 			<li class='jm'>{@link oajr.RestServlet#setRestResourceResolver(RestResourceResolver) setRestResourceResolver(RestResourceResolver)}
 			<li class='jm'>{@link oajr.RestServlet#getPath() getPath()}
 		</ul>
+	<li>
+		The registered resource resolver is now used to instantiate objects of classes defined via <ja>@RestResource</ja>.
+		<br>This allows for any of those instance to be injectable beans.
 </ul>
 
 <h5 class='topic w800'>juneau-microservice-server</h5>
diff --git a/juneau-examples/juneau-examples-rest-springboot/src/main/java/org/apache/juneau/examples/rest/springboot/SpringRestResourceResolver.java b/juneau-examples/juneau-examples-rest-springboot/src/main/java/org/apache/juneau/examples/rest/springboot/SpringRestResourceResolver.java
index 029ef85..1a12c1c 100644
--- a/juneau-examples/juneau-examples-rest-springboot/src/main/java/org/apache/juneau/examples/rest/springboot/SpringRestResourceResolver.java
+++ b/juneau-examples/juneau-examples-rest-springboot/src/main/java/org/apache/juneau/examples/rest/springboot/SpringRestResourceResolver.java
@@ -27,12 +27,12 @@ public class SpringRestResourceResolver extends BasicRestResourceResolver {
 	}
 
 	@Override
-	public Object resolve(Object parent, Class<?> type, RestContextBuilder builder) throws Exception {
+	public <T> T resolve(Object parent, Class<T> type, RestContextBuilder builder, Object...args) {
 		try {
-			Object o = appContext.getBean(type);
+			T o = appContext.getBean(type);
 			if (o != null)
 				return o;
-		} catch(Exception e) {
+		} catch (Exception e) {
 			// Ignore
 		}
 		return super.resolve(parent, type, builder);
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
index bae6b20..e47b84f 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
@@ -520,8 +520,8 @@ public class RestClient extends BeanContext implements Closeable {
 		}
 
 		this.urlEncodingSerializer = new SerializerBuilder(ps).build(UrlEncodingSerializer.class);
-		this.partSerializer = getInstanceProperty(RESTCLIENT_partSerializer, HttpPartSerializer.class, OpenApiSerializer.class, true, ps);
-		this.partParser = getInstanceProperty(RESTCLIENT_partParser, HttpPartParser.class, OpenApiParser.class, true, ps);
+		this.partSerializer = getInstanceProperty(RESTCLIENT_partSerializer, HttpPartSerializer.class, OpenApiSerializer.class, ResourceResolver.FUZZY, ps);
+		this.partParser = getInstanceProperty(RESTCLIENT_partParser, HttpPartParser.class, OpenApiParser.class, ResourceResolver.FUZZY, ps);
 		this.executorService = getInstanceProperty(RESTCLIENT_executorService, ExecutorService.class, null);
 
 		RestCallInterceptor[] rci = getInstanceArrayProperty(RESTCLIENT_interceptors, RestCallInterceptor.class, new RestCallInterceptor[0]);
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestResourceResolver.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestResourceResolver.java
index 2b377ed..110f412 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestResourceResolver.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestResourceResolver.java
@@ -12,6 +12,7 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.rest;
 
+import org.apache.juneau.*;
 import org.apache.juneau.internal.*;
 
 /**
@@ -36,17 +37,10 @@ import org.apache.juneau.internal.*;
  * 	<li class='link'>{@doc juneau-rest-server.Instantiation.ResourceResolvers}
  * </ul>
  */
-public class BasicRestResourceResolver implements RestResourceResolver {
+public class BasicRestResourceResolver extends FuzzyResourceResolver implements RestResourceResolver {
 
 	@Override /* RestResourceResolver */
-	public Object resolve(Object parent, Class<?> c, RestContextBuilder builder) throws Exception {
-		try {
-			Object r = ClassUtils.newInstanceFromOuter(parent, Object.class, c, true, builder);
-			if (r != null)
-				return r;
-		} catch (Exception e) {
-			throw new RestServletException("Could not instantiate resource class ''{0}''", c.getName()).initCause(e);
-		}
-		throw new RestServletException("Could not find public constructor for class ''{0}''.", c);
+	public <T> T resolve(Object parent, Class<T> c, RestContextBuilder builder, Object...args) {
+		return resolve(parent, c, ArrayUtils.append(args, builder));
 	}
 }
\ No newline at end of file
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 159b1e6..e1967df 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -3095,6 +3095,7 @@ public final class RestContext extends BeanContext {
 			this.resource = builder.resource;
 			this.builder = builder;
 			this.parentContext = builder.parentContext;
+			resourceResolver = getInstanceProperty(REST_resourceResolver, resource, RestResourceResolver.class, parentContext == null ? BasicRestResourceResolver.class : parentContext.resourceResolver, ResourceResolver.FUZZY, this);
 
 			PropertyStore ps = getPropertyStore().builder().add(builder.properties).build();
 			Class<?> resourceClass = resource.getClass();
@@ -3114,12 +3115,12 @@ public final class RestContext extends BeanContext {
 			maxInput = getLongProperty(REST_maxInput, 100_000_000l);
 			clientVersionHeader = getStringProperty(REST_clientVersionHeader, "X-Client-Version");
 
-			converters = getInstanceArrayProperty(REST_converters, resource, RestConverter.class, new RestConverter[0], true, this);
-			guards = getInstanceArrayProperty(REST_guards, resource, RestGuard.class, new RestGuard[0], true, this);
-			responseHandlers = getInstanceArrayProperty(REST_responseHandlers, resource, ResponseHandler.class, new ResponseHandler[0], true, this);
+			converters = getInstanceArrayProperty(REST_converters, resource, RestConverter.class, new RestConverter[0], resourceResolver, this);
+			guards = getInstanceArrayProperty(REST_guards, resource, RestGuard.class, new RestGuard[0], resourceResolver, this);
+			responseHandlers = getInstanceArrayProperty(REST_responseHandlers, resource, ResponseHandler.class, new ResponseHandler[0], resourceResolver, this);
 
 			Map<Class<?>,RestMethodParam> _paramResolvers = new HashMap<>();
-			for (RestMethodParam rp : getInstanceArrayProperty(REST_paramResolvers, RestMethodParam.class, new RestMethodParam[0], true, this))
+			for (RestMethodParam rp : getInstanceArrayProperty(REST_paramResolvers, RestMethodParam.class, new RestMethodParam[0], resourceResolver, this))
 				_paramResolvers.put(rp.forClass(), rp);
 			paramResolvers = unmodifiableMap(_paramResolvers);
 
@@ -3130,7 +3131,7 @@ public final class RestContext extends BeanContext {
 			defaultResponseHeaders = getMapProperty(REST_defaultResponseHeaders, Object.class);
 			staticFileResponseHeaders = getMapProperty(REST_staticFileResponseHeaders, Object.class);
 
-			logger = getInstanceProperty(REST_logger, resource, RestLogger.class, NoOpRestLogger.class, true, this);
+			logger = getInstanceProperty(REST_logger, resource, RestLogger.class, NoOpRestLogger.class, resourceResolver, this);
 			if (debug)
 				logger.setLevel(Level.FINE);
 
@@ -3158,18 +3159,18 @@ public final class RestContext extends BeanContext {
 			config = builder.config.resolving(this.varResolver.createSession());
 
 			properties = builder.properties;
-			serializers = SerializerGroup.create().append(getInstanceArrayProperty(REST_serializers, Serializer.class, new Serializer[0], true, resource, ps)).build();
-			parsers = ParserGroup.create().append(getInstanceArrayProperty(REST_parsers, Parser.class, new Parser[0], true, resource, ps)).build();
-			partSerializer = getInstanceProperty(REST_partSerializer, HttpPartSerializer.class, OpenApiSerializer.class, true, resource, ps);
-			partParser = getInstanceProperty(REST_partParser, HttpPartParser.class, OpenApiParser.class, true, resource, ps);
-			encoders = new EncoderGroupBuilder().append(getInstanceArrayProperty(REST_encoders, Encoder.class, new Encoder[0], true, resource, ps)).build();
+			serializers = SerializerGroup.create().append(getInstanceArrayProperty(REST_serializers, Serializer.class, new Serializer[0], resourceResolver, resource, ps)).build();
+			parsers = ParserGroup.create().append(getInstanceArrayProperty(REST_parsers, Parser.class, new Parser[0], resourceResolver, resource, ps)).build();
+			partSerializer = getInstanceProperty(REST_partSerializer, HttpPartSerializer.class, OpenApiSerializer.class, resourceResolver, resource, ps);
+			partParser = getInstanceProperty(REST_partParser, HttpPartParser.class, OpenApiParser.class, resourceResolver, resource, ps);
+			encoders = new EncoderGroupBuilder().append(getInstanceArrayProperty(REST_encoders, Encoder.class, new Encoder[0], resourceResolver, resource, ps)).build();
 			beanContext = BeanContext.create().apply(ps).build();
 
 			mimetypesFileTypeMap = new ExtendedMimetypesFileTypeMap();
 			for (String mimeType : getArrayProperty(REST_mimeTypes, String.class))
 				mimetypesFileTypeMap.addMimeTypes(mimeType);
 
-			ClasspathResourceFinder rf = getInstanceProperty(REST_classpathResourceFinder, ClasspathResourceFinder.class, ClasspathResourceFinderBasic.class, true, this);
+			ClasspathResourceFinder rf = getInstanceProperty(REST_classpathResourceFinder, ClasspathResourceFinder.class, ClasspathResourceFinderBasic.class, resourceResolver, this);
 			useClasspathResourceCaching = getProperty(REST_useClasspathResourceCaching, boolean.class, true);
 			staticResourceManager = new ClasspathResourceManager(resourceClass, rf, useClasspathResourceCaching);
 
@@ -3196,7 +3197,7 @@ public final class RestContext extends BeanContext {
 			this.childResources = Collections.synchronizedMap(new LinkedHashMap<String,RestContext>());  // Not unmodifiable on purpose so that children can be replaced.
 
 			Map<String,Widget> _widgets = new LinkedHashMap<>();
-			for (Widget w : getInstanceArrayProperty(REST_widgets, resource, Widget.class, new Widget[0], true, ps))
+			for (Widget w : getInstanceArrayProperty(REST_widgets, resource, Widget.class, new Widget[0], resourceResolver, ps))
 				_widgets.put(w.getName(), w);
 			this.widgets = unmodifiableMap(_widgets);
 
@@ -3398,7 +3399,6 @@ public final class RestContext extends BeanContext {
 			this.callRouters = unmodifiableMap(_callRouters);
 
 			// Initialize our child resources.
-			resourceResolver = getInstanceProperty(REST_resourceResolver, resource, RestResourceResolver.class, parentContext == null ? BasicRestResourceResolver.class : parentContext.resourceResolver, true, this);
 			for (Object o : getArrayProperty(REST_children, Object.class)) {
 				String path = null;
 				Object r = null;
@@ -3438,8 +3438,8 @@ public final class RestContext extends BeanContext {
 				childResources.put(path, rc2);
 			}
 
-			callHandler = getInstanceProperty(REST_callHandler, resource, RestCallHandler.class, BasicRestCallHandler.class, true, this);
-			infoProvider = getInstanceProperty(REST_infoProvider, resource, RestInfoProvider.class, BasicRestInfoProvider.class, true, this);
+			callHandler = getInstanceProperty(REST_callHandler, resource, RestCallHandler.class, BasicRestCallHandler.class, resourceResolver, this);
+			infoProvider = getInstanceProperty(REST_infoProvider, resource, RestInfoProvider.class, BasicRestInfoProvider.class, resourceResolver, this);
 
 		} catch (RestException e) {
 			_initException = e;
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 7c66905..dfaab03 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
@@ -280,7 +280,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 		// We want to do that here so that we can update the script/style properties while they're still modifiable.
 		HtmlDocBuilder hdb = getHtmlDocBuilder();
 		PropertyStore ps = getPropertyStore();
-		Widget[] widgets = ps.getInstanceArrayProperty(REST_widgets, Widget.class, new Widget[0], true, ps, resource);
+		Widget[] widgets = ps.getInstanceArrayProperty(REST_widgets, Widget.class, new Widget[0], ResourceResolver.FUZZY, ps, resource);
 		for (Widget w : widgets) {
 			hdb.script("INHERIT", "$W{"+w.getName()+".script}");
 			hdb.style("INHERIT", "$W{"+w.getName()+".style}");
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolver.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
index 24396be..649a57e 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
@@ -12,6 +12,7 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.rest;
 
+import org.apache.juneau.*;
 import org.apache.juneau.rest.annotation.*;
 
 /**
@@ -40,7 +41,7 @@ import org.apache.juneau.rest.annotation.*;
  * 	<li class='link'>{@doc juneau-rest-server.Instantiation.ResourceResolvers}
  * </ul>
  */
-public interface RestResourceResolver {
+public interface RestResourceResolver extends ResourceResolver {
 
 	/**
 	 * Represents no RestResourceResolver.
@@ -64,8 +65,9 @@ public interface RestResourceResolver {
 	 * 	The parent resource (i.e. the instance whose class has the {@link RestResource#children() @RestResource(children)} annotation.
 	 * @param c The class to resolve.
 	 * @param builder The initialization configuration for the resource.
+	 * @param args Optional arguments to pass to constructor
 	 * @return The instance of that class.
 	 * @throws Exception If class could not be resolved.
 	 */
-	Object resolve(Object parent, Class<?> c, RestContextBuilder builder) throws Exception;
+	<T> T resolve(Object parent, Class<T> c, RestContextBuilder builder, Object...args) throws Exception;
 }