Skip to content

Commit a383633

Browse files
committed
Added helper methods to access method parameter annotations.
1 parent 4a0c2f8 commit a383633

File tree

2 files changed

+253
-1
lines changed

2 files changed

+253
-1
lines changed

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

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818

1919
import java.lang.annotation.Annotation;
2020
import java.lang.reflect.Method;
21+
import java.util.ArrayList;
2122
import java.util.Arrays;
2223
import java.util.HashMap;
24+
import java.util.List;
2325
import java.util.Map;
2426

2527
import org.springframework.core.BridgeMethodResolver;
@@ -41,6 +43,7 @@
4143
* @author Juergen Hoeller
4244
* @author Sam Brannen
4345
* @author Mark Fisher
46+
* @author Oliver Gierke
4447
* @since 2.0
4548
* @see java.lang.reflect.Method#getAnnotations()
4649
* @see java.lang.reflect.Method#getAnnotation(Class)
@@ -373,4 +376,201 @@ public static Object getDefaultValue(Class<? extends Annotation> annotationType,
373376
}
374377
}
375378

379+
/**
380+
* Returns a {@link ParameterAnnotation} instance for the first parameter with the given annotation.
381+
* @param <T>
382+
* @param method the {@link Method} whose parameters shall be inspected
383+
* @param annotationType the annotation typed the parameter shall carry
384+
* @return
385+
*/
386+
public static <T extends Annotation> ParameterAnnotation<T> getParameterAnnotation(Method method,
387+
Class<T> annotationType) {
388+
389+
List<ParameterAnnotation<T>> annotations = getParameterAnnotations(method, annotationType);
390+
return annotations.isEmpty() ? null : annotations.get(0);
391+
}
392+
393+
/**
394+
* Returns all {@link ParameterAnnotation}s for parameters with the given annotation.
395+
* @param <T>
396+
* @param method the {@link Method} whose parameters shall be scanned for annotations
397+
* @param annotationType the annotation type the paramter shall carry
398+
* @return
399+
*/
400+
public static <T extends Annotation> List<ParameterAnnotation<T>> getParameterAnnotations(Method method,
401+
Class<T> annotationType) {
402+
403+
return getParameterAnnotations(method, new AnnotationTypeFilter<T>(annotationType));
404+
}
405+
406+
/**
407+
* Returns the {@link ParameterAnnotation} for a parameter of the given type that carries an annotation of the given
408+
* type.
409+
* @param <T>
410+
* @param method the {@link Method} that shall be scanned for parameters carrying the annotation
411+
* @param annotationType the annotation type that shall be discovered
412+
* @param parameterType the parameter type that shall be considered
413+
* @return
414+
*/
415+
public static <T extends Annotation> ParameterAnnotation<T> getParameterAnnotation(Method method,
416+
Class<T> annotationType, final Class<?> parameterType) {
417+
418+
List<ParameterAnnotation<T>> annotations = getParameterAnnotations(method, annotationType, parameterType);
419+
return annotations.isEmpty() ? null : annotations.get(0);
420+
}
421+
422+
/**
423+
* Returns all {@link ParameterAnnotation}s for parameters of the given type that carry an annotation of the given
424+
* type.
425+
* @param <T>
426+
* @param method the {@link Method} that shall be scanned for parameters carrying the annotation
427+
* @param annotationType the annotation type that shall be discovered
428+
* @param parameterType the parameter type that shall be considered
429+
* @return
430+
*/
431+
public static <T extends Annotation> List<ParameterAnnotation<T>> getParameterAnnotations(Method method,
432+
Class<T> annotationType, final Class<?> parameterType) {
433+
434+
return getParameterAnnotations(method, new AnnotationTypeFilter<T>(annotationType) {
435+
436+
@Override
437+
public boolean matches(ParameterAnnotation<?> parameterAnnotation) {
438+
return super.matches(parameterAnnotation)
439+
&& parameterType.equals(parameterAnnotation.getParameterType());
440+
}
441+
});
442+
}
443+
444+
/**
445+
* Returns all {@link ParameterAnnotation}s for parameters of the given method that match the given
446+
* {@link ParameterAnnotationFilter}.
447+
* @see ParameterAnnotationFilter
448+
* @param <T>
449+
* @param method the {@link Method} that shall be scanned for annotated parameters
450+
* @param filter a {@link ParameterAnnotationFilter} to apply criterias on the {@link ParameterAnnotation}s to be
451+
* returned
452+
* @return
453+
*/
454+
@SuppressWarnings("unchecked")
455+
public static <T extends Annotation> List<ParameterAnnotation<T>> getParameterAnnotations(Method method,
456+
ParameterAnnotationFilter filter) {
457+
458+
List<ParameterAnnotation<T>> result = new ArrayList<ParameterAnnotation<T>>();
459+
460+
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
461+
Class<?>[] parameterTypes = method.getParameterTypes();
462+
463+
for (int i = 0; i < parameterAnnotations.length; i++) {
464+
for (Annotation annotation : parameterAnnotations[i]) {
465+
466+
ParameterAnnotation<T> parameterAnnotation = new ParameterAnnotation<T>((T) annotation, i,
467+
parameterTypes[i]);
468+
469+
if (filter.matches(parameterAnnotation)) {
470+
result.add(parameterAnnotation);
471+
}
472+
}
473+
}
474+
475+
return result;
476+
}
477+
478+
/**
479+
* Allows filtering {@link ParameterAnnotation} instances.
480+
* @see AnnotationUtils#getParameterAnnotations(Method, ParameterAnnotationFilter)
481+
*/
482+
public interface ParameterAnnotationFilter {
483+
boolean matches(ParameterAnnotation<?> annotation);
484+
}
485+
486+
/**
487+
* {@link ParameterAnnotationFilter} that matches all {@link ParameterAnnotation}s that have the given annotation
488+
* type.
489+
*/
490+
public static class AnnotationTypeFilter<T extends Annotation> implements ParameterAnnotationFilter {
491+
492+
private final Class<T> annotationType;
493+
494+
public AnnotationTypeFilter(Class<T> annotationType) {
495+
this.annotationType = annotationType;
496+
}
497+
498+
public boolean matches(ParameterAnnotation<?> parameterAnnotation) {
499+
return annotationType.equals(parameterAnnotation.getAnnotation().annotationType());
500+
}
501+
}
502+
503+
/**
504+
* Captures an annotation for a method parameter as well as some meta information about the parameter.
505+
*/
506+
public static class ParameterAnnotation<T extends Annotation> {
507+
508+
private final T annotation;
509+
private final int parameterIndex;
510+
private final Class<?> parameterType;
511+
512+
ParameterAnnotation(T annotation, int parameterIndex, Class<?> parameterType) {
513+
Assert.notNull(annotation);
514+
Assert.notNull(parameterType);
515+
this.annotation = annotation;
516+
this.parameterIndex = parameterIndex;
517+
this.parameterType = parameterType;
518+
}
519+
520+
/**
521+
* Returns the annotation bound to the parameter.
522+
* @return the annotation
523+
*/
524+
public T getAnnotation() {
525+
return annotation;
526+
}
527+
528+
/**
529+
* Returns the method parameter index in the list of parameters.
530+
* @return the parameterIndex
531+
*/
532+
public int getParameterIndex() {
533+
return parameterIndex;
534+
}
535+
536+
/**
537+
* Returns the type of the method parameter the annotation is bound to.
538+
* @return the parameterType
539+
*/
540+
public Class<?> getParameterType() {
541+
return parameterType;
542+
}
543+
544+
/*
545+
* (non-Javadoc)
546+
*
547+
* @see java.lang.Object#equals(java.lang.Object)
548+
*/
549+
@Override
550+
public boolean equals(Object obj) {
551+
if (obj == this) {
552+
return true;
553+
}
554+
if (!(obj instanceof ParameterAnnotation<?>)) {
555+
return false;
556+
}
557+
558+
ParameterAnnotation<?> that = (ParameterAnnotation<?>) obj;
559+
return this.annotation.equals(that.annotation) && this.parameterType.equals(that.parameterType)
560+
&& this.parameterIndex == that.parameterIndex;
561+
}
562+
563+
/*
564+
* (non-Javadoc)
565+
*
566+
* @see java.lang.Object#hashCode()
567+
*/
568+
@Override
569+
public int hashCode() {
570+
int result = 17;
571+
result = 31 * result + annotation.hashCode();
572+
result = 31 * result + parameterType.hashCode();
573+
return 31 * result + (parameterIndex ^ parameterIndex >>> 32);
574+
}
575+
}
376576
}

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

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,45 @@ public void testFindAnnotationFromInterfaceOnSuper() throws Exception {
204204
assertNotNull(order);
205205
}
206206

