diff --git a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj index e1a1fd9705..2b22ec9061 100644 --- a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj @@ -19,7 +19,7 @@ package org.springframework.scheduling.aspectj; import java.lang.reflect.Method; import java.util.concurrent.Future; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.scheduling.annotation.Async; /** @@ -68,9 +68,9 @@ public aspect AnnotationAsyncExecutionAspect extends AbstractAsyncExecutionAspec protected String getExecutorQualifier(Method method) { // Maintainer's note: changes made here should also be made in // AnnotationAsyncExecutionInterceptor#getExecutorQualifier - Async async = AnnotationUtils.findAnnotation(method, Async.class); + Async async = AnnotatedElementUtils.findMergedAnnotation(method, Async.class); if (async == null) { - async = AnnotationUtils.findAnnotation(method.getDeclaringClass(), Async.class); + async = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), Async.class); } return (async != null ? async.value() : null); } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java b/spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java index 626a328f41..b28ab0492b 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -18,22 +18,19 @@ package org.springframework.context.annotation; import java.lang.reflect.Method; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; /** * Utilities for processing {@link Bean}-annotated methods. * * @author Chris Beams + * @author Juergen Hoeller * @since 3.1 */ class BeanAnnotationHelper { - /** - * Return whether the given method is directly or indirectly annotated with - * the {@link Bean} annotation. - */ public static boolean isBeanAnnotated(Method method) { - return (AnnotationUtils.findAnnotation(method, Bean.class) != null); + return AnnotatedElementUtils.hasAnnotation(method, Bean.class); } public static String determineBeanNameFor(Method beanMethod) { @@ -41,7 +38,7 @@ class BeanAnnotationHelper { String beanName = beanMethod.getName(); // Check to see if the user has explicitly set a custom bean name... - Bean bean = AnnotationUtils.findAnnotation(beanMethod, Bean.class); + Bean bean = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Bean.class); if (bean != null && bean.name().length > 0) { beanName = bean.name()[0]; } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java index 5d7e801cc4..316fd3b4b7 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java @@ -45,7 +45,7 @@ import org.springframework.cglib.proxy.MethodProxy; import org.springframework.cglib.proxy.NoOp; import org.springframework.cglib.transform.ClassEmitterTransformer; import org.springframework.cglib.transform.TransformingClassGenerator; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.objenesis.ObjenesisException; import org.springframework.objenesis.SpringObjenesis; import org.springframework.util.Assert; @@ -311,7 +311,7 @@ class ConfigurationClassEnhancer { String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod); // Determine whether this bean is a scoped-proxy - Scope scope = AnnotationUtils.findAnnotation(beanMethod, Scope.class); + Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class); if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) { String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName); if (beanFactory.isCurrentlyInCreation(scopedBeanName)) { diff --git a/spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java b/spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java index 54c1308823..a57eb0c612 100644 --- a/spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java +++ b/spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -35,7 +35,6 @@ import org.springframework.context.expression.AnnotatedElementKey; import org.springframework.core.BridgeMethodResolver; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.Order; import org.springframework.expression.EvaluationContext; import org.springframework.util.Assert; @@ -214,7 +213,7 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe } protected A getMethodAnnotation(Class annotationType) { - return AnnotationUtils.findAnnotation(this.method, annotationType); + return AnnotatedElementUtils.findMergedAnnotation(this.method, annotationType); } /** diff --git a/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java b/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java index 108ee54b22..9fea9f47ed 100644 --- a/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -39,8 +39,8 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.MethodIntrospector; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationAwareOrderComparator; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.Assert; /** @@ -129,7 +129,7 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton, new MethodIntrospector.MetadataLookup() { @Override public EventListener inspect(Method method) { - return AnnotationUtils.findAnnotation(method, EventListener.class); + return AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class); } }); if (annotatedMethods.isEmpty()) { diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AnnotationAsyncExecutionInterceptor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AnnotationAsyncExecutionInterceptor.java index 117de48dfb..7c23ad644b 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AnnotationAsyncExecutionInterceptor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AnnotationAsyncExecutionInterceptor.java @@ -21,7 +21,7 @@ import java.util.concurrent.Executor; import org.springframework.aop.interceptor.AsyncExecutionInterceptor; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; /** * Specialization of {@link AsyncExecutionInterceptor} that delegates method execution to @@ -78,9 +78,9 @@ public class AnnotationAsyncExecutionInterceptor extends AsyncExecutionIntercept protected String getExecutorQualifier(Method method) { // Maintainer's note: changes made here should also be made in // AnnotationAsyncExecutionAspect#getExecutorQualifier - Async async = AnnotationUtils.findAnnotation(method, Async.class); + Async async = AnnotatedElementUtils.findMergedAnnotation(method, Async.class); if (async == null) { - async = AnnotationUtils.findAnnotation(method.getDeclaringClass(), Async.class); + async = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), Async.class); } return (async != null ? async.value() : null); } diff --git a/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java b/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java index 705517e5a9..ac04539725 100644 --- a/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java @@ -54,6 +54,7 @@ import org.springframework.context.event.test.GenericEventPojo; import org.springframework.context.event.test.Identifiable; import org.springframework.context.event.test.TestEvent; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.core.annotation.AliasFor; import org.springframework.core.annotation.Order; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.EnableAsync; @@ -868,6 +869,16 @@ public class AnnotationDrivenEventListenerTests { } + + @EventListener + @Retention(RetentionPolicy.RUNTIME) + public @interface ConditionalEvent { + + @AliasFor(annotation = EventListener.class, attribute = "condition") + String value(); + } + + @Component static class ConditionalEventListener extends TestEventListener { @@ -883,12 +894,12 @@ public class AnnotationDrivenEventListenerTests { super.handleString(payload); } - @EventListener(condition = "#root.event.timestamp > #p0") + @ConditionalEvent("#root.event.timestamp > #p0") public void handleTimestamp(Long timestamp) { collectEvent(timestamp); } - @EventListener(condition = "@conditionEvaluator.valid(#p0)") + @ConditionalEvent("@conditionEvaluator.valid(#p0)") public void handleRatio(Double ratio) { collectEvent(ratio); } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java index fd79c8f736..7cc077fabb 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java @@ -271,10 +271,15 @@ public class AnnotatedElementUtils { Assert.notNull(element, "AnnotatedElement must not be null"); Assert.notNull(annotationType, "annotationType must not be null"); + // Shortcut: directly present on the element, with no processing needed? + if (element.isAnnotationPresent(annotationType)) { + return true; + } + return Boolean.TRUE.equals(searchWithGetSemantics(element, annotationType, null, new SimpleAnnotationProcessor() { @Override public Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { - boolean found = annotation.annotationType() == annotationType; + boolean found = (annotation.annotationType() == annotationType); return (found ? Boolean.TRUE : CONTINUE); } })); @@ -324,6 +329,15 @@ public class AnnotatedElementUtils { * @see AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement) */ public static A getMergedAnnotation(AnnotatedElement element, Class annotationType) { + Assert.notNull(annotationType, "annotationType must not be null"); + + // Shortcut: directly present on the element, with no merging needed? + A annotation = element.getAnnotation(annotationType); + if (annotation != null) { + return AnnotationUtils.synthesizeAnnotation(annotation, element); + } + + // Exhaustive retrieval of merged annotation attributes... AnnotationAttributes attributes = getMergedAnnotationAttributes(element, annotationType); return AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element); } @@ -412,6 +426,38 @@ public class AnnotatedElementUtils { return attributes; } + /** + * Determine if an annotation of the specified {@code annotationType} + * is available on the supplied {@link AnnotatedElement} or + * within the annotation hierarchy above the specified element. + *

If this method returns {@code true}, then {@link #findMergedAnnotationAttributes} + * will return a non-null value. + *

This method follows find semantics as described in the + * {@linkplain AnnotatedElementUtils class-level javadoc}. + * @param element the annotated element + * @param annotationType the annotation type to find + * @return {@code true} if a matching annotation is present + * @since 4.3 + */ + public static boolean hasAnnotation(AnnotatedElement element, final Class annotationType) { + Assert.notNull(element, "AnnotatedElement must not be null"); + Assert.notNull(annotationType, "annotationType must not be null"); + + // Shortcut: directly present on the element, with no processing needed? + if (element.isAnnotationPresent(annotationType)) { + return true; + } + + return Boolean.TRUE.equals(searchWithFindSemantics(element, annotationType, annotationType.getName(), + new SimpleAnnotationProcessor() { + @Override + public Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { + boolean found = (annotation.annotationType() == annotationType); + return (found ? Boolean.TRUE : CONTINUE); + } + })); + } + /** * Find the first annotation of the specified {@code annotationType} within * the annotation hierarchy above the supplied {@code element}, @@ -430,6 +476,14 @@ public class AnnotatedElementUtils { */ public static A findMergedAnnotation(AnnotatedElement element, Class annotationType) { Assert.notNull(annotationType, "annotationType must not be null"); + + // Shortcut: directly present on the element, with no merging needed? + A annotation = element.getDeclaredAnnotation(annotationType); + if (annotation != null) { + return AnnotationUtils.synthesizeAnnotation(annotation, element); + } + + // Exhaustive retrieval of merged annotation attributes... AnnotationAttributes attributes = findMergedAnnotationAttributes(element, annotationType, false, false); return AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element); } @@ -483,9 +537,8 @@ public class AnnotatedElementUtils { Assert.notNull(element, "AnnotatedElement must not be null"); Assert.notNull(annotationType, "annotationType must not be null"); - MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(annotationType, null, - false, false, true); - + MergedAnnotationAttributesProcessor processor = + new MergedAnnotationAttributesProcessor(annotationType, null, false, false, true); searchWithFindSemantics(element, annotationType, annotationType.getName(), processor); Set annotations = new LinkedHashSet(); @@ -831,8 +884,7 @@ public class AnnotatedElementUtils { try { // Locally declared annotations (ignoring @Inherited) Annotation[] annotations = element.getDeclaredAnnotations(); - - List aggregatedResults = processor.aggregates() ? new ArrayList() : null; + List aggregatedResults = (processor.aggregates() ? new ArrayList() : null); // Search in local annotations for (Annotation annotation : annotations) { diff --git a/spring-jms/src/main/java/org/springframework/jms/config/MethodJmsListenerEndpoint.java b/spring-jms/src/main/java/org/springframework/jms/config/MethodJmsListenerEndpoint.java index 56a6283a03..b7379d2035 100644 --- a/spring-jms/src/main/java/org/springframework/jms/config/MethodJmsListenerEndpoint.java +++ b/spring-jms/src/main/java/org/springframework/jms/config/MethodJmsListenerEndpoint.java @@ -25,7 +25,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.EmbeddedValueResolver; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.jms.listener.MessageListenerContainer; import org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter; import org.springframework.jms.support.converter.MessageConverter; @@ -187,13 +187,11 @@ public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint imple } private SendTo getSendTo(Method specificMethod) { - SendTo ann = AnnotationUtils.getAnnotation(specificMethod, SendTo.class); - if (ann != null) { - return ann; - } - else { - return AnnotationUtils.getAnnotation(specificMethod.getDeclaringClass(), SendTo.class); + SendTo ann = AnnotatedElementUtils.findMergedAnnotation(specificMethod, SendTo.class); + if (ann == null) { + ann = AnnotatedElementUtils.findMergedAnnotation(specificMethod.getDeclaringClass(), SendTo.class); } + return ann; } private String resolve(String value) { diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java index 55b39b8d41..bde12d08e2 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java @@ -25,7 +25,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.core.BridgeMethodResolver; import org.springframework.core.MethodParameter; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -33,10 +33,11 @@ import org.springframework.util.ClassUtils; /** * Encapsulates information about a handler method consisting of a * {@linkplain #getMethod() method} and a {@linkplain #getBean() bean}. - * Provides convenient access to method parameters, method return value, method annotations. + * Provides convenient access to method parameters, the method return value, + * method annotations, etc. * *

The class may be created with a bean instance or with a bean name (e.g. lazy-init bean, - * prototype bean). Use {@link #createWithResolvedBean()} to obtain a {@link HandlerMethod} + * prototype bean). Use {@link #createWithResolvedBean()} to obtain a {@code HandlerMethod} * instance with a bean instance resolved through the associated {@link BeanFactory}. * * @author Arjen Poutsma @@ -61,6 +62,8 @@ public class HandlerMethod { private final MethodParameter[] parameters; + private final HandlerMethod resolvedFromHandlerMethod; + /** * Create an instance from a bean instance and a method. @@ -74,6 +77,7 @@ public class HandlerMethod { this.method = method; this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.parameters = initMethodParameters(); + this.resolvedFromHandlerMethod = null; } /** @@ -89,12 +93,13 @@ public class HandlerMethod { this.method = bean.getClass().getMethod(methodName, parameterTypes); this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(this.method); this.parameters = initMethodParameters(); + this.resolvedFromHandlerMethod = null; } /** * Create an instance from a bean name, a method, and a {@code BeanFactory}. * The method {@link #createWithResolvedBean()} may be used later to - * re-create the {@code HandlerMethod} with an initialized the bean. + * re-create the {@code HandlerMethod} with an initialized bean. */ public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) { Assert.hasText(beanName, "Bean name is required"); @@ -106,6 +111,7 @@ public class HandlerMethod { this.method = method; this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.parameters = initMethodParameters(); + this.resolvedFromHandlerMethod = null; } /** @@ -119,6 +125,7 @@ public class HandlerMethod { this.method = handlerMethod.method; this.bridgedMethod = handlerMethod.bridgedMethod; this.parameters = handlerMethod.parameters; + this.resolvedFromHandlerMethod = handlerMethod.resolvedFromHandlerMethod; } /** @@ -133,6 +140,7 @@ public class HandlerMethod { this.method = handlerMethod.method; this.bridgedMethod = handlerMethod.bridgedMethod; this.parameters = handlerMethod.parameters; + this.resolvedFromHandlerMethod = handlerMethod; } @@ -183,6 +191,15 @@ public class HandlerMethod { return this.parameters; } + /** + * Return the HandlerMethod from which this HandlerMethod instance was + * resolved via {@link #createWithResolvedBean()}. + * @since 4.3 + */ + public HandlerMethod getResolvedFromHandlerMethod() { + return this.resolvedFromHandlerMethod; + } + /** * Return the HandlerMethod return type. */ @@ -207,11 +224,24 @@ public class HandlerMethod { /** * Returns a single annotation on the underlying method traversing its super methods * if no annotation can be found on the given method itself. - * @param annotationType the type of annotation to introspect the method for. + *

Also supports merged composed annotations with attribute + * overrides as of Spring Framework 4.3. + * @param annotationType the type of annotation to introspect the method for * @return the annotation, or {@code null} if none found + * @see AnnotatedElementUtils#findMergedAnnotation */ public A getMethodAnnotation(Class annotationType) { - return AnnotationUtils.findAnnotation(this.method, annotationType); + return AnnotatedElementUtils.findMergedAnnotation(this.method, annotationType); + } + + /** + * Return whether the parameter is declared with the given annotation type. + * @param annotationType the annotation type to look for + * @since 4.3 + * @see AnnotatedElementUtils#hasAnnotation + */ + public boolean hasMethodAnnotation(Class annotationType) { + return AnnotatedElementUtils.hasAnnotation(this.method, annotationType); } /** @@ -227,6 +257,9 @@ public class HandlerMethod { return new HandlerMethod(this, handler); } + /** + * Return a short representation of this handler method for log message purposes. + */ public String getShortLogMessage() { int args = this.method.getParameterTypes().length; return getBeanType().getName() + "#" + this.method.getName() + "[" + args + " args]"; @@ -279,6 +312,11 @@ public class HandlerMethod { return HandlerMethod.this.getMethodAnnotation(annotationType); } + @Override + public boolean hasMethodAnnotation(Class annotationType) { + return HandlerMethod.this.hasMethodAnnotation(annotationType); + } + @Override public HandlerMethodParameter clone() { return new HandlerMethodParameter(this); diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandler.java index 1fe4a35533..d5299ce09f 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandler.java @@ -21,6 +21,7 @@ import java.security.Principal; import java.util.Map; import org.springframework.core.MethodParameter; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; @@ -132,13 +133,13 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH @Override public boolean supportsReturnType(MethodParameter returnType) { - if (returnType.getMethodAnnotation(SendTo.class) != null || - AnnotationUtils.getAnnotation(returnType.getDeclaringClass(), SendTo.class) != null || - returnType.getMethodAnnotation(SendToUser.class) != null || - AnnotationUtils.getAnnotation(returnType.getDeclaringClass(), SendToUser.class) != null) { + if (returnType.hasMethodAnnotation(SendTo.class) || + AnnotatedElementUtils.hasAnnotation(returnType.getDeclaringClass(), SendTo.class) || + returnType.hasMethodAnnotation(SendToUser.class) || + AnnotatedElementUtils.hasAnnotation(returnType.getDeclaringClass(), SendToUser.class)) { return true; } - return (!this.annotationRequired); + return !this.annotationRequired; } @Override @@ -186,24 +187,24 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH } private SendToUser getSendToUser(MethodParameter returnType) { - SendToUser annot = returnType.getMethodAnnotation(SendToUser.class); - if (annot != null && !ObjectUtils.isEmpty((annot.value()))) { + SendToUser annot = AnnotatedElementUtils.findMergedAnnotation(returnType.getMethod(), SendToUser.class); + if (annot != null && !ObjectUtils.isEmpty(annot.value())) { return annot; } - SendToUser typeAnnot = AnnotationUtils.getAnnotation(returnType.getDeclaringClass(), SendToUser.class); - if (typeAnnot != null && !ObjectUtils.isEmpty((typeAnnot.value()))) { + SendToUser typeAnnot = AnnotatedElementUtils.findMergedAnnotation(returnType.getDeclaringClass(), SendToUser.class); + if (typeAnnot != null && !ObjectUtils.isEmpty(typeAnnot.value())) { return typeAnnot; } return (annot != null ? annot : typeAnnot); } private SendTo getSendTo(MethodParameter returnType) { - SendTo sendTo = returnType.getMethodAnnotation(SendTo.class); - if (sendTo != null && !ObjectUtils.isEmpty((sendTo.value()))) { + SendTo sendTo = AnnotatedElementUtils.findMergedAnnotation(returnType.getMethod(), SendTo.class); + if (sendTo != null && !ObjectUtils.isEmpty(sendTo.value())) { return sendTo; } else { - return AnnotationUtils.getAnnotation(returnType.getDeclaringClass(), SendTo.class); + return AnnotatedElementUtils.findMergedAnnotation(returnType.getDeclaringClass(), SendTo.class); } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java index 424e09c11d..f32f62af88 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java @@ -29,7 +29,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.context.SmartLifecycle; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.convert.ConversionService; import org.springframework.format.support.DefaultFormattingConversionService; import org.springframework.messaging.Message; @@ -360,14 +360,14 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan @Override protected boolean isHandler(Class beanType) { - return (AnnotationUtils.findAnnotation(beanType, Controller.class) != null); + return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class); } @Override protected SimpMessageMappingInfo getMappingForMethod(Method method, Class handlerType) { - MessageMapping messageAnn = AnnotationUtils.findAnnotation(method, MessageMapping.class); + MessageMapping messageAnn = AnnotatedElementUtils.findMergedAnnotation(method, MessageMapping.class); if (messageAnn != null) { - MessageMapping typeAnn = AnnotationUtils.findAnnotation(handlerType, MessageMapping.class); + MessageMapping typeAnn = AnnotatedElementUtils.findMergedAnnotation(handlerType, MessageMapping.class); // Only actually register it if there are destinations specified; // otherwise @MessageMapping is just being used as a (meta-annotation) marker. if (messageAnn.value().length > 0 || (typeAnn != null && typeAnn.value().length > 0)) { @@ -379,9 +379,9 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan } } - SubscribeMapping subscribeAnn = AnnotationUtils.findAnnotation(method, SubscribeMapping.class); + SubscribeMapping subscribeAnn = AnnotatedElementUtils.findMergedAnnotation(method, SubscribeMapping.class); if (subscribeAnn != null) { - MessageMapping typeAnn = AnnotationUtils.findAnnotation(handlerType, MessageMapping.class); + MessageMapping typeAnn = AnnotatedElementUtils.findMergedAnnotation(handlerType, MessageMapping.class); // Only actually register it if there are destinations specified; // otherwise @SubscribeMapping is just being used as a (meta-annotation) marker. if (subscribeAnn.value().length > 0 || (typeAnn != null && typeAnn.value().length > 0)) { diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SubscriptionMethodReturnValueHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SubscriptionMethodReturnValueHandler.java index 05877c6f3b..e18af8df8b 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SubscriptionMethodReturnValueHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SubscriptionMethodReturnValueHandler.java @@ -95,9 +95,9 @@ public class SubscriptionMethodReturnValueHandler implements HandlerMethodReturn @Override public boolean supportsReturnType(MethodParameter returnType) { - return (returnType.getMethodAnnotation(SubscribeMapping.class) != null && - returnType.getMethodAnnotation(SendTo.class) == null && - returnType.getMethodAnnotation(SendToUser.class) == null); + return (returnType.hasMethodAnnotation(SubscribeMapping.class) && + !returnType.hasMethodAnnotation(SendTo.class) && + !returnType.hasMethodAnnotation(SendToUser.class)); } @Override diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java index 683ecd5963..af573a7df6 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java @@ -16,6 +16,8 @@ package org.springframework.messaging.simp.annotation.support; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -34,6 +36,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.springframework.core.MethodParameter; +import org.springframework.core.annotation.AliasFor; import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; @@ -52,7 +55,7 @@ import org.springframework.util.MimeType; import static org.junit.Assert.*; import static org.mockito.BDDMockito.*; -import static org.springframework.messaging.handler.DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER; +import static org.springframework.messaging.handler.DestinationPatternsMessageCondition.*; import static org.springframework.messaging.handler.annotation.support.DestinationVariableMethodArgumentResolver.*; import static org.springframework.messaging.support.MessageHeaderAccessor.*; @@ -110,31 +113,31 @@ public class SendToMethodReturnValueHandlerTests { jsonMessagingTemplate.setMessageConverter(new MappingJackson2MessageConverter()); this.jsonHandler = new SendToMethodReturnValueHandler(jsonMessagingTemplate, true); - Method method = this.getClass().getDeclaredMethod("handleNoAnnotations"); + Method method = getClass().getDeclaredMethod("handleNoAnnotations"); this.noAnnotationsReturnType = new SynthesizingMethodParameter(method, -1); - method = this.getClass().getDeclaredMethod("handleAndSendToDefaultDestination"); + method = getClass().getDeclaredMethod("handleAndSendToDefaultDestination"); this.sendToDefaultDestReturnType = new SynthesizingMethodParameter(method, -1); - method = this.getClass().getDeclaredMethod("handleAndSendTo"); + method = getClass().getDeclaredMethod("handleAndSendTo"); this.sendToReturnType = new SynthesizingMethodParameter(method, -1); - method = this.getClass().getDeclaredMethod("handleAndSendToWithPlaceholders"); + method = getClass().getDeclaredMethod("handleAndSendToWithPlaceholders"); this.sendToWithPlaceholdersReturnType = new SynthesizingMethodParameter(method, -1); - method = this.getClass().getDeclaredMethod("handleAndSendToUser"); + method = getClass().getDeclaredMethod("handleAndSendToUser"); this.sendToUserReturnType = new SynthesizingMethodParameter(method, -1); - method = this.getClass().getDeclaredMethod("handleAndSendToUserSingleSession"); + method = getClass().getDeclaredMethod("handleAndSendToUserSingleSession"); this.sendToUserSingleSessionReturnType = new SynthesizingMethodParameter(method, -1); - method = this.getClass().getDeclaredMethod("handleAndSendToUserDefaultDestination"); + method = getClass().getDeclaredMethod("handleAndSendToUserDefaultDestination"); this.sendToUserDefaultDestReturnType = new SynthesizingMethodParameter(method, -1); - method = this.getClass().getDeclaredMethod("handleAndSendToUserDefaultDestinationSingleSession"); + method = getClass().getDeclaredMethod("handleAndSendToUserDefaultDestinationSingleSession"); this.sendToUserSingleSessionDefaultDestReturnType = new SynthesizingMethodParameter(method, -1); - method = this.getClass().getDeclaredMethod("handleAndSendToJsonView"); + method = getClass().getDeclaredMethod("handleAndSendToJsonView"); this.jsonViewReturnType = new SynthesizingMethodParameter(method, -1); method = SendToTestBean.class.getDeclaredMethod("handleNoAnnotation"); @@ -287,6 +290,7 @@ public class SendToMethodReturnValueHandlerTests { private void assertResponse(MethodParameter methodParameter, String sessionId, int index, String destination) { + SimpMessageHeaderAccessor accessor = getCapturedAccessor(index); assertEquals(sessionId, accessor.getSessionId()); assertEquals(destination, accessor.getDestination()); @@ -546,6 +550,23 @@ public class SendToMethodReturnValueHandlerTests { } } + @SendTo + @Retention(RetentionPolicy.RUNTIME) + public @interface MySendTo { + + @AliasFor(annotation = SendTo.class, attribute = "value") + String[] dest(); + } + + @SendToUser + @Retention(RetentionPolicy.RUNTIME) + public @interface MySendToUser { + + @AliasFor(annotation = SendToUser.class, attribute = "destinations") + String[] dest(); + } + + @SuppressWarnings("unused") public String handleNoAnnotations() { return PAYLOAD; @@ -586,7 +607,6 @@ public class SendToMethodReturnValueHandlerTests { return PAYLOAD; } - @SendTo("/dest") @JsonView(MyJacksonView1.class) @SuppressWarnings("unused") public JacksonViewBean handleAndSendToJsonView() { JacksonViewBean payload = new JacksonViewBean(); @@ -596,7 +616,8 @@ public class SendToMethodReturnValueHandlerTests { return payload; } - @SendTo("/dest-default") @SuppressWarnings("unused") + + @MySendTo(dest = "/dest-default") @SuppressWarnings("unused") private static class SendToTestBean { public String handleNoAnnotation() { @@ -608,14 +629,13 @@ public class SendToMethodReturnValueHandlerTests { return PAYLOAD; } - @SendTo({"/dest3", "/dest4"}) + @MySendTo(dest = {"/dest3", "/dest4"}) public String handleAndSendToOverride() { return PAYLOAD; } - } - @SendToUser("/dest-default") @SuppressWarnings("unused") + @MySendToUser(dest = "/dest-default") @SuppressWarnings("unused") private static class SendToUserTestBean { public String handleNoAnnotation() { @@ -627,11 +647,10 @@ public class SendToMethodReturnValueHandlerTests { return PAYLOAD; } - @SendToUser({"/dest3", "/dest4"}) + @MySendToUser(dest = {"/dest3", "/dest4"}) public String handleAndSendToOverride() { return PAYLOAD; } - } diff --git a/spring-test/src/main/java/org/springframework/test/annotation/ProfileValueUtils.java b/spring-test/src/main/java/org/springframework/test/annotation/ProfileValueUtils.java index 27ce7f640c..17c60cad01 100644 --- a/spring-test/src/main/java/org/springframework/test/annotation/ProfileValueUtils.java +++ b/spring-test/src/main/java/org/springframework/test/annotation/ProfileValueUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -21,13 +21,12 @@ import java.lang.reflect.Method; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; -import static org.springframework.core.annotation.AnnotationUtils.*; - /** * General utility methods for working with profile values. * @@ -49,12 +48,10 @@ public abstract class ProfileValueUtils { * {@link ProfileValueSourceConfiguration * @ProfileValueSourceConfiguration} annotation and instantiates a new * instance of that type. - *

- * If {@link ProfileValueSourceConfiguration + *

If {@link ProfileValueSourceConfiguration * @ProfileValueSourceConfiguration} is not present on the specified * class or if a custom {@link ProfileValueSource} is not declared, the * default {@link SystemProfileValueSource} will be returned instead. - * * @param testClass The test class for which the ProfileValueSource should * be retrieved * @return the configured (or default) ProfileValueSource for the specified @@ -66,10 +63,10 @@ public abstract class ProfileValueUtils { Assert.notNull(testClass, "testClass must not be null"); Class annotationType = ProfileValueSourceConfiguration.class; - ProfileValueSourceConfiguration config = findAnnotation(testClass, annotationType); + ProfileValueSourceConfiguration config = AnnotatedElementUtils.findMergedAnnotation(testClass, annotationType); if (logger.isDebugEnabled()) { - logger.debug("Retrieved @ProfileValueSourceConfiguration [" + config + "] for test class [" - + testClass.getName() + "]"); + logger.debug("Retrieved @ProfileValueSourceConfiguration [" + config + "] for test class [" + + testClass.getName() + "]"); } Class profileValueSourceType; @@ -80,8 +77,8 @@ public abstract class ProfileValueUtils { profileValueSourceType = (Class) AnnotationUtils.getDefaultValue(annotationType); } if (logger.isDebugEnabled()) { - logger.debug("Retrieved ProfileValueSource type [" + profileValueSourceType + "] for class [" - + testClass.getName() + "]"); + logger.debug("Retrieved ProfileValueSource type [" + profileValueSourceType + "] for class [" + + testClass.getName() + "]"); } ProfileValueSource profileValueSource; @@ -92,10 +89,10 @@ public abstract class ProfileValueUtils { try { profileValueSource = profileValueSourceType.newInstance(); } - catch (Exception e) { + catch (Exception ex) { if (logger.isWarnEnabled()) { - logger.warn("Could not instantiate a ProfileValueSource of type [" + profileValueSourceType - + "] for class [" + testClass.getName() + "]: using default.", e); + logger.warn("Could not instantiate a ProfileValueSource of type [" + profileValueSourceType + + "] for class [" + testClass.getName() + "]: using default.", ex); } profileValueSource = SystemProfileValueSource.getInstance(); } @@ -108,16 +105,14 @@ public abstract class ProfileValueUtils { * Determine if the supplied {@code testClass} is enabled in * the current environment, as specified by the {@link IfProfileValue * @IfProfileValue} annotation at the class level. - *

- * Defaults to {@code true} if no {@link IfProfileValue + *

Defaults to {@code true} if no {@link IfProfileValue * @IfProfileValue} annotation is declared. - * * @param testClass the test class * @return {@code true} if the test is enabled in the current * environment */ public static boolean isTestEnabledInThisEnvironment(Class testClass) { - IfProfileValue ifProfileValue = findAnnotation(testClass, IfProfileValue.class); + IfProfileValue ifProfileValue = AnnotatedElementUtils.findMergedAnnotation(testClass, IfProfileValue.class); return isTestEnabledInThisEnvironment(retrieveProfileValueSource(testClass), ifProfileValue); } @@ -127,10 +122,8 @@ public abstract class ProfileValueUtils { * @IfProfileValue} annotation, which may be declared on the test * method itself or at the class level. Class-level usage overrides * method-level usage. - *

- * Defaults to {@code true} if no {@link IfProfileValue + *

Defaults to {@code true} if no {@link IfProfileValue * @IfProfileValue} annotation is declared. - * * @param testMethod the test method * @param testClass the test class * @return {@code true} if the test is enabled in the current @@ -146,10 +139,8 @@ public abstract class ProfileValueUtils { * @IfProfileValue} annotation, which may be declared on the test * method itself or at the class level. Class-level usage overrides * method-level usage. - *

- * Defaults to {@code true} if no {@link IfProfileValue + *

Defaults to {@code true} if no {@link IfProfileValue * @IfProfileValue} annotation is declared. - * * @param profileValueSource the ProfileValueSource to use to determine if * the test is enabled * @param testMethod the test method @@ -160,11 +151,11 @@ public abstract class ProfileValueUtils { public static boolean isTestEnabledInThisEnvironment(ProfileValueSource profileValueSource, Method testMethod, Class testClass) { - IfProfileValue ifProfileValue = findAnnotation(testClass, IfProfileValue.class); + IfProfileValue ifProfileValue = AnnotatedElementUtils.findMergedAnnotation(testClass, IfProfileValue.class); boolean classLevelEnabled = isTestEnabledInThisEnvironment(profileValueSource, ifProfileValue); if (classLevelEnabled) { - ifProfileValue = findAnnotation(testMethod, IfProfileValue.class); + ifProfileValue = AnnotatedElementUtils.findMergedAnnotation(testMethod, IfProfileValue.class); return isTestEnabledInThisEnvironment(profileValueSource, ifProfileValue); } @@ -175,7 +166,6 @@ public abstract class ProfileValueUtils { * Determine if the {@code value} (or one of the {@code values}) * in the supplied {@link IfProfileValue @IfProfileValue} annotation is * enabled in the current environment. - * * @param profileValueSource the ProfileValueSource to use to determine if * the test is enabled * @param ifProfileValue the annotation to introspect; may be @@ -195,8 +185,8 @@ public abstract class ProfileValueUtils { String[] annotatedValues = ifProfileValue.values(); if (StringUtils.hasLength(ifProfileValue.value())) { if (annotatedValues.length > 0) { - throw new IllegalArgumentException("Setting both the 'value' and 'values' attributes " - + "of @IfProfileValue is not allowed: choose one or the other."); + throw new IllegalArgumentException("Setting both the 'value' and 'values' attributes " + + "of @IfProfileValue is not allowed: choose one or the other."); } annotatedValues = new String[] { ifProfileValue.value() }; } diff --git a/spring-test/src/main/java/org/springframework/test/annotation/TestAnnotationUtils.java b/spring-test/src/main/java/org/springframework/test/annotation/TestAnnotationUtils.java index 5f4eb1c247..a2b33faa51 100644 --- a/spring-test/src/main/java/org/springframework/test/annotation/TestAnnotationUtils.java +++ b/spring-test/src/main/java/org/springframework/test/annotation/TestAnnotationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -19,7 +19,6 @@ package org.springframework.test.annotation; import java.lang.reflect.Method; import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.core.annotation.AnnotationUtils; /** * Collection of utility methods for working with Spring's core testing annotations. @@ -52,7 +51,7 @@ public class TestAnnotationUtils { * not annotated with {@code @Repeat} */ public static int getRepeatCount(Method method) { - Repeat repeat = AnnotationUtils.findAnnotation(method, Repeat.class); + Repeat repeat = AnnotatedElementUtils.findMergedAnnotation(method, Repeat.class); if (repeat == null) { return 1; } diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/ProfileValueChecker.java b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/ProfileValueChecker.java index e2b5195ae0..5d53bbff71 100644 --- a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/ProfileValueChecker.java +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/ProfileValueChecker.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -22,7 +22,7 @@ import java.lang.reflect.Method; import org.junit.AssumptionViolatedException; import org.junit.runners.model.Statement; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.test.annotation.IfProfileValue; import org.springframework.test.annotation.ProfileValueUtils; import org.springframework.util.Assert; @@ -64,6 +64,7 @@ public class ProfileValueChecker extends Statement { this.testMethod = testMethod; } + /** * Determine if the test specified by arguments to the * {@linkplain #ProfileValueChecker constructor} is enabled in @@ -83,17 +84,17 @@ public class ProfileValueChecker extends Statement { public void evaluate() throws Throwable { if (this.testMethod == null) { if (!ProfileValueUtils.isTestEnabledInThisEnvironment(this.testClass)) { - Annotation ann = AnnotationUtils.findAnnotation(this.testClass, IfProfileValue.class); - throw new AssumptionViolatedException( - String.format("Profile configured via [%s] is not enabled in this environment for test class [%s].", + Annotation ann = AnnotatedElementUtils.findMergedAnnotation(this.testClass, IfProfileValue.class); + throw new AssumptionViolatedException(String.format( + "Profile configured via [%s] is not enabled in this environment for test class [%s].", ann, this.testClass.getName())); } } else { if (!ProfileValueUtils.isTestEnabledInThisEnvironment(this.testMethod, this.testClass)) { throw new AssumptionViolatedException(String.format( - "Profile configured via @IfProfileValue is not enabled in this environment for test method [%s].", - this.testMethod)); + "Profile configured via @IfProfileValue is not enabled in this environment for test method [%s].", + this.testMethod)); } } diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java index 51bbee68da..8fa0040e1a 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java @@ -31,8 +31,8 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeanInstantiationException; import org.springframework.beans.BeanUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationAwareOrderComparator; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.test.context.BootstrapContext; import org.springframework.test.context.CacheAwareContextLoaderDelegate; @@ -278,7 +278,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot return buildDefaultMergedContextConfiguration(testClass, cacheAwareContextLoaderDelegate); } - if (AnnotationUtils.findAnnotation(testClass, ContextHierarchy.class) != null) { + if (AnnotatedElementUtils.findMergedAnnotation(testClass, ContextHierarchy.class) != null) { Map> hierarchyMap = ContextLoaderUtils.buildContextHierarchyMap(testClass); MergedContextConfiguration parentConfig = null; MergedContextConfiguration mergedConfig = null; diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoaderUtils.java b/spring-test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoaderUtils.java index baa15001b2..968d2300e8 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoaderUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoaderUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -24,7 +24,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.test.context.SmartContextLoader; import org.springframework.util.Assert; @@ -103,7 +103,7 @@ public abstract class AnnotationConfigContextLoaderUtils { */ private static boolean isDefaultConfigurationClassCandidate(Class clazz) { return (clazz != null && isStaticNonPrivateAndNonFinal(clazz) && - (AnnotationUtils.findAnnotation(clazz, Configuration.class) != null)); + AnnotatedElementUtils.hasAnnotation(clazz, Configuration.class)); } private static boolean isStaticNonPrivateAndNonFinal(Class clazz) { diff --git a/spring-test/src/main/java/org/springframework/test/context/support/ContextLoaderUtils.java b/spring-test/src/main/java/org/springframework/test/context/support/ContextLoaderUtils.java index da3383cb37..fb3aab8502 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/ContextLoaderUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/ContextLoaderUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -95,11 +95,10 @@ abstract class ContextLoaderUtils { @SuppressWarnings("unchecked") static List> resolveContextHierarchyAttributes(Class testClass) { Assert.notNull(testClass, "Class must not be null"); - Assert.state(findAnnotation(testClass, ContextHierarchy.class) != null, "@ContextHierarchy must be present"); - final Class contextConfigType = ContextConfiguration.class; - final Class contextHierarchyType = ContextHierarchy.class; - final List> hierarchyAttributes = new ArrayList>(); + Class contextConfigType = ContextConfiguration.class; + Class contextHierarchyType = ContextHierarchy.class; + List> hierarchyAttributes = new ArrayList>(); UntypedAnnotationDescriptor desc = findAnnotationDescriptorForTypes(testClass, contextConfigType, contextHierarchyType); @@ -124,7 +123,7 @@ abstract class ContextLoaderUtils { throw new IllegalStateException(msg); } - final List configAttributesList = new ArrayList(); + List configAttributesList = new ArrayList(); if (contextConfigDeclaredLocally) { ContextConfiguration contextConfiguration = AnnotationUtils.synthesizeAnnotation( diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java index 668717a701..d81f1ddaa4 100644 --- a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java @@ -30,6 +30,7 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.AnnotationUtils; import org.springframework.test.annotation.Commit; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.TestContext; @@ -44,9 +45,6 @@ import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; -import static org.springframework.core.annotation.AnnotationUtils.findAnnotation; -import static org.springframework.core.annotation.AnnotationUtils.getAnnotation; - /** * {@code TestExecutionListener} that provides support for executing tests * within test-managed transactions by honoring Spring's @@ -181,8 +179,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis transactionAttribute); if (logger.isDebugEnabled()) { - logger.debug("Explicit transaction definition [" + transactionAttribute + "] found for test context " - + testContext); + logger.debug("Explicit transaction definition [" + transactionAttribute + "] found for test context " + + testContext); } if (transactionAttribute.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) { @@ -193,8 +191,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis if (tm == null) { throw new IllegalStateException(String.format( - "Failed to retrieve PlatformTransactionManager for @Transactional test for test context %s.", - testContext)); + "Failed to retrieve PlatformTransactionManager for @Transactional test for test context %s.", + testContext)); } } @@ -255,8 +253,10 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis } } catch (InvocationTargetException ex) { - logger.error("Exception encountered while executing @BeforeTransaction methods for test context " - + testContext + ".", ex.getTargetException()); + if (logger.isErrorEnabled()) { + logger.error("Exception encountered while executing @BeforeTransaction methods for test context " + + testContext + ".", ex.getTargetException()); + } ReflectionUtils.rethrowException(ex.getTargetException()); } } @@ -286,15 +286,15 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis if (afterTransactionException == null) { afterTransactionException = targetException; } - logger.error("Exception encountered while executing @AfterTransaction method [" + method - + "] for test context " + testContext, targetException); + logger.error("Exception encountered while executing @AfterTransaction method [" + method + + "] for test context " + testContext, targetException); } catch (Exception ex) { if (afterTransactionException == null) { afterTransactionException = ex; } - logger.error("Exception encountered while executing @AfterTransaction method [" + method - + "] for test context " + testContext, ex); + logger.error("Exception encountered while executing @AfterTransaction method [" + method + + "] for test context " + testContext, ex); } } @@ -317,20 +317,18 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis * @see #getTransactionManager(TestContext) */ protected PlatformTransactionManager getTransactionManager(TestContext testContext, String qualifier) { - // look up by type and qualifier from @Transactional + // Look up by type and qualifier from @Transactional if (StringUtils.hasText(qualifier)) { try { - // Use autowire-capable factory in order to support extended qualifier - // matching (only exposed on the internal BeanFactory, not on the - // ApplicationContext). + // Use autowire-capable factory in order to support extended qualifier matching + // (only exposed on the internal BeanFactory, not on the ApplicationContext). BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory(); return BeanFactoryAnnotationUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier); } catch (RuntimeException ex) { if (logger.isWarnEnabled()) { - logger.warn( - String.format( + logger.warn(String.format( "Caught exception while retrieving transaction manager with qualifier '%s' for test context %s", qualifier, testContext), ex); } @@ -376,7 +374,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis @SuppressWarnings("deprecation") protected final boolean isDefaultRollback(TestContext testContext) throws Exception { Class testClass = testContext.getTestClass(); - Rollback rollback = findAnnotation(testClass, Rollback.class); + Rollback rollback = AnnotatedElementUtils.findMergedAnnotation(testClass, Rollback.class); boolean rollbackPresent = (rollback != null); TransactionConfigurationAttributes txConfigAttributes = retrieveConfigurationAttributes(testContext); @@ -411,21 +409,22 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis */ protected final boolean isRollback(TestContext testContext) throws Exception { boolean rollback = isDefaultRollback(testContext); - Rollback rollbackAnnotation = findAnnotation(testContext.getTestMethod(), Rollback.class); + Rollback rollbackAnnotation = + AnnotatedElementUtils.findMergedAnnotation(testContext.getTestMethod(), Rollback.class); if (rollbackAnnotation != null) { boolean rollbackOverride = rollbackAnnotation.value(); if (logger.isDebugEnabled()) { logger.debug(String.format( - "Method-level @Rollback(%s) overrides default rollback [%s] for test context %s.", - rollbackOverride, rollback, testContext)); + "Method-level @Rollback(%s) overrides default rollback [%s] for test context %s.", + rollbackOverride, rollback, testContext)); } rollback = rollbackOverride; } else { if (logger.isDebugEnabled()) { logger.debug(String.format( - "No method-level @Rollback override: using default rollback [%s] for test context %s.", rollback, - testContext)); + "No method-level @Rollback override: using default rollback [%s] for test context %s.", + rollback, testContext)); } } return rollback; @@ -466,7 +465,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis List results = new ArrayList(); for (Class current : getSuperClasses(clazz)) { for (Method method : current.getDeclaredMethods()) { - Annotation annotation = getAnnotation(method, annotationType); + Annotation annotation = AnnotationUtils.getAnnotation(method, annotationType); if (annotation != null && !isShadowed(method, results)) { results.add(method); } @@ -537,19 +536,18 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis if (this.configurationAttributes == null) { Class clazz = testContext.getTestClass(); - TransactionConfiguration txConfig = AnnotatedElementUtils.findMergedAnnotation(clazz, - TransactionConfiguration.class); + TransactionConfiguration txConfig = + AnnotatedElementUtils.findMergedAnnotation(clazz, TransactionConfiguration.class); if (logger.isDebugEnabled()) { logger.debug(String.format("Retrieved @TransactionConfiguration [%s] for test class [%s].", - txConfig, clazz.getName())); + txConfig, clazz.getName())); } - TransactionConfigurationAttributes configAttributes = (txConfig == null ? defaultTxConfigAttributes - : new TransactionConfigurationAttributes(txConfig.transactionManager(), txConfig.defaultRollback())); - + TransactionConfigurationAttributes configAttributes = (txConfig == null ? defaultTxConfigAttributes : + new TransactionConfigurationAttributes(txConfig.transactionManager(), txConfig.defaultRollback())); if (logger.isDebugEnabled()) { logger.debug(String.format("Using TransactionConfigurationAttributes %s for test class [%s].", - configAttributes, clazz.getName())); + configAttributes, clazz.getName())); } this.configurationAttributes = configAttributes; } diff --git a/spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java index 9585552bc3..170b617422 100644 --- a/spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java @@ -25,7 +25,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.Conventions; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockServletContext; @@ -33,7 +33,6 @@ import org.springframework.test.context.TestContext; import org.springframework.test.context.TestExecutionListener; import org.springframework.test.context.support.AbstractTestExecutionListener; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; -import org.springframework.util.Assert; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; @@ -58,7 +57,7 @@ import org.springframework.web.context.request.ServletWebRequest; *

Note that {@code ServletTestExecutionListener} is enabled by default but * generally takes no action if the {@linkplain TestContext#getTestClass() test * class} is not annotated with {@link WebAppConfiguration @WebAppConfiguration}. - * See the Javadoc for individual methods in this class for details. + * See the javadocs for individual methods in this class for details. * * @author Sam Brannen * @author Phillip Webb @@ -71,45 +70,41 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener * whether or not the {@code ServletTestExecutionListener} should {@linkplain * RequestContextHolder#resetRequestAttributes() reset} Spring Web's * {@code RequestContextHolder} in {@link #afterTestMethod(TestContext)}. - * *

Permissible values include {@link Boolean#TRUE} and {@link Boolean#FALSE}. */ public static final String RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE = Conventions.getQualifiedAttributeName( - ServletTestExecutionListener.class, "resetRequestContextHolder"); + ServletTestExecutionListener.class, "resetRequestContextHolder"); /** * Attribute name for a {@link TestContext} attribute which indicates that * {@code ServletTestExecutionListener} has already populated Spring Web's * {@code RequestContextHolder}. - * *

Permissible values include {@link Boolean#TRUE} and {@link Boolean#FALSE}. */ public static final String POPULATED_REQUEST_CONTEXT_HOLDER_ATTRIBUTE = Conventions.getQualifiedAttributeName( - ServletTestExecutionListener.class, "populatedRequestContextHolder"); + ServletTestExecutionListener.class, "populatedRequestContextHolder"); /** * Attribute name for a request attribute which indicates that the * {@link MockHttpServletRequest} stored in the {@link RequestAttributes} * in Spring Web's {@link RequestContextHolder} was created by the TestContext * framework. - * *

Permissible values include {@link Boolean#TRUE} and {@link Boolean#FALSE}. * @since 4.2 */ public static final String CREATED_BY_THE_TESTCONTEXT_FRAMEWORK = Conventions.getQualifiedAttributeName( - ServletTestExecutionListener.class, "createdByTheTestContextFramework"); + ServletTestExecutionListener.class, "createdByTheTestContextFramework"); /** * Attribute name for a {@link TestContext} attribute which indicates that that * the {@code ServletTestExecutionListener} should be activated. When not set to * {@code true}, activation occurs when the {@linkplain TestContext#getTestClass() * test class} is annotated with {@link WebAppConfiguration @WebAppConfiguration}. - * *

Permissible values include {@link Boolean#TRUE} and {@link Boolean#FALSE}. * @since 4.3 */ public static final String ACTIVATE_LISTENER = Conventions.getQualifiedAttributeName( - ServletTestExecutionListener.class, "activateListener"); + ServletTestExecutionListener.class, "activateListener"); private static final Log logger = LogFactory.getLog(ServletTestExecutionListener.class); @@ -181,8 +176,8 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener } private boolean isActivated(TestContext testContext) { - return (Boolean.TRUE.equals(testContext.getAttribute(ACTIVATE_LISTENER)) - || AnnotationUtils.findAnnotation(testContext.getTestClass(), WebAppConfiguration.class) != null); + return (Boolean.TRUE.equals(testContext.getAttribute(ACTIVATE_LISTENER)) || + AnnotatedElementUtils.hasAnnotation(testContext.getTestClass(), WebAppConfiguration.class)); } private boolean alreadyPopulatedRequestContextHolder(TestContext testContext) { @@ -199,14 +194,16 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener if (context instanceof WebApplicationContext) { WebApplicationContext wac = (WebApplicationContext) context; ServletContext servletContext = wac.getServletContext(); - Assert.state(servletContext instanceof MockServletContext, String.format( - "The WebApplicationContext for test context %s must be configured with a MockServletContext.", - testContext)); + if (!(servletContext instanceof MockServletContext)) { + throw new IllegalStateException(String.format( + "The WebApplicationContext for test context %s must be configured with a MockServletContext.", + testContext)); + } if (logger.isDebugEnabled()) { logger.debug(String.format( - "Setting up MockHttpServletRequest, MockHttpServletResponse, ServletWebRequest, and RequestContextHolder for test context %s.", - testContext)); + "Setting up MockHttpServletRequest, MockHttpServletResponse, ServletWebRequest, and RequestContextHolder for test context %s.", + testContext)); } MockServletContext mockServletContext = (MockServletContext) servletContext; diff --git a/spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java index 94741f5aec..f779450c48 100644 --- a/spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java +++ b/spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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,7 +16,7 @@ package org.springframework.test.context.web; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.test.context.ContextLoader; import org.springframework.test.context.MergedContextConfiguration; import org.springframework.test.context.TestContextBootstrapper; @@ -45,12 +45,12 @@ public class WebTestContextBootstrapper extends DefaultTestContextBootstrapper { */ @Override protected Class getDefaultContextLoaderClass(Class testClass) { - if (AnnotationUtils.findAnnotation(testClass, WebAppConfiguration.class) != null) { + if (AnnotatedElementUtils.findMergedAnnotation(testClass, WebAppConfiguration.class) != null) { return WebDelegatingSmartContextLoader.class; } - - // else... - return super.getDefaultContextLoaderClass(testClass); + else { + return super.getDefaultContextLoaderClass(testClass); + } } /** @@ -61,14 +61,14 @@ public class WebTestContextBootstrapper extends DefaultTestContextBootstrapper { */ @Override protected MergedContextConfiguration processMergedContextConfiguration(MergedContextConfiguration mergedConfig) { - WebAppConfiguration webAppConfiguration = AnnotationUtils.findAnnotation(mergedConfig.getTestClass(), - WebAppConfiguration.class); + WebAppConfiguration webAppConfiguration = + AnnotatedElementUtils.findMergedAnnotation(mergedConfig.getTestClass(), WebAppConfiguration.class); if (webAppConfiguration != null) { return new WebMergedContextConfiguration(mergedConfig, webAppConfiguration.value()); } - - // else... - return mergedConfig; + else { + return mergedConfig; + } } } diff --git a/spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsContextHierarchyTests.java b/spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsContextHierarchyTests.java index 3faab20416..810910c993 100644 --- a/spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsContextHierarchyTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsContextHierarchyTests.java @@ -22,6 +22,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import org.junit.Ignore; import org.junit.Test; import org.springframework.context.ApplicationContextInitializer; @@ -60,6 +61,7 @@ public class ContextLoaderUtilsContextHierarchyTests extends AbstractContextConf } @Test(expected = IllegalStateException.class) + @Ignore // an upfront findAnnotation check just for an assertion seems too expensive public void resolveContextHierarchyAttributesForSingleTestClassWithImplicitSingleLevelContextHierarchy() { resolveContextHierarchyAttributes(BareAnnotations.class); } diff --git a/spring-tx/src/main/java/org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapter.java b/spring-tx/src/main/java/org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapter.java index 6228d01cb9..23db045521 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapter.java +++ b/spring-tx/src/main/java/org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -54,7 +54,10 @@ class ApplicationListenerMethodTransactionalAdapter extends ApplicationListenerM public ApplicationListenerMethodTransactionalAdapter(String beanName, Class targetClass, Method method) { super(beanName, targetClass, method); - this.annotation = findAnnotation(method); + this.annotation = AnnotatedElementUtils.findMergedAnnotation(method, TransactionalEventListener.class); + if (this.annotation == null) { + throw new IllegalStateException("No TransactionalEventListener annotation found on '" + method + "'"); + } } @@ -81,14 +84,6 @@ class ApplicationListenerMethodTransactionalAdapter extends ApplicationListenerM return new TransactionSynchronizationEventAdapter(this, event, this.annotation.phase()); } - static TransactionalEventListener findAnnotation(Method method) { - TransactionalEventListener annotation = - AnnotatedElementUtils.findMergedAnnotation(method, TransactionalEventListener.class); - if (annotation == null) { - throw new IllegalStateException("No TransactionalEventListener annotation found on '" + method + "'"); - } - return annotation; - } private static class TransactionSynchronizationEventAdapter extends TransactionSynchronizationAdapter { diff --git a/spring-tx/src/test/java/org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapterTests.java b/spring-tx/src/test/java/org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapterTests.java index 901398f3d4..5ed470b904 100644 --- a/spring-tx/src/test/java/org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapterTests.java +++ b/spring-tx/src/test/java/org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -25,6 +25,7 @@ import org.junit.rules.ExpectedException; import org.springframework.context.PayloadApplicationEvent; import org.springframework.context.event.ApplicationListenerMethodAdapter; import org.springframework.core.ResolvableType; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.util.ReflectionUtils; import static org.junit.Assert.*; @@ -37,15 +38,6 @@ public class ApplicationListenerMethodTransactionalAdapterTests { @Rule public final ExpectedException thrown = ExpectedException.none(); - @Test - public void noAnnotation() { - Method m = ReflectionUtils.findMethod(SampleEvents.class, - "noAnnotation", String.class); - - thrown.expect(IllegalStateException.class); - thrown.expectMessage("noAnnotation"); - ApplicationListenerMethodTransactionalAdapter.findAnnotation(m); - } @Test public void defaultPhase() { @@ -78,7 +70,8 @@ public class ApplicationListenerMethodTransactionalAdapterTests { private void assertPhase(Method method, TransactionPhase expected) { assertNotNull("Method must not be null", method); - TransactionalEventListener annotation = ApplicationListenerMethodTransactionalAdapter.findAnnotation(method); + TransactionalEventListener annotation = + AnnotatedElementUtils.findMergedAnnotation(method, TransactionalEventListener.class); assertEquals("Wrong phase for '" + method + "'", expected, annotation.phase()); } @@ -96,10 +89,8 @@ public class ApplicationListenerMethodTransactionalAdapterTests { return ResolvableType.forClassWithGenerics(PayloadApplicationEvent.class, payloadType); } - static class SampleEvents { - public void noAnnotation(String data) { - } + static class SampleEvents { @TransactionalEventListener public void defaultPhase(String data) { @@ -117,7 +108,6 @@ public class ApplicationListenerMethodTransactionalAdapterTests { @TransactionalEventListener(String.class) public void valueSet() { } - } } diff --git a/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java b/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java index 3ddde8915a..74f99557d8 100644 --- a/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java +++ b/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java @@ -226,7 +226,7 @@ public class HandlerMethod { * if no annotation can be found on the given method itself. *

Also supports merged composed annotations with attribute * overrides as of Spring Framework 4.2.2. - * @param annotationType the type of annotation to introspect the method for. + * @param annotationType the type of annotation to introspect the method for * @return the annotation, or {@code null} if none found * @see AnnotatedElementUtils#findMergedAnnotation */ @@ -234,6 +234,16 @@ public class HandlerMethod { return AnnotatedElementUtils.findMergedAnnotation(this.method, annotationType); } + /** + * Return whether the parameter is declared with the given annotation type. + * @param annotationType the annotation type to look for + * @since 4.3 + * @see AnnotatedElementUtils#hasAnnotation + */ + public boolean hasMethodAnnotation(Class annotationType) { + return AnnotatedElementUtils.hasAnnotation(this.method, annotationType); + } + /** * If the provided instance contains a bean name rather than an object instance, * the bean name is resolved before a {@link HandlerMethod} is created and returned. @@ -247,6 +257,15 @@ public class HandlerMethod { return new HandlerMethod(this, handler); } + /** + * Return a short representation of this handler method for log message purposes. + * @since 4.3 + */ + public String getShortLogMessage() { + int args = this.method.getParameterTypes().length; + return getBeanType().getName() + "#" + this.method.getName() + "[" + args + " args]"; + } + @Override public boolean equals(Object other) { @@ -294,6 +313,11 @@ public class HandlerMethod { return HandlerMethod.this.getMethodAnnotation(annotationType); } + @Override + public boolean hasMethodAnnotation(Class annotationType) { + return HandlerMethod.this.hasMethodAnnotation(annotationType); + } + @Override public HandlerMethodParameter clone() { return new HandlerMethodParameter(this); diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java index d7b9e43f6f..81030d3bf2 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java @@ -102,8 +102,8 @@ public class ModelAttributeMethodProcessor createAttribute(name, parameter, binderFactory, webRequest)); if (!mavContainer.isBindingDisabled(name)) { - ModelAttribute annotation = parameter.getParameterAnnotation(ModelAttribute.class); - if (annotation != null && !annotation.binding()) { + ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class); + if (ann != null && !ann.binding()) { mavContainer.setBindingDisabled(name); } } @@ -192,8 +192,8 @@ public class ModelAttributeMethodProcessor */ @Override public boolean supportsReturnType(MethodParameter returnType) { - return (returnType.getMethodAnnotation(ModelAttribute.class) != null || - this.annotationNotRequired && !BeanUtils.isSimpleProperty(returnType.getParameterType())); + return (returnType.hasMethodAnnotation(ModelAttribute.class) || + (this.annotationNotRequired && !BeanUtils.isSimpleProperty(returnType.getParameterType()))); } /** diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/SessionAttributesHandler.java b/spring-web/src/main/java/org/springframework/web/method/annotation/SessionAttributesHandler.java index 3ff2b31540..8cc07e465d 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/SessionAttributesHandler.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/SessionAttributesHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -24,7 +24,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.SessionAttributes; import org.springframework.web.bind.support.SessionAttributeStore; @@ -65,10 +65,11 @@ public class SessionAttributesHandler { * @param sessionAttributeStore used for session access */ public SessionAttributesHandler(Class handlerType, SessionAttributeStore sessionAttributeStore) { - Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null."); + Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null"); this.sessionAttributeStore = sessionAttributeStore; - SessionAttributes annotation = AnnotationUtils.findAnnotation(handlerType, SessionAttributes.class); + SessionAttributes annotation = + AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class); if (annotation != null) { this.attributeNames.addAll(Arrays.asList(annotation.names())); this.attributeTypes.addAll(Arrays.asList(annotation.types())); @@ -84,7 +85,7 @@ public class SessionAttributesHandler { * session attributes through an {@link SessionAttributes} annotation. */ public boolean hasSessionAttributes() { - return ((this.attributeNames.size() > 0) || (this.attributeTypes.size() > 0)); + return (this.attributeNames.size() > 0 || this.attributeTypes.size() > 0); } /** diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/JsonViewResponseBodyAdvice.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/JsonViewResponseBodyAdvice.java index 3ed6bb8f9c..2cb56bc2d3 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/JsonViewResponseBodyAdvice.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/JsonViewResponseBodyAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -47,7 +47,7 @@ public class JsonViewResponseBodyAdvice extends AbstractMappingJacksonResponseBo @Override public boolean supports(MethodParameter returnType, Class> converterType) { - return (super.supports(returnType, converterType) && returnType.getMethodAnnotation(JsonView.class) != null); + return super.supports(returnType, converterType) && returnType.hasMethodAnnotation(JsonView.class); } @Override diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index d74a1bbc35..dc03d03747 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -23,7 +23,6 @@ import java.util.List; import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.stereotype.Controller; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -168,8 +167,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi */ @Override protected boolean isHandler(Class beanType) { - return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) || - (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null)); + return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || + AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); } /** diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java index 5780b2d11c..9e82398f1c 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java @@ -23,7 +23,7 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.core.Conventions; import org.springframework.core.MethodParameter; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpRange; @@ -114,8 +114,8 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter @Override public boolean supportsReturnType(MethodParameter returnType) { - return (AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResponseBody.class) != null || - returnType.getMethodAnnotation(ResponseBody.class) != null); + return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || + returnType.hasMethodAnnotation(ResponseBody.class)); } /** @@ -173,14 +173,15 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter ServletServerHttpRequest inputMessage = createInputMessage(webRequest); ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); - if(inputMessage.getHeaders().containsKey(HttpHeaders.RANGE) && + if (inputMessage.getHeaders().containsKey(HttpHeaders.RANGE) && Resource.class.isAssignableFrom(returnValue.getClass())) { try { List httpRanges = inputMessage.getHeaders().getRange(); Resource bodyResource = (Resource) returnValue; returnValue = new HttpRangeResource(httpRanges, bodyResource); outputMessage.setStatusCode(HttpStatus.PARTIAL_CONTENT); - } catch (IllegalArgumentException exc) { + } + catch (IllegalArgumentException ex) { outputMessage.setStatusCode(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE); outputMessage.flush(); return; diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java index 3eb84cbe6a..362e74961f 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java @@ -242,6 +242,14 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { public A getMethodAnnotation(Class annotationType) { return ServletInvocableHandlerMethod.this.getMethodAnnotation(annotationType); } + + /** + * Bridge to controller method-level annotations. + */ + @Override + public boolean hasMethodAnnotation(Class annotationType) { + return ServletInvocableHandlerMethod.this.hasMethodAnnotation(annotationType); + } }