diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebEndpointDiscoverer.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebEndpointDiscoverer.java index e669c70501..a41ce4ffd9 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebEndpointDiscoverer.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebEndpointDiscoverer.java @@ -29,8 +29,7 @@ import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExten import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer; import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.context.ApplicationContext; -import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.annotation.MergedAnnotations; /** * {@link WebEndpointDiscoverer} for Cloud Foundry that uses Cloud Foundry specific @@ -70,16 +69,14 @@ public class CloudFoundryWebEndpointDiscoverer extends WebEndpointDiscoverer { } private boolean isHealthEndpointExtension(Object extensionBean) { - AnnotationAttributes attributes = AnnotatedElementUtils - .getMergedAnnotationAttributes(extensionBean.getClass(), - EndpointWebExtension.class); - Class endpoint = (attributes != null) ? attributes.getClass("endpoint") : null; - return (endpoint != null && HealthEndpoint.class.isAssignableFrom(endpoint)); + return MergedAnnotations.from(extensionBean.getClass()) + .get(EndpointWebExtension.class).getValue("endpoint", Class.class) + .map(HealthEndpoint.class::isAssignableFrom).orElse(false); } private boolean isCloudFoundryHealthEndpointExtension(Object extensionBean) { - return AnnotatedElementUtils.hasAnnotation(extensionBean.getClass(), - EndpointCloudFoundryExtension.class); + return MergedAnnotations.from(extensionBean.getClass()) + .isPresent(EndpointCloudFoundryExtension.class); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/AbstractEndpointCondition.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/AbstractEndpointCondition.java index 7f425c5138..b39f84246e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/AbstractEndpointCondition.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/AbstractEndpointCondition.java @@ -23,8 +23,10 @@ import org.springframework.boot.actuate.endpoint.annotation.EndpointExtension; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ConditionContext; -import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.MethodMetadata; import org.springframework.util.Assert; @@ -71,17 +73,18 @@ abstract class AbstractEndpointCondition extends SpringBootCondition { } AnnotationAttributes getEndpointAttributes(Class type) { - AnnotationAttributes attributes = AnnotatedElementUtils - .findMergedAnnotationAttributes(type, Endpoint.class, true, true); - if (attributes != null) { - return attributes; + MergedAnnotations annotations = MergedAnnotations.from(type, + SearchStrategy.EXHAUSTIVE); + MergedAnnotation endpoint = annotations.get(Endpoint.class); + if (endpoint.isPresent()) { + return endpoint.asAnnotationAttributes(); } - attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(type, - EndpointExtension.class, false, true); - Assert.state(attributes != null, + MergedAnnotation extension = annotations + .get(EndpointExtension.class); + Assert.state(extension.isPresent(), "No endpoint is specified and the return type of the @Bean method is " + "neither an @Endpoint, nor an @EndpointExtension"); - return getEndpointAttributes(attributes.getClass("endpoint")); + return getEndpointAttributes(extension.getClass("endpoint")); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java index add4beaf76..010f54a31a 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java @@ -37,7 +37,8 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.security.reactive.ApplicationContextServerWebExchangeMatcher; import org.springframework.context.ApplicationContext; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; @@ -225,11 +226,11 @@ public final class EndpointRequest { } private EndpointId getEndpointId(Class source) { - Endpoint annotation = AnnotatedElementUtils.getMergedAnnotation(source, - Endpoint.class); - Assert.state(annotation != null, + MergedAnnotation annotation = MergedAnnotations.from(source) + .get(Endpoint.class); + Assert.state(annotation.isPresent(), () -> "Class " + source + " is not annotated with @Endpoint"); - return EndpointId.of(annotation.id()); + return EndpointId.of(annotation.getString("id")); } private List getDelegateMatchers(Set paths) { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java index 1b42a91a2a..5cf22323b1 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java @@ -37,7 +37,8 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.autoconfigure.security.servlet.RequestMatcherProvider; import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; @@ -275,11 +276,11 @@ public final class EndpointRequest { } private EndpointId getEndpointId(Class source) { - Endpoint annotation = AnnotatedElementUtils.getMergedAnnotation(source, - Endpoint.class); - Assert.state(annotation != null, + MergedAnnotation annotation = MergedAnnotations.from(source) + .get(Endpoint.class); + Assert.state(annotation.isPresent(), () -> "Class " + source + " is not annotated with @Endpoint"); - return EndpointId.of(annotation.id()); + return EndpointId.of(annotation.getString("id")); } private List getDelegateMatchers( diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/DiscoveredOperationsFactory.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/DiscoveredOperationsFactory.java index b9341bd506..98049f92ee 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/DiscoveredOperationsFactory.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/DiscoveredOperationsFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -34,8 +34,8 @@ import org.springframework.boot.actuate.endpoint.invoke.reflect.OperationMethod; import org.springframework.boot.actuate.endpoint.invoke.reflect.ReflectiveOperationInvoker; import org.springframework.core.MethodIntrospector; import org.springframework.core.MethodIntrospector.MetadataLookup; -import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; /** * Factory to create an {@link Operation} for annotated methods on an @@ -84,13 +84,13 @@ abstract class DiscoveredOperationsFactory { private O createOperation(EndpointId endpointId, Object target, Method method, OperationType operationType, Class annotationType) { - AnnotationAttributes annotationAttributes = AnnotatedElementUtils - .getMergedAnnotationAttributes(method, annotationType); - if (annotationAttributes == null) { + MergedAnnotation annotation = MergedAnnotations.from(method) + .get(annotationType); + if (!annotation.isPresent()) { return null; } DiscoveredOperationMethod operationMethod = new DiscoveredOperationMethod(method, - operationType, annotationAttributes); + operationType, annotation.asAnnotationAttributes()); OperationInvoker invoker = new ReflectiveOperationInvoker(target, operationMethod, this.parameterValueMapper); invoker = applyAdvisors(endpointId, operationMethod, invoker); diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/EndpointDiscoverer.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/EndpointDiscoverer.java index c05412d707..8f6bc5eeeb 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/EndpointDiscoverer.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/EndpointDiscoverer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -44,8 +44,9 @@ import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper; import org.springframework.boot.util.LambdaSafe; import org.springframework.context.ApplicationContext; import org.springframework.core.ResolvableType; -import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.LinkedMultiValueMap; @@ -429,17 +430,16 @@ public abstract class EndpointDiscoverer, O exten private Set extensions = new LinkedHashSet<>(); EndpointBean(String beanName, Object bean) { - AnnotationAttributes attributes = AnnotatedElementUtils - .findMergedAnnotationAttributes(bean.getClass(), Endpoint.class, true, - true); - String id = attributes.getString("id"); + MergedAnnotation annotation = MergedAnnotations + .from(bean.getClass(), SearchStrategy.EXHAUSTIVE).get(Endpoint.class); + String id = annotation.getString("id"); Assert.state(StringUtils.hasText(id), () -> "No @Endpoint id attribute specified for " + bean.getClass().getName()); this.beanName = beanName; this.bean = bean; this.id = EndpointId.of(id); - this.enabledByDefault = (Boolean) attributes.get("enableByDefault"); + this.enabledByDefault = annotation.getBoolean("enableByDefault"); this.filter = getFilter(this.bean.getClass()); } @@ -452,12 +452,8 @@ public abstract class EndpointDiscoverer, O exten } private Class getFilter(Class type) { - AnnotationAttributes attributes = AnnotatedElementUtils - .getMergedAnnotationAttributes(type, FilteredEndpoint.class); - if (attributes == null) { - return null; - } - return attributes.getClass("value"); + return MergedAnnotations.from(type).get(FilteredEndpoint.class) + .getValue(MergedAnnotation.VALUE, Class.class).orElse(null); } public String getBeanName() { @@ -498,17 +494,15 @@ public abstract class EndpointDiscoverer, O exten ExtensionBean(String beanName, Object bean) { this.bean = bean; this.beanName = beanName; - AnnotationAttributes attributes = AnnotatedElementUtils - .getMergedAnnotationAttributes(bean.getClass(), - EndpointExtension.class); - Class endpointType = attributes.getClass("endpoint"); - AnnotationAttributes endpointAttributes = AnnotatedElementUtils - .findMergedAnnotationAttributes(endpointType, Endpoint.class, true, - true); - Assert.state(endpointAttributes != null, () -> "Extension " + MergedAnnotation extensionAnnotation = MergedAnnotations + .from(bean.getClass()).get(EndpointExtension.class); + Class endpointType = extensionAnnotation.getClass("endpoint"); + MergedAnnotation endpointAnnotation = MergedAnnotations + .from(endpointType, SearchStrategy.EXHAUSTIVE).get(Endpoint.class); + Assert.state(endpointAnnotation.isPresent(), () -> "Extension " + endpointType.getName() + " does not specify an endpoint"); - this.endpointId = EndpointId.of(endpointAttributes.getString("id")); - this.filter = attributes.getClass("filter"); + this.endpointId = EndpointId.of(endpointAnnotation.getString("id")); + this.filter = extensionAnnotation.getClass("filter"); } public String getBeanName() { diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/ControllerEndpointDiscoverer.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/ControllerEndpointDiscoverer.java index 48b24a5e8c..ec548de1af 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/ControllerEndpointDiscoverer.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/ControllerEndpointDiscoverer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -29,7 +29,7 @@ import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker; import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper; import org.springframework.boot.actuate.endpoint.web.PathMapper; import org.springframework.context.ApplicationContext; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; import org.springframework.util.ClassUtils; /** @@ -62,8 +62,9 @@ public class ControllerEndpointDiscoverer @Override protected boolean isEndpointExposed(Object endpointBean) { Class type = ClassUtils.getUserClass(endpointBean.getClass()); - return AnnotatedElementUtils.isAnnotated(type, ControllerEndpoint.class) - || AnnotatedElementUtils.isAnnotated(type, RestControllerEndpoint.class); + MergedAnnotations annotations = MergedAnnotations.from(type); + return annotations.isPresent(ControllerEndpoint.class) + || annotations.isPresent(RestControllerEndpoint.class); } @Override diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/ServletEndpointDiscoverer.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/ServletEndpointDiscoverer.java index b566c7975e..90c0209028 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/ServletEndpointDiscoverer.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/ServletEndpointDiscoverer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -30,7 +30,7 @@ import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper; import org.springframework.boot.actuate.endpoint.web.ExposableServletEndpoint; import org.springframework.boot.actuate.endpoint.web.PathMapper; import org.springframework.context.ApplicationContext; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; import org.springframework.util.ClassUtils; /** @@ -62,7 +62,7 @@ public class ServletEndpointDiscoverer @Override protected boolean isEndpointExposed(Object endpointBean) { Class type = ClassUtils.getUserClass(endpointBean.getClass()); - return AnnotatedElementUtils.isAnnotated(type, ServletEndpoint.class); + return MergedAnnotations.from(type).isPresent(ServletEndpoint.class); } @Override diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/LongTaskTimingHandlerInterceptor.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/LongTaskTimingHandlerInterceptor.java index 088415feba..1f8e9894f4 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/LongTaskTimingHandlerInterceptor.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/LongTaskTimingHandlerInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -31,7 +31,8 @@ import io.micrometer.core.instrument.LongTaskTimer; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotationCollectors; +import org.springframework.core.annotation.MergedAnnotations; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; @@ -116,7 +117,8 @@ public class LongTaskTimingHandlerInterceptor implements HandlerInterceptor { } private Set findTimedAnnotations(AnnotatedElement element) { - return AnnotationUtils.getDeclaredRepeatableAnnotations(element, Timed.class); + return MergedAnnotations.from(element).stream(Timed.class) + .collect(MergedAnnotationCollectors.toAnnotationSet()); } private void stopLongTaskTimers(LongTaskTimingContext timingContext) { diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilter.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilter.java index 39bb72e352..088fbae655 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilter.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilter.java @@ -34,7 +34,8 @@ import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.Timer.Builder; import io.micrometer.core.instrument.Timer.Sample; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotationCollectors; +import org.springframework.core.annotation.MergedAnnotations; import org.springframework.http.HttpStatus; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.method.HandlerMethod; @@ -141,7 +142,8 @@ public class WebMvcMetricsFilter extends OncePerRequestFilter { } private Set findTimedAnnotations(AnnotatedElement element) { - return AnnotationUtils.getDeclaredRepeatableAnnotations(element, Timed.class); + return MergedAnnotations.from(element).stream(Timed.class) + .collect(MergedAnnotationCollectors.toAnnotationSet()); } private void record(TimingContext timingContext, HttpServletResponse response, diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/AbstractWebEndpointRunner.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/AbstractWebEndpointRunner.java index e3aa4b1154..abef8fd38e 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/AbstractWebEndpointRunner.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/AbstractWebEndpointRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2019 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. @@ -31,7 +31,8 @@ import org.junit.runners.model.Statement; import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; import org.springframework.test.web.reactive.server.WebTestClient; @@ -181,7 +182,8 @@ abstract class AbstractWebEndpointRunner extends BlockJUnit4ClassRunner { } private boolean isConfiguration(Class candidate) { - return AnnotationUtils.findAnnotation(candidate, Configuration.class) != null; + return MergedAnnotations.from(candidate, SearchStrategy.EXHAUSTIVE) + .isPresent(Configuration.class); } private WebTestClient createWebTestClient() { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java index 366f5819e0..e86fd92713 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -42,7 +42,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.ResolvableType; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.StandardMethodMetadata; import org.springframework.util.Assert; @@ -117,8 +117,10 @@ final class BeanTypeRegistry implements SmartInitializingSingleton { public Set getNamesForAnnotation(Class annotation) { updateTypesIfNecessary(); return this.beanTypes.entrySet().stream() - .filter((entry) -> entry.getValue() != null && AnnotationUtils - .findAnnotation(entry.getValue().resolve(), annotation) != null) + .filter((entry) -> entry.getValue() != null && MergedAnnotations + .from(entry.getValue().resolve(), + MergedAnnotations.SearchStrategy.EXHAUSTIVE) + .isPresent(annotation)) .map(Map.Entry::getKey) .collect(Collectors.toCollection(LinkedHashSet::new)); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java index 4915a3709d..7411bebb4f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -44,7 +44,7 @@ import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConfigurationCondition; import org.springframework.core.Ordered; import org.springframework.core.ResolvableType; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.Order; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.MethodMetadata; @@ -531,8 +531,9 @@ class OnBeanCondition extends FilteringSpringBootCondition } private boolean isBeanMethod(Method method) { - return method != null - && AnnotatedElementUtils.hasAnnotation(method, Bean.class); + return method != null && MergedAnnotations + .from(method, MergedAnnotations.SearchStrategy.EXHAUSTIVE) + .isPresent(Bean.class); } public TypeExtractor getTypeExtractor(ClassLoader classLoader) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultJerseyApplicationPath.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultJerseyApplicationPath.java index 8ac8d36253..a2cbc6dc83 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultJerseyApplicationPath.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultJerseyApplicationPath.java @@ -20,7 +20,9 @@ import javax.ws.rs.ApplicationPath; import org.glassfish.jersey.server.ResourceConfig; import org.springframework.boot.autoconfigure.jersey.JerseyProperties; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.util.StringUtils; /** @@ -49,16 +51,11 @@ public class DefaultJerseyApplicationPath implements JerseyApplicationPath { if (StringUtils.hasLength(this.applicationPath)) { return this.applicationPath; } - return findApplicationPath(AnnotationUtils.findAnnotation( - this.config.getApplication().getClass(), ApplicationPath.class)); - } - - private static String findApplicationPath(ApplicationPath annotation) { // Jersey doesn't like to be the default servlet, so map to /* as a fallback - if (annotation == null) { - return "/*"; - } - return annotation.value(); + return MergedAnnotations + .from(this.config.getApplication().getClass(), SearchStrategy.EXHAUSTIVE) + .get(ApplicationPath.class).getValue(MergedAnnotation.VALUE, String.class) + .orElse("/*"); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/OverrideAutoConfigurationContextCustomizerFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/OverrideAutoConfigurationContextCustomizerFactory.java index ce8cbac8a4..1b16518b99 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/OverrideAutoConfigurationContextCustomizerFactory.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/OverrideAutoConfigurationContextCustomizerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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,7 +21,8 @@ import java.util.List; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.ContextConfigurationAttributes; import org.springframework.test.context.ContextCustomizer; import org.springframework.test.context.ContextCustomizerFactory; @@ -39,12 +40,10 @@ class OverrideAutoConfigurationContextCustomizerFactory @Override public ContextCustomizer createContextCustomizer(Class testClass, List configurationAttributes) { - OverrideAutoConfiguration annotation = AnnotatedElementUtils - .findMergedAnnotation(testClass, OverrideAutoConfiguration.class); - if (annotation != null && !annotation.enabled()) { - return new DisableAutoConfigurationContextCustomizer(); - } - return null; + boolean enabled = MergedAnnotations.from(testClass, SearchStrategy.EXHAUSTIVE) + .get(OverrideAutoConfiguration.class).getValue("enabled", Boolean.class) + .orElse(true); + return !enabled ? new DisableAutoConfigurationContextCustomizer() : null; } /** diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/jdbc/DataJdbcTestContextBootstrapper.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/jdbc/DataJdbcTestContextBootstrapper.java index ebf4704bee..daca83fbf8 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/jdbc/DataJdbcTestContextBootstrapper.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/jdbc/DataJdbcTestContextBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -17,7 +17,8 @@ package org.springframework.boot.test.autoconfigure.data.jdbc; import org.springframework.boot.test.context.SpringBootTestContextBootstrapper; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.TestContextBootstrapper; /** @@ -29,9 +30,9 @@ class DataJdbcTestContextBootstrapper extends SpringBootTestContextBootstrapper @Override protected String[] getProperties(Class testClass) { - DataJdbcTest annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - DataJdbcTest.class); - return (annotation != null) ? annotation.properties() : null; + return MergedAnnotations.from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) + .get(DataJdbcTest.class).getValue("properties", String[].class) + .orElse(null); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/jdbc/DataJdbcTypeExcludeFilter.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/jdbc/DataJdbcTypeExcludeFilter.java index 4b7a6bb971..b4b9dea056 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/jdbc/DataJdbcTypeExcludeFilter.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/jdbc/DataJdbcTypeExcludeFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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,58 +16,19 @@ package org.springframework.boot.test.autoconfigure.data.jdbc; -import java.util.Collections; -import java.util.Set; - import org.springframework.boot.context.TypeExcludeFilter; -import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.boot.test.autoconfigure.filter.StandardAnnotationCustomizableTypeExcludeFilter; /** * {@link TypeExcludeFilter} for {@link DataJdbcTest @DataJdbcTest}. * * @author Andy Wilkinson */ -class DataJdbcTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { - - private final DataJdbcTest annotation; +class DataJdbcTypeExcludeFilter + extends StandardAnnotationCustomizableTypeExcludeFilter { DataJdbcTypeExcludeFilter(Class testClass) { - this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - DataJdbcTest.class); - } - - @Override - protected boolean hasAnnotation() { - return this.annotation != null; - } - - @Override - protected Filter[] getFilters(FilterType type) { - switch (type) { - case INCLUDE: - return this.annotation.includeFilters(); - case EXCLUDE: - return this.annotation.excludeFilters(); - default: - throw new IllegalStateException("Unsupported type " + type); - } - } - - @Override - protected boolean isUseDefaultFilters() { - return this.annotation.useDefaultFilters(); - } - - @Override - protected Set> getDefaultIncludes() { - return Collections.emptySet(); - } - - @Override - protected Set> getComponentIncludes() { - return Collections.emptySet(); + super(testClass); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/ldap/DataLdapTestContextBootstrapper.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/ldap/DataLdapTestContextBootstrapper.java index d57b61a9bd..88bd87608a 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/ldap/DataLdapTestContextBootstrapper.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/ldap/DataLdapTestContextBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -17,7 +17,8 @@ package org.springframework.boot.test.autoconfigure.data.ldap; import org.springframework.boot.test.context.SpringBootTestContextBootstrapper; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.TestContextBootstrapper; /** @@ -29,9 +30,9 @@ class DataLdapTestContextBootstrapper extends SpringBootTestContextBootstrapper @Override protected String[] getProperties(Class testClass) { - DataLdapTest annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - DataLdapTest.class); - return (annotation != null) ? annotation.properties() : null; + return MergedAnnotations.from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) + .get(DataLdapTest.class).getValue("properties", String[].class) + .orElse(null); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/ldap/DataLdapTypeExcludeFilter.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/ldap/DataLdapTypeExcludeFilter.java index 7f991b3eec..b0a0bbc771 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/ldap/DataLdapTypeExcludeFilter.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/ldap/DataLdapTypeExcludeFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2019 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,58 +16,19 @@ package org.springframework.boot.test.autoconfigure.data.ldap; -import java.util.Collections; -import java.util.Set; - import org.springframework.boot.context.TypeExcludeFilter; -import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.boot.test.autoconfigure.filter.StandardAnnotationCustomizableTypeExcludeFilter; /** * {@link TypeExcludeFilter} for {@link DataLdapTest @DataLdapTest}. * * @author Eddú Meléndez */ -class DataLdapTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { - - private final DataLdapTest annotation; +class DataLdapTypeExcludeFilter + extends StandardAnnotationCustomizableTypeExcludeFilter { DataLdapTypeExcludeFilter(Class testClass) { - this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - DataLdapTest.class); - } - - @Override - protected boolean hasAnnotation() { - return this.annotation != null; - } - - @Override - protected Filter[] getFilters(FilterType type) { - switch (type) { - case INCLUDE: - return this.annotation.includeFilters(); - case EXCLUDE: - return this.annotation.excludeFilters(); - default: - throw new IllegalStateException("Unsupported type " + type); - } - } - - @Override - protected boolean isUseDefaultFilters() { - return this.annotation.useDefaultFilters(); - } - - @Override - protected Set> getDefaultIncludes() { - return Collections.emptySet(); - } - - @Override - protected Set> getComponentIncludes() { - return Collections.emptySet(); + super(testClass); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestContextBootstrapper.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestContextBootstrapper.java index ec1e441f5e..e4fe8c8a59 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestContextBootstrapper.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestContextBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -17,7 +17,8 @@ package org.springframework.boot.test.autoconfigure.data.mongo; import org.springframework.boot.test.context.SpringBootTestContextBootstrapper; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.TestContextBootstrapper; /** @@ -29,9 +30,9 @@ class DataMongoTestContextBootstrapper extends SpringBootTestContextBootstrapper @Override protected String[] getProperties(Class testClass) { - DataMongoTest annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - DataMongoTest.class); - return (annotation != null) ? annotation.properties() : null; + return MergedAnnotations.from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) + .get(DataMongoTest.class).getValue("properties", String[].class) + .orElse(null); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTypeExcludeFilter.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTypeExcludeFilter.java index 41b626b88b..e3c36a06b7 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTypeExcludeFilter.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTypeExcludeFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2019 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,58 +16,19 @@ package org.springframework.boot.test.autoconfigure.data.mongo; -import java.util.Collections; -import java.util.Set; - import org.springframework.boot.context.TypeExcludeFilter; -import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.boot.test.autoconfigure.filter.StandardAnnotationCustomizableTypeExcludeFilter; /** * {@link TypeExcludeFilter} for {@link DataMongoTest @DataMongoTest}. * * @author Michael Simons */ -class DataMongoTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { - - private final DataMongoTest annotation; +class DataMongoTypeExcludeFilter + extends StandardAnnotationCustomizableTypeExcludeFilter { DataMongoTypeExcludeFilter(Class testClass) { - this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - DataMongoTest.class); - } - - @Override - protected boolean hasAnnotation() { - return this.annotation != null; - } - - @Override - protected Filter[] getFilters(FilterType type) { - switch (type) { - case INCLUDE: - return this.annotation.includeFilters(); - case EXCLUDE: - return this.annotation.excludeFilters(); - default: - throw new IllegalStateException("Unsupported type " + type); - } - } - - @Override - protected boolean isUseDefaultFilters() { - return this.annotation.useDefaultFilters(); - } - - @Override - protected Set> getDefaultIncludes() { - return Collections.emptySet(); - } - - @Override - protected Set> getComponentIncludes() { - return Collections.emptySet(); + super(testClass); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestContextBootstrapper.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestContextBootstrapper.java index 5b9914f514..8af130d51e 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestContextBootstrapper.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestContextBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -17,7 +17,8 @@ package org.springframework.boot.test.autoconfigure.data.neo4j; import org.springframework.boot.test.context.SpringBootTestContextBootstrapper; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.TestContextBootstrapper; /** @@ -29,9 +30,9 @@ class DataNeo4jTestContextBootstrapper extends SpringBootTestContextBootstrapper @Override protected String[] getProperties(Class testClass) { - DataNeo4jTest annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - DataNeo4jTest.class); - return (annotation != null) ? annotation.properties() : null; + return MergedAnnotations.from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) + .get(DataNeo4jTest.class).getValue("properties", String[].class) + .orElse(null); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTypeExcludeFilter.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTypeExcludeFilter.java index ff1b10633e..ca56bb7d12 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTypeExcludeFilter.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTypeExcludeFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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,58 +16,19 @@ package org.springframework.boot.test.autoconfigure.data.neo4j; -import java.util.Collections; -import java.util.Set; - import org.springframework.boot.context.TypeExcludeFilter; -import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.boot.test.autoconfigure.filter.StandardAnnotationCustomizableTypeExcludeFilter; /** * {@link TypeExcludeFilter} for {@link DataNeo4jTest @DataNeo4jTest}. * * @author Eddú Meléndez */ -class DataNeo4jTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { - - private final DataNeo4jTest annotation; +class DataNeo4jTypeExcludeFilter + extends StandardAnnotationCustomizableTypeExcludeFilter { DataNeo4jTypeExcludeFilter(Class testClass) { - this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - DataNeo4jTest.class); - } - - @Override - protected boolean hasAnnotation() { - return this.annotation != null; - } - - @Override - protected Filter[] getFilters(FilterType type) { - switch (type) { - case INCLUDE: - return this.annotation.includeFilters(); - case EXCLUDE: - return this.annotation.excludeFilters(); - default: - throw new IllegalStateException("Unsupported type " + type); - } - } - - @Override - protected boolean isUseDefaultFilters() { - return this.annotation.useDefaultFilters(); - } - - @Override - protected Set> getDefaultIncludes() { - return Collections.emptySet(); - } - - @Override - protected Set> getComponentIncludes() { - return Collections.emptySet(); + super(testClass); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestContextBootstrapper.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestContextBootstrapper.java index 0fabc301be..69e82a5da6 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestContextBootstrapper.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestContextBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -17,7 +17,8 @@ package org.springframework.boot.test.autoconfigure.data.redis; import org.springframework.boot.test.context.SpringBootTestContextBootstrapper; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.TestContextBootstrapper; /** @@ -29,9 +30,9 @@ class DataRedisTestContextBootstrapper extends SpringBootTestContextBootstrapper @Override protected String[] getProperties(Class testClass) { - DataRedisTest annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - DataRedisTest.class); - return (annotation != null) ? annotation.properties() : null; + return MergedAnnotations.from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) + .get(DataRedisTest.class).getValue("properties", String[].class) + .orElse(null); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTypeExcludeFilter.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTypeExcludeFilter.java index d9bca9d28b..9308d54d27 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTypeExcludeFilter.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTypeExcludeFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2019 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,58 +16,19 @@ package org.springframework.boot.test.autoconfigure.data.redis; -import java.util.Collections; -import java.util.Set; - import org.springframework.boot.context.TypeExcludeFilter; -import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.boot.test.autoconfigure.filter.StandardAnnotationCustomizableTypeExcludeFilter; /** * {@link TypeExcludeFilter} for {@link DataRedisTest @DataRedisTest}. * * @author Jayaram Pradhan */ -class DataRedisTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { - - private final DataRedisTest annotation; +class DataRedisTypeExcludeFilter + extends StandardAnnotationCustomizableTypeExcludeFilter { DataRedisTypeExcludeFilter(Class testClass) { - this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - DataRedisTest.class); - } - - @Override - protected boolean hasAnnotation() { - return this.annotation != null; - } - - @Override - protected Filter[] getFilters(FilterType type) { - switch (type) { - case INCLUDE: - return this.annotation.includeFilters(); - case EXCLUDE: - return this.annotation.excludeFilters(); - default: - throw new IllegalStateException("Unsupported type " + type); - } - } - - @Override - protected boolean isUseDefaultFilters() { - return this.annotation.useDefaultFilters(); - } - - @Override - protected Set> getDefaultIncludes() { - return Collections.emptySet(); - } - - @Override - protected Set> getComponentIncludes() { - return Collections.emptySet(); + super(testClass); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/filter/StandardAnnotationCustomizableTypeExcludeFilter.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/filter/StandardAnnotationCustomizableTypeExcludeFilter.java new file mode 100644 index 0000000000..10833be222 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/filter/StandardAnnotationCustomizableTypeExcludeFilter.java @@ -0,0 +1,98 @@ +/* + * Copyright 2012-2019 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.test.autoconfigure.filter; + +import java.lang.annotation.Annotation; +import java.util.Collections; +import java.util.Set; + +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.core.ResolvableType; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; + +/** + * {@link AnnotationCustomizableTypeExcludeFilter} that can be used to any test annotation + * that uses the standard {code includeFilters}, {code excludeFilters} and + * {@code useDefaultFilters} attributes. + * + * @param the annotation type + * @author Phillip Webb + * @since 2.2.0 + */ +public abstract class StandardAnnotationCustomizableTypeExcludeFilter + extends AnnotationCustomizableTypeExcludeFilter { + + private static final Filter[] NO_FILTERS = {}; + + private static final String[] FILTER_TYPE_ATTRIBUTES; + static { + FilterType[] filterValues = FilterType.values(); + FILTER_TYPE_ATTRIBUTES = new String[filterValues.length]; + for (int i = 0; i < filterValues.length; i++) { + FILTER_TYPE_ATTRIBUTES[i] = filterValues[i].name().toLowerCase() + "Filters"; + } + } + + private MergedAnnotation annotation; + + protected StandardAnnotationCustomizableTypeExcludeFilter(Class testClass) { + this.annotation = MergedAnnotations + .from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) + .get(getAnnotationType()); + } + + protected final MergedAnnotation getAnnotation() { + return this.annotation; + } + + @Override + protected boolean hasAnnotation() { + return this.annotation.isPresent(); + } + + @Override + protected Filter[] getFilters(FilterType type) { + return this.annotation + .getValue(FILTER_TYPE_ATTRIBUTES[type.ordinal()], Filter[].class) + .orElse(NO_FILTERS); + } + + @Override + protected boolean isUseDefaultFilters() { + return this.annotation.getValue("useDefaultFilters", Boolean.class).orElse(false); + } + + @Override + protected Set> getDefaultIncludes() { + return Collections.emptySet(); + } + + @Override + protected Set> getComponentIncludes() { + return Collections.emptySet(); + } + + @SuppressWarnings("unchecked") + protected Class getAnnotationType() { + ResolvableType type = ResolvableType.forClass( + StandardAnnotationCustomizableTypeExcludeFilter.class, getClass()); + return (Class) type.resolveGeneric(); + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/filter/TypeExcludeFiltersContextCustomizerFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/filter/TypeExcludeFiltersContextCustomizerFactory.java index 19ca184120..05f5f221cd 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/filter/TypeExcludeFiltersContextCustomizerFactory.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/filter/TypeExcludeFiltersContextCustomizerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2019 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,13 +19,15 @@ package org.springframework.boot.test.autoconfigure.filter; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.List; -import java.util.Set; import org.springframework.boot.context.TypeExcludeFilter; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.ContextConfigurationAttributes; import org.springframework.test.context.ContextCustomizer; import org.springframework.test.context.ContextCustomizerFactory; +import org.springframework.util.ObjectUtils; /** * {@link ContextCustomizerFactory} to support @@ -36,17 +38,26 @@ import org.springframework.test.context.ContextCustomizerFactory; */ class TypeExcludeFiltersContextCustomizerFactory implements ContextCustomizerFactory { + private static final Class[] NO_FILTERS = {}; + @Override public ContextCustomizer createContextCustomizer(Class testClass, List configurationAttributes) { - TypeExcludeFilters annotation = AnnotatedElementUtils - .findMergedAnnotation(testClass, TypeExcludeFilters.class); - if (annotation != null) { - Set> filterClasses = new LinkedHashSet<>( - Arrays.asList(annotation.value())); - return new TypeExcludeFiltersContextCustomizer(testClass, filterClasses); + Class[] filterClasses = MergedAnnotations + .from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) + .get(TypeExcludeFilters.class) + .getValue(MergedAnnotation.VALUE, Class[].class).orElse(NO_FILTERS); + if (ObjectUtils.isEmpty(filterClasses)) { + return null; } - return null; + return createContextCustomizer(testClass, filterClasses); + } + + @SuppressWarnings("unchecked") + private ContextCustomizer createContextCustomizer(Class testClass, + Class[] filterClasses) { + return new TypeExcludeFiltersContextCustomizer(testClass, new LinkedHashSet<>( + Arrays.asList((Class[]) filterClasses))); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/JdbcTestContextBootstrapper.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/JdbcTestContextBootstrapper.java index 84c5d47dd7..ddec9486bf 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/JdbcTestContextBootstrapper.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/JdbcTestContextBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -17,7 +17,8 @@ package org.springframework.boot.test.autoconfigure.jdbc; import org.springframework.boot.test.context.SpringBootTestContextBootstrapper; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.TestContextBootstrapper; /** @@ -29,9 +30,8 @@ class JdbcTestContextBootstrapper extends SpringBootTestContextBootstrapper { @Override protected String[] getProperties(Class testClass) { - JdbcTest annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - JdbcTest.class); - return (annotation != null) ? annotation.properties() : null; + return MergedAnnotations.from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) + .get(JdbcTest.class).getValue("properties", String[].class).orElse(null); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/JdbcTypeExcludeFilter.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/JdbcTypeExcludeFilter.java index 94571c3730..acaabc09da 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/JdbcTypeExcludeFilter.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/JdbcTypeExcludeFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2019 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,57 +16,19 @@ package org.springframework.boot.test.autoconfigure.jdbc; -import java.util.Collections; -import java.util.Set; - import org.springframework.boot.context.TypeExcludeFilter; -import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.boot.test.autoconfigure.filter.StandardAnnotationCustomizableTypeExcludeFilter; /** * {@link TypeExcludeFilter} for {@link JdbcTest @JdbcTest}. * * @author Stephane Nicoll */ -class JdbcTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { - - private final JdbcTest annotation; +class JdbcTypeExcludeFilter + extends StandardAnnotationCustomizableTypeExcludeFilter { JdbcTypeExcludeFilter(Class testClass) { - this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - JdbcTest.class); - } - - @Override - protected boolean hasAnnotation() { - return this.annotation != null; - } - - @Override - protected ComponentScan.Filter[] getFilters(FilterType type) { - switch (type) { - case INCLUDE: - return this.annotation.includeFilters(); - case EXCLUDE: - return this.annotation.excludeFilters(); - } - throw new IllegalStateException("Unsupported type " + type); - } - - @Override - protected boolean isUseDefaultFilters() { - return this.annotation.useDefaultFilters(); - } - - @Override - protected Set> getDefaultIncludes() { - return Collections.emptySet(); - } - - @Override - protected Set> getComponentIncludes() { - return Collections.emptySet(); + super(testClass); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jooq/JooqTestContextBootstrapper.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jooq/JooqTestContextBootstrapper.java index 0956a168f2..ae356cd33d 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jooq/JooqTestContextBootstrapper.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jooq/JooqTestContextBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -17,7 +17,8 @@ package org.springframework.boot.test.autoconfigure.jooq; import org.springframework.boot.test.context.SpringBootTestContextBootstrapper; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.TestContextBootstrapper; /** @@ -29,9 +30,8 @@ class JooqTestContextBootstrapper extends SpringBootTestContextBootstrapper { @Override protected String[] getProperties(Class testClass) { - JooqTest annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - JooqTest.class); - return (annotation != null) ? annotation.properties() : null; + return MergedAnnotations.from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) + .get(JooqTest.class).getValue("properties", String[].class).orElse(null); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jooq/JooqTypeExcludeFilter.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jooq/JooqTypeExcludeFilter.java index 47385f46ea..333f6ad4d0 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jooq/JooqTypeExcludeFilter.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jooq/JooqTypeExcludeFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2019 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,58 +16,19 @@ package org.springframework.boot.test.autoconfigure.jooq; -import java.util.Collections; -import java.util.Set; - import org.springframework.boot.context.TypeExcludeFilter; -import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.boot.test.autoconfigure.filter.StandardAnnotationCustomizableTypeExcludeFilter; /** * {@link TypeExcludeFilter} for {@link JooqTest @JooqTest}. * * @author Michael Simons */ -class JooqTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { - - private final JooqTest annotation; +class JooqTypeExcludeFilter + extends StandardAnnotationCustomizableTypeExcludeFilter { JooqTypeExcludeFilter(Class testClass) { - this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - JooqTest.class); - } - - @Override - protected boolean hasAnnotation() { - return this.annotation != null; - } - - @Override - protected Filter[] getFilters(FilterType type) { - switch (type) { - case INCLUDE: - return this.annotation.includeFilters(); - case EXCLUDE: - return this.annotation.excludeFilters(); - default: - throw new IllegalStateException("Unsupported type " + type); - } - } - - @Override - protected boolean isUseDefaultFilters() { - return this.annotation.useDefaultFilters(); - } - - @Override - protected Set> getDefaultIncludes() { - return Collections.emptySet(); - } - - @Override - protected Set> getComponentIncludes() { - return Collections.emptySet(); + super(testClass); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonExcludeFilter.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonExcludeFilter.java index 14530e5961..adf980e9a3 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonExcludeFilter.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonExcludeFilter.java @@ -22,9 +22,7 @@ import java.util.Set; import org.springframework.boot.context.TypeExcludeFilter; import org.springframework.boot.jackson.JsonComponent; -import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.boot.test.autoconfigure.filter.StandardAnnotationCustomizableTypeExcludeFilter; import org.springframework.util.ClassUtils; /** @@ -32,7 +30,8 @@ import org.springframework.util.ClassUtils; * * @author Phillip Webb */ -class JsonExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { +class JsonExcludeFilter + extends StandardAnnotationCustomizableTypeExcludeFilter { private static final String JACKSON_MODULE = "com.fasterxml.jackson.databind.Module"; @@ -49,32 +48,8 @@ class JsonExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { DEFAULT_INCLUDES = Collections.unmodifiableSet(includes); } - private final JsonTest annotation; - JsonExcludeFilter(Class testClass) { - this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - JsonTest.class); - } - - @Override - protected boolean hasAnnotation() { - return this.annotation != null; - } - - @Override - protected Filter[] getFilters(FilterType type) { - switch (type) { - case INCLUDE: - return this.annotation.includeFilters(); - case EXCLUDE: - return this.annotation.excludeFilters(); - } - throw new IllegalStateException("Unsupported type " + type); - } - - @Override - protected boolean isUseDefaultFilters() { - return this.annotation.useDefaultFilters(); + super(testClass); } @Override @@ -82,9 +57,4 @@ class JsonExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { return DEFAULT_INCLUDES; } - @Override - protected Set> getComponentIncludes() { - return Collections.emptySet(); - } - } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonTestContextBootstrapper.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonTestContextBootstrapper.java index f05ca60ade..b75b96306c 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonTestContextBootstrapper.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonTestContextBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -17,7 +17,8 @@ package org.springframework.boot.test.autoconfigure.json; import org.springframework.boot.test.context.SpringBootTestContextBootstrapper; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.TestContextBootstrapper; /** @@ -29,9 +30,8 @@ class JsonTestContextBootstrapper extends SpringBootTestContextBootstrapper { @Override protected String[] getProperties(Class testClass) { - JsonTest annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - JsonTest.class); - return (annotation != null) ? annotation.properties() : null; + return MergedAnnotations.from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) + .get(JsonTest.class).getValue("properties", String[].class).orElse(null); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/orm/jpa/DataJpaTestContextBootstrapper.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/orm/jpa/DataJpaTestContextBootstrapper.java index 845e346dfe..5f460507e9 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/orm/jpa/DataJpaTestContextBootstrapper.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/orm/jpa/DataJpaTestContextBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -17,7 +17,8 @@ package org.springframework.boot.test.autoconfigure.orm.jpa; import org.springframework.boot.test.context.SpringBootTestContextBootstrapper; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.TestContextBootstrapper; /** @@ -29,9 +30,9 @@ class DataJpaTestContextBootstrapper extends SpringBootTestContextBootstrapper { @Override protected String[] getProperties(Class testClass) { - DataJpaTest annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - DataJpaTest.class); - return (annotation != null) ? annotation.properties() : null; + return MergedAnnotations.from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) + .get(DataJpaTest.class).getValue("properties", String[].class) + .orElse(null); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/orm/jpa/DataJpaTypeExcludeFilter.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/orm/jpa/DataJpaTypeExcludeFilter.java index 0699509900..bd0085c676 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/orm/jpa/DataJpaTypeExcludeFilter.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/orm/jpa/DataJpaTypeExcludeFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2019 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,57 +16,19 @@ package org.springframework.boot.test.autoconfigure.orm.jpa; -import java.util.Collections; -import java.util.Set; - import org.springframework.boot.context.TypeExcludeFilter; -import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.boot.test.autoconfigure.filter.StandardAnnotationCustomizableTypeExcludeFilter; /** * {@link TypeExcludeFilter} for {@link DataJpaTest @DataJpaTest}. * * @author Phillip Webb */ -class DataJpaTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { - - private final DataJpaTest annotation; +class DataJpaTypeExcludeFilter + extends StandardAnnotationCustomizableTypeExcludeFilter { DataJpaTypeExcludeFilter(Class testClass) { - this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - DataJpaTest.class); - } - - @Override - protected boolean hasAnnotation() { - return this.annotation != null; - } - - @Override - protected Filter[] getFilters(FilterType type) { - switch (type) { - case INCLUDE: - return this.annotation.includeFilters(); - case EXCLUDE: - return this.annotation.excludeFilters(); - } - throw new IllegalStateException("Unsupported type " + type); - } - - @Override - protected boolean isUseDefaultFilters() { - return this.annotation.useDefaultFilters(); - } - - @Override - protected Set> getDefaultIncludes() { - return Collections.emptySet(); - } - - @Override - protected Set> getComponentIncludes() { - return Collections.emptySet(); + super(testClass); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/properties/AnnotationsPropertySource.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/properties/AnnotationsPropertySource.java index 06f389f290..3bb2ecf4ed 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/properties/AnnotationsPropertySource.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/properties/AnnotationsPropertySource.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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.boot.test.autoconfigure.properties; import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Set; +import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotationPredicates; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.util.ObjectUtils; -import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; /** @@ -61,93 +58,53 @@ public class AnnotationsPropertySource extends EnumerablePropertySource private Map getProperties(Class source) { Map properties = new LinkedHashMap<>(); - collectProperties(source, source, properties, new HashSet<>()); - return Collections.unmodifiableMap(properties); - } - - private void collectProperties(Class root, Class source, - Map properties, Set> seen) { - if (source != null && seen.add(source)) { - for (Annotation annotation : getMergedAnnotations(root, source)) { - if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) { - PropertyMapping typeMapping = annotation.annotationType() - .getAnnotation(PropertyMapping.class); - for (Method attribute : annotation.annotationType() - .getDeclaredMethods()) { - collectProperties(annotation, attribute, typeMapping, properties); + MergedAnnotations.from(source, SearchStrategy.SUPERCLASS).stream() + .filter(MergedAnnotationPredicates.unique(MergedAnnotation::getType)) + .forEach((annotation) -> { + Class type = annotation.getType(); + MergedAnnotation typeMapping = MergedAnnotations.from(type).get( + PropertyMapping.class, MergedAnnotation::isDirectlyPresent); + String prefix = typeMapping + .getValue(MergedAnnotation.VALUE, String.class).orElse(""); + SkipPropertyMapping defaultSkip = typeMapping + .getValue("skip", SkipPropertyMapping.class) + .orElse(SkipPropertyMapping.YES); + for (Method attribute : type.getDeclaredMethods()) { + collectProperties(prefix, defaultSkip, annotation, attribute, + properties); } - collectProperties(root, annotation.annotationType(), properties, - seen); - } - } - collectProperties(root, source.getSuperclass(), properties, seen); - } + }); + return properties; } - private List getMergedAnnotations(Class root, Class source) { - List mergedAnnotations = new ArrayList<>(); - Annotation[] annotations = AnnotationUtils.getAnnotations(source); - if (annotations != null) { - for (Annotation annotation : annotations) { - if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) { - Annotation mergedAnnotation = findMergedAnnotation(root, - annotation.annotationType()); - if (mergedAnnotation != null) { - mergedAnnotations.add(mergedAnnotation); - } - } - } - } - return mergedAnnotations; - } - - private Annotation findMergedAnnotation(Class source, - Class annotationType) { - if (source == null) { - return null; - } - Annotation mergedAnnotation = AnnotatedElementUtils.getMergedAnnotation(source, - annotationType); - return (mergedAnnotation != null) ? mergedAnnotation - : findMergedAnnotation(source.getSuperclass(), annotationType); - } - - private void collectProperties(Annotation annotation, Method attribute, - PropertyMapping typeMapping, Map properties) { - PropertyMapping attributeMapping = AnnotationUtils.getAnnotation(attribute, - PropertyMapping.class); - SkipPropertyMapping skip = getMappingType(typeMapping, attributeMapping); + private void collectProperties(String prefix, SkipPropertyMapping defaultSkip, + MergedAnnotation annotation, Method attribute, + Map properties) { + MergedAnnotation attributeMapping = MergedAnnotations.from(attribute) + .get(PropertyMapping.class); + SkipPropertyMapping skip = attributeMapping + .getValue("skip", SkipPropertyMapping.class).orElse(defaultSkip); if (skip == SkipPropertyMapping.YES) { return; } - ReflectionUtils.makeAccessible(attribute); - Object value = ReflectionUtils.invokeMethod(attribute, annotation); + Optional value = annotation.getValue(attribute.getName()); + if (!value.isPresent()) { + return; + } if (skip == SkipPropertyMapping.ON_DEFAULT_VALUE) { - Object defaultValue = AnnotationUtils.getDefaultValue(annotation, - attribute.getName()); - if (ObjectUtils.nullSafeEquals(value, defaultValue)) { + if (ObjectUtils.nullSafeEquals(value.get(), + annotation.getDefaultValue(attribute.getName()).orElse(null))) { return; } } - String name = getName(typeMapping, attributeMapping, attribute); - putProperties(name, value, properties); + String name = getName(prefix, attributeMapping, attribute); + putProperties(name, value.get(), properties); } - private SkipPropertyMapping getMappingType(PropertyMapping typeMapping, - PropertyMapping attributeMapping) { - if (attributeMapping != null) { - return attributeMapping.skip(); - } - if (typeMapping != null) { - return typeMapping.skip(); - } - return SkipPropertyMapping.YES; - } - - private String getName(PropertyMapping typeMapping, PropertyMapping attributeMapping, + private String getName(String prefix, MergedAnnotation attributeMapping, Method attribute) { - String prefix = (typeMapping != null) ? typeMapping.value() : ""; - String name = (attributeMapping != null) ? attributeMapping.value() : ""; + String name = attributeMapping.getValue(MergedAnnotation.VALUE, String.class) + .orElse(""); if (!StringUtils.hasText(name)) { name = toKebabCase(attribute.getName()); } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/properties/PropertyMappingContextCustomizer.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/properties/PropertyMappingContextCustomizer.java index 85b9f41144..157902a5b3 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/properties/PropertyMappingContextCustomizer.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/properties/PropertyMappingContextCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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,14 +16,15 @@ package org.springframework.boot.test.autoconfigure.properties; -import java.lang.annotation.Annotation; -import java.util.LinkedHashSet; import java.util.Set; +import java.util.stream.Collectors; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import org.springframework.test.context.ContextCustomizer; @@ -76,22 +77,12 @@ class PropertyMappingContextCustomizer implements ContextCustomizer { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { Class beanClass = bean.getClass(); - Set> components = new LinkedHashSet<>(); - Set> propertyMappings = new LinkedHashSet<>(); - while (beanClass != null) { - Annotation[] annotations = AnnotationUtils.getAnnotations(beanClass); - if (annotations != null) { - for (Annotation annotation : annotations) { - if (isAnnotated(annotation, Component.class)) { - components.add(annotation.annotationType()); - } - if (isAnnotated(annotation, PropertyMapping.class)) { - propertyMappings.add(annotation.annotationType()); - } - } - } - beanClass = beanClass.getSuperclass(); - } + MergedAnnotations annotations = MergedAnnotations.from(beanClass, + SearchStrategy.SUPERCLASS); + Set> components = annotations.stream(Component.class) + .map(this::getRoot).collect(Collectors.toSet()); + Set> propertyMappings = annotations.stream(PropertyMapping.class) + .map(this::getRoot).collect(Collectors.toSet()); if (!components.isEmpty() && !propertyMappings.isEmpty()) { throw new IllegalStateException("The @PropertyMapping " + getAnnotationsDescription(propertyMappings) @@ -101,15 +92,11 @@ class PropertyMappingContextCustomizer implements ContextCustomizer { return bean; } - private boolean isAnnotated(Annotation element, - Class annotationType) { - try { - return element.annotationType().equals(annotationType) || AnnotationUtils - .findAnnotation(element.annotationType(), annotationType) != null; - } - catch (Throwable ex) { - return false; + private Class getRoot(MergedAnnotation annotation) { + while (annotation.getParent() != null) { + annotation = annotation.getParent(); } + return annotation.getType(); } private String getAnnotationsDescription(Set> annotations) { diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/client/RestClientExcludeFilter.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/client/RestClientExcludeFilter.java index 493ff63a86..2876cc86b4 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/client/RestClientExcludeFilter.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/client/RestClientExcludeFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2019 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. @@ -23,9 +23,7 @@ import java.util.Set; import org.springframework.boot.context.TypeExcludeFilter; import org.springframework.boot.jackson.JsonComponent; -import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.boot.test.autoconfigure.filter.StandardAnnotationCustomizableTypeExcludeFilter; import org.springframework.util.ClassUtils; /** @@ -33,7 +31,10 @@ import org.springframework.util.ClassUtils; * * @author Stephane Nicoll */ -class RestClientExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { +class RestClientExcludeFilter + extends StandardAnnotationCustomizableTypeExcludeFilter { + + private static final Class[] NO_COMPONENTS = {}; private static final String DATABIND_MODULE_CLASS_NAME = "com.fasterxml.jackson.databind.Module"; @@ -56,32 +57,12 @@ class RestClientExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { DEFAULT_INCLUDES = Collections.unmodifiableSet(includes); } - private final RestClientTest annotation; + private final Class[] components; RestClientExcludeFilter(Class testClass) { - this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - RestClientTest.class); - } - - @Override - protected boolean hasAnnotation() { - return this.annotation != null; - } - - @Override - protected Filter[] getFilters(FilterType type) { - switch (type) { - case INCLUDE: - return this.annotation.includeFilters(); - case EXCLUDE: - return this.annotation.excludeFilters(); - } - throw new IllegalStateException("Unsupported type " + type); - } - - @Override - protected boolean isUseDefaultFilters() { - return this.annotation.useDefaultFilters(); + super(testClass); + this.components = getAnnotation().getValue("components", Class[].class) + .orElse(NO_COMPONENTS); } @Override @@ -91,7 +72,7 @@ class RestClientExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { @Override protected Set> getComponentIncludes() { - return new LinkedHashSet<>(Arrays.asList(this.annotation.components())); + return new LinkedHashSet<>(Arrays.asList(this.components)); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/client/RestClientTestContextBootstrapper.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/client/RestClientTestContextBootstrapper.java index 400a7bc94a..520a4c9b0e 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/client/RestClientTestContextBootstrapper.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/client/RestClientTestContextBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -17,7 +17,8 @@ package org.springframework.boot.test.autoconfigure.web.client; import org.springframework.boot.test.context.SpringBootTestContextBootstrapper; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.TestContextBootstrapper; /** @@ -29,9 +30,9 @@ class RestClientTestContextBootstrapper extends SpringBootTestContextBootstrappe @Override protected String[] getProperties(Class testClass) { - RestClientTest annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - RestClientTest.class); - return (annotation != null) ? annotation.properties() : null; + return MergedAnnotations.from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) + .get(RestClientTest.class).getValue("properties", String[].class) + .orElse(null); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebFluxTestContextBootstrapper.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebFluxTestContextBootstrapper.java index f649d9d816..3127ca7cb7 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebFluxTestContextBootstrapper.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebFluxTestContextBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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,7 +18,8 @@ package org.springframework.boot.test.autoconfigure.web.reactive; import org.springframework.boot.test.context.ReactiveWebMergedContextConfiguration; import org.springframework.boot.test.context.SpringBootTestContextBootstrapper; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.MergedContextConfiguration; import org.springframework.test.context.TestContextBootstrapper; @@ -39,9 +40,9 @@ class WebFluxTestContextBootstrapper extends SpringBootTestContextBootstrapper { @Override protected String[] getProperties(Class testClass) { - WebFluxTest annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - WebFluxTest.class); - return (annotation != null) ? annotation.properties() : null; + return MergedAnnotations.from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) + .get(WebFluxTest.class).getValue("properties", String[].class) + .orElse(null); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebFluxTypeExcludeFilter.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebFluxTypeExcludeFilter.java index 2f5aa7e029..bf25a0020b 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebFluxTypeExcludeFilter.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebFluxTypeExcludeFilter.java @@ -23,9 +23,7 @@ import java.util.Set; import org.springframework.boot.context.TypeExcludeFilter; import org.springframework.boot.jackson.JsonComponent; -import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.boot.test.autoconfigure.filter.StandardAnnotationCustomizableTypeExcludeFilter; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.GenericConverter; import org.springframework.stereotype.Controller; @@ -39,7 +37,10 @@ import org.springframework.web.server.WebExceptionHandler; * * @author Stephane Nicoll */ -class WebFluxTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { +class WebFluxTypeExcludeFilter + extends StandardAnnotationCustomizableTypeExcludeFilter { + + private static final Class[] NO_CONTROLLERS = {}; private static final Set> DEFAULT_INCLUDES; @@ -62,37 +63,17 @@ class WebFluxTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { DEFAULT_INCLUDES_AND_CONTROLLER = Collections.unmodifiableSet(includes); } - private final WebFluxTest annotation; + private final Class[] controllers; WebFluxTypeExcludeFilter(Class testClass) { - this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - WebFluxTest.class); - } - - @Override - protected boolean hasAnnotation() { - return this.annotation != null; - } - - @Override - protected ComponentScan.Filter[] getFilters(FilterType type) { - switch (type) { - case INCLUDE: - return this.annotation.includeFilters(); - case EXCLUDE: - return this.annotation.excludeFilters(); - } - throw new IllegalStateException("Unsupported type " + type); - } - - @Override - protected boolean isUseDefaultFilters() { - return this.annotation.useDefaultFilters(); + super(testClass); + this.controllers = getAnnotation().getValue("controllers", Class[].class) + .orElse(NO_CONTROLLERS); } @Override protected Set> getDefaultIncludes() { - if (ObjectUtils.isEmpty(this.annotation.controllers())) { + if (ObjectUtils.isEmpty(this.controllers)) { return DEFAULT_INCLUDES_AND_CONTROLLER; } return DEFAULT_INCLUDES; @@ -100,7 +81,7 @@ class WebFluxTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { @Override protected Set> getComponentIncludes() { - return new LinkedHashSet<>(Arrays.asList(this.annotation.controllers())); + return new LinkedHashSet<>(Arrays.asList(this.controllers)); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTestContextBootstrapper.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTestContextBootstrapper.java index 43a76d2f8d..db0be79cf7 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTestContextBootstrapper.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTestContextBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -17,7 +17,8 @@ package org.springframework.boot.test.autoconfigure.web.servlet; import org.springframework.boot.test.context.SpringBootTestContextBootstrapper; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.MergedContextConfiguration; import org.springframework.test.context.TestContextBootstrapper; import org.springframework.test.context.web.WebMergedContextConfiguration; @@ -39,9 +40,9 @@ class WebMvcTestContextBootstrapper extends SpringBootTestContextBootstrapper { @Override protected String[] getProperties(Class testClass) { - WebMvcTest annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - WebMvcTest.class); - return (annotation != null) ? annotation.properties() : null; + return MergedAnnotations.from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) + .get(WebMvcTest.class).getValue("properties", String[].class) + .orElse(null); } } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTypeExcludeFilter.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTypeExcludeFilter.java index c1e1485341..d392d83ccf 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTypeExcludeFilter.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTypeExcludeFilter.java @@ -23,12 +23,10 @@ import java.util.Set; import org.springframework.boot.context.TypeExcludeFilter; import org.springframework.boot.jackson.JsonComponent; -import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter; +import org.springframework.boot.test.autoconfigure.filter.StandardAnnotationCustomizableTypeExcludeFilter; import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.error.ErrorAttributes; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.GenericConverter; import org.springframework.http.converter.HttpMessageConverter; @@ -45,7 +43,10 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; * @author Phillip Webb * @author Madhura Bhave */ -class WebMvcTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { +class WebMvcTypeExcludeFilter + extends StandardAnnotationCustomizableTypeExcludeFilter { + + private static final Class[] NO_CONTROLLERS = {}; private static final String[] OPTIONAL_INCLUDES = { "org.springframework.security.config.annotation.web.WebSecurityConfigurer" }; @@ -84,37 +85,17 @@ class WebMvcTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { DEFAULT_INCLUDES_AND_CONTROLLER = Collections.unmodifiableSet(includes); } - private final WebMvcTest annotation; + private final Class[] controllers; WebMvcTypeExcludeFilter(Class testClass) { - this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, - WebMvcTest.class); - } - - @Override - protected boolean hasAnnotation() { - return this.annotation != null; - } - - @Override - protected Filter[] getFilters(FilterType type) { - switch (type) { - case INCLUDE: - return this.annotation.includeFilters(); - case EXCLUDE: - return this.annotation.excludeFilters(); - } - throw new IllegalStateException("Unsupported type " + type); - } - - @Override - protected boolean isUseDefaultFilters() { - return this.annotation.useDefaultFilters(); + super(testClass); + this.controllers = getAnnotation().getValue("controllers", Class[].class) + .orElse(NO_CONTROLLERS); } @Override protected Set> getDefaultIncludes() { - if (ObjectUtils.isEmpty(this.annotation.controllers())) { + if (ObjectUtils.isEmpty(this.controllers)) { return DEFAULT_INCLUDES_AND_CONTROLLER; } return DEFAULT_INCLUDES; @@ -122,7 +103,7 @@ class WebMvcTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { @Override protected Set> getComponentIncludes() { - return new LinkedHashSet<>(Arrays.asList(this.annotation.controllers())); + return new LinkedHashSet<>(Arrays.asList(this.controllers)); } } diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ImportsContextCustomizerFactory.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ImportsContextCustomizerFactory.java index 21722887fb..c4c5e45fb8 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ImportsContextCustomizerFactory.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ImportsContextCustomizerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2019 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,7 +21,8 @@ import java.util.List; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.ContextConfigurationAttributes; import org.springframework.test.context.ContextCustomizer; import org.springframework.test.context.ContextCustomizerFactory; @@ -40,7 +41,8 @@ class ImportsContextCustomizerFactory implements ContextCustomizerFactory { @Override public ContextCustomizer createContextCustomizer(Class testClass, List configAttributes) { - if (AnnotatedElementUtils.findMergedAnnotation(testClass, Import.class) != null) { + if (MergedAnnotations.from(testClass, SearchStrategy.EXHAUSTIVE) + .isPresent(Import.class)) { assertHasNoBeanMethods(testClass); return new ImportsContextCustomizer(testClass); } @@ -52,7 +54,7 @@ class ImportsContextCustomizerFactory implements ContextCustomizerFactory { } private void assertHasNoBeanMethods(Method method) { - Assert.state(!AnnotatedElementUtils.isAnnotated(method, Bean.class), + Assert.state(!MergedAnnotations.from(method).isPresent(Bean.class), "Test classes cannot include @Bean methods"); } diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java index 52389c5d6e..a0dea39e8d 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java @@ -27,6 +27,7 @@ import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.source.ConfigurationPropertySource; import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.mock.web.SpringBootMockServletContext; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext; @@ -36,7 +37,8 @@ import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.Ordered; import org.springframework.core.SpringVersion; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.annotation.Order; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.StandardEnvironment; @@ -155,9 +157,9 @@ public class SpringBootContextLoader extends AbstractContextLoader { * @see SpringApplication#run(String...) */ protected String[] getArgs(MergedContextConfiguration config) { - SpringBootTest annotation = AnnotatedElementUtils - .findMergedAnnotation(config.getTestClass(), SpringBootTest.class); - return (annotation != null) ? annotation.args() : NO_ARGS; + return MergedAnnotations.from(config.getTestClass(), SearchStrategy.EXHAUSTIVE) + .get(SpringBootTest.class).getValue("args", String[].class) + .orElse(NO_ARGS); } private void setActiveProfiles(ConfigurableEnvironment environment, @@ -225,12 +227,10 @@ public class SpringBootContextLoader extends AbstractContextLoader { } private boolean isEmbeddedWebEnvironment(MergedContextConfiguration config) { - SpringBootTest annotation = AnnotatedElementUtils - .findMergedAnnotation(config.getTestClass(), SpringBootTest.class); - if (annotation != null && annotation.webEnvironment().isEmbedded()) { - return true; - } - return false; + return MergedAnnotations.from(config.getTestClass(), SearchStrategy.EXHAUSTIVE) + .get(SpringBootTest.class) + .getValue("webEnvironment", WebEnvironment.class) + .orElse(WebEnvironment.NONE).isEmbedded(); } @Override diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextBootstrapper.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextBootstrapper.java index 4582473128..d8c5355a4c 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextBootstrapper.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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,6 @@ package org.springframework.boot.test.context; -import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -33,8 +32,10 @@ import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.source.ConfigurationPropertySource; import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.env.Environment; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.test.context.ContextConfiguration; @@ -163,11 +164,11 @@ public class SpringBootTestContextBootstrapper extends DefaultTestContextBootstr if (webApplicationType == WebApplicationType.SERVLET && (webEnvironment.isEmbedded() || webEnvironment == WebEnvironment.MOCK)) { - WebAppConfiguration webAppConfiguration = AnnotatedElementUtils - .findMergedAnnotation(mergedConfig.getTestClass(), - WebAppConfiguration.class); - String resourceBasePath = (webAppConfiguration != null) - ? webAppConfiguration.value() : "src/main/webapp"; + String resourceBasePath = MergedAnnotations + .from(mergedConfig.getTestClass(), SearchStrategy.EXHAUSTIVE) + .get(WebAppConfiguration.class) + .getValue(MergedAnnotation.VALUE, String.class) + .orElse("src/main/webapp"); mergedConfig = new WebMergedContextConfiguration(mergedConfig, resourceBasePath); } @@ -251,7 +252,8 @@ public class SpringBootTestContextBootstrapper extends DefaultTestContextBootstr private boolean containsNonTestComponent(Class[] classes) { for (Class candidate : classes) { - if (!AnnotatedElementUtils.isAnnotated(candidate, TestConfiguration.class)) { + if (!MergedAnnotations.from(candidate, SearchStrategy.INHERITED_ANNOTATIONS) + .isPresent(TestConfiguration.class)) { return true; } } @@ -330,15 +332,16 @@ public class SpringBootTestContextBootstrapper extends DefaultTestContextBootstr } protected SpringBootTest getAnnotation(Class testClass) { - return AnnotatedElementUtils.getMergedAnnotation(testClass, SpringBootTest.class); + return MergedAnnotations.from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) + .get(SpringBootTest.class).synthesize(MergedAnnotation::isPresent) + .orElse(null); } protected void verifyConfiguration(Class testClass) { SpringBootTest springBootTest = getAnnotation(testClass); - if (springBootTest != null - && (springBootTest.webEnvironment() == WebEnvironment.DEFINED_PORT - || springBootTest.webEnvironment() == WebEnvironment.RANDOM_PORT) - && getAnnotation(WebAppConfiguration.class, testClass) != null) { + if (springBootTest != null && isListeningOnPort(springBootTest.webEnvironment()) + && MergedAnnotations.from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) + .isPresent(WebAppConfiguration.class)) { throw new IllegalStateException("@WebAppConfiguration should only be used " + "with @SpringBootTest when @SpringBootTest is configured with a " + "mock web environment. Please remove @WebAppConfiguration or " @@ -346,9 +349,9 @@ public class SpringBootTestContextBootstrapper extends DefaultTestContextBootstr } } - private T getAnnotation(Class annotationType, - Class testClass) { - return AnnotatedElementUtils.getMergedAnnotation(testClass, annotationType); + private boolean isListeningOnPort(WebEnvironment webEnvironment) { + return webEnvironment == WebEnvironment.DEFINED_PORT + || webEnvironment == WebEnvironment.RANDOM_PORT; } /** diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/DefinitionsParser.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/DefinitionsParser.java index b2973870e2..4376d0c9ee 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/DefinitionsParser.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/DefinitionsParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,9 @@ import java.util.Map; import java.util.Set; import org.springframework.core.ResolvableType; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; @@ -63,14 +65,12 @@ class DefinitionsParser { } private void parseElement(AnnotatedElement element) { - for (MockBean annotation : AnnotationUtils.getRepeatableAnnotations(element, - MockBean.class, MockBeans.class)) { - parseMockBeanAnnotation(annotation, element); - } - for (SpyBean annotation : AnnotationUtils.getRepeatableAnnotations(element, - SpyBean.class, SpyBeans.class)) { - parseSpyBeanAnnotation(annotation, element); - } + MergedAnnotations annotations = MergedAnnotations.from(element, + SearchStrategy.SUPERCLASS); + annotations.stream(MockBean.class).map(MergedAnnotation::synthesize) + .forEach((annotation) -> parseMockBeanAnnotation(annotation, element)); + annotations.stream(SpyBean.class).map(MergedAnnotation::synthesize) + .forEach((annotation) -> parseSpyBeanAnnotation(annotation, element)); } private void parseMockBeanAnnotation(MockBean annotation, AnnotatedElement element) { diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/QualifierDefinition.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/QualifierDefinition.java index 9c7dd7b7ec..a5cb92659d 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/QualifierDefinition.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/QualifierDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotations; /** * Definition of a Spring {@link Qualifier @Qualifier}. @@ -93,18 +93,20 @@ class QualifierDefinition { Annotation[] candidates = field.getDeclaredAnnotations(); Set annotations = new HashSet<>(candidates.length); for (Annotation candidate : candidates) { - if (!isMockOrSpyAnnotation(candidate)) { + if (!isMockOrSpyAnnotation(candidate.annotationType())) { annotations.add(candidate); } } return annotations; } - private static boolean isMockOrSpyAnnotation(Annotation candidate) { - Class type = candidate.annotationType(); - return (type.equals(MockBean.class) || type.equals(SpyBean.class) - || AnnotationUtils.isAnnotationMetaPresent(type, MockBean.class) - || AnnotationUtils.isAnnotationMetaPresent(type, SpyBean.class)); + private static boolean isMockOrSpyAnnotation(Class type) { + if (type.equals(MockBean.class) || type.equals(SpyBean.class)) { + return true; + } + MergedAnnotations metaAnnotations = MergedAnnotations.from(type); + return metaAnnotations.isPresent(MockBean.class) + || metaAnnotations.isPresent(SpyBean.class); } } diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplateContextCustomizer.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplateContextCustomizer.java index 956d875f89..8c97bb2a3e 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplateContextCustomizer.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplateContextCustomizer.java @@ -29,6 +29,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.client.TestRestTemplate.HttpClientOption; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory; @@ -37,7 +38,9 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ConfigurationClassPostProcessor; import org.springframework.core.Ordered; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.ContextCustomizer; import org.springframework.test.context.MergedContextConfiguration; @@ -52,9 +55,11 @@ class TestRestTemplateContextCustomizer implements ContextCustomizer { @Override public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedContextConfiguration) { - SpringBootTest annotation = AnnotatedElementUtils.getMergedAnnotation( - mergedContextConfiguration.getTestClass(), SpringBootTest.class); - if (annotation.webEnvironment().isEmbedded()) { + MergedAnnotation annotation = MergedAnnotations + .from(mergedContextConfiguration.getTestClass(), + SearchStrategy.INHERITED_ANNOTATIONS) + .get(SpringBootTest.class); + if (annotation.getEnum("webEnvironment", WebEnvironment.class).isEmbedded()) { registerTestRestTemplate(context); } } diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplateContextCustomizerFactory.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplateContextCustomizerFactory.java index 0a397c6cb9..f626d12275 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplateContextCustomizerFactory.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplateContextCustomizerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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,8 @@ package org.springframework.boot.test.web.client; import java.util.List; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.ContextConfigurationAttributes; import org.springframework.test.context.ContextCustomizer; import org.springframework.test.context.ContextCustomizerFactory; @@ -35,8 +36,9 @@ class TestRestTemplateContextCustomizerFactory implements ContextCustomizerFacto @Override public ContextCustomizer createContextCustomizer(Class testClass, List configAttributes) { - if (AnnotatedElementUtils.findMergedAnnotation(testClass, - SpringBootTest.class) != null) { + MergedAnnotations annotations = MergedAnnotations.from(testClass, + SearchStrategy.INHERITED_ANNOTATIONS); + if (annotations.isPresent(SpringBootTest.class)) { return new TestRestTemplateContextCustomizer(); } return null; diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizer.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizer.java index 108c554f14..2f73b6585f 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizer.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizer.java @@ -31,6 +31,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.web.codec.CodecCustomizer; import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory; import org.springframework.context.ApplicationContext; @@ -38,7 +39,9 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ConfigurationClassPostProcessor; import org.springframework.core.Ordered; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.ContextCustomizer; import org.springframework.test.context.MergedContextConfiguration; import org.springframework.test.web.reactive.server.WebTestClient; @@ -55,9 +58,10 @@ class WebTestClientContextCustomizer implements ContextCustomizer { @Override public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) { - SpringBootTest annotation = AnnotatedElementUtils - .getMergedAnnotation(mergedConfig.getTestClass(), SpringBootTest.class); - if (annotation.webEnvironment().isEmbedded()) { + MergedAnnotation annotation = MergedAnnotations + .from(mergedConfig.getTestClass(), SearchStrategy.INHERITED_ANNOTATIONS) + .get(SpringBootTest.class); + if (annotation.getEnum("webEnvironment", WebEnvironment.class).isEmbedded()) { registerWebTestClient(context); } } diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizerFactory.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizerFactory.java index 738f954c54..a39ff77418 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizerFactory.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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,8 @@ package org.springframework.boot.test.web.reactive.server; import java.util.List; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.ContextConfigurationAttributes; import org.springframework.test.context.ContextCustomizer; import org.springframework.test.context.ContextCustomizerFactory; @@ -37,8 +38,9 @@ class WebTestClientContextCustomizerFactory implements ContextCustomizerFactory @Override public ContextCustomizer createContextCustomizer(Class testClass, List configAttributes) { - if (isWebClientPresent() && AnnotatedElementUtils.findMergedAnnotation(testClass, - SpringBootTest.class) != null) { + MergedAnnotations annotations = MergedAnnotations.from(testClass, + SearchStrategy.INHERITED_ANNOTATIONS); + if (isWebClientPresent() && annotations.isPresent(SpringBootTest.class)) { return new WebTestClientContextCustomizer(); } return null; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/runner/classpath/ModifiedClassPathRunner.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/runner/classpath/ModifiedClassPathRunner.java index 893fe85b5f..6615dc0b38 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/runner/classpath/ModifiedClassPathRunner.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/runner/classpath/ModifiedClassPathRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -52,7 +52,9 @@ import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import org.junit.runners.model.TestClass; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.util.AntPathMatcher; import org.springframework.util.StringUtils; @@ -178,9 +180,14 @@ public class ModifiedClassPathRunner extends BlockJUnit4ClassRunner { } private URL[] processUrls(URL[] urls, Class testClass) throws Exception { - ClassPathEntryFilter filter = new ClassPathEntryFilter(testClass); + MergedAnnotations annotations = MergedAnnotations.from(testClass, + SearchStrategy.EXHAUSTIVE); + ClassPathEntryFilter filter = new ClassPathEntryFilter( + annotations.get(ClassPathExclusions.class)); List processedUrls = new ArrayList<>(); - processedUrls.addAll(getAdditionalUrls(testClass)); + List additionalUrls = getAdditionalUrls( + annotations.get(ClassPathOverrides.class)); + processedUrls.addAll(additionalUrls); for (URL url : urls) { if (!filter.isExcluded(url)) { processedUrls.add(url); @@ -189,13 +196,12 @@ public class ModifiedClassPathRunner extends BlockJUnit4ClassRunner { return processedUrls.toArray(new URL[0]); } - private List getAdditionalUrls(Class testClass) throws Exception { - ClassPathOverrides overrides = AnnotationUtils.findAnnotation(testClass, - ClassPathOverrides.class); - if (overrides == null) { + private List getAdditionalUrls(MergedAnnotation annotation) + throws Exception { + if (!annotation.isPresent()) { return Collections.emptyList(); } - return resolveCoordinates(overrides.value()); + return resolveCoordinates(annotation.getStringArray(MergedAnnotation.VALUE)); } private List resolveCoordinates(String[] coordinates) throws Exception { @@ -243,13 +249,13 @@ public class ModifiedClassPathRunner extends BlockJUnit4ClassRunner { private final AntPathMatcher matcher = new AntPathMatcher(); - private ClassPathEntryFilter(Class testClass) throws Exception { + private ClassPathEntryFilter(MergedAnnotation annotation) + throws Exception { this.exclusions = new ArrayList<>(); this.exclusions.add("log4j-*.jar"); - ClassPathExclusions exclusions = AnnotationUtils.findAnnotation(testClass, - ClassPathExclusions.class); - if (exclusions != null) { - this.exclusions.addAll(Arrays.asList(exclusions.value())); + if (annotation.isPresent()) { + this.exclusions.addAll( + Arrays.asList(annotation.getStringArray(MergedAnnotation.VALUE))); } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/BeanDefinitionLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/BeanDefinitionLoader.java index 79a2b4ef36..745d512807 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/BeanDefinitionLoader.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/BeanDefinitionLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -31,7 +31,8 @@ import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; @@ -282,7 +283,8 @@ class BeanDefinitionLoader { private boolean isComponent(Class type) { // This has to be a bit of a guess. The only way to be sure that this type is // eligible is to make a bean definition out of it and try to instantiate it. - if (AnnotationUtils.findAnnotation(type, Component.class) != null) { + if (MergedAnnotations.from(type, SearchStrategy.EXHAUSTIVE) + .isPresent(Component.class)) { return true; } // Nested anonymous classes are not eligible for registration, nor are groovy diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanDefinition.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanDefinition.java index cec738ea71..1c6e47cc4e 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanDefinition.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanDefinition.java @@ -46,6 +46,7 @@ final class ConfigurationPropertiesBeanDefinition extends GenericBeanDefinition private static Supplier createBean(ConfigurableListableBeanFactory beanFactory, String beanName, Class type) { return () -> { + // FIXME review ConfigurationProperties annotation = getAnnotation(type, ConfigurationProperties.class); Validated validated = getAnnotation(type, Validated.class); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanDefinitionRegistrar.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanDefinitionRegistrar.java index 2636cf6bc4..51363e851f 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanDefinitionRegistrar.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanDefinitionRegistrar.java @@ -28,6 +28,8 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.core.KotlinDetector; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -80,8 +82,9 @@ final class ConfigurationPropertiesBeanDefinitionRegistrar { } private static void assertHasAnnotation(Class type) { - Assert.notNull( - AnnotationUtils.findAnnotation(type, ConfigurationProperties.class), + Assert.isTrue( + MergedAnnotations.from(type, SearchStrategy.EXHAUSTIVE) + .isPresent(ConfigurationProperties.class), () -> "No " + ConfigurationProperties.class.getSimpleName() + " annotation found on '" + type.getName() + "'."); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/error/DefaultErrorAttributes.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/error/DefaultErrorAttributes.java index b7f119367d..19515f178c 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/error/DefaultErrorAttributes.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/error/DefaultErrorAttributes.java @@ -22,7 +22,9 @@ import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; -import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.http.HttpStatus; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; @@ -83,40 +85,37 @@ public class DefaultErrorAttributes implements ErrorAttributes { errorAttributes.put("timestamp", new Date()); errorAttributes.put("path", request.path()); Throwable error = getError(request); - HttpStatus errorStatus = determineHttpStatus(error); + MergedAnnotation responseStatusAnnotation = MergedAnnotations + .from(error.getClass(), SearchStrategy.EXHAUSTIVE) + .get(ResponseStatus.class); + HttpStatus errorStatus = determineHttpStatus(error, responseStatusAnnotation); errorAttributes.put("status", errorStatus.value()); errorAttributes.put("error", errorStatus.getReasonPhrase()); - errorAttributes.put("message", determineMessage(error)); + errorAttributes.put("message", determineMessage(error, responseStatusAnnotation)); errorAttributes.put("requestId", request.exchange().getRequest().getId()); handleException(errorAttributes, determineException(error), includeStackTrace); return errorAttributes; } - private HttpStatus determineHttpStatus(Throwable error) { + private HttpStatus determineHttpStatus(Throwable error, + MergedAnnotation responseStatusAnnotation) { if (error instanceof ResponseStatusException) { return ((ResponseStatusException) error).getStatus(); } - ResponseStatus responseStatus = AnnotatedElementUtils - .findMergedAnnotation(error.getClass(), ResponseStatus.class); - if (responseStatus != null) { - return responseStatus.code(); - } - return HttpStatus.INTERNAL_SERVER_ERROR; + return responseStatusAnnotation.getValue("code", HttpStatus.class) + .orElse(HttpStatus.INTERNAL_SERVER_ERROR); } - private String determineMessage(Throwable error) { + private String determineMessage(Throwable error, + MergedAnnotation responseStatusAnnotation) { if (error instanceof WebExchangeBindException) { return error.getMessage(); } if (error instanceof ResponseStatusException) { return ((ResponseStatusException) error).getReason(); } - ResponseStatus responseStatus = AnnotatedElementUtils - .findMergedAnnotation(error.getClass(), ResponseStatus.class); - if (responseStatus != null) { - return responseStatus.reason(); - } - return error.getMessage(); + return responseStatusAnnotation.getValue("reason", String.class) + .orElseGet(error::getMessage); } private Throwable determineException(Throwable error) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializer.java index f568ed3415..194387215c 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -37,7 +37,8 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.util.Assert; import org.springframework.web.WebApplicationInitializer; @@ -122,8 +123,9 @@ public abstract class SpringBootServletInitializer implements WebApplicationInit builder = configure(builder); builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext)); SpringApplication application = builder.build(); - if (application.getAllSources().isEmpty() && AnnotationUtils - .findAnnotation(getClass(), Configuration.class) != null) { + if (application.getAllSources().isEmpty() + && MergedAnnotations.from(getClass(), SearchStrategy.EXHAUSTIVE) + .isPresent(Configuration.class)) { application.addPrimarySources(Collections.singleton(getClass())); } Assert.state(!application.getAllSources().isEmpty(),