Cache MethodParameter annotation lookup results

Prior to this change, Spring's MethodParameter#getParameterAnnotations
called java.lang.Method#getParameterAnnotations on every invocation.
The latter ends up contending for a monitor inside (Sun) JDK code. This
is problematic when dealing with the high number of @RequestMapping
invocations that can occur in a Spring MVC @Controller.

This commit eliminates this contention by caching values returned by
java.lang.Method#getParameterAnnotations in a static ConcurrentMap.

Note that only Method parameter annotations are cached, while
Constructor parameter annotations are not. This is because the
issue of primary concern is, as mentioned above, @RequestMapping
methods. By nature, constructors are called much more infrequently, and
in most cases in a single-threaded fashion.

Issue: SPR-9298
This commit is contained in:
Nikita Tovstoles
2012-04-03 15:33:41 -07:00
committed by Chris Beams
parent 39f74b2374
commit c10d63dc01
2 changed files with 78 additions and 6 deletions

View File

@@ -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());
public int method(String p1, long p2) {
//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(@DummyAnnotation String p1, long p2) {
return 42;
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface DummyAnnotation {
}
}