207+
@Test
208+
public void testDetectsParameterAnnotation() throws Exception {
209+
210+
Method method = InterfaceWithAnnotatedMethodParameters.class.getMethod("foo", String.class, Long.class);
211+
ParameterAnnotation<MyAnnotation> parameterAnnotation = getParameterAnnotation(method, MyAnnotation.class);
212+
assertParameterAnnotationWith(parameterAnnotation, "foo", 1, Long.class);
213+
214+
List<ParameterAnnotation<MyAnnotation>> parameterAnnotations = getParameterAnnotations(method,
215+
MyAnnotation.class);
216+
assertNotNull(parameterAnnotations);
217+
assertEquals(1, parameterAnnotations.size());
218+
assertEquals(parameterAnnotation, parameterAnnotations.get(0));
219+
}
220+
221+
@Test
222+
public void testDetectsFirstParameterAnnotationForMultipleOnes() throws Exception {
223+
224+
Method method = InterfaceWithAnnotatedMethodParameters.class.getMethod("bar", String.class, String.class,
225+
Serializable.class);
226+
ParameterAnnotation<MyAnnotation> parameterAnnotation = getParameterAnnotation(method, MyAnnotation.class);
227+
assertParameterAnnotationWith(parameterAnnotation, "first", 0, String.class);
228+
229+
List<ParameterAnnotation<MyAnnotation>> parameterAnnotations = getParameterAnnotations(method,
230+
MyAnnotation.class);
231+
assertNotNull(parameterAnnotations);
232+
assertEquals(2, parameterAnnotations.size());
233+
assertEquals(parameterAnnotation, parameterAnnotations.get(0));
234+
assertParameterAnnotationWith(parameterAnnotations.get(1), "third", 2, Serializable.class);
235+
}
236+
237+
private void assertParameterAnnotationWith(ParameterAnnotation<MyAnnotation> parameterAnnotation, String value,
238+
int index, Class<?> parameterType) {
239+
assertNotNull(parameterAnnotation);
240+
assertEquals(index, parameterAnnotation.getParameterIndex());
241+
assertNotNull(parameterAnnotation.getAnnotation());
242+
assertEquals(value, parameterAnnotation.getAnnotation().value());
243+
assertEquals(parameterType, parameterAnnotation.getParameterType());
244+
}
245+
207246
public static interface AnnotatedInterface {
208247

209248
@Order(0)
@@ -321,10 +360,23 @@ public void foo() {
321360
}
322361
}
323362

363+
public static interface InterfaceWithAnnotatedMethodParameters {
364+
365+
void foo(String foo, @MyAnnotation("foo") Long bar);
366+
367+
void bar(@MyAnnotation("first") String first, String second, @MyAnnotation("third") Serializable third);
368+
}
324369
}
325370

326371
@Retention(RetentionPolicy.RUNTIME)
327372
@Inherited
328373
@interface Transactional {
329-
374+
375+
}
376+
377+
@Retention(RetentionPolicy.RUNTIME)
378+
@Target(ElementType.PARAMETER)
379+
@interface MyAnnotation {
380+
381+
String value() default "";
330382
}

0 commit comments

Comments
 (0)