diff --git a/spring-core/src/main/java/org/springframework/core/MethodParameter.java b/spring-core/src/main/java/org/springframework/core/MethodParameter.java index 9c33cb2578..2ca92c33b6 100644 --- a/spring-core/src/main/java/org/springframework/core/MethodParameter.java +++ b/spring-core/src/main/java/org/springframework/core/MethodParameter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,8 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.springframework.util.Assert; @@ -37,11 +39,21 @@ import org.springframework.util.Assert; * @author Juergen Hoeller * @author Rob Harrop * @author Andy Clement + * @author Nikita Tovstoles + * @author Chris Beams * @since 2.0 * @see GenericCollectionTypeResolver */ public class MethodParameter { + + private static final Annotation[][] EMPTY_ANNOTATION_MATRIX = new Annotation[0][0]; + + private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0]; + + static final ConcurrentMap methodParamAnnotationsCache = + new ConcurrentHashMap(); + private final Method method; private final Constructor constructor; @@ -280,7 +292,7 @@ public class MethodParameter { public Annotation[] getParameterAnnotations() { if (this.parameterAnnotations == null) { Annotation[][] annotationArray = (this.method != null ? - this.method.getParameterAnnotations() : this.constructor.getParameterAnnotations()); + getMethodParameterAnnotations(this.method) : this.constructor.getParameterAnnotations()); if (this.parameterIndex >= 0 && this.parameterIndex < annotationArray.length) { this.parameterAnnotations = annotationArray[this.parameterIndex]; } @@ -439,6 +451,41 @@ public class MethodParameter { } } + /** + * Return the parameter annotations for the given method, retrieving cached values + * if a lookup has already been performed for this method, otherwise perform a fresh + * lookup and populate the cache with the result before returning. For + * internal use only. + * @param method the method to introspect for parameter annotations + */ + static Annotation[][] getMethodParameterAnnotations(Method method) { + Assert.notNull(method); + + Annotation[][] result = methodParamAnnotationsCache.get(method); + if (result == null) { + result = method.getParameterAnnotations(); + + if(result.length == 0) { + result = EMPTY_ANNOTATION_MATRIX; + } + else { + for (int i = 0; i < result.length; i++) { + if (result[i].length == 0) { + result[i] = EMPTY_ANNOTATION_ARRAY; + } + } + } + methodParamAnnotationsCache.put(method, result); + } + + //always return deep copy to prevent caller from modifying cache state + Annotation[][] resultCopy = new Annotation[result.length][0]; + for(int i = 0; i < result.length; i++) { + resultCopy[i] = result[i].clone(); + } + return resultCopy; + } + @Override public boolean equals(Object obj) { if (this == obj) { @@ -460,7 +507,6 @@ public class MethodParameter { return false; } - @Override public int hashCode() { int result = this.hash; diff --git a/spring-core/src/test/java/org/springframework/core/MethodParameterTests.java b/spring-core/src/test/java/org/springframework/core/MethodParameterTests.java index 2f1ce99bfd..fd4f1818ae 100644 --- a/spring-core/src/test/java/org/springframework/core/MethodParameterTests.java +++ b/spring-core/src/test/java/org/springframework/core/MethodParameterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,11 @@ package org.springframework.core; +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.reflect.Method; import org.junit.Before; @@ -25,6 +30,7 @@ import static org.junit.Assert.*; /** * @author Arjen Poutsma + * @author Nikita Tovstoles */ public class MethodParameterTests { @@ -43,7 +49,6 @@ public class MethodParameterTests { } - @Test public void testEquals() throws NoSuchMethodException { assertEquals(stringParameter, stringParameter); @@ -77,9 +82,30 @@ public class MethodParameterTests { assertTrue(longParameter.hashCode() != methodParameter.hashCode()); } + @Test + public void testGetMethodParamaterAnnotations() { + Method method = stringParameter.getMethod(); + Annotation[][] expectedAnnotations = method.getParameterAnnotations(); + assertEquals(2, expectedAnnotations.length); + assertEquals(DummyAnnotation.class, expectedAnnotations[0][0].annotationType()); + + //start with empty cache + MethodParameter.methodParamAnnotationsCache.clear(); + + //check correctness + assertArrayEquals(expectedAnnotations, MethodParameter.getMethodParameterAnnotations(method)); + //check that return value's been cached + assertArrayEquals(expectedAnnotations, MethodParameter.methodParamAnnotationsCache.get(method)); + } + - public int method(String p1, long p2) { + public int method(@DummyAnnotation String p1, long p2) { return 42; } + @Target(ElementType.PARAMETER) + @Retention(RetentionPolicy.RUNTIME) + public @interface DummyAnnotation { + + } }