Skip to content

Commit b830d73

Browse files
committed
Support non-public anno. attr. values in AnnoUtils
Prior to this commit, the getValue(Annotation, String) method in AnnotationUtils failed to retrieve the value of the desired annotation attribute if the annotation itself was not public -- for example if the annotation was declared as package private. This commit addresses this issue by ensuring that getValue(Annotation, String) uses reflection to make the desired annotation attribute method accessible before attempting to invoke it to retrieve the value. Issue: SPR-11104
1 parent 45afd4f commit b830d73

File tree

4 files changed

+101
-15
lines changed

4 files changed

+101
-15
lines changed

spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.core.BridgeMethodResolver;
3232
import org.springframework.util.Assert;
3333
import org.springframework.util.ObjectUtils;
34+
import org.springframework.util.ReflectionUtils;
3435

3536
/**
3637
* General utility methods for working with annotations, handling bridge methods (which the compiler
@@ -525,6 +526,7 @@ public static Object getValue(Annotation annotation) {
525526
public static Object getValue(Annotation annotation, String attributeName) {
526527
try {
527528
Method method = annotation.annotationType().getDeclaredMethod(attributeName, new Class[0]);
529+
ReflectionUtils.makeAccessible(method);
528530
return method.invoke(annotation);
529531
}
530532
catch (Exception ex) {
@@ -585,7 +587,6 @@ public static Object getDefaultValue(Class<? extends Annotation> annotationType,
585587

586588
private static class AnnotationCollector<A extends Annotation> {
587589

588-
589590
private final Class<? extends Annotation> containerAnnotationType;
590591

591592
private final Class<A> annotationType;
@@ -628,6 +629,7 @@ else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, annotation.ann
628629
private A[] getValue(Annotation annotation) {
629630
try {
630631
Method method = annotation.annotationType().getDeclaredMethod("value");
632+
ReflectionUtils.makeAccessible(method);
631633
return (A[]) method.invoke(annotation);
632634
}
633635
catch (Exception ex) {

spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@
2929

3030
import org.junit.Test;
3131
import org.springframework.core.Ordered;
32+
import org.springframework.core.annotation.subpackage.NonPublicAnnotatedClass;
3233
import org.springframework.stereotype.Component;
3334

3435
import static org.hamcrest.Matchers.*;
35-
3636
import static org.junit.Assert.*;
3737
import static org.springframework.core.annotation.AnnotationUtils.*;
3838

@@ -46,37 +46,37 @@
4646
public class AnnotationUtilsTests {
4747

4848
@Test
49-
public void testFindMethodAnnotationOnLeaf() throws SecurityException, NoSuchMethodException {
49+
public void findMethodAnnotationOnLeaf() throws Exception {
5050
Method m = Leaf.class.getMethod("annotatedOnLeaf", (Class[]) null);
5151
assertNotNull(m.getAnnotation(Order.class));
5252
assertNotNull(getAnnotation(m, Order.class));
5353
assertNotNull(findAnnotation(m, Order.class));
5454
}
5555

5656
@Test
57-
public void testFindMethodAnnotationOnRoot() throws SecurityException, NoSuchMethodException {
57+
public void findMethodAnnotationOnRoot() throws Exception {
5858
Method m = Leaf.class.getMethod("annotatedOnRoot", (Class[]) null);
5959
assertNotNull(m.getAnnotation(Order.class));
6060
assertNotNull(getAnnotation(m, Order.class));
6161
assertNotNull(findAnnotation(m, Order.class));
6262
}
6363

6464
@Test
65-
public void testFindMethodAnnotationOnRootButOverridden() throws SecurityException, NoSuchMethodException {
65+
public void findMethodAnnotationOnRootButOverridden() throws Exception {
6666
Method m = Leaf.class.getMethod("overrideWithoutNewAnnotation", (Class[]) null);
6767
assertNull(m.getAnnotation(Order.class));
6868
assertNull(getAnnotation(m, Order.class));
6969
assertNotNull(findAnnotation(m, Order.class));
7070
}
7171

7272
@Test
73-
public void testFindMethodAnnotationNotAnnotated() throws SecurityException, NoSuchMethodException {
73+
public void findMethodAnnotationNotAnnotated() throws Exception {
7474
Method m = Leaf.class.getMethod("notAnnotated", (Class[]) null);
7575
assertNull(findAnnotation(m, Order.class));
7676
}
7777

7878
@Test
79-
public void testFindMethodAnnotationOnBridgeMethod() throws Exception {
79+
public void findMethodAnnotationOnBridgeMethod() throws Exception {
8080
Method m = SimpleFoo.class.getMethod("something", Object.class);
8181
assertTrue(m.isBridge());
8282
assertNull(m.getAnnotation(Order.class));
@@ -88,7 +88,7 @@ public void testFindMethodAnnotationOnBridgeMethod() throws Exception {
8888
}
8989

9090
// TODO consider whether we want this to handle annotations on interfaces
91-
// public void testFindMethodAnnotationFromInterfaceImplementedByRoot()
91+
// public void findMethodAnnotationFromInterfaceImplementedByRoot()
9292
// throws Exception {
9393
// Method m = Leaf.class.getMethod("fromInterfaceImplementedByRoot",
9494
// (Class[]) null);
@@ -241,7 +241,7 @@ public void testIsAnnotationInherited() throws Exception {
241241
}
242242

243243
@Test
244-
public void testGetValueFromAnnotation() throws Exception {
244+
public void getValueFromAnnotation() throws Exception {
245245
Method method = SimpleFoo.class.getMethod("something", Object.class);
246246
Order order = findAnnotation(method, Order.class);
247247

@@ -250,7 +250,18 @@ public void testGetValueFromAnnotation() throws Exception {
250250
}
251251

252252
@Test
253-
public void testGetDefaultValueFromAnnotation() throws Exception {
253+
public void getValueFromNonPublicAnnotation() throws Exception {
254+
Annotation[] declaredAnnotations = NonPublicAnnotatedClass.class.getDeclaredAnnotations();
255+
assertEquals(1, declaredAnnotations.length);
256+
Annotation annotation = declaredAnnotations[0];
257+
assertNotNull(annotation);
258+
assertEquals("NonPublicAnnotation", annotation.annotationType().getSimpleName());
259+
assertEquals(42, AnnotationUtils.getValue(annotation, AnnotationUtils.VALUE));
260+
assertEquals(42, AnnotationUtils.getValue(annotation));
261+
}
262+
263+
@Test
264+
public void getDefaultValueFromAnnotation() throws Exception {
254265
Method method = SimpleFoo.class.getMethod("something", Object.class);
255266
Order order = findAnnotation(method, Order.class);
256267

@@ -259,34 +270,46 @@ public void testGetDefaultValueFromAnnotation() throws Exception {
259270
}
260271

261272
@Test
262-
public void testGetDefaultValueFromAnnotationType() throws Exception {
273+
public void getDefaultValueFromNonPublicAnnotation() throws Exception {
274+
Annotation[] declaredAnnotations = NonPublicAnnotatedClass.class.getDeclaredAnnotations();
275+
assertEquals(1, declaredAnnotations.length);
276+
Annotation annotation = declaredAnnotations[0];
277+
assertNotNull(annotation);
278+
assertEquals("NonPublicAnnotation", annotation.annotationType().getSimpleName());
279+
assertEquals(-1, AnnotationUtils.getDefaultValue(annotation, AnnotationUtils.VALUE));
280+
assertEquals(-1, AnnotationUtils.getDefaultValue(annotation));
281+
}
282+
283+
@Test
284+
public void getDefaultValueFromAnnotationType() throws Exception {
263285
assertEquals(Ordered.LOWEST_PRECEDENCE, AnnotationUtils.getDefaultValue(Order.class, AnnotationUtils.VALUE));
264286
assertEquals(Ordered.LOWEST_PRECEDENCE, AnnotationUtils.getDefaultValue(Order.class));
265287
}
266288

267289
@Test
268-
public void testFindAnnotationFromInterface() throws Exception {
290+
public void findAnnotationFromInterface() throws Exception {
269291
Method method = ImplementsInterfaceWithAnnotatedMethod.class.getMethod("foo");
270292
Order order = findAnnotation(method, Order.class);
271293
assertNotNull(order);
272294
}
273295

274296
@Test
275-
public void testFindAnnotationFromInterfaceOnSuper() throws Exception {
297+
public void findAnnotationFromInterfaceOnSuper() throws Exception {
276298
Method method = SubOfImplementsInterfaceWithAnnotatedMethod.class.getMethod("foo");
277299
Order order = findAnnotation(method, Order.class);
278300
assertNotNull(order);
279301
}
280302

281303
@Test
282-
public void testFindAnnotationFromInterfaceWhenSuperDoesNotImplementMethod() throws Exception {
304+
public void findAnnotationFromInterfaceWhenSuperDoesNotImplementMethod()
305+
throws Exception {
283306
Method method = SubOfAbstractImplementsInterfaceWithAnnotatedMethod.class.getMethod("foo");
284307
Order order = findAnnotation(method, Order.class);
285308
assertNotNull(order);
286309
}
287310

288311
@Test
289-
public void testGetRepeatableFromMethod() throws Exception {
312+
public void getRepeatableFromMethod() throws Exception {
290313
Method method = InterfaceWithRepeated.class.getMethod("foo");
291314
Set<MyRepeatable> annotions = AnnotationUtils.getRepeatableAnnotation(method,
292315
MyRepeatableContainer.class, MyRepeatable.class);
@@ -319,6 +342,7 @@ static interface InterfaceWithMetaAnnotation {
319342
static class ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface implements InterfaceWithMetaAnnotation {
320343
}
321344

345+
322346
public static interface AnnotatedInterface {
323347

324348
@Order(0)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core.annotation.subpackage;
18+
19+
/**
20+
* Class annotated with a non-public (i.e., package private) custom annotation.
21+
*
22+
* @author Sam Brannen
23+
* @since 4.0
24+
*/
25+
@NonPublicAnnotation(42)
26+
public class NonPublicAnnotatedClass {
27+
28+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core.annotation.subpackage;
18+
19+
import java.lang.annotation.Retention;
20+
import java.lang.annotation.RetentionPolicy;
21+
22+
/**
23+
* Non-public (i.e., package private) custom annotation.
24+
*
25+
* @author Sam Brannen
26+
* @since 4.0
27+
*/
28+
@Retention(RetentionPolicy.RUNTIME)
29+
@interface NonPublicAnnotation {
30+
31+
int value() default -1;
32+
}

0 commit comments

Comments
 (0)