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 2019/03/16 18:31:12 UTC

[juneau] branch master updated: ClassUtils reorg.

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 f91af4f  ClassUtils reorg.
f91af4f is described below

commit f91af4ff24fbbf2f39a2549a13c3e18d473c7bdc
Author: JamesBognar <ja...@apache.org>
AuthorDate: Sat Mar 16 14:30:50 2019 -0400

    ClassUtils reorg.
---
 .../juneau/httppart/HttpPartSchemaTest_Body.java   |   7 +-
 .../httppart/HttpPartSchemaTest_FormData.java      |   7 +-
 .../juneau/httppart/HttpPartSchemaTest_Header.java |   7 +-
 .../juneau/httppart/HttpPartSchemaTest_Path.java   |   7 +-
 .../juneau/httppart/HttpPartSchemaTest_Query.java  |   7 +-
 .../HttpPartSchemaTest_ResponseHeader.java         |   7 +-
 .../org/apache/juneau/utils/ClassUtilsTest.java    |  17 +-
 .../java/org/apache/juneau/jena/RdfClassMeta.java  |   9 +-
 .../src/main/java/org/apache/juneau/BeanMeta.java  |   8 +-
 .../java/org/apache/juneau/BeanPropertyMeta.java   |  22 +-
 .../src/main/java/org/apache/juneau/ClassInfo.java | 266 +++++++++++++++
 .../src/main/java/org/apache/juneau/ClassMeta.java |  12 +
 .../main/java/org/apache/juneau/MethodInfo.java    | 268 +++++++++++++++
 .../java/org/apache/juneau/MethodParamInfo.java    | 224 ++++++++++++
 .../java/org/apache/juneau/html/HtmlClassMeta.java |   3 +-
 .../org/apache/juneau/httppart/HttpPartSchema.java |   9 +-
 .../juneau/httppart/HttpPartSchemaBuilder.java     |  10 +-
 .../juneau/httppart/bean/RequestBeanMeta.java      |  33 +-
 .../httppart/bean/RequestBeanPropertyMeta.java     |   2 +-
 .../juneau/httppart/bean/ResponseBeanMeta.java     |  31 +-
 .../org/apache/juneau/httppart/bean/Utils.java     |  10 -
 .../org/apache/juneau/internal/ClassUtils.java     | 380 +--------------------
 .../java/org/apache/juneau/json/JsonClassMeta.java |   3 +-
 .../juneau/jsonschema/JsonSchemaClassMeta.java     |   3 +-
 .../apache/juneau/remote/RemoteInterfaceMeta.java  |   5 +-
 .../juneau/urlencoding/UrlEncodingClassMeta.java   |   3 +-
 .../java/org/apache/juneau/xml/XmlClassMeta.java   |  17 +-
 .../juneau/rest/client/remote/RemoteMethodArg.java |  28 +-
 .../rest/client/remote/RemoteMethodMeta.java       |  15 +-
 .../rest/client/remote/RemoteMethodReturn.java     |   5 +-
 .../rest/client/remote/RemoteResourceMeta.java     |   5 +-
 .../apache/juneau/rest/BasicRestInfoProvider.java  |  10 +-
 .../apache/juneau/rest/ClientVersionMatcher.java   |   2 +-
 .../java/org/apache/juneau/rest/RestContext.java   |  49 +--
 .../org/apache/juneau/rest/RestJavaMethod.java     |   4 +-
 .../org/apache/juneau/rest/RestMethodParam.java    |  38 ++-
 .../org/apache/juneau/rest/RestParamDefaults.java  | 112 +++---
 .../java/org/apache/juneau/rest/RestServlet.java   |   5 +-
 .../org/apache/juneau/rest/SwaggerGenerator.java   |  50 +--
 39 files changed, 1066 insertions(+), 634 deletions(-)

diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Body.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Body.java
index 957afa5..c369562 100644
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Body.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Body.java
@@ -16,6 +16,7 @@ import static org.junit.Assert.*;
 
 import static org.apache.juneau.testutils.TestUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.internal.ClassUtils.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.http.annotation.*;
@@ -72,7 +73,8 @@ public class HttpPartSchemaTest_Body {
 
 	@Test
 	public void a03_basic_onParameter() throws Exception {
-		HttpPartSchema s = HttpPartSchema.create().apply(Body.class, A03.class.getMethod("a", String.class), 0).noValidate().build();
+		MethodParamInfo mpi = getMethodInfo(A03.class.getMethod("a", String.class)).getParam(0);
+		HttpPartSchema s = HttpPartSchema.create().apply(Body.class, mpi).noValidate().build();
 		assertTrue(s.isRequired());
 	}
 
@@ -92,7 +94,8 @@ public class HttpPartSchemaTest_Body {
 
 	@Test
 	public void a04_basic_onParameterAndClass() throws Exception {
-		HttpPartSchema s = HttpPartSchema.create().apply(Body.class, A04.class.getMethod("a", A02.class), 0).noValidate().build();
+		MethodParamInfo mpi = getMethodInfo(A04.class.getMethod("a", A02.class)).getParam(0);
+		HttpPartSchema s = HttpPartSchema.create().apply(Body.class, mpi).noValidate().build();
 		assertTrue(s.isRequired());
 	}
 
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_FormData.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_FormData.java
index da5f888..f87d709 100644
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_FormData.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_FormData.java
@@ -16,6 +16,7 @@ import static org.junit.Assert.*;
 
 import static org.apache.juneau.testutils.TestUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.internal.ClassUtils.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.http.annotation.*;
@@ -134,7 +135,8 @@ public class HttpPartSchemaTest_FormData {
 
 	@Test
 	public void a03_basic_onParameter() throws Exception {
-		HttpPartSchema s = HttpPartSchema.create().apply(FormData.class, A03.class.getMethod("a", String.class), 0).noValidate().build();
+		MethodParamInfo mpi = getMethodInfo(A03.class.getMethod("a", String.class)).getParam(0);
+		HttpPartSchema s = HttpPartSchema.create().apply(FormData.class, mpi).noValidate().build();
 		assertEquals("x", s.getName());
 		assertEquals(HttpPartSchema.Type.NUMBER, s.getType());
 		assertEquals(HttpPartSchema.Format.INT32, s.getFormat());
@@ -190,7 +192,8 @@ public class HttpPartSchemaTest_FormData {
 
 	@Test
 	public void a04_basic_onParameterAndClass() throws Exception {
-		HttpPartSchema s = HttpPartSchema.create().apply(FormData.class, A04.class.getMethod("a", A01.class), 0).noValidate().build();
+		MethodParamInfo mpi = getMethodInfo(A04.class.getMethod("a", A01.class)).getParam(0);
+		HttpPartSchema s = HttpPartSchema.create().apply(FormData.class, mpi).noValidate().build();
 		assertEquals("y", s.getName());
 		assertEquals(HttpPartSchema.Type.INTEGER, s.getType());
 		assertEquals(HttpPartSchema.Format.INT64, s.getFormat());
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Header.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Header.java
index c09e087..e769351 100644
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Header.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Header.java
@@ -15,6 +15,7 @@ package org.apache.juneau.httppart;
 import static org.junit.Assert.*;
 import static org.junit.Assert.assertEquals;
 import static org.apache.juneau.testutils.TestUtils.*;
+import static org.apache.juneau.internal.ClassUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
 
 import org.apache.juneau.*;
@@ -134,7 +135,8 @@ public class HttpPartSchemaTest_Header {
 
 	@Test
 	public void a03_basic_onParameter() throws Exception {
-		HttpPartSchema s = HttpPartSchema.create().apply(Header.class, A03.class.getMethod("a", String.class), 0).noValidate().build();
+		MethodParamInfo mpi = getMethodInfo(A03.class.getMethod("a", String.class)).getParam(0);
+		HttpPartSchema s = HttpPartSchema.create().apply(Header.class, mpi).noValidate().build();
 		assertEquals("x", s.getName());
 		assertEquals(HttpPartSchema.Type.NUMBER, s.getType());
 		assertEquals(HttpPartSchema.Format.INT32, s.getFormat());
@@ -190,7 +192,8 @@ public class HttpPartSchemaTest_Header {
 
 	@Test
 	public void a04_basic_onParameterAndClass() throws Exception {
-		HttpPartSchema s = HttpPartSchema.create().apply(Header.class, A04.class.getMethod("a", A01.class), 0).noValidate().build();
+		MethodParamInfo mpi = getMethodInfo(A04.class.getMethod("a", A01.class)).getParam(0);
+		HttpPartSchema s = HttpPartSchema.create().apply(Header.class, mpi).noValidate().build();
 		assertEquals("y", s.getName());
 		assertEquals(HttpPartSchema.Type.INTEGER, s.getType());
 		assertEquals(HttpPartSchema.Format.INT64, s.getFormat());
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Path.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Path.java
index 97c4e9d..abc7105 100644
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Path.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Path.java
@@ -15,6 +15,7 @@ package org.apache.juneau.httppart;
 import static org.junit.Assert.*;
 import static org.junit.Assert.assertEquals;
 import static org.apache.juneau.testutils.TestUtils.*;
+import static org.apache.juneau.internal.ClassUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
 
 import org.apache.juneau.*;
@@ -116,7 +117,8 @@ public class HttpPartSchemaTest_Path {
 
 	@Test
 	public void a03_basic_onParameter() throws Exception {
-		HttpPartSchema s = HttpPartSchema.create().apply(Path.class, A03.class.getMethod("a", String.class), 0).noValidate().build();
+		MethodParamInfo mpi = getMethodInfo(A03.class.getMethod("a", String.class)).getParam(0);
+		HttpPartSchema s = HttpPartSchema.create().apply(Path.class, mpi).noValidate().build();
 		assertEquals("x", s.getName());
 		assertEquals(HttpPartSchema.Type.NUMBER, s.getType());
 		assertEquals(HttpPartSchema.Format.INT32, s.getFormat());
@@ -160,7 +162,8 @@ public class HttpPartSchemaTest_Path {
 
 	@Test
 	public void a04_basic_onParameterAndClass() throws Exception {
-		HttpPartSchema s = HttpPartSchema.create().apply(Path.class, A04.class.getMethod("a", A01.class), 0).noValidate().build();
+		MethodParamInfo mpi = getMethodInfo(A04.class.getMethod("a", A01.class)).getParam(0);
+		HttpPartSchema s = HttpPartSchema.create().apply(Path.class, mpi).noValidate().build();
 		assertEquals("y", s.getName());
 		assertEquals(HttpPartSchema.Type.INTEGER, s.getType());
 		assertEquals(HttpPartSchema.Format.INT64, s.getFormat());
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Query.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Query.java
index e66ba38..e403bc6 100644
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Query.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Query.java
@@ -15,6 +15,7 @@ package org.apache.juneau.httppart;
 import static org.junit.Assert.*;
 import static org.junit.Assert.assertEquals;
 import static org.apache.juneau.testutils.TestUtils.*;
+import static org.apache.juneau.internal.ClassUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
 
 import org.apache.juneau.*;
@@ -134,7 +135,8 @@ public class HttpPartSchemaTest_Query {
 
 	@Test
 	public void a03_basic_onParameter() throws Exception {
-		HttpPartSchema s = HttpPartSchema.create().apply(Query.class, A03.class.getMethod("a", String.class), 0).noValidate().build();
+		MethodParamInfo mpi = getMethodInfo(A03.class.getMethod("a", String.class)).getParam(0);
+		HttpPartSchema s = HttpPartSchema.create().apply(Query.class, mpi).noValidate().build();
 		assertEquals("x", s.getName());
 		assertEquals(HttpPartSchema.Type.NUMBER, s.getType());
 		assertEquals(HttpPartSchema.Format.INT32, s.getFormat());
@@ -190,7 +192,8 @@ public class HttpPartSchemaTest_Query {
 
 	@Test
 	public void a04_basic_onParameterAndClass() throws Exception {
-		HttpPartSchema s = HttpPartSchema.create().apply(Query.class, A04.class.getMethod("a", A01.class), 0).noValidate().build();
+		MethodParamInfo mpi = getMethodInfo(A04.class.getMethod("a", A01.class)).getParam(0);
+		HttpPartSchema s = HttpPartSchema.create().apply(Query.class, mpi).noValidate().build();
 		assertEquals("y", s.getName());
 		assertEquals(HttpPartSchema.Type.INTEGER, s.getType());
 		assertEquals(HttpPartSchema.Format.INT64, s.getFormat());
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_ResponseHeader.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_ResponseHeader.java
index 8787e83..4e87a58 100644
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_ResponseHeader.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_ResponseHeader.java
@@ -12,6 +12,7 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.httppart;
 
+import static org.apache.juneau.internal.ClassUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
 import static org.apache.juneau.testutils.TestUtils.*;
 import static org.junit.Assert.*;
@@ -128,7 +129,8 @@ public class HttpPartSchemaTest_ResponseHeader {
 
 	@Test
 	public void a03_basic_onParameter() throws Exception {
-		HttpPartSchema s = HttpPartSchema.create().apply(ResponseHeader.class, A03.class.getMethod("a", String.class), 0).noValidate().build();
+		MethodParamInfo mpi = getMethodInfo(A03.class.getMethod("a", String.class)).getParam(0);
+		HttpPartSchema s = HttpPartSchema.create().apply(ResponseHeader.class, mpi).noValidate().build();
 		assertEquals("x", s.getName());
 		assertEquals(HttpPartSchema.Type.NUMBER, s.getType());
 		assertEquals(HttpPartSchema.Format.INT32, s.getFormat());
@@ -180,7 +182,8 @@ public class HttpPartSchemaTest_ResponseHeader {
 
 	@Test
 	public void a04_basic_onParameterAndClass() throws Exception {
-		HttpPartSchema s = HttpPartSchema.create().apply(ResponseHeader.class, A04.class.getMethod("a", A01.class), 0).noValidate().build();
+		MethodParamInfo mpi = getMethodInfo(A04.class.getMethod("a", A01.class)).getParam(0);
+		HttpPartSchema s = HttpPartSchema.create().apply(ResponseHeader.class, mpi).noValidate().build();
 		assertEquals("y", s.getName());
 		assertEquals(HttpPartSchema.Type.INTEGER, s.getType());
 		assertEquals(HttpPartSchema.Format.INT64, s.getFormat());
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
index 703a78a..9b75d28 100755
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
@@ -164,10 +164,10 @@ public class ClassUtilsTest {
 	//====================================================================================================
 	@Test
 	public void getMethodAnnotations() throws Exception {
-		assertEquals("a1", getAnnotation(TestAnnotation.class, CI3.class.getMethod("a1")).value());
-		assertEquals("a2b", getAnnotation(TestAnnotation.class, CI3.class.getMethod("a2")).value());
-		assertEquals("a3", getAnnotation(TestAnnotation.class, CI3.class.getMethod("a3", CharSequence.class)).value());
-		assertEquals("a4", getAnnotation(TestAnnotation.class, CI3.class.getMethod("a4")).value());
+		assertEquals("a1", getMethodInfo(CI3.class.getMethod("a1")).getAnnotation(TestAnnotation.class).value());
+		assertEquals("a2b", getMethodInfo(CI3.class.getMethod("a2")).getAnnotation(TestAnnotation.class).value());
+		assertEquals("a3", getMethodInfo(CI3.class.getMethod("a3", CharSequence.class)).getAnnotation(TestAnnotation.class).value());
+		assertEquals("a4", getMethodInfo(CI3.class.getMethod("a4")).getAnnotation(TestAnnotation.class).value());
 	}
 
 	public static interface CI1 {
@@ -417,7 +417,8 @@ public class ClassUtilsTest {
 	@Test
 	public void getAnnotationsOnParameter() throws Exception {
 		ObjectList l = new ObjectList();
-		for (HI1 ia : getAnnotations(HI1.class, HA.class.getMethod("doX", HA01.class), 0)) {
+		MethodParamInfo mpi = getMethodInfo(HA.class.getMethod("doX", HA01.class)).getParam(0);
+		for (HI1 ia : mpi.getAnnotations(HI1.class)) {
 			l.add(ia.value());
 		}
 		assertEquals("['0','1','2','3','4']", l.toString());
@@ -442,7 +443,8 @@ public class ClassUtilsTest {
 	@Test
 	public void getAnnotationsOnParameterInherited() throws Exception {
 		ObjectList l = new ObjectList();
-		for (HI2 ib : getAnnotations(HI2.class, HB.class.getMethod("doX", HB01.class), 0)) {
+		MethodParamInfo mpi = getMethodInfo(HB.class.getMethod("doX", HB01.class)).getParam(0);
+		for (HI2 ib : mpi.getAnnotations(HI2.class)) {
 			l.add(ib.value());
 		}
 		assertEquals("['0','1','2','3','4']", l.toString());
@@ -474,7 +476,8 @@ public class ClassUtilsTest {
 
 	@Test
 	public void findMatchingMethods() throws Exception {
-		assertObjectEquals("['public int org.apache.juneau.utils.ClassUtilsTest$I3.foo(int)','public int org.apache.juneau.utils.ClassUtilsTest$I2.foo(int)','public abstract int org.apache.juneau.utils.ClassUtilsTest$I1.foo(int)']", ClassUtils.findMatchingMethods(I3.class.getMethod("foo", int.class)));
+		MethodInfo mi = getMethodInfo(I3.class.getMethod("foo", int.class));
+		assertObjectEquals("['public int org.apache.juneau.utils.ClassUtilsTest$I3.foo(int)','public int org.apache.juneau.utils.ClassUtilsTest$I2.foo(int)','public abstract int org.apache.juneau.utils.ClassUtilsTest$I1.foo(int)']", mi.getMatching());
 	}
 
 }
diff --git a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfClassMeta.java b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfClassMeta.java
index dff9256..9c06be1 100644
--- a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfClassMeta.java
+++ b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfClassMeta.java
@@ -15,7 +15,6 @@ package org.apache.juneau.jena;
 import java.util.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.internal.*;
 import org.apache.juneau.jena.annotation.*;
 import org.apache.juneau.xml.*;
 
@@ -36,15 +35,15 @@ public class RdfClassMeta extends ClassMetaExtended {
 	 */
 	public RdfClassMeta(ClassMeta<?> cm) {
 		super(cm);
-		Class<?> c = getInnerClass();
-		this.rdf = ClassUtils.getAnnotation(Rdf.class, c);
+		ClassInfo ci = cm.getClassInfo();
+		this.rdf = ci.getAnnotation(Rdf.class);
 		if (rdf != null) {
 			collectionFormat = rdf.collectionFormat();
 		} else {
 			collectionFormat = RdfCollectionFormat.DEFAULT;
 		}
-		List<Rdf> rdfs = ClassUtils.getAnnotations(Rdf.class, c);
-		List<RdfSchema> schemas = ClassUtils.getAnnotations(RdfSchema.class, c);
+		List<Rdf> rdfs = ci.getAnnotations(Rdf.class);
+		List<RdfSchema> schemas = ci.getAnnotations(RdfSchema.class);
 		this.namespace = RdfUtils.findNamespace(rdfs, schemas);
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
index d2b9683..b6c1e3e 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
@@ -184,7 +184,7 @@ public class BeanMeta<T> {
 				}
 				this.beanRegistry = new BeanRegistry(ctx, null, bdClasses.toArray(new Class<?>[bdClasses.size()]));
 
-				for (Bean b : getAnnotationsParentFirst(Bean.class, classMeta.innerClass))
+				for (Bean b : classMeta.getClassInfo().getAnnotationsParentFirst(Bean.class))
 					if (! b.typePropertyName().isEmpty())
 						typePropertyName = b.typePropertyName();
 				if (typePropertyName == null)
@@ -607,11 +607,13 @@ public class BeanMeta<T> {
 				if (m.isBridge())   // This eliminates methods with covariant return types from parent classes on child classes.
 					continue;
 
-				BeanIgnore bi = getAnnotation(BeanIgnore.class, m);
+				MethodInfo mi = getMethodInfo(m);
+
+				BeanIgnore bi = mi.getAnnotation(BeanIgnore.class);
 				if (bi != null)
 					continue;
 
-				BeanProperty bp = getAnnotation(BeanProperty.class, m);
+				BeanProperty bp = mi.getAnnotation(BeanProperty.class);
 				if (! (v.isVisible(m) || bp != null))
 					continue;
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
index 236904f..444944a 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
@@ -52,6 +52,7 @@ public final class BeanPropertyMeta {
 	private final Field field;                                // The bean property field (if it has one).
 	private final Field innerField;                                // The bean property field (if it has one).
 	private final Method getter, setter, extraKeys;           // The bean property getter and setter.
+	private final MethodInfo getterInfo, setterInfo, extraKeysInfo;           // The bean property getter and setter.
 	private final boolean isUri;                              // True if this is a URL/URI or annotated with @URI.
 	private final boolean isDyna, isDynaGetterMap;            // This is a dyna property (i.e. name="*")
 
@@ -194,7 +195,7 @@ public final class BeanPropertyMeta {
 			}
 
 			if (getter != null) {
-				BeanProperty p = ClassUtils.getAnnotation(BeanProperty.class, getter);
+				BeanProperty p = getMethodInfo(getter).getAnnotation(BeanProperty.class);
 				if (rawTypeMeta == null)
 					rawTypeMeta = f.resolveClassMeta(p, getter.getGenericReturnType(), typeVarImpls);
 				isUri |= (rawTypeMeta.isUri() || getter.isAnnotationPresent(org.apache.juneau.annotation.URI.class));
@@ -210,7 +211,7 @@ public final class BeanPropertyMeta {
 			}
 
 			if (setter != null) {
-				BeanProperty p = ClassUtils.getAnnotation(BeanProperty.class, setter);
+				BeanProperty p = getMethodInfo(setter).getAnnotation(BeanProperty.class);
 				if (rawTypeMeta == null)
 					rawTypeMeta = f.resolveClassMeta(p, setter.getGenericParameterTypes()[0], typeVarImpls);
 				isUri |= (rawTypeMeta.isUri() || setter.isAnnotationPresent(org.apache.juneau.annotation.URI.class));
@@ -370,8 +371,11 @@ public final class BeanPropertyMeta {
 		this.field = b.field;
 		this.innerField = b.innerField;
 		this.getter = b.getter;
+		this.getterInfo = getMethodInfo(b.getter);
 		this.setter = b.setter;
+		this.setterInfo = getMethodInfo(b.setter);
 		this.extraKeys = b.extraKeys;
+		this.extraKeysInfo = getMethodInfo(b.extraKeys);
 		this.isUri = b.isUri;
 		this.beanMeta = b.beanMeta;
 		this.beanContext = b.beanContext;
@@ -1064,15 +1068,15 @@ public final class BeanPropertyMeta {
 			appendAnnotations(a, field.getType(), l);
 		}
 		if (getter != null) {
-			addIfNotNull(l, ClassUtils.getAnnotation(a, getter));
+			addIfNotNull(l, ClassUtils.getMethodInfo(getter).getAnnotation(a));
 			appendAnnotations(a, getter.getReturnType(), l);
 		}
 		if (setter != null) {
-			addIfNotNull(l, ClassUtils.getAnnotation(a, setter));
+			addIfNotNull(l, ClassUtils.getMethodInfo(setter).getAnnotation(a));
 			appendAnnotations(a, setter.getReturnType(), l);
 		}
 		if (extraKeys != null) {
-			addIfNotNull(l, ClassUtils.getAnnotation(a, extraKeys));
+			addIfNotNull(l, ClassUtils.getMethodInfo(extraKeys).getAnnotation(a));
 			appendAnnotations(a, extraKeys.getReturnType(), l);
 		}
 
@@ -1095,13 +1099,13 @@ public final class BeanPropertyMeta {
 		if (field != null)
 			t = field.getAnnotation(a);
 		if (t == null && getter != null)
-			t = ClassUtils.getAnnotation(a, getter);
+			t = getterInfo.getAnnotation(a);
 		if (t == null && setter != null)
-			t = ClassUtils.getAnnotation(a, setter);
+			t = setterInfo.getAnnotation(a);
 		if (t == null && extraKeys != null)
-			t = ClassUtils.getAnnotation(a, extraKeys);
+			t = extraKeysInfo.getAnnotation(a);
 		if (t == null)
-			t = ClassUtils.getAnnotation(a, typeMeta.getInnerClass());
+			t = typeMeta.getClassInfo().getAnnotation(a);
 		return t;
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassInfo.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassInfo.java
new file mode 100644
index 0000000..2508988
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassInfo.java
@@ -0,0 +1,266 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "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;
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import org.apache.juneau.internal.*;
+
+/**
+ * Utility class for introspecting information about a class.
+ */
+public class ClassInfo {
+
+	private final Type type;
+	private final Class<?> c;
+	private Map<Class<?>,Optional<Annotation>> annotationMap;
+	private Map<Class<?>,List<?>> annotationsMap;
+	private Map<Class<?>,List<?>> annotationsPfMap;
+	private Optional<ClassInfo> parent;
+	private ClassInfo[] interfaces;
+
+	private static final Map<Type,ClassInfo> CACHE = new ConcurrentHashMap<>();
+
+	/**
+	 * Constructor.
+	 *
+	 * @param t The class type.
+	 */
+	public ClassInfo(Type t) {
+		this.type = t;
+		this.c = ClassUtils.toClass(t);
+	}
+
+	/**
+	 * Same as using the constructor, but returns <jk>null</jk> if the type is <jk>null</jk>.
+	 *
+	 * @param t The class type.
+	 * @return The constructed class info.
+	 */
+	public static ClassInfo create(Type t) {
+		if (t == null)
+			return null;
+		return new ClassInfo(t);
+	}
+
+	/**
+	 * Returns the cached instance of the specified type.
+	 *
+	 * @param t The class type.
+	 * @return The cached class info, or <jk>null</jk> if the type is <jk>null</jk>.
+	 */
+	public synchronized static ClassInfo lookup(Type t) {
+		if (t == null)
+			return null;
+		ClassInfo ci = CACHE.get(t);
+		if (ci == null) {
+			ci = create(t);
+			CACHE.put(t, ci);
+		}
+		return ci;
+	}
+
+	/**
+	 * Returns the wrapped class.
+	 *
+	 * @return The wrapped class.
+	 */
+	public Type getInner() {
+		return type;
+	}
+
+	/**
+	 * Returns the wrapped class.
+	 *
+	 * @return The wrapped class or <jk>null</jk> if it's not a class.
+	 */
+	public Class<?> getInnerClass() {
+		return c;
+	}
+
+	/**
+	 * Returns the parent class info.
+	 *
+	 * @return The parent class info, or <jk>null</jk> if the class has no parent.
+	 */
+	public synchronized ClassInfo getParent() {
+		if (parent == null)
+			parent = Optional.ofNullable(c == null ? null : create(c.getSuperclass()));
+		return parent.isPresent() ? parent.get() : null;
+	}
+
+	/**
+	 * Returns the interfaces info.
+	 *
+	 * @return The implemented interfaces info, or an empty array if the class has no interfaces.
+	 */
+	public synchronized ClassInfo[] getInterfaces() {
+		if (interfaces == null) {
+			interfaces = new ClassInfo[c == null ? 0 : c.getInterfaces().length];
+			for (int i = 0; i < interfaces.length; i++)
+				interfaces[i] = ClassInfo.create(c.getInterfaces()[i]);
+		}
+		return interfaces;
+	}
+
+	/**
+	 * Finds the annotation of the specified type defined on this method.
+	 *
+	 * <p>
+	 * If the annotation cannot be found on the immediate method, searches methods with the same
+	 * signature on the parent classes or interfaces.
+	 * <br>The search is performed in child-to-parent order.
+	 *
+	 * <p>
+	 * If still not found, searches for the annotation on the return type of the method.
+	 *
+	 * @param a
+	 * 	The annotation to search for.
+	 * @return
+	 * 	The annotation if found, or <jk>null</jk> if not.
+	 */
+	@SuppressWarnings("unchecked")
+	public <T extends Annotation> T getAnnotation(Class<T> a) {
+		Optional<Annotation> o = annotationMap().get(a);
+		if (o == null) {
+			o = Optional.ofNullable(findAnnotation(a));
+			annotationMap().put(a, o);
+		}
+		return o.isPresent() ? (T)o.get() : null;
+	}
+
+	/**
+	 * Returns <jk>true</jk> if this method has the specified annotation.
+	 *
+	 * @param a
+	 * 	The annotation to search for.
+	 * @return
+	 * 	The <jk>true</jk> if annotation if found.
+	 */
+	public boolean hasAnnotation(Class<? extends Annotation> a) {
+		return getAnnotation(a) != null;
+	}
+
+	private <T extends Annotation> T findAnnotation(Class<T> a) {
+		if (c != null) {
+			T t2 = getDeclaredAnnotation(a);
+			if (t2 != null)
+				return t2;
+
+			ClassInfo sci = getParent();
+			if (sci != null) {
+				t2 = sci.getAnnotation(a);
+				if (t2 != null)
+					return t2;
+			}
+
+			for (ClassInfo c2 : getInterfaces()) {
+				t2 = c2.getAnnotation(a);
+				if (t2 != null)
+					return t2;
+			}
+		}
+		return null;
+
+	}
+
+	/**
+	 * Returns all annotations of the specified type defined on the specified method.
+	 *
+	 * <p>
+	 * Searches all methods with the same signature on the parent classes or interfaces
+	 * and the return type on the method.
+	 *
+	 * @param a
+	 * 	The annotation to search for.
+	 * @return
+	 * 	A list of all matching annotations found in child-to-parent order, or an empty list if none found.
+	 */
+	@SuppressWarnings("unchecked")
+	public <T extends Annotation> List<T> getAnnotations(Class<T> a) {
+		List<T> l = (List<T>)annotationsMap().get(a);
+		if (l == null) {
+			l = Collections.unmodifiableList(findAnnotations(a));
+			annotationsMap().put(a, l);
+		}
+		return l;
+	}
+
+	/**
+	 * Identical to {@link #getAnnotations(Class)} but returns the list in reverse (parent-to-child) order.
+	 *
+	 * @param a
+	 * 	The annotation to search for.
+	 * @return
+	 * 	A list of all matching annotations found in parent-to-child order, or an empty list if none found.
+	 */
+	@SuppressWarnings("unchecked")
+	public <T extends Annotation> List<T> getAnnotationsParentFirst(Class<T> a) {
+		List<T> l = (List<T>)annotationsPfMap().get(a);
+		if (l == null) {
+			l = new ArrayList<>(getAnnotations(a));
+			Collections.reverse(l);
+			l = Collections.unmodifiableList(l);
+			annotationsPfMap().put(a, l);
+		}
+		return l;
+	}
+
+	private <T extends Annotation> List<T> findAnnotations(Class<T> a) {
+		List<T> l = new LinkedList<>();
+		ClassUtils.appendAnnotations(a, type, l);
+		return l;
+	}
+
+	private synchronized Map<Class<?>,Optional<Annotation>> annotationMap() {
+		if (annotationMap == null)
+			annotationMap = new ConcurrentHashMap<>();
+		return annotationMap;
+	}
+
+	private synchronized Map<Class<?>,List<?>> annotationsMap() {
+		if (annotationsMap == null)
+			annotationsMap = new ConcurrentHashMap<>();
+		return annotationsMap;
+	}
+
+	private synchronized Map<Class<?>,List<?>> annotationsPfMap() {
+		if (annotationsPfMap == null)
+			annotationsPfMap = new ConcurrentHashMap<>();
+		return annotationsPfMap;
+	}
+
+
+	/**
+	 * Returns the specified annotation only if it's been declared on the specified class.
+	 *
+	 * <p>
+	 * More efficient than calling {@link Class#getAnnotation(Class)} since it doesn't recursively look for the class
+	 * up the parent chain.
+	 *
+	 * @param <T> The annotation class type.
+	 * @param a The annotation class.
+	 * @return The annotation, or <jk>null</jk> if not found.
+	 */
+	@SuppressWarnings("unchecked")
+	public <T extends Annotation> T getDeclaredAnnotation(Class<T> a) {
+		if (c != null)
+			for (Annotation a2 : c.getDeclaredAnnotations())
+				if (a2.annotationType() == a)
+					return (T)a2;
+		return null;
+	}
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
index 571b9ff..56f1503 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
@@ -65,6 +65,7 @@ public final class ClassMeta<T> implements Type {
 	}
 
 	final Class<T> innerClass;                              // The class being wrapped.
+	final ClassInfo info;
 
 	private final Class<? extends T> implClass;             // The implementation class to use if this is an interface.
 	private final ClassCategory cc;                         // The class category.
@@ -151,6 +152,7 @@ public final class ClassMeta<T> implements Type {
 	@SuppressWarnings({ "rawtypes", "unchecked" })
 	ClassMeta(Class<T> innerClass, BeanContext beanContext, Class<? extends T> implClass, BeanFilter beanFilter, PojoSwap<T,?>[] pojoSwaps, PojoSwap<?,?>[] childPojoSwaps, Object example) {
 		this.innerClass = innerClass;
+		this.info = ClassInfo.lookup(innerClass);
 		this.beanContext = beanContext;
 		this.extMeta = new MetadataMap();
 		String notABeanReason = null;
@@ -229,6 +231,7 @@ public final class ClassMeta<T> implements Type {
 	 */
 	ClassMeta(ClassMeta<T> mainType, ClassMeta<?> keyType, ClassMeta<?> valueType, ClassMeta<?> elementType) {
 		this.innerClass = mainType.innerClass;
+		this.info = ClassInfo.lookup(innerClass);
 		this.implClass = mainType.implClass;
 		this.childPojoSwaps = mainType.childPojoSwaps;
 		this.childSwapMap = mainType.childSwapMap;
@@ -280,6 +283,7 @@ public final class ClassMeta<T> implements Type {
 	@SuppressWarnings("unchecked")
 	ClassMeta(ClassMeta<?>[] args) {
 		this.innerClass = (Class<T>) Object[].class;
+		this.info = ClassInfo.lookup(innerClass);
 		this.extMeta = new MetadataMap();
 		this.args = args;
 		this.implClass = null;
@@ -817,6 +821,14 @@ public final class ClassMeta<T> implements Type {
 		}
 	}
 
+	/**
+	 * Returns the {@link ClassInfo} wrapper for the underlying class.
+	 *
+	 * @return The {@link ClassInfo} wrapper for the underlying class, never <jk>null</jk>.
+	 */
+	public ClassInfo getClassInfo() {
+		return info;
+	}
 
 	/**
 	 * Returns the type property name associated with this class and subclasses.
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MethodInfo.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MethodInfo.java
new file mode 100644
index 0000000..51a4d25
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MethodInfo.java
@@ -0,0 +1,268 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "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;
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * Utility class for introspecting information about a method.
+ */
+public class MethodInfo {
+
+	private final Method method;
+	private final MethodParamInfo[] params;
+	private List<Method> matching;
+	private Map<Class<?>,Optional<Annotation>> annotationMap;
+	private Map<Class<?>,List<?>> annotationsMap;
+	private Map<Class<?>,List<?>> annotationsPfMap;
+	private ClassInfo returnTypeInfo;
+	private ClassInfo[] exceptionInfos;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param m The method being wrapped.
+	 */
+	public MethodInfo(Method m) {
+		this.method = m;
+		params = new MethodParamInfo[m.getParameterCount()];
+		for (int i = 0; i < m.getParameterCount(); i++)
+			params[i] = new MethodParamInfo(this, i);
+	}
+
+	/**
+	 * Returns the wrapped method.
+	 *
+	 * @return The wrapped method.
+	 */
+	public Method getInner() {
+		return method;
+	}
+
+	/**
+	 * Returns the parameters defined on this method.
+	 *
+	 * @return An array of parameter information, never <jk>null</jk>.
+	 */
+	public MethodParamInfo[] getParams() {
+		return params;
+	}
+
+	/**
+	 * Returns parameter information at the specified index.
+	 *
+	 * @param index The parameter index.
+	 * @return The parameter information, never <jk>null</jk>.
+	 */
+	public MethodParamInfo getParam(int index) {
+		return params[index];
+	}
+
+	/**
+	 * Finds all declared methods with the same name and arguments on all superclasses and interfaces.
+	 *
+	 * @return
+	 * 	All matching methods including this method itself.
+	 * 	<br>Methods are ordered from child-to-parent order.
+	 */
+	public List<Method> getMatching() {
+		if (matching == null)
+			matching = Collections.unmodifiableList(findMatching(new ArrayList<>(), method, method.getDeclaringClass()));
+		return matching;
+	}
+
+	private static List<Method> findMatching(List<Method> l, Method m, Class<?> c) {
+		for (Method m2 : c.getDeclaredMethods())
+			if (m.getName().equals(m2.getName()) && Arrays.equals(m.getParameterTypes(), m2.getParameterTypes()))
+				l.add(m2);
+		Class<?> pc = c.getSuperclass();
+		if (pc != null)
+			findMatching(l, m, pc);
+		for (Class<?> ic : c.getInterfaces())
+			findMatching(l, m, ic);
+		return l;
+	}
+
+	/**
+	 * Returns the {@link ClassInfo} object associated with the return type on this method.
+	 *
+	 * @return The {@link ClassInfo} object associated with the return type on this method.
+	 */
+	public synchronized ClassInfo getReturnTypeInfo() {
+		if (returnTypeInfo == null)
+			returnTypeInfo = ClassInfo.lookup(method.getReturnType());
+		return returnTypeInfo;
+	}
+
+	/**
+	 * Returns the {@link ClassInfo} objects associated with the exception types on this method.
+	 *
+	 * @return The {@link ClassInfo} objects associated with the exception types on this method.
+	 */
+	public synchronized ClassInfo[] getExceptionInfos() {
+		if (exceptionInfos == null) {
+			Class<?>[] exceptionTypes = method.getExceptionTypes();
+			exceptionInfos = new ClassInfo[exceptionTypes.length];
+			for (int i = 0; i < exceptionTypes.length; i++)
+				exceptionInfos[i] = ClassInfo.lookup(exceptionTypes[i]);
+		}
+		return exceptionInfos;
+	}
+
+	/**
+	 * Finds the annotation of the specified type defined on this method.
+	 *
+	 * <p>
+	 * If the annotation cannot be found on the immediate method, searches methods with the same
+	 * signature on the parent classes or interfaces.
+	 * <br>The search is performed in child-to-parent order.
+	 *
+	 * <p>
+	 * If still not found, searches for the annotation on the return type of the method.
+	 *
+	 * @param a
+	 * 	The annotation to search for.
+	 * @return
+	 * 	The annotation if found, or <jk>null</jk> if not.
+	 */
+	@SuppressWarnings("unchecked")
+	public <T extends Annotation> T getAnnotation(Class<T> a) {
+		Optional<Annotation> o = annotationMap().get(a);
+		if (o == null) {
+			o = Optional.ofNullable(findAnnotation(a));
+			annotationMap().put(a, o);
+		}
+		return o.isPresent() ? (T)o.get() : null;
+	}
+
+	/**
+	 * Returns <jk>true</jk> if this method has the specified annotation.
+	 *
+	 * @param a
+	 * 	The annotation to search for.
+	 * @return
+	 * 	The <jk>true</jk> if annotation if found.
+	 */
+	public boolean hasAnnotation(Class<? extends Annotation> a) {
+		return getAnnotation(a) != null;
+	}
+
+	@SuppressWarnings("unchecked")
+	private <T extends Annotation> T findAnnotation(Class<T> a) {
+		List<Method> methods = getMatching();
+		for (Method m2 : methods)
+			for (Annotation a2 :  m2.getAnnotations())
+				if (a.isInstance(a2))
+					return (T)a2;
+		Type t = method.getGenericReturnType();
+		if (Value.isType(t))
+			return ClassInfo.lookup(Value.getParameterType(t)).getAnnotation(a);
+		return ClassInfo.lookup(t).getAnnotation(a);
+	}
+
+	/**
+	 * Returns all annotations of the specified type defined on the specified method.
+	 *
+	 * <p>
+	 * Searches all methods with the same signature on the parent classes or interfaces
+	 * and the return type on the method.
+	 *
+	 * @param a
+	 * 	The annotation to search for.
+	 * @return
+	 * 	A list of all matching annotations found in child-to-parent order, or an empty list if none found.
+	 */
+	@SuppressWarnings("unchecked")
+	public <T extends Annotation> List<T> getAnnotations(Class<T> a) {
+		List<T> l = (List<T>)annotationsMap().get(a);
+		if (l == null) {
+			l = Collections.unmodifiableList(findAnnotations(a));
+			annotationsMap().put(a, l);
+		}
+		return l;
+	}
+
+	/**
+	 * Identical to {@link #getAnnotations(Class)} but returns the list in reverse (parent-to-child) order.
+	 *
+	 * @param a
+	 * 	The annotation to search for.
+	 * @return
+	 * 	A list of all matching annotations found in parent-to-child order, or an empty list if none found.
+	 */
+	@SuppressWarnings("unchecked")
+	public <T extends Annotation> List<T> getAnnotationsParentFirst(Class<T> a) {
+		List<T> l = (List<T>)annotationsPfMap().get(a);
+		if (l == null) {
+			l = new ArrayList<>(getAnnotations(a));
+			Collections.reverse(l);
+			l = Collections.unmodifiableList(l);
+			annotationsPfMap().put(a, l);
+		}
+		return l;
+	}
+
+	/**
+	 * Asserts that the specified method that's annotated with the specified annotation cannot also be annotated with other annotations.
+	 *
+	 * @param a The annotation known to exist on the method.
+	 * @param c The annotations that cannot be present on the method.
+	 * @throws InvalidAnnotationException
+	 */
+	@SafeVarargs
+	public final void assertNoAnnotations(Class<? extends Annotation> a, Class<? extends Annotation>...c) throws InvalidAnnotationException {
+		for (Class<? extends Annotation> cc : c)
+			if (hasAnnotation(cc))
+				throw new InvalidAnnotationException("@{0} annotation cannot be used in a @{1} bean.  Method=''{2}''", cc.getSimpleName(), a.getSimpleName(), method);
+	}
+
+	@SuppressWarnings("unchecked")
+	private <T extends Annotation> List<T> findAnnotations(Class<T> a) {
+		List<T> l = new ArrayList<>();
+		List<Method> methods = getMatching();
+		for (Method m2 : methods)
+			for (Annotation a2 :  m2.getAnnotations())
+				if (a.isInstance(a2))
+					l.add((T)a2);
+		Type t = method.getGenericReturnType();
+		if (Value.isType(t))
+			ClassUtils.appendAnnotations(a, Value.getParameterType(t), l);
+		else
+			ClassUtils.appendAnnotations(a, t, l);
+		return l;
+	}
+
+	private synchronized Map<Class<?>,Optional<Annotation>> annotationMap() {
+		if (annotationMap == null)
+			annotationMap = new ConcurrentHashMap<>();
+		return annotationMap;
+	}
+
+	private synchronized Map<Class<?>,List<?>> annotationsMap() {
+		if (annotationsMap == null)
+			annotationsMap = new ConcurrentHashMap<>();
+		return annotationsMap;
+	}
+
+	private synchronized Map<Class<?>,List<?>> annotationsPfMap() {
+		if (annotationsPfMap == null)
+			annotationsPfMap = new ConcurrentHashMap<>();
+		return annotationsPfMap;
+	}
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MethodParamInfo.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MethodParamInfo.java
new file mode 100644
index 0000000..1805439
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MethodParamInfo.java
@@ -0,0 +1,224 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "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;
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import org.apache.juneau.internal.*;
+
+/**
+ * Utility class for introspecting information about a method parameter.
+ */
+public class MethodParamInfo {
+
+	private MethodInfo methodInfo;
+	private int index;
+	private Map<Class<?>,Optional<Annotation>> annotationMap = new ConcurrentHashMap<>();
+	private Map<Class<?>,List<?>> annotationsMap = new ConcurrentHashMap<>();
+	private Map<Class<?>,List<?>> annotationsPfMap = new ConcurrentHashMap<>();
+
+	/**
+	 * Constructor.
+	 *
+	 * @param methodInfo The method wrapper.
+	 * @param index The parameter index.
+	 */
+	protected MethodParamInfo(MethodInfo methodInfo, int index) {
+		this.methodInfo = methodInfo;
+		this.index = index;
+	}
+
+	/**
+	 * Returns the index position of this parameter.
+	 *
+	 * @return The index position of this parameter.
+	 */
+	public int getIndex() {
+		return index;
+	}
+
+	/**
+	 * Returns the method that this parameter belongs to.
+	 *
+	 * @return The method that this parameter belongs to.
+	 */
+	public Method getMethod() {
+		return methodInfo.getInner();
+	}
+
+	/**
+	 * Returns the class type of this parameter.
+	 *
+	 * @return The class type of this parameter.
+	 */
+	public Class<?> getParameterType() {
+		return getMethod().getParameterTypes()[index];
+	}
+
+	/**
+	 * Returns the generic class type of this parameter.
+	 *
+	 * @return The generic class type of htis parameter.
+	 */
+	public Type getGenericParameterType() {
+		return methodInfo.getInner().getGenericParameterTypes()[index];
+	}
+
+	/**
+	 * Returns the parameter annotations defined on this parameter.
+	 *
+	 * @return The parameter annotations defined on this parameter.
+	 */
+	public Annotation[] getParameterAnnotations() {
+		return methodInfo.getInner().getParameterAnnotations()[index];
+	}
+
+	/**
+	 * Finds the annotation of the specified type defined on this method parameter.
+	 *
+	 * <p>
+	 * If the annotation cannot be found on the immediate method, searches methods with the same
+	 * signature on the parent classes or interfaces.
+	 * <br>The search is performed in child-to-parent order.
+	 *
+	 * <p>
+	 * If still not found, searches for the annotation on the return type of the method.
+	 *
+	 * @param a
+	 * 	The annotation to search for.
+	 * @return
+	 * 	The annotation if found, or <jk>null</jk> if not.
+	 */
+	@SuppressWarnings("unchecked")
+	public <T extends Annotation> T getAnnotation(Class<T> a) {
+		Optional<Annotation> o = annotationMap().get(a);
+		if (o == null) {
+			o = Optional.ofNullable(findAnnotation(a));
+			annotationMap().put(a, o);
+		}
+		return o.isPresent() ? (T)o.get() : null;
+	}
+
+	/**
+	 * Returns <jk>true</jk> if this parameter has the specified annotation.
+	 *
+	 * @param a
+	 * 	The annotation to search for.
+	 * @return
+	 * 	The <jk>true</jk> if annotation if found.
+	 */
+	public boolean hasAnnotation(Class<? extends Annotation> a) {
+		return getAnnotation(a) != null;
+	}
+
+	@SuppressWarnings("unchecked")
+	private <T extends Annotation> T findAnnotation(Class<T> a) {
+		List<Method> methods = methodInfo.getMatching();
+		for (Method m2 : methods)
+			for (Annotation a2 :  m2.getParameterAnnotations()[index])
+				if (a.isInstance(a2))
+					return (T)a2;
+		Type t = methodInfo.getInner().getGenericParameterTypes()[index];
+		if (Value.isType(t))
+			return ClassInfo.lookup(Value.getParameterType(t)).getAnnotation(a);
+		return ClassInfo.lookup(t).getAnnotation(a);
+	}
+
+	/**
+	 * Returns all annotations of the specified type defined on the specified method.
+	 *
+	 * <p>
+	 * Searches all methods with the same signature on the parent classes or interfaces
+	 * and the return type on the method.
+	 *
+	 * @param a
+	 * 	The annotation to search for.
+	 * @return
+	 * 	A list of all matching annotations found in child-to-parent order, or an empty list if none found.
+	 */
+	@SuppressWarnings("unchecked")
+	public <T extends Annotation> List<T> getAnnotations(Class<T> a) {
+		List<T> l = (List<T>)annotationsMap().get(a);
+		if (l == null) {
+			l = Collections.unmodifiableList(findAnnotations(a));
+			annotationsMap().put(a, l);
+		}
+		return l;
+	}
+
+	/**
+	 * Returns all annotations of the specified type defined on the specified method.
+	 *
+	 * <p>
+	 * Searches all methods with the same signature on the parent classes or interfaces
+	 * and the return type on the method.
+	 *
+	 * @param a
+	 * 	The annotation to search for.
+	 * @return
+	 * 	A list of all matching annotations found in child-to-parent order, or an empty list if none found.
+	 */
+	@SuppressWarnings("unchecked")
+	public <T extends Annotation> List<T> getAnnotationsParentFirst(Class<T> a) {
+		List<T> l = (List<T>)annotationsPfMap().get(a);
+		if (l == null) {
+			l = new ArrayList<>(getAnnotations(a));
+			Collections.reverse(l);
+			l = Collections.unmodifiableList(l);
+			annotationsPfMap().put(a, l);
+		}
+		return l;
+	}
+
+	@SuppressWarnings("unchecked")
+	private <T extends Annotation> List<T> findAnnotations(Class<T> a) {
+		List<T> l = new ArrayList<>();
+		List<Method> methods = methodInfo.getMatching();
+		for (Method m2 : methods)
+			for (Annotation a2 :  m2.getParameterAnnotations()[index])
+				if (a.isInstance(a2))
+					l.add((T)a2);
+		Type t = methodInfo.getInner().getGenericParameterTypes()[index];
+		if (Value.isType(t))
+			ClassUtils.appendAnnotations(a, Value.getParameterType(t), l);
+		else
+			ClassUtils.appendAnnotations(a, t, l);
+		return l;
+	}
+
+	private synchronized Map<Class<?>,Optional<Annotation>> annotationMap() {
+		if (annotationMap == null)
+			annotationMap = new ConcurrentHashMap<>();
+		return annotationMap;
+	}
+
+	private synchronized Map<Class<?>,List<?>> annotationsMap() {
+		if (annotationsMap == null)
+			annotationsMap = new ConcurrentHashMap<>();
+		return annotationsMap;
+	}
+
+	private synchronized Map<Class<?>,List<?>> annotationsPfMap() {
+		if (annotationsPfMap == null)
+			annotationsPfMap = new ConcurrentHashMap<>();
+		return annotationsPfMap;
+	}
+
+	@Override
+	public String toString() {
+		return getMethod().getName() + "[" + index + "]";
+	}
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlClassMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlClassMeta.java
index 7edb103..a6fa303 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlClassMeta.java
@@ -14,7 +14,6 @@ package org.apache.juneau.html;
 
 import org.apache.juneau.*;
 import org.apache.juneau.html.annotation.*;
-import org.apache.juneau.internal.*;
 
 /**
  * Metadata on classes specific to the HTML serializers and parsers pulled from the {@link Html @Html} annotation on
@@ -34,7 +33,7 @@ public class HtmlClassMeta extends ClassMetaExtended {
 	 */
 	public HtmlClassMeta(ClassMeta<?> cm) {
 		super(cm);
-		this.html = ClassUtils.getAnnotation(Html.class, getInnerClass());
+		this.html = cm.getClassInfo().getAnnotation(Html.class);
 		if (html != null) {
 			format = html.format();
 			noTables = html.noTables();
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java
index ac54950..425a551 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java
@@ -109,14 +109,11 @@ public class HttpPartSchema {
 	 * 		<li>{@link HasQuery}
 	 * 		<li>{@link HasFormData}
 	 * 	</ul>
-	 * @param m
-	 * 	The Java method containing the parameter.
-	 * @param mi
-	 * 	The index of the parameter on the method.
+	 * @param mpi The Java method parameter.
 	 * @return The schema information about the parameter.
 	 */
-	public static HttpPartSchema create(Class<? extends Annotation> c, Method m, int mi) {
-		return create().apply(c, m, mi).build();
+	public static HttpPartSchema create(Class<? extends Annotation> c, MethodParamInfo mpi) {
+		return create().apply(c, mpi).build();
 	}
 
 	/**
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchemaBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchemaBuilder.java
index cb414b2..9d136f3 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchemaBuilder.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchemaBuilder.java
@@ -26,7 +26,6 @@ import org.apache.juneau.jsonschema.annotation.Items;
 import org.apache.juneau.jsonschema.annotation.SubItems;
 import org.apache.juneau.httppart.HttpPartSchema.*;
 import org.apache.juneau.httppart.HttpPartSchema.Type;
-import org.apache.juneau.internal.*;
 import org.apache.juneau.utils.*;
 
 /**
@@ -64,9 +63,9 @@ public class HttpPartSchemaBuilder {
 		return new HttpPartSchema(this);
 	}
 
-	HttpPartSchemaBuilder apply(Class<? extends Annotation> c, Method m, int index) {
-		apply(c, m.getGenericParameterTypes()[index]);
-		for (Annotation a :  m.getParameterAnnotations()[index])
+	HttpPartSchemaBuilder apply(Class<? extends Annotation> c, MethodParamInfo mpi) {
+		apply(c, mpi.getGenericParameterType());
+		for (Annotation a : mpi.getParameterAnnotations())
 			if (c.isInstance(a))
 				apply(a);
 		return this;
@@ -83,7 +82,8 @@ public class HttpPartSchemaBuilder {
 	HttpPartSchemaBuilder apply(Class<? extends Annotation> c, java.lang.reflect.Type t) {
 		if (t instanceof Class<?>) {
 			Class<?> tc = (Class<?>)t;
-			for (Annotation a : ClassUtils.getAnnotationsParentFirst(c, tc))
+			ClassInfo ci = ClassInfo.lookup(tc);
+			for (Annotation a : ci.getAnnotationsParentFirst(c))
 				apply(a);
 		} else if (Value.isType(t)) {
 			apply(c, Value.getParameterType(t));
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/RequestBeanMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/RequestBeanMeta.java
index 7d3ef9d..5d055fc 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/RequestBeanMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/RequestBeanMeta.java
@@ -32,17 +32,16 @@ public class RequestBeanMeta {
 	/**
 	 * Create metadata from specified parameter.
 	 *
-	 * @param m The method containing the parameter or parameter type annotated with {@link Request}.
-	 * @param i The parameter index.
+	 * @param mpi The method parameter.
 	 * @param ps
 	 * 	Configuration information used to instantiate part serializers and part parsers.
 	 * 	<br>Can be <jk>null</jk>.
 	 * @return Metadata about the parameter, or <jk>null</jk> if parameter or parameter type not annotated with {@link Request}.
 	 */
-	public static RequestBeanMeta create(Method m, int i, PropertyStore ps) {
-		if (! hasAnnotation(Request.class, m, i))
+	public static RequestBeanMeta create(MethodParamInfo mpi, PropertyStore ps) {
+		if (! mpi.hasAnnotation(Request.class))
 			return null;
-		return new RequestBeanMeta.Builder(ps).apply(m, i).build();
+		return new RequestBeanMeta.Builder(ps).apply(mpi).build();
 	}
 
 	/**
@@ -55,7 +54,8 @@ public class RequestBeanMeta {
 	 * @return Metadata about the class, or <jk>null</jk> if class not annotated with {@link Request}.
 	 */
 	public static RequestBeanMeta create(Class<?> c, PropertyStore ps) {
-		if (! hasAnnotation(Request.class, c))
+		ClassInfo ci = ClassInfo.lookup(c);
+		if (! ci.hasAnnotation(Request.class))
 			return null;
 		return new RequestBeanMeta.Builder(ps).apply(c).build();
 	}
@@ -90,34 +90,37 @@ public class RequestBeanMeta {
 			this.ps = ps;
 		}
 
-		Builder apply(Method m, int i) {
-			return apply(m.getParameterTypes()[i]).apply(getAnnotation(Request.class, m, i));
+		Builder apply(MethodParamInfo mpi) {
+			return apply(mpi.getParameterType()).apply(mpi.getAnnotation(Request.class));
 		}
 
 		Builder apply(Class<?> c) {
-			apply(getAnnotation(Request.class, c));
+			ClassInfo ci = ClassInfo.lookup(c);
+			apply(ci.getAnnotation(Request.class));
 			this.cm = BeanContext.DEFAULT.getClassMeta(c);
 			for (Method m : ClassUtils.getAllMethods(c, false)) {
+
 				if (isPublic(m)) {
-					assertNoAnnotations(m, Request.class, ResponseHeader.class, ResponseBody.class, ResponseStatus.class);
+					MethodInfo mi = getMethodInfo(m);
+					mi.assertNoAnnotations(Request.class, ResponseHeader.class, ResponseBody.class, ResponseStatus.class);
 					String n = m.getName();
-					if (hasAnnotation(Body.class, m)) {
+					if (mi.hasAnnotation(Body.class)) {
 						assertNoArgs(m, Body.class);
 						assertReturnNotVoid(m, Body.class);
 						properties.put(n, RequestBeanPropertyMeta.create(BODY, Body.class, m));
-					} else if (hasAnnotation(Header.class, m)) {
+					} else if (mi.hasAnnotation(Header.class)) {
 						assertNoArgs(m, Header.class);
 						assertReturnNotVoid(m, Header.class);
 						properties.put(n, RequestBeanPropertyMeta.create(HEADER, Header.class, m));
-					} else if (hasAnnotation(Query.class, m)) {
+					} else if (mi.hasAnnotation(Query.class)) {
 						assertNoArgs(m, Query.class);
 						assertReturnNotVoid(m, Query.class);
 						properties.put(n, RequestBeanPropertyMeta.create(QUERY, Query.class, m));
-					} else if (hasAnnotation(FormData.class, m)) {
+					} else if (mi.hasAnnotation(FormData.class)) {
 						assertNoArgs(m, FormData.class);
 						assertReturnNotVoid(m, FormData.class);
 						properties.put(n, RequestBeanPropertyMeta.create(FORMDATA, FormData.class, m));
-					} else if (hasAnnotation(Path.class, m)) {
+					} else if (mi.hasAnnotation(Path.class)) {
 						assertNoArgs(m, Path.class);
 						assertReturnNotVoid(m, Path.class);
 						properties.put(n, RequestBeanPropertyMeta.create(PATH, Path.class, m));
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/RequestBeanPropertyMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/RequestBeanPropertyMeta.java
index ca0d2c5..f72a349 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/RequestBeanPropertyMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/RequestBeanPropertyMeta.java
@@ -30,7 +30,7 @@ public class RequestBeanPropertyMeta {
 
 	static RequestBeanPropertyMeta.Builder create(HttpPartType partType, Class<? extends Annotation> c, Method m) {
 		HttpPartSchemaBuilder sb = HttpPartSchema.create().name(getPropertyName(m));
-		for (Annotation a : getAnnotationsParentFirst(c, m))
+		for (Annotation a : getMethodInfo(m).getAnnotationsParentFirst(c))
 			sb.apply(a);
 		return new Builder().partType(partType).schema(sb.build()).getter(m);
 	}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanMeta.java
index eab256f..fe5d453 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanMeta.java
@@ -45,14 +45,15 @@ public class ResponseBeanMeta {
 	 * @return Metadata about the class, or <jk>null</jk> if class not annotated with {@link Response}.
 	 */
 	public static ResponseBeanMeta create(Type t, PropertyStore ps) {
-		if (! hasAnnotation(Response.class, t))
+		ClassInfo ci = ClassInfo.create(t);
+		if (! ci.hasAnnotation(Response.class))
 			return null;
 		Builder b = new Builder(ps);
 		if (Value.isType(t))
 			b.apply(Value.getParameterType(t));
 		else
 			b.apply(t);
-		for (Response r : getAnnotationsParentFirst(Response.class, t))
+		for (Response r : ci.getAnnotationsParentFirst(Response.class))
 			b.apply(r);
 		return b.build();
 	}
@@ -67,7 +68,7 @@ public class ResponseBeanMeta {
 	 * @return Metadata about the class, or <jk>null</jk> if class not annotated with {@link Response}.
 	 */
 	public static ResponseBeanMeta create(Method m, PropertyStore ps) {
-		if (! hasAnnotation(Response.class, m))
+		if (! getMethodInfo(m).hasAnnotation(Response.class))
 			return null;
 		Builder b = new Builder(ps);
 		Type t = m.getGenericReturnType();
@@ -75,7 +76,7 @@ public class ResponseBeanMeta {
 			b.apply(Value.getParameterType(t));
 		else
 			b.apply(t);
-		for (Response r : getAnnotationsParentFirst(Response.class, m))
+		for (Response r : getMethodInfo(m).getAnnotationsParentFirst(Response.class))
 			b.apply(r);
 		return b.build();
 	}
@@ -83,23 +84,22 @@ public class ResponseBeanMeta {
 	/**
 	 * Create metadata from specified method parameter.
 	 *
-	 * @param m The method containing the parameter annotated with {@link Response}.
-	 * @param i The parameter index.
+	 * @param mpi The method parameter.
 	 * @param ps
 	 * 	Configuration information used to instantiate part serializers and part parsers.
 	 * 	<br>Can be <jk>null</jk>.
 	 * @return Metadata about the class, or <jk>null</jk> if class not annotated with {@link Response}.
 	 */
-	public static ResponseBeanMeta create(Method m, int i, PropertyStore ps) {
-		if (! hasAnnotation(Response.class, m, i))
+	public static ResponseBeanMeta create(MethodParamInfo mpi, PropertyStore ps) {
+		if (! mpi.hasAnnotation(Response.class))
 			return null;
 		Builder b = new Builder(ps);
-		Type t = m.getGenericParameterTypes()[i];
+		Type t = mpi.getGenericParameterType();
 		if (Value.isType(t))
 			b.apply(Value.getParameterType(t));
 		else
 			b.apply(t);
-		for (Response r : getAnnotationsParentFirst(Response.class, m, i))
+		for (Response r : mpi.getAnnotationsParentFirst(Response.class))
 			b.apply(r);
 		return b.build();
 	}
@@ -166,17 +166,18 @@ public class ResponseBeanMeta {
 			this.cm = BeanContext.DEFAULT.getClassMeta(c);
 			for (Method m : ClassUtils.getAllMethods(c, false)) {
 				if (isPublic(m)) {
-					assertNoAnnotations(m, Response.class, Body.class, Header.class, Query.class, FormData.class, Path.class);
-					if (hasAnnotation(ResponseHeader.class, m)) {
+					MethodInfo mi = getMethodInfo(m);
+					mi.assertNoAnnotations(Response.class, Body.class, Header.class, Query.class, FormData.class, Path.class);
+					if (mi.hasAnnotation(ResponseHeader.class)) {
 						assertNoArgs(m, ResponseHeader.class);
 						assertReturnNotVoid(m, ResponseHeader.class);
-						HttpPartSchema s = HttpPartSchema.create(getAnnotation(ResponseHeader.class, m), getPropertyName(m));
+						HttpPartSchema s = HttpPartSchema.create(getMethodInfo(m).getAnnotation(ResponseHeader.class), getPropertyName(m));
 						headerMethods.put(s.getName(), ResponseBeanPropertyMeta.create(RESPONSE_HEADER, s, m));
-					} else if (hasAnnotation(ResponseStatus.class, m)) {
+					} else if (mi.hasAnnotation(ResponseStatus.class)) {
 						assertNoArgs(m, ResponseHeader.class);
 						assertReturnType(m, ResponseHeader.class, int.class, Integer.class);
 						statusMethod = ResponseBeanPropertyMeta.create(RESPONSE_STATUS, m);
-					} else if (hasAnnotation(ResponseBody.class, m)) {
+					} else if (mi.hasAnnotation(ResponseBody.class)) {
 						Class<?>[] pt = m.getParameterTypes();
 						if (pt.length == 0)
 							assertReturnNotVoid(m, ResponseHeader.class);
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/Utils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/Utils.java
index f227465..ea737fd 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/Utils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/Utils.java
@@ -12,8 +12,6 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.httppart.bean;
 
-import static org.apache.juneau.internal.ClassUtils.*;
-
 import java.beans.*;
 import java.lang.annotation.*;
 import java.lang.reflect.*;
@@ -25,14 +23,6 @@ import org.apache.juneau.annotation.*;
  */
 class Utils {
 
-	@SafeVarargs
-	static final void assertNoAnnotations(Method m, Class<? extends Annotation> a, Class<? extends Annotation>...c) throws InvalidAnnotationException {
-		for (Class<? extends Annotation> cc : c) {
-			if (hasAnnotation(cc, m))
-				throw new InvalidAnnotationException("@{0} annotation cannot be used in a @{1} bean.  Method=''{2}''", cc.getSimpleName(), a.getSimpleName(), m);
-		}
-	}
-
 	static void assertNoArgs(Method m, Class<?> a) throws InvalidAnnotationException {
 		if (m.getParameterTypes().length != 0)
 			throw new InvalidAnnotationException("Method with @{0} annotation cannot have arguments.  Method=''{1}''", a.getSimpleName(), m);
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
index 937a103..c281387 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
@@ -1113,256 +1113,6 @@ public final class ClassUtils {
 		return isParentClass(c, m.getReturnType());
 	}
 
-	//-----------------------------------------------------------------------------------------------------------------
-	// Method annotations.
-	//-----------------------------------------------------------------------------------------------------------------
-
-	/**
-	 * Returns all annotations of the specified type defined on the specified method.
-	 *
-	 * @param a
-	 * 	The annotation to search for.
-	 * @param m
-	 * 	The method to find the annotation on.
-	 * @param searchParentMethods
-	 * 	If <jk>true</jk>, searches methods with the same signature on the parent classes or interfaces.
-	 * @param searchReturnType
-	 * 	If <jk>true</jk>, searches the return type on the method for the specified annotation.
-	 * @param parentFirst
-	 * 	If <jk>true</jk>, returns the results in parent-to-child order.
-	 * @return
-	 * 	A list of all matching annotations found, or an empty list if none found.
-	 */
-	@SuppressWarnings("unchecked")
-	public static <T extends Annotation> List<T> getAnnotations(Class<T> a, Method m, boolean searchParentMethods, boolean searchReturnType, boolean parentFirst) {
-		List<T> l = new ArrayList<>();
-		List<Method> methods = searchParentMethods ? findMatchingMethods(m) : Collections.singletonList(m);
-		for (Method m2 : methods)
-			for (Annotation a2 :  m2.getAnnotations())
-				if (a.isInstance(a2))
-					l.add((T)a2);
-		if (searchReturnType) {
-			Type t = m.getGenericReturnType();
-			if (Value.isType(t))
-				appendAnnotations(a, Value.getParameterType(t), l);
-			else
-				appendAnnotations(a, t, l);
-		}
-		if (parentFirst)
-			Collections.reverse(l);
-		return l;
-	}
-
-	/**
-	 * Shortcut for calling <code>getAnnotations(a,m,<jk>true</jk>,<jk>true</jk>,<jk>false</jk>)</code>
-	 *
-	 * <p>
-	 * Annotations are ordered method first, then return class, then return superclasses.
-	 *
-	 * @param a The annotation to look for.
-	 * @param m The method being inspected.
-	 * @return All instances of the annotation with the
-	 */
-	public static <T extends Annotation> List<T> getAnnotations(Class<T> a, Method m) {
-		return getAnnotations(a, m, true, true, false);
-	}
-
-	/**
-	 * Shortcut for calling <code>getAnnotations(a,m,<jk>true</jk>,<jk>true</jk>,<jk>true</jk>)</code>
-	 *
-	 * @param a The annotation to look for.
-	 * @param m The method being inspected.
-	 * @return All instances of the annotation with the
-	 */
-	public static <T extends Annotation> List<T> getAnnotationsParentFirst(Class<T> a, Method m) {
-		return getAnnotations(a, m, true, true, true);
-	}
-
-	/**
-	 * Finds the annotation of the specified type defined on the specified method.
-	 *
-	 * @param a
-	 * 	The annotation to search for.
-	 * @param m
-	 * 	The method to find the annotation on.
-	 * @param searchParentMethods
-	 * 	If <jk>true</jk>, searches methods with the same signature on the parent classes or interfaces.
-	 * 	<br>The search is performed in child-to-parent order.
-	 * @param searchReturnType
-	 * 	If <jk>true</jk>, searches the return type on the method for the specified annotation.
-	 * @return
-	 * 	The annotation if found, or <jk>null</jk> if not.
-	 */
-	@SuppressWarnings("unchecked")
-	public static <T extends Annotation> T getAnnotation(Class<T> a, Method m, boolean searchParentMethods, boolean searchReturnType) {
-		List<Method> methods = searchParentMethods ? findMatchingMethods(m) : Collections.singletonList(m);
-		for (Method m2 : methods)
-			for (Annotation a2 :  m2.getAnnotations())
-				if (a.isInstance(a2))
-					return (T)a2;
-		if (searchReturnType) {
-			Type t = m.getGenericReturnType();
-			if (Value.isType(t))
-				return getAnnotation(a, Value.getParameterType(t));
-			return getAnnotation(a, t);
-		}
-		return null;
-	}
-
-	/**
-	 * Shortcut for calling <code>getAnnotation(a,m,<jk>true</jk>,<jk>true</jk>)</code>
-	 *
-	 * @param a
-	 * 	The annotation to search for.
-	 * @param m
-	 * 	The method to find the annotation on.
-	 * @return
-	 * 	The annotation if found, or <jk>null</jk> if not.
-	 */
-	public static <T extends Annotation> T getAnnotation(Class<T> a, Method m) {
-		return getAnnotation(a, m, true, true);
-	}
-
-	//-----------------------------------------------------------------------------------------------------------------
-	// Method argument annotations.
-	//-----------------------------------------------------------------------------------------------------------------
-
-	/**
-	 * Returns all annotations of the specified type defined on the specified method argument.
-	 *
-	 * @param a
-	 * 	The annotation to search for.
-	 * @param m
-	 * 	The method containing the argument to find the annotation on.
-	 * @param index
-	 * 	The argument index position.
-	 * @param searchParentMethods
-	 * 	If <jk>true</jk>, searches methods with the same signature on the parent classes or interfaces.
-	 * @param searchArgType
-	 * 	If <jk>true</jk>, searches the argument type for the specified annotation.
-	 * @param parentFirst
-	 * 	If <jk>true</jk>, returns the results in parent-to-child order.
-	 * @return
-	 * 	A list of all matching annotations found, or an empty list if none found.
-	 */
-	@SuppressWarnings("unchecked")
-	public static <T extends Annotation> List<T> getAnnotations(Class<T> a, Method m, int index, boolean searchParentMethods, boolean searchArgType, boolean parentFirst) {
-		List<T> l = new ArrayList<>();
-		List<Method> methods = searchParentMethods ? findMatchingMethods(m) : Collections.singletonList(m);
-		for (Method m2 : methods)
-			for (Annotation a2 :  m2.getParameterAnnotations()[index])
-				if (a.isInstance(a2))
-					l.add((T)a2);
-		if (searchArgType) {
-			Type t = m.getGenericParameterTypes()[index];
-			if (Value.isType(t))
-				appendAnnotations(a, Value.getParameterType(t), l);
-			else
-				appendAnnotations(a, t, l);
-		}
-		if (parentFirst)
-			Collections.reverse(l);
-		return l;
-	}
-
-	/**
-	 * Finds the annotation of the specified type defined on the specified method argument.
-	 *
-	 * @param a
-	 * 	The annotation to search for.
-	 * @param m
-	 * 	The method containing the argument to find the annotation on.
-	 * @param index
-	 * 	The argument index position.
-	 * @param searchParentMethods
-	 * 	If <jk>true</jk>, searches methods with the same signature on the parent classes or interfaces.
-	 * 	<br>The search is performed in child-to-parent order.
-	 * @param searchArgType
-	 * 	If <jk>true</jk>, searches the argument type for the specified annotation.
-	 * @return
-	 * 	The annotation if found, or <jk>null</jk> if not.
-	 */
-	@SuppressWarnings("unchecked")
-	public static <T extends Annotation> T getAnnotation(Class<T> a, Method m, int index, boolean searchParentMethods, boolean searchArgType) {
-		List<Method> methods = searchParentMethods ? findMatchingMethods(m) : Collections.singletonList(m);
-		for (Method m2 : methods)
-			for (Annotation a2 :  m2.getParameterAnnotations()[index])
-				if (a.isInstance(a2))
-					return (T)a2;
-		if (searchArgType) {
-			Type t = m.getGenericParameterTypes()[index];
-			if (Value.isType(t))
-				return getAnnotation(a, Value.getParameterType(t));
-			return getAnnotation(a, t);
-		}
-		return null;
-	}
-
-	/**
-	 * Shortcut for calling <code>getAnnotation(a,m,index,<jk>true</jk>,<jk>true</jk>)</code>
-	 *
-	 * @param a The annotation to check for.
-	 * @param m The method containing the parameter to check.
-	 * @param index The parameter index.
-	 * @return <jk>true</jk> if the {@link #getAnnotation(Class, Method, int)} returns a value.
-	 */
-	public static <T extends Annotation> T getAnnotation(Class<T> a, Method m, int index) {
-		return getAnnotation(a, m, index, true, true);
-	}
-
-	/**
-	 * Shortcut for calling <code>getAnnotations(a, m, index, <jk>true</jk>, <jk>true</jk>, <jk>false</jk>);</code>
-	 *
-	 * @param a The annotation to look for.
-	 * @param m The method containing the parameter.
-	 * @param index The parameter index.
-	 * @return All instances of the annotation with the
-	 */
-	public static <T extends Annotation> List<T> getAnnotations(Class<T> a, Method m, int index) {
-		return getAnnotations(a, m, index, true, true, false);
-	}
-
-	/**
-	 * Shortcut for calling <code>getAnnotations(a, m, index, <jk>true</jk>, <jk>true</jk>, <jk>true</jk>);</code>
-	 *
-	 * @param a The annotation to look for.
-	 * @param m The method containing the parameter.
-	 * @param index The parameter index.
-	 * @return All instances of the annotation with the
-	 */
-	public static <T extends Annotation> List<T> getAnnotationsParentFirst(Class<T> a, Method m, int index) {
-		return getAnnotations(a, m, index, true, true, true);
-	}
-
-	/**
-	 * Given a specific method, finds all declared methods with the same name and arguments on all
-	 * superclasses and interfaces.
-	 *
-	 * @param m The method to find matches against.
-	 * @return
-	 * 	All matching methods including the input method itself.
-	 * 	Methods are ordered from child-to-parent order.
-	 */
-	public static List<Method> findMatchingMethods(Method m) {
-		return findMatchingMethods(new ArrayList<Method>(), m, m.getDeclaringClass());
-	}
-
-	private static List<Method> findMatchingMethods(List<Method> l, Method m, Class<?> c) {
-		for (Method m2 : c.getDeclaredMethods())
-			if (isSameMethod(m, m2))
-				l.add(m2);
-		Class<?> pc = c.getSuperclass();
-		if (pc != null)
-			findMatchingMethods(l, m, pc);
-		for (Class<?> ic : c.getInterfaces())
-			findMatchingMethods(l, m, ic);
-		return l;
-	}
-
-	private static boolean isSameMethod(Method m1, Method m2) {
-		return m1.getName().equals(m2.getName()) && Arrays.equals(m1.getParameterTypes(), m2.getParameterTypes());
-	}
-
 	/**
 	 * Locates the no-arg constructor for the specified class.
 	 *
@@ -1739,43 +1489,12 @@ public final class ClassUtils {
 	 * @return The bean with information about the method.
 	 */
 	public static MethodInfo getMethodInfo(Method m) {
+		if (m == null)
+			return null;
 		return new MethodInfo(m);
 	}
 
 	/**
-	 * Returns {@link MethodInfo} beans that describe the specified methods.
-	 *
-	 * @param m The methods to describe.
-	 * @return The beans with information about the methods.
-	 */
-	public static MethodInfo[] getMethodInfo(Collection<Method> m) {
-		MethodInfo[] mi = new MethodInfo[m.size()];
-		int i = 0;
-		for (Method mm : m)
-			mi[i++] = getMethodInfo(mm);
-		return mi;
-	}
-
-	/**
-	 * Simple bean that shows the name, parameter types, and return type of a method.
-	 */
-	@SuppressWarnings("javadoc")
-	public static class MethodInfo {
-		public final String methodName;
-		public final String[] parameterTypes;
-		public final String returnType;
-
-		MethodInfo(Method m) {
-			methodName = m.getName();
-			Type[] pt = m.getGenericParameterTypes();
-			parameterTypes = new String[pt.length];
-			for (int i  = 0; i < pt.length; i++)
-				parameterTypes[i] = BeanContext.DEFAULT.getClassMeta(pt[i]).toString();
-			returnType = BeanContext.DEFAULT.getClassMeta(m.getGenericReturnType()).toString();
-		}
-	}
-
-	/**
 	 * Creates an instance of the specified class.
 	 *
 	 * @param c
@@ -2366,68 +2085,6 @@ public final class ClassUtils {
 	}
 
 	/**
-	 * Returns <jk>true</jk> if the {@link #getAnnotation(Class, Method, int)} returns a value.
-	 *
-	 * @param a The annotation to check for.
-	 * @param m The method containing the parameter to check.
-	 * @param index The parameter index.
-	 * @return <jk>true</jk> if the {@link #getAnnotation(Class, Method, int)} returns a value.
-	 */
-	public static boolean hasAnnotation(Class<? extends Annotation> a, Method m, int index) {
-		return getAnnotation(a, m, index) != null;
-	}
-
-	/**
-	 * Returns <jk>true</jk> if the {@link #getAnnotation(Class, Method)} returns a value.
-	 *
-	 * @param a The annotation to check for.
-	 * @param m The method to check.
-	 * @return <jk>true</jk> if the {@link #getAnnotation(Class, Method)} returns a value.
-	 */
-	public static boolean hasAnnotation(Class<? extends Annotation> a, Method m) {
-		return getAnnotation(a, m) != null;
-	}
-
-	/**
-	 * Returns <jk>true</jk> if the {@link #getAnnotation(Class, Type)} returns a value.
-	 *
-	 * @param a The annotation to check for.
-	 * @param t The class to check.
-	 * @return <jk>true</jk> if the {@link #getAnnotation(Class, Type)} returns a value.
-	 */
-	public static boolean hasAnnotation(Class<? extends Annotation> a, Type t) {
-		return getAnnotation(a, t) != null;
-	}
-
-	/**
-	 * Similar to {@link Class#getAnnotation(Class)} except also searches annotations on interfaces.
-	 *
-	 * @param <T> The annotation class type.
-	 * @param a The annotation class.
-	 * @param t The annotated class.
-	 * @return The annotation, or <jk>null</jk> if not found.
-	 */
-	public static <T extends Annotation> T getAnnotation(Class<T> a, Type t) {
-		Class<?> c = toClass(t);
-		if (c != null) {
-			T t2 = getDeclaredAnnotation(a, c);
-			if (t2 != null)
-				return t2;
-
-			t2 = getAnnotation(a, c.getSuperclass());
-			if (t2 != null)
-				return t2;
-
-			for (Class<?> c2 : c.getInterfaces()) {
-				t2 = getAnnotation(a, c2);
-				if (t2 != null)
-					return t2;
-			}
-		}
-		return null;
-	}
-
-	/**
 	 * Returns the specified annotation only if it's been declared on the specified class.
 	 *
 	 * <p>
@@ -2450,38 +2107,7 @@ public final class ClassUtils {
 	}
 
 	/**
-	 * Returns all instances of the specified annotation on the specified class.
-	 *
-	 * <p>
-	 * Searches all superclasses and superinterfaces.
-	 * Results are ordered child-to-parent.
-	 *
-	 * @param <T> The annotation class type.
-	 * @param a The annotation class type.
-	 * @param t The class being searched.
-	 * @return The found matches, or an empty array if annotation was not found.
-	 */
-	public static <T extends Annotation> List<T> getAnnotations(Class<T> a, Type t) {
-		List<T> l = new LinkedList<>();
-		appendAnnotations(a, t, l);
-		return l;
-	}
-
-	/**
-	 * Same as {@link #getAnnotations(Class, Type)} but returns the list in parent-to-child order.
-	 *
-	 * @param a The annotation class type.
-	 * @param t The class being searched.
-	 * @return The found matches, or an empty array if annotation was not found.
-	 */
-	public static <T extends Annotation> List<T> getAnnotationsParentFirst(Class<T> a, Type t) {
-		List<T> l = getAnnotations(a, t);
-		Collections.reverse(l);
-		return l;
-	}
-
-	/**
-	 * Same as {@link #getAnnotations(Class, Type)} except returns the annotations as a map with the keys being the
+	 * Same as getAnnotations(Class, Type) except returns the annotations as a map with the keys being the
 	 * class on which the annotation was found.
 	 *
 	 * <p>
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonClassMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonClassMeta.java
index 007cd05..5b85c19 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonClassMeta.java
@@ -15,7 +15,6 @@ package org.apache.juneau.json;
 import static org.apache.juneau.internal.StringUtils.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.internal.*;
 import org.apache.juneau.json.annotation.*;
 
 /**
@@ -34,7 +33,7 @@ public class JsonClassMeta extends ClassMetaExtended {
 	 */
 	public JsonClassMeta(ClassMeta<?> cm) {
 		super(cm);
-		this.json = ClassUtils.getAnnotation(Json.class, getInnerClass());
+		this.json = cm.getClassInfo().getAnnotation(Json.class);
 		if (json != null) {
 			wrapperAttr = nullIfEmpty(json.wrapperAttr());
 		} else {
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaClassMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaClassMeta.java
index 8b131f8..a51d095 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaClassMeta.java
@@ -13,7 +13,6 @@
 package org.apache.juneau.jsonschema;
 
 import org.apache.juneau.*;
-import org.apache.juneau.internal.*;
 import org.apache.juneau.jsonschema.annotation.*;
 
 /**
@@ -32,7 +31,7 @@ public class JsonSchemaClassMeta extends ClassMetaExtended {
 	 */
 	public JsonSchemaClassMeta(ClassMeta<?> cm) throws Exception {
 		super(cm);
-		Schema s = ClassUtils.getAnnotation(Schema.class, getInnerClass());
+		Schema s = cm.getClassInfo().getAnnotation(Schema.class);
 		schema = s == null ? ObjectMap.EMPTY_MAP : SchemaUtils.asMap(s);
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remote/RemoteInterfaceMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remote/RemoteInterfaceMeta.java
index f45f7ce..eff0704 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remote/RemoteInterfaceMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remote/RemoteInterfaceMeta.java
@@ -19,6 +19,8 @@ import static org.apache.juneau.internal.StringUtils.*;
 import java.lang.reflect.*;
 import java.util.*;
 
+import org.apache.juneau.*;
+
 /**
  * Contains the meta-data about a remote proxy REST interface.
  *
@@ -50,7 +52,8 @@ public class RemoteInterfaceMeta {
 	public RemoteInterfaceMeta(Class<?> c, String uri) {
 		this.c = c;
 		String path = "";
-		List<RemoteInterface> rr = getAnnotationsParentFirst(RemoteInterface.class, c);
+		ClassInfo ci = ClassInfo.lookup(c);
+		List<RemoteInterface> rr = ci.getAnnotationsParentFirst(RemoteInterface.class);
 		for (RemoteInterface r : rr)
 			if (! r.path().isEmpty())
 				path = trimSlashes(r.path());
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingClassMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingClassMeta.java
index a60501a..660d259 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingClassMeta.java
@@ -13,7 +13,6 @@
 package org.apache.juneau.urlencoding;
 
 import org.apache.juneau.*;
-import org.apache.juneau.internal.*;
 import org.apache.juneau.urlencoding.annotation.*;
 
 /**
@@ -31,7 +30,7 @@ public class UrlEncodingClassMeta extends ClassMetaExtended {
 	 */
 	public UrlEncodingClassMeta(ClassMeta<?> cm) {
 		super(cm);
-		this.urlEncoding = ClassUtils.getAnnotation(UrlEncoding.class, getInnerClass());
+		this.urlEncoding = cm.getClassInfo().getAnnotation(UrlEncoding.class);
 		if (urlEncoding != null) {
 			expandedParams = urlEncoding.expandedParams();
 		} else {
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlClassMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlClassMeta.java
index cffe7ab..1ac0e21 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlClassMeta.java
@@ -12,13 +12,11 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.xml;
 
-import static org.apache.juneau.internal.ClassUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
 
 import java.util.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.internal.*;
 import org.apache.juneau.xml.annotation.*;
 
 /**
@@ -39,9 +37,8 @@ public class XmlClassMeta extends ClassMetaExtended {
 	 */
 	public XmlClassMeta(ClassMeta<?> cm) {
 		super(cm);
-		Class<?> c = getInnerClass();
-		this.namespace = findNamespace(c);
-		this.xml = ClassUtils.getAnnotation(Xml.class, c);
+		this.namespace = findNamespace(cm);
+		this.xml = cm.getClassInfo().getAnnotation(Xml.class);
 		if (xml != null) {
 			this.format = xml.format();
 			this.childName = nullIfEmpty(xml.childName());
@@ -100,12 +97,12 @@ public class XmlClassMeta extends ClassMetaExtended {
 		return namespace;
 	}
 
-	private static Namespace findNamespace(Class<?> c) {
-		if (c == null)
+	private static Namespace findNamespace(ClassMeta<?> cm) {
+		if (cm == null)
 			return null;
-
-		List<Xml> xmls = getAnnotations(Xml.class, c);
-		List<XmlSchema> schemas = getAnnotations(XmlSchema.class, c);
+		ClassInfo ci = cm.getClassInfo();
+		List<Xml> xmls = ci.getAnnotations(Xml.class);
+		List<XmlSchema> schemas = ci.getAnnotations(XmlSchema.class);
 		return XmlUtils.findNamespace(xmls, schemas);
 	}
 }
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodArg.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodArg.java
index d71d703..46b0576 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodArg.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodArg.java
@@ -13,9 +13,10 @@
 package org.apache.juneau.rest.client.remote;
 
 import static org.apache.juneau.internal.ClassUtils.*;
-import static org.apache.juneau.httppart.HttpPartType.*;
 
-import java.lang.reflect.*;
+import org.apache.juneau.*;
+
+import static org.apache.juneau.httppart.HttpPartType.*;
 
 import org.apache.juneau.http.annotation.*;
 import org.apache.juneau.httppart.*;
@@ -107,17 +108,18 @@ public final class RemoteMethodArg {
 		return schema;
 	}
 
-	static RemoteMethodArg create(Method m, int i) {
-		if (hasAnnotation(Header.class, m, i)) {
-			return new RemoteMethodArg(i, HEADER, HttpPartSchema.create(Header.class, m, i));
-		} else if (hasAnnotation(Query.class, m, i)) {
-			return new RemoteMethodArg(i, QUERY, HttpPartSchema.create(Query.class, m, i));
-		} else if (hasAnnotation(FormData.class, m, i)) {
-			return new RemoteMethodArg(i, FORMDATA, HttpPartSchema.create(FormData.class, m, i));
-		} else if (hasAnnotation(Path.class, m, i)) {
-			return new RemoteMethodArg(i, PATH, HttpPartSchema.create(Path.class, m, i));
-		} else if (hasAnnotation(Body.class, m, i)) {
-			return new RemoteMethodArg(i, BODY, HttpPartSchema.create(Body.class, m, i));
+	static RemoteMethodArg create(MethodParamInfo mpi) {
+		int i = mpi.getIndex();
+		if (mpi.hasAnnotation(Header.class)) {
+			return new RemoteMethodArg(i, HEADER, HttpPartSchema.create(Header.class, mpi));
+		} else if (mpi.hasAnnotation(Query.class)) {
+			return new RemoteMethodArg(i, QUERY, HttpPartSchema.create(Query.class, mpi));
+		} else if (mpi.hasAnnotation(FormData.class)) {
+			return new RemoteMethodArg(i, FORMDATA, HttpPartSchema.create(FormData.class, mpi));
+		} else if (mpi.hasAnnotation(Path.class)) {
+			return new RemoteMethodArg(i, PATH, HttpPartSchema.create(Path.class, mpi));
+		} else if (mpi.hasAnnotation(Body.class)) {
+			return new RemoteMethodArg(i, BODY, HttpPartSchema.create(Body.class, mpi));
 		}
 		return null;
 	}
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodMeta.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodMeta.java
index 97f088e..87303f6 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodMeta.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodMeta.java
@@ -13,6 +13,7 @@
 package org.apache.juneau.rest.client.remote;
 
 import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.internal.ClassUtils.*;
 import static org.apache.juneau.httppart.HttpPartType.*;
 
 import java.lang.reflect.*;
@@ -86,7 +87,9 @@ public class RemoteMethodMeta {
 
 		Builder(String parentPath, Method m, boolean useMethodSignatures, String defaultMethod) {
 
-			RemoteMethod rm = m.getAnnotation(RemoteMethod.class);
+			MethodInfo mi = getMethodInfo(m);
+
+			RemoteMethod rm = mi.getAnnotation(RemoteMethod.class);
 
 			httpMethod = rm == null ? "" : rm.method();
 			path = rm == null ? "" : rm.path();
@@ -109,8 +112,8 @@ public class RemoteMethodMeta {
 
 			fullPath = path.indexOf("://") != -1 ? path : (parentPath.isEmpty() ? urlEncodePath(path) : (trimSlashes(parentPath) + '/' + urlEncodePath(path)));
 
-			for (int i = 0; i < m.getParameterTypes().length; i++) {
-				RemoteMethodArg rma = RemoteMethodArg.create(m, i);
+			for (MethodParamInfo mpi : mi.getParams()) {
+				RemoteMethodArg rma = RemoteMethodArg.create(mpi);
 				boolean annotated = false;
 				if (rma != null) {
 					annotated = true;
@@ -128,13 +131,13 @@ public class RemoteMethodMeta {
 					else
 						annotated = false;
 				}
-				RequestBeanMeta rmba = RequestBeanMeta.create(m, i, PropertyStore.DEFAULT);
+				RequestBeanMeta rmba = RequestBeanMeta.create(mpi, PropertyStore.DEFAULT);
 				if (rmba != null) {
 					annotated = true;
-					requestArgs.add(new RemoteMethodBeanArg(i, null, rmba));
+					requestArgs.add(new RemoteMethodBeanArg(mpi.getIndex(), null, rmba));
 				}
 				if (! annotated) {
-					otherArgs.add(new RemoteMethodArg(i, BODY, null));
+					otherArgs.add(new RemoteMethodArg(mpi.getIndex(), BODY, null));
 				}
 			}
 		}
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodReturn.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodReturn.java
index eb985a9..7b9aa0d 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodReturn.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodReturn.java
@@ -12,8 +12,6 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.rest.client.remote;
 
-import static org.apache.juneau.internal.ClassUtils.*;
-
 import java.lang.reflect.*;
 
 import org.apache.juneau.*;
@@ -38,7 +36,8 @@ public final class RemoteMethodReturn {
 		RemoteMethod rm = m.getAnnotation(RemoteMethod.class);
 		Class<?> rt = m.getReturnType();
 		RemoteReturn rv = rt == void.class ? RemoteReturn.NONE : rm == null ? RemoteReturn.BODY : rm.returns();
-		if (hasAnnotation(Response.class, rt) && rt.isInterface()) {
+		ClassInfo rti = ClassInfo.lookup(rt);
+		if (rti.hasAnnotation(Response.class) && rt.isInterface()) {
 			this.meta = ResponseBeanMeta.create(m, PropertyStore.DEFAULT);
 			rv = RemoteReturn.BEAN;
 		} else {
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteResourceMeta.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteResourceMeta.java
index 9c786cd..f4afcd3 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteResourceMeta.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteResourceMeta.java
@@ -19,6 +19,8 @@ import static org.apache.juneau.internal.StringUtils.*;
 import java.lang.reflect.*;
 import java.util.*;
 
+import org.apache.juneau.*;
+
 /**
  * Contains the meta-data about a REST proxy class.
  *
@@ -44,7 +46,8 @@ public class RemoteResourceMeta {
 	public RemoteResourceMeta(Class<?> c) {
 		String path = "";
 
-		for (RemoteResource r : getAnnotationsParentFirst(RemoteResource.class, c))
+		ClassInfo ci = ClassInfo.lookup(c);
+		for (RemoteResource r : ci.getAnnotationsParentFirst(RemoteResource.class))
 			if (! r.path().isEmpty())
 				path = trimSlashes(r.path());
 
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java
index e7ec31f..ad6d000 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java
@@ -20,6 +20,7 @@ import java.lang.reflect.Method;
 import java.util.*;
 import java.util.concurrent.*;
 
+import org.apache.juneau.*;
 import org.apache.juneau.dto.swagger.*;
 import org.apache.juneau.internal.*;
 import org.apache.juneau.rest.annotation.*;
@@ -67,7 +68,8 @@ public class BasicRestInfoProvider implements RestInfoProvider {
 			description;
 
 		Builder(RestContext context) {
-			for (RestResource r : getAnnotationsParentFirst(RestResource.class, context.getResource().getClass())) {
+			ClassInfo ci = ClassInfo.lookup(context.getResource().getClass());
+			for (RestResource r : ci.getAnnotationsParentFirst(RestResource.class)) {
 				if (! r.siteName().isEmpty())
 					siteName = r.siteName();
 				if (r.title().length > 0)
@@ -164,7 +166,7 @@ public class BasicRestInfoProvider implements RestInfoProvider {
 	public String getMethodSummary(Method method, RestRequest req) throws Exception {
 		VarResolverSession vr = req.getVarResolverSession();
 
-		String s = getAnnotation(RestMethod.class, method).summary();
+		String s = getMethodInfo(method).getAnnotation(RestMethod.class).summary();
 		if (s.isEmpty()) {
 			Operation o = getSwaggerOperation(method, req);
 			if (o != null)
@@ -220,7 +222,7 @@ public class BasicRestInfoProvider implements RestInfoProvider {
 	public String getMethodDescription(Method method, RestRequest req) throws Exception {
 		VarResolverSession vr = req.getVarResolverSession();
 
-		String s = joinnl(getAnnotation(RestMethod.class, method).description());
+		String s = joinnl(getMethodInfo(method).getAnnotation(RestMethod.class).description());
 		if (s.isEmpty()) {
 			Operation o = getSwaggerOperation(method, req);
 			if (o != null)
@@ -398,7 +400,7 @@ public class BasicRestInfoProvider implements RestInfoProvider {
 		if (s != null) {
 			Map<String,OperationMap> sp = s.getPaths();
 			if (sp != null) {
-				Map<String,Operation> spp = sp.get(fixMethodPath(getAnnotation(RestMethod.class, method).path()));
+				Map<String,Operation> spp = sp.get(fixMethodPath(getMethodInfo(method).getAnnotation(RestMethod.class).path()));
 				if (spp != null)
 					return spp.get(req.getMethod());
 			}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/ClientVersionMatcher.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/ClientVersionMatcher.java
index bbad35b..9d3a7aa 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/ClientVersionMatcher.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/ClientVersionMatcher.java
@@ -41,7 +41,7 @@ public class ClientVersionMatcher extends RestMatcher {
 	 */
 	protected ClientVersionMatcher(String clientVersionHeader, java.lang.reflect.Method javaMethod) {
 		this.clientVersionHeader = isEmpty(clientVersionHeader) ? "X-Client-Version" : clientVersionHeader;
-		RestMethod m = getAnnotation(RestMethod.class, javaMethod);
+		RestMethod m = getMethodInfo(javaMethod).getAnnotation(RestMethod.class);
 		range = new VersionRange(m.clientVersion());
 	}
 
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 66703de..50c20a3 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
@@ -3228,7 +3228,7 @@ public final class RestContext extends BeanContext {
 				_destroyMethodParams = new ArrayList<>();
 
 			for (java.lang.reflect.Method method : resourceClass.getMethods()) {
-				RestMethod a = ClassUtils.getAnnotation(RestMethod.class, method);
+				RestMethod a = ClassUtils.getMethodInfo(method).getAnnotation(RestMethod.class);
 				if (a != null) {
 					methodsFound.add(method.getName() + "," + emptyIfNull(firstNonEmpty(a.name(), a.method())) + "," + fixMethodPath(a.path()));
 					try {
@@ -4572,6 +4572,7 @@ public final class RestContext extends BeanContext {
 		Type[] pt = method.getGenericParameterTypes();
 		RestMethodParam[] rp = new RestMethodParam[pt.length];
 		PropertyStore ps = getPropertyStore();
+		MethodInfo mi = getMethodInfo(method);
 
 		for (int i = 0; i < pt.length; i++) {
 
@@ -4583,29 +4584,31 @@ public final class RestContext extends BeanContext {
 					rp[i] = RestParamDefaults.STANDARD_RESOLVERS.get(c);
 			}
 
-			if (hasAnnotation(Header.class, method, i)) {
-				rp[i] = new RestParamDefaults.HeaderObject(method, i, ps);
-			} else if (hasAnnotation(Query.class, method, i)) {
-				rp[i] = new RestParamDefaults.QueryObject(method, i, ps);
-			} else if (hasAnnotation(FormData.class, method, i)) {
-				rp[i] = new RestParamDefaults.FormDataObject(method, i, ps);
-			} else if (hasAnnotation(Path.class, method, i)) {
-				rp[i] = new RestParamDefaults.PathObject(method, i, ps, pathPattern);
-			} else if (hasAnnotation(Body.class, method, i)) {
-				rp[i] = new RestParamDefaults.BodyObject(method, i, ps);
-			} else if (hasAnnotation(Request.class, method, i)) {
-				rp[i] = new RestParamDefaults.RequestObject(method, i, ps);
-			} else if (hasAnnotation(Response.class, method, i)) {
-				rp[i] = new RestParamDefaults.ResponseObject(method, i, ps);
-			} else if (hasAnnotation(ResponseHeader.class, method, i)) {
-				rp[i] = new RestParamDefaults.ResponseHeaderObject(method, i, ps);
-			} else if (hasAnnotation(ResponseStatus.class, method, i)) {
+			MethodParamInfo mpi = mi.getParam(i);
+
+			if (mpi.hasAnnotation(Header.class)) {
+				rp[i] = new RestParamDefaults.HeaderObject(mpi, ps);
+			} else if (mpi.hasAnnotation(Query.class)) {
+				rp[i] = new RestParamDefaults.QueryObject(mpi, ps);
+			} else if (mpi.hasAnnotation(FormData.class)) {
+				rp[i] = new RestParamDefaults.FormDataObject(mpi, ps);
+			} else if (mpi.hasAnnotation(Path.class)) {
+				rp[i] = new RestParamDefaults.PathObject(mpi, ps, pathPattern);
+			} else if (mpi.hasAnnotation(Body.class)) {
+				rp[i] = new RestParamDefaults.BodyObject(mpi, ps);
+			} else if (mpi.hasAnnotation(Request.class)) {
+				rp[i] = new RestParamDefaults.RequestObject(mpi, ps);
+			} else if (mpi.hasAnnotation(Response.class)) {
+				rp[i] = new RestParamDefaults.ResponseObject(mpi, ps);
+			} else if (mpi.hasAnnotation(ResponseHeader.class)) {
+				rp[i] = new RestParamDefaults.ResponseHeaderObject(mpi, ps);
+			} else if (mpi.hasAnnotation(ResponseStatus.class)) {
 				rp[i] = new RestParamDefaults.ResponseStatusObject(method, t);
-			} else if (hasAnnotation(HasFormData.class, method, i)) {
-				rp[i] = new RestParamDefaults.HasFormDataObject(method, i);
-			} else if (hasAnnotation(HasQuery.class, method, i)) {
-				rp[i] = new RestParamDefaults.HasQueryObject(method, i);
-			} else if (hasAnnotation(org.apache.juneau.rest.annotation.Method.class, method, i)) {
+			} else if (mpi.hasAnnotation(HasFormData.class)) {
+				rp[i] = new RestParamDefaults.HasFormDataObject(mpi);
+			} else if (mpi.hasAnnotation(HasQuery.class)) {
+				rp[i] = new RestParamDefaults.HasQueryObject(mpi);
+			} else if (mpi.hasAnnotation(org.apache.juneau.rest.annotation.Method.class)) {
 				rp[i] = new RestParamDefaults.MethodObject(method, t);
 			}
 
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestJavaMethod.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestJavaMethod.java
index a31ca75..95f19d6 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestJavaMethod.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestJavaMethod.java
@@ -146,7 +146,7 @@ public class RestJavaMethod implements Comparable<RestJavaMethod>  {
 
 			try {
 
-				RestMethod m = getAnnotation(RestMethod.class, method);
+				RestMethod m = getMethodInfo(method).getAnnotation(RestMethod.class);
 				if (m == null)
 					throw new RestServletException("@RestMethod annotation not found on method ''{0}''", sig);
 
@@ -394,7 +394,7 @@ public class RestJavaMethod implements Comparable<RestJavaMethod>  {
 
 				methodParams = context.findParams(method, false, pathPattern);
 
-				if (hasAnnotation(Response.class, method))
+				if (getMethodInfo(method).hasAnnotation(Response.class))
 					responseMeta = ResponseBeanMeta.create(method, serializers.getPropertyStore());
 
 				// Need this to access methods in anonymous inner classes.
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodParam.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodParam.java
index a41b674..b81f9a3 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodParam.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodParam.java
@@ -114,8 +114,7 @@ import org.apache.juneau.utils.*;
 public abstract class RestMethodParam {
 
 	final RestParamType paramType;
-	final Method method;
-	final int index;
+	final MethodParamInfo mpi;
 	final String name;
 	final Type type;
 	final Class<?> c;
@@ -124,17 +123,15 @@ public abstract class RestMethodParam {
 	 * Constructor.
 	 *
 	 * @param paramType The Swagger parameter type.
-	 * @param method The method on which the parameter resides.
-	 * @param index The method parameter index.
+	 * @param mpi The method parameter.
 	 * @param name
 	 * 	The parameter name.
 	 * 	Can be <jk>null</jk> if parameter doesn't have a name (e.g. the request body).
 	 * @param type The object type to convert the parameter to.
 	 */
-	protected RestMethodParam(RestParamType paramType, Method method, int index, String name, Type type) {
+	protected RestMethodParam(RestParamType paramType, MethodParamInfo mpi, String name, Type type) {
 		this.paramType = paramType;
-		this.method = method;
-		this.index = index;
+		this.mpi = mpi;
 		this.name = name;
 		this.type = type;
 		this.c = type instanceof Class ? (Class<?>)type : type instanceof ParameterizedType ? (Class<?>)((ParameterizedType)type).getRawType() : null;
@@ -144,25 +141,23 @@ public abstract class RestMethodParam {
 	 * Constructor.
 	 *
 	 * @param paramType The Swagger parameter type.
-	 * @param method The method on which the parameter resides.
-	 * @param index The method parameter index.
+	 * @param mpi The method parameter.
 	 * @param name
 	 * 	The parameter name.
 	 * 	Can be <jk>null</jk> if parameter doesn't have a name (e.g. the request body).
 	 */
-	protected RestMethodParam(RestParamType paramType, Method method, int index, String name) {
-		this(paramType, method, index, name, method.getGenericParameterTypes()[index]);
+	protected RestMethodParam(RestParamType paramType, MethodParamInfo mpi, String name) {
+		this(paramType, mpi, name, mpi.getGenericParameterType());
 	}
 
 	/**
 	 * Constructor.
 	 *
 	 * @param paramType The Swagger parameter type.
-	 * @param method The method on which the parameter resides.
-	 * @param index The method parameter index.
+	 * @param mpi The method parameter.
 	 */
-	protected RestMethodParam(RestParamType paramType, Method method, int index) {
-		this(paramType, method, index, null, method.getGenericParameterTypes()[index]);
+	protected RestMethodParam(RestParamType paramType, MethodParamInfo mpi) {
+		this(paramType, mpi, null, mpi.getGenericParameterType());
 	}
 
 	/**
@@ -172,7 +167,7 @@ public abstract class RestMethodParam {
 	 * @param type The object type to convert the parameter to.
 	 */
 	protected RestMethodParam(RestParamType paramType, Type type) {
-		this(paramType, null, -1, null, type);
+		this(paramType, null, null, type);
 	}
 
 	/**
@@ -185,7 +180,7 @@ public abstract class RestMethodParam {
 	 * @param type The object type to convert the parameter to.
 	 */
 	protected RestMethodParam(RestParamType paramType, String name, Type type) {
-		this(paramType, null, -1, name, type);
+		this(paramType, null, name, type);
 	}
 
 	/**
@@ -219,6 +214,15 @@ public abstract class RestMethodParam {
 	}
 
 	/**
+	 * Returns the parameter info.
+	 *
+	 * @return The parameter info.
+	 */
+	public MethodParamInfo getMethodParamInfo() {
+		return mpi;
+	}
+
+	/**
 	 * Returns the parameter name for this parameter as shown in the Swagger doc.
 	 *
 	 * @return the parameter name for this parameter.
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
index 7e632aa..8dbc40c 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
@@ -195,14 +195,14 @@ class RestParamDefaults {
 		private final HttpPartParser partParser;
 		private final HttpPartSchema schema;
 
-		protected PathObject(Method m, int i, PropertyStore ps, UrlPathPattern pathPattern) {
-			super(PATH, m, i, getName(m, i, pathPattern));
-			this.schema = HttpPartSchema.create(Path.class, m, i);
+		protected PathObject(MethodParamInfo mpi, PropertyStore ps, UrlPathPattern pathPattern) {
+			super(PATH, mpi, getName(mpi, pathPattern));
+			this.schema = HttpPartSchema.create(Path.class, mpi);
 			this.partParser = createPartParser(schema.getParser(), ps);
 		}
 
-		private static String getName(Method m, int i, UrlPathPattern pathPattern) {
-			for (Path h : getAnnotations(Path.class, m, i)) {
+		private static String getName(MethodParamInfo mpi, UrlPathPattern pathPattern) {
+			for (Path h : mpi.getAnnotations(Path.class)) {
 				if (! h.name().isEmpty())
 					return h.name();
 				if (! h.value().isEmpty())
@@ -210,9 +210,11 @@ class RestParamDefaults {
 			}
 			if (pathPattern != null) {
 				int idx = 0;
+				int i = mpi.getIndex();
+				Method m = mpi.getMethod();
 
 				for (int j = 0; j < i; j++)
-					if (getAnnotation(Path.class, m, i) != null)
+					if (getMethodInfo(m).getParam(i).getAnnotation(Path.class) != null)
 						idx++;
 
 				String[] vars = pathPattern.getVars();
@@ -227,7 +229,7 @@ class RestParamDefaults {
 
 				return pathPattern.getVars()[idx];
 			}
-			throw new InternalServerError("@Path used without name or value on method ''{0}'' parameter ''{1}''.", m, i);
+			throw new InternalServerError("@Path used without name or value on method parameter ''{0}''.", mpi);
 		}
 
 		@Override /* RestMethodParam */
@@ -239,9 +241,9 @@ class RestParamDefaults {
 	static final class BodyObject extends RestMethodParam {
 		private final HttpPartSchema schema;
 
-		protected BodyObject(Method m, int i, PropertyStore ps) {
-			super(BODY, m, i);
-			this.schema = HttpPartSchema.create(Body.class, m, i);
+		protected BodyObject(MethodParamInfo mpi, PropertyStore ps) {
+			super(BODY, mpi);
+			this.schema = HttpPartSchema.create(Body.class, mpi);
 		}
 
 		@Override /* RestMethodParam */
@@ -254,20 +256,20 @@ class RestParamDefaults {
 		private final HttpPartParser partParser;
 		private final HttpPartSchema schema;
 
-		protected HeaderObject(Method m, int i, PropertyStore ps) {
-			super(HEADER, m, i, getName(m, i));
-			this.schema = HttpPartSchema.create(Header.class, m, i);
+		protected HeaderObject(MethodParamInfo mpi, PropertyStore ps) {
+			super(HEADER, mpi, getName(mpi));
+			this.schema = HttpPartSchema.create(Header.class, mpi);
 			this.partParser = createPartParser(schema.getParser(), ps);
 		}
 
-		private static String getName(Method m, int i) {
-			for (Header h : getAnnotations(Header.class, m, i)) {
+		private static String getName(MethodParamInfo mpi) {
+			for (Header h : mpi.getAnnotations(Header.class)) {
 				if (! h.name().isEmpty())
 					return h.name();
 				if (! h.value().isEmpty())
 					return h.value();
 			}
-			throw new InternalServerError("@Header used without name or value on method ''{0}'' parameter ''{1}''.", m, i);
+			throw new InternalServerError("@Header used without name or value on method parameter ''{0}''.", mpi);
 		}
 
 		@Override /* RestMethodParam */
@@ -279,9 +281,9 @@ class RestParamDefaults {
 	static final class RequestObject extends RestMethodParam {
 		private final RequestBeanMeta meta;
 
-		protected RequestObject(Method m, int i, PropertyStore ps) {
-			super(RESPONSE_BODY, m, i);
-			this.meta = RequestBeanMeta.create(method, i, ps);
+		protected RequestObject(MethodParamInfo mpi, PropertyStore ps) {
+			super(RESPONSE_BODY, mpi);
+			this.meta = RequestBeanMeta.create(mpi, ps);
 		}
 
 		@Override /* RestMethodParam */
@@ -293,23 +295,23 @@ class RestParamDefaults {
 	static final class ResponseHeaderObject extends RestMethodParam {
 		final ResponsePartMeta meta;
 
-		protected ResponseHeaderObject(Method m, int i, PropertyStore ps) {
-			super(RESPONSE_HEADER, m, i, getName(m, i));
-			HttpPartSchema schema = HttpPartSchema.create(ResponseHeader.class, method, i);
+		protected ResponseHeaderObject(MethodParamInfo mpi, PropertyStore ps) {
+			super(RESPONSE_HEADER, mpi, getName(mpi));
+			HttpPartSchema schema = HttpPartSchema.create(ResponseHeader.class, mpi);
 			this.meta = new ResponsePartMeta(HttpPartType.HEADER, schema, createPartSerializer(schema.getSerializer(), ps));
 
 			if (getTypeClass() != Value.class)
 				throw new InternalServerError("Invalid type {0} specified with @ResponseHeader annotation.  It must be Value.", type);
 		}
 
-		private static String getName(Method m, int i) {
-			for (ResponseHeader h : getAnnotations(ResponseHeader.class, m, i)) {
+		private static String getName(MethodParamInfo mpi) {
+			for (ResponseHeader h : mpi.getAnnotations(ResponseHeader.class)) {
 				if (! h.name().isEmpty())
 					return h.name();
 				if (! h.value().isEmpty())
 					return h.value();
 			}
-			throw new InternalServerError("@ResponseHeader used without name or value on method ''{0}'' parameter ''{1}''.", m, i);
+			throw new InternalServerError("@ResponseHeader used without name or value on method parameter ''{0}''.", mpi);
 		}
 
 		@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -336,9 +338,9 @@ class RestParamDefaults {
 	static final class ResponseObject extends RestMethodParam {
 		final ResponseBeanMeta meta;
 
-		protected ResponseObject(Method m, int i, PropertyStore ps) {
-			super(RESPONSE, m, i);
-			this.meta = ResponseBeanMeta.create(m, i, ps);
+		protected ResponseObject(MethodParamInfo mpi, PropertyStore ps) {
+			super(RESPONSE, mpi);
+			this.meta = ResponseBeanMeta.create(mpi, ps);
 			if (getTypeClass() != Value.class)
 				throw new InternalServerError("Invalid type {0} specified with @Response annotation.  It must be Value.", type);
 		}
@@ -386,7 +388,7 @@ class RestParamDefaults {
 	static final class MethodObject extends RestMethodParam {
 
 		protected MethodObject(Method m, Type t) throws ServletException {
-			super(OTHER, null, null);
+			super(OTHER, (MethodParamInfo)null);
 			if (t != String.class)
 				throw new RestServletException("Use of @Method annotation on parameter that is not a String on method ''{0}''", m);
 		}
@@ -402,24 +404,24 @@ class RestParamDefaults {
 		private final HttpPartParser partParser;
 		private final HttpPartSchema schema;
 
-		protected FormDataObject(Method m, int i, PropertyStore ps) {
-			super(FORM_DATA, m, i, getName(m, i));
-			this.schema = HttpPartSchema.create(FormData.class, m, i);
+		protected FormDataObject(MethodParamInfo mpi, PropertyStore ps) {
+			super(FORM_DATA, mpi, getName(mpi));
+			this.schema = HttpPartSchema.create(FormData.class, mpi);
 			this.partParser = createPartParser(schema.getParser(), ps);
 			this.multiPart = schema.getCollectionFormat() == HttpPartSchema.CollectionFormat.MULTI;
 
 			if (multiPart && ! isCollection(type))
-				throw new InternalServerError("Use of multipart flag on @FormData parameter that's not an array or Collection on method ''{0}''", method);
+				throw new InternalServerError("Use of multipart flag on @FormData parameter that's not an array or Collection on method ''{0}''", mpi.getMethod());
 		}
 
-		private static String getName(Method m, int i) {
-			for (FormData h : getAnnotations(FormData.class, m, i)) {
+		private static String getName(MethodParamInfo mpi) {
+			for (FormData h : mpi.getAnnotations(FormData.class)) {
 				if (! h.name().isEmpty())
 					return h.name();
 				if (! h.value().isEmpty())
 					return h.value();
 			}
-			throw new InternalServerError("@FormData used without name or value on method ''{0}'' parameter ''{1}''.", m, i);
+			throw new InternalServerError("@FormData used without name or value on method parameter ''{0}''.", mpi);
 		}
 
 		@Override /* RestMethodParam */
@@ -435,24 +437,24 @@ class RestParamDefaults {
 		private final HttpPartParser partParser;
 		private final HttpPartSchema schema;
 
-		protected QueryObject(Method m, int i, PropertyStore ps) {
-			super(QUERY, m, i, getName(m, i));
-			this.schema = HttpPartSchema.create(Query.class, m, i);
+		protected QueryObject(MethodParamInfo mpi, PropertyStore ps) {
+			super(QUERY, mpi, getName(mpi));
+			this.schema = HttpPartSchema.create(Query.class, mpi);
 			this.partParser = createPartParser(schema.getParser(), ps);
 			this.multiPart = schema.getCollectionFormat() == HttpPartSchema.CollectionFormat.MULTI;
 
 			if (multiPart && ! isCollection(type))
-				throw new InternalServerError("Use of multipart flag on @Query parameter that's not an array or Collection on method ''{0}''", method);
+				throw new InternalServerError("Use of multipart flag on @Query parameter that's not an array or Collection on method ''{0}''", mpi.getMethod());
 		}
 
-		private static String getName(Method m, int i) {
-			for (Query h : getAnnotations(Query.class, m, i)) {
+		private static String getName(MethodParamInfo mpi) {
+			for (Query h : mpi.getAnnotations(Query.class)) {
 				if (! h.name().isEmpty())
 					return h.name();
 				if (! h.value().isEmpty())
 					return h.value();
 			}
-			throw new InternalServerError("@Query used without name or value on method ''{0}'' parameter ''{1}''.", m, i);
+			throw new InternalServerError("@Query used without name or value on method param ''{0}''.", mpi);
 		}
 
 		@Override /* RestMethodParam */
@@ -465,20 +467,20 @@ class RestParamDefaults {
 
 	static final class HasFormDataObject extends RestMethodParam {
 
-		protected HasFormDataObject(Method m, int i) throws ServletException {
-			super(FORM_DATA, m, i, getName(m, i));
+		protected HasFormDataObject(MethodParamInfo mpi) throws ServletException {
+			super(FORM_DATA, mpi, getName(mpi));
 			if (getType() != Boolean.class && getType() != boolean.class)
-				throw new RestServletException("Use of @HasForm annotation on parameter that is not a boolean on method ''{0}''", m);
+				throw new RestServletException("Use of @HasForm annotation on parameter that is not a boolean on method ''{0}''", mpi.getMethod());
 		}
 
-		private static String getName(Method m, int i) {
-			for (HasFormData h : getAnnotations(HasFormData.class, m, i)) {
+		private static String getName(MethodParamInfo mpi) {
+			for (HasFormData h : mpi.getAnnotations(HasFormData.class)) {
 				if (! h.name().isEmpty())
 					return h.name();
 				if (! h.value().isEmpty())
 					return h.value();
 			}
-			throw new InternalServerError("@HasFormData used without name or value on method ''{0}'' parameter ''{1}''.", m, i);
+			throw new InternalServerError("@HasFormData used without name or value on method parameter ''{o}''.", mpi);
 		}
 
 		@Override /* RestMethodParam */
@@ -490,20 +492,20 @@ class RestParamDefaults {
 
 	static final class HasQueryObject extends RestMethodParam {
 
-		protected HasQueryObject(Method m, int i) throws ServletException {
-			super(QUERY, m, i, getName(m, i));
+		protected HasQueryObject(MethodParamInfo mpi) throws ServletException {
+			super(QUERY, mpi, getName(mpi));
 			if (getType() != Boolean.class && getType() != boolean.class)
-				throw new RestServletException("Use of @HasQuery annotation on parameter that is not a boolean on method ''{0}''", m);
+				throw new RestServletException("Use of @HasQuery annotation on parameter that is not a boolean on method ''{0}''", mpi.getMethod());
 		}
 
-		private static String getName(Method m, int i) {
-			for (HasQuery h : getAnnotations(HasQuery.class, m, i)) {
+		private static String getName(MethodParamInfo mpi) {
+			for (HasQuery h : mpi.getAnnotations(HasQuery.class)) {
 				if (! h.name().isEmpty())
 					return h.name();
 				if (! h.value().isEmpty())
 					return h.value();
 			}
-			throw new InternalServerError("@HasQuery used without name or value on method ''{0}'' parameter ''{1}''.", m, i);
+			throw new InternalServerError("@HasQuery used without name or value on method parameter ''{0}''.", mpi);
 		}
 
 		@Override /* RestMethodParam */
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
index d49236e..a01e6de 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
@@ -23,7 +23,7 @@ import java.util.logging.*;
 import javax.servlet.*;
 import javax.servlet.http.*;
 
-import org.apache.juneau.internal.*;
+import org.apache.juneau.*;
 import org.apache.juneau.rest.annotation.*;
 import org.apache.juneau.rest.exception.*;
 
@@ -121,7 +121,8 @@ public abstract class RestServlet extends HttpServlet {
 	public synchronized String getPath() {
 		if (context != null)
 			return context.getPath();
-		for (RestResource rr : ClassUtils.getAnnotations(RestResource.class, this.getClass()))
+		ClassInfo ci = ClassInfo.lookup(getClass());
+		for (RestResource rr : ci.getAnnotations(RestResource.class))
 			if (! rr.path().isEmpty())
 				return trimSlashes(rr.path());
 		return "";
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerGenerator.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerGenerator.java
index 9786207..bbf7fb4 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerGenerator.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerGenerator.java
@@ -226,7 +226,8 @@ final class SwaggerGenerator {
 				continue;
 
 			Method m = sm.method;
-			RestMethod rm = getAnnotation(RestMethod.class, m);
+			MethodInfo mi = getMethodInfo(m);
+			RestMethod rm = mi.getAnnotation(RestMethod.class);
 			String mn = m.getName();
 
 			// Get the operation from the existing swagger so far.
@@ -323,7 +324,7 @@ final class SwaggerGenerator {
 			for (RestMethodParam mp : context.getRestMethodParams(m)) {
 
 				RestParamType in = mp.getParamType();
-				int index = mp.index;
+				MethodParamInfo mpi = mp.getMethodParamInfo();
 
 				if (in.isAny(BODY, QUERY, FORM_DATA, HEADER, PATH)) {
 
@@ -337,26 +338,26 @@ final class SwaggerGenerator {
 						param.append("name", mp.name);
 
 					try {
-						if (mp.method != null) {
+						if (mpi != null) {
 							if (in == BODY) {
-								for (Body a : getAnnotationsParentFirst(Body.class, mp.method, mp.index))
+								for (Body a : mpi.getAnnotationsParentFirst(Body.class))
 									merge(param, a);
 							} else if (in == QUERY) {
-								for (Query a : getAnnotationsParentFirst(Query.class, mp.method, mp.index))
+								for (Query a : mpi.getAnnotationsParentFirst(Query.class))
 									merge(param, a);
 							} else if (in == FORM_DATA) {
-								for (FormData a : getAnnotationsParentFirst(FormData.class, mp.method, mp.index))
+								for (FormData a : mpi.getAnnotationsParentFirst(FormData.class))
 									merge(param, a);
 							} else if (in == HEADER) {
-								for (Header a : getAnnotationsParentFirst(Header.class, mp.method, mp.index))
+								for (Header a : mpi.getAnnotationsParentFirst(Header.class))
 									merge(param, a);
 							} else if (in == PATH) {
-								for (Path a : getAnnotationsParentFirst(Path.class, mp.method, mp.index))
+								for (Path a : mpi.getAnnotationsParentFirst(Path.class))
 									merge(param, a);
 							}
 						}
 					} catch (ParseException e) {
-						throw new SwaggerException(e, "Malformed swagger JSON object encountered in {0} class {1} method {2} parameter {3}", in, c, m, index);
+						throw new SwaggerException(e, "Malformed swagger JSON object encountered in {0} class {1} method parameter {2}", in, c, mpi);
 					}
 
 
@@ -378,21 +379,22 @@ final class SwaggerGenerator {
 
 			ObjectMap responses = op.getObjectMap("responses", true);
 
-			for (Class<?> ec : m.getExceptionTypes()) {
-				if (hasAnnotation(Response.class, ec)) {
-					List<Response> la = getAnnotationsParentFirst(Response.class, ec);
+			for (ClassInfo eci : mi.getExceptionInfos()) {
+				if (eci.hasAnnotation(Response.class)) {
+					List<Response> la = eci.getAnnotationsParentFirst(Response.class);
 					Set<Integer> codes = getCodes(la, 500);
 					for (Response a : la) {
 						for (Integer code : codes) {
 							ObjectMap om = responses.getObjectMap(String.valueOf(code), true);
 							merge(om, a);
 							if (! om.containsKey("schema"))
-								om.appendSkipEmpty("schema", getSchema(om.getObjectMap("schema"), ec));
+								om.appendSkipEmpty("schema", getSchema(om.getObjectMap("schema"), eci.getInnerClass()));
 						}
 					}
-					for (Method ecm : getAllMethods(ec, true)) {
-						if (hasAnnotation(ResponseHeader.class, ecm)) {
-							ResponseHeader a = ecm.getAnnotation(ResponseHeader.class);
+					for (Method ecm : getAllMethods(eci.getInnerClass(), true)) {
+						MethodInfo ecmi = getMethodInfo(ecm);
+						if (ecmi.hasAnnotation(ResponseHeader.class)) {
+							ResponseHeader a = ecmi.getAnnotation(ResponseHeader.class);
 							String ha = a.name();
 							for (Integer code : codes) {
 								ObjectMap header = responses.getObjectMap(String.valueOf(code), true).getObjectMap("headers", true).getObjectMap(ha, true);
@@ -404,8 +406,8 @@ final class SwaggerGenerator {
 				}
 			}
 
-			if (hasAnnotation(Response.class, m)) {
-				List<Response> la = getAnnotationsParentFirst(Response.class, m);
+			if (mi.hasAnnotation(Response.class)) {
+				List<Response> la = mi.getAnnotationsParentFirst(Response.class);
 				Set<Integer> codes = getCodes(la, 200);
 				for (Response a : la) {
 					for (Integer code : codes) {
@@ -416,10 +418,11 @@ final class SwaggerGenerator {
 						addBodyExamples(sm, om, true, m.getGenericReturnType());
 					}
 				}
-				if (hasAnnotation(Response.class, m.getReturnType())) {
+				if (mi.getReturnTypeInfo().hasAnnotation(Response.class)) {
 					for (Method ecm : getAllMethods(m.getReturnType(), true)) {
-						if (hasAnnotation(ResponseHeader.class, ecm)) {
-							ResponseHeader a = ecm.getAnnotation(ResponseHeader.class);
+						MethodInfo ecmi = getMethodInfo(ecm);
+						if (ecmi.hasAnnotation(ResponseHeader.class)) {
+							ResponseHeader a = ecmi.getAnnotation(ResponseHeader.class);
 							String ha = a.name();
 							for (Integer code : codes) {
 								ObjectMap header = responses.getObjectMap(String.valueOf(code), true).getObjectMap("headers", true).getObjectMap(ha, true);
@@ -440,9 +443,10 @@ final class SwaggerGenerator {
 			for (RestMethodParam mp : context.getRestMethodParams(m)) {
 
 				RestParamType in = mp.getParamType();
+				MethodParamInfo mpi = mp.getMethodParamInfo();
 
 				if (in == RESPONSE_HEADER) {
-					List<ResponseHeader> la = getAnnotationsParentFirst(ResponseHeader.class, mp.method, mp.index);
+					List<ResponseHeader> la = mpi.getAnnotationsParentFirst(ResponseHeader.class);
 					Set<Integer> codes = getCodes2(la, 200);
 					for (ResponseHeader a : la) {
 						for (Integer code : codes) {
@@ -453,7 +457,7 @@ final class SwaggerGenerator {
 					}
 
 				} else if (in == RESPONSE) {
-					List<Response> la = getAnnotationsParentFirst(Response.class, mp.method, mp.index);
+					List<Response> la = mpi.getAnnotationsParentFirst(Response.class);
 					Set<Integer> codes = getCodes(la, 200);
 					for (Response a : la) {
 						for (Integer code : codes) {