diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java index 303f31ab582..9c52357ff04 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java @@ -16,11 +16,11 @@ package org.springframework.boot.actuate.autoconfigure.cloudfoundry.reactive; +import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Objects; import java.util.stream.Collectors; import org.reactivestreams.Publisher; @@ -44,6 +44,7 @@ import org.springframework.context.aot.BindingReflectionHintsRegistrar; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.reactive.result.method.RequestMappingInfoHandlerMapping; @@ -159,8 +160,10 @@ class CloudFoundryWebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointH @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { - hints.reflection().registerMethod(Objects.requireNonNull( - ReflectionUtils.findMethod(CloudFoundryLinksHandler.class, "links", ServerWebExchange.class))); + Method linksMethod = ReflectionUtils.findMethod(CloudFoundryLinksHandler.class, "links", + ServerWebExchange.class); + Assert.state(linksMethod != null, "Unable to find 'links' method"); + hints.reflection().registerMethod(linksMethod); this.bindingRegistrar.registerReflectionHints(hints.reflection(), Link.class); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java index f3761e777bc..b2b086f35b0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java @@ -16,11 +16,11 @@ package org.springframework.boot.actuate.autoconfigure.cloudfoundry.servlet; +import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Objects; import java.util.stream.Collectors; import jakarta.servlet.http.HttpServletRequest; @@ -45,6 +45,7 @@ import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.context.aot.BindingReflectionHintsRegistrar; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.cors.CorsConfiguration; @@ -161,9 +162,10 @@ class CloudFoundryWebEndpointServletHandlerMapping extends AbstractWebMvcEndpoin @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { - hints.reflection() - .registerMethod(Objects.requireNonNull(ReflectionUtils.findMethod(CloudFoundryLinksHandler.class, - "links", HttpServletRequest.class, HttpServletResponse.class))); + Method linksMethod = ReflectionUtils.findMethod(CloudFoundryLinksHandler.class, "links", + HttpServletRequest.class, HttpServletResponse.class); + Assert.state(linksMethod != null, "Unable to find 'links' method"); + hints.reflection().registerMethod(linksMethod); this.bindingRegistrar.registerReflectionHints(hints.reflection(), Link.class); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/ActuatorAnnotationsRuntimeHints.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/ActuatorAnnotationsRuntimeHints.java index 02491929345..7072bcf5132 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/ActuatorAnnotationsRuntimeHints.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/ActuatorAnnotationsRuntimeHints.java @@ -40,7 +40,6 @@ class ActuatorAnnotationsRuntimeHints implements RuntimeHintsRegistrar { Stream.of(Endpoint.class, ReadOperation.class, WriteOperation.class, DeleteOperation.class, EndpointExtension.class) .forEach((annotationType) -> RuntimeHintsUtils.registerAnnotation(hints, annotationType)); - // TODO: See https://github.com/spring-projects/spring-framework/issues/28767 Stream.of(Endpoint.class, EndpointExtension.class).forEach( (annotationType) -> hints.proxies().registerJdkProxy(annotationType, SynthesizedAnnotation.class)); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/OperationReflectiveProcessor.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/OperationReflectiveProcessor.java index 2e934e40d1d..bb4a261c122 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/OperationReflectiveProcessor.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/OperationReflectiveProcessor.java @@ -49,17 +49,16 @@ class OperationReflectiveProcessor extends SimpleReflectiveProcessor { private Type extractReturnType(Method method) { ResolvableType returnType = ResolvableType.forMethodReturnType(method); - if (WebEndpointResponse.class.isAssignableFrom(method.getReturnType())) { - return returnType.as(WebEndpointResponse.class).getGeneric(0).getType(); + if (!WebEndpointResponse.class.isAssignableFrom(method.getReturnType())) { + return returnType.getType(); } - return returnType.getType(); + return returnType.as(WebEndpointResponse.class).getGeneric(0).getType(); } private void registerReflectionHints(ReflectionHints hints, Type type) { - if (type.equals(Resource.class)) { - return; + if (!type.equals(Resource.class)) { + this.bindingRegistrar.registerReflectionHints(hints, type); } - this.bindingRegistrar.registerReflectionHints(hints, type); } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java index 93c24f72d6f..f7cd0064645 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java @@ -23,7 +23,6 @@ import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Objects; import java.util.function.Supplier; import org.reactivestreams.Publisher; @@ -60,6 +59,7 @@ import org.springframework.security.access.vote.RoleVoter; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.util.AntPathMatcher; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; @@ -494,11 +494,13 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { - hints.reflection() - .registerMethod(Objects.requireNonNull(ReflectionUtils.findMethod(WriteOperationHandler.class, - "handle", ServerWebExchange.class, Map.class))) - .registerMethod(Objects.requireNonNull( - ReflectionUtils.findMethod(ReadOperationHandler.class, "handle", ServerWebExchange.class))); + Method writeOperationHandleMethod = ReflectionUtils.findMethod(WriteOperationHandler.class, "handle", + ServerWebExchange.class, Map.class); + Assert.state(writeOperationHandleMethod != null, () -> "Unable to find write operation 'handle' method"); + Method readOperationHandleMethod = ReflectionUtils.findMethod(ReadOperationHandler.class, "handle", + ServerWebExchange.class); + Assert.state(readOperationHandleMethod != null, () -> "Unable to find read operation 'handle' method"); + hints.reflection().registerMethod(writeOperationHandleMethod).registerMethod(readOperationHandleMethod); } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointHandlerMapping.java index dff2b56b919..f27b1e50d1d 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/WebFluxEndpointHandlerMapping.java @@ -16,10 +16,10 @@ package org.springframework.boot.actuate.endpoint.web.reactive; +import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; import java.util.Map; -import java.util.Objects; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; @@ -32,6 +32,7 @@ import org.springframework.boot.actuate.endpoint.web.Link; import org.springframework.boot.actuate.endpoint.web.reactive.WebFluxEndpointHandlerMapping.WebFluxEndpointHandlerMappingRuntimeHints; import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.context.aot.BindingReflectionHintsRegistrar; +import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.cors.CorsConfiguration; @@ -103,8 +104,10 @@ public class WebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointHandle @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { - hints.reflection().registerMethod(Objects.requireNonNull( - ReflectionUtils.findMethod(WebFluxLinksHandler.class, "links", ServerWebExchange.class))); + Method linksMethod = ReflectionUtils.findMethod(WebFluxLinksHandler.class, "links", + ServerWebExchange.class); + Assert.state(linksMethod != null, "Unable to find 'links' method"); + hints.reflection().registerMethod(linksMethod); this.bindingRegistrar.registerReflectionHints(hints.reflection(), Link.class); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java index bd90fa23775..652f12c8f05 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java @@ -26,7 +26,6 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.function.Function; import jakarta.servlet.http.HttpServletRequest; @@ -486,8 +485,10 @@ public abstract class AbstractWebMvcEndpointHandlerMapping extends RequestMappin @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { - hints.reflection().registerMethod(Objects.requireNonNull( - ReflectionUtils.findMethod(OperationHandler.class, "handle", HttpServletRequest.class, Map.class))); + Method handlerMethod = ReflectionUtils.findMethod(OperationHandler.class, "handle", + HttpServletRequest.class, Map.class); + Assert.state(handlerMethod != null, "Unable to find 'handler' method"); + hints.reflection().registerMethod(handlerMethod); } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcEndpointHandlerMapping.java index 41b8cfae4ea..3ba970e537b 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcEndpointHandlerMapping.java @@ -16,10 +16,10 @@ package org.springframework.boot.actuate.endpoint.web.servlet; +import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; import java.util.Map; -import java.util.Objects; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -34,6 +34,7 @@ import org.springframework.boot.actuate.endpoint.web.Link; import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping.WebMvcEndpointHandlerMappingRuntimeHints; import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.context.aot.BindingReflectionHintsRegistrar; +import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.cors.CorsConfiguration; @@ -100,9 +101,10 @@ public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerM @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { - hints.reflection() - .registerMethod(Objects.requireNonNull(ReflectionUtils.findMethod(WebMvcLinksHandler.class, "links", - HttpServletRequest.class, HttpServletResponse.class))); + Method linksMethod = ReflectionUtils.findMethod(WebMvcLinksHandler.class, "links", HttpServletRequest.class, + HttpServletResponse.class); + Assert.state(linksMethod != null, "Unable to find 'links' method"); + hints.reflection().registerMethod(linksMethod); this.bindingRegistrar.registerReflectionHints(hints.reflection(), Link.class); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/cache/HazelcastCacheMeterBinderProvider.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/cache/HazelcastCacheMeterBinderProvider.java index 7f38ad7fb0b..56535904d6f 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/cache/HazelcastCacheMeterBinderProvider.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/cache/HazelcastCacheMeterBinderProvider.java @@ -16,8 +16,8 @@ package org.springframework.boot.actuate.metrics.cache; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; -import java.util.Objects; import com.hazelcast.spring.cache.HazelcastCache; import io.micrometer.core.instrument.Tag; @@ -28,6 +28,7 @@ import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.boot.actuate.metrics.cache.HazelcastCacheMeterBinderProvider.HazelcastCacheMeterBinderProviderRuntimeHints; import org.springframework.context.annotation.ImportRuntimeHints; +import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; /** @@ -67,10 +68,10 @@ public class HazelcastCacheMeterBinderProvider implements CacheMeterBinderProvid @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { try { - hints.reflection() - .registerMethod(Objects - .requireNonNull(ReflectionUtils.findMethod(HazelcastCache.class, "getNativeCache"))) - .registerConstructor(HazelcastCacheMetrics.class.getConstructor(Object.class, Iterable.class)); + Method getNativeCacheMethod = ReflectionUtils.findMethod(HazelcastCache.class, "getNativeCache"); + Assert.state(getNativeCacheMethod != null, "Unable to find 'getNativeCache' method"); + Constructor constructor = HazelcastCacheMetrics.class.getConstructor(Object.class, Iterable.class); + hints.reflection().registerMethod(getNativeCacheMethod).registerConstructor(constructor); } catch (NoSuchMethodException ex) { throw new IllegalStateException(ex); diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/startup/StartupEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/startup/StartupEndpoint.java index e0c03227aab..d035152c983 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/startup/StartupEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/startup/StartupEndpoint.java @@ -92,22 +92,24 @@ public class StartupEndpoint { static class StartupEndpointRuntimeHints implements RuntimeHintsRegistrar { + private static final TypeReference DEFAULT_TAG = TypeReference + .of("org.springframework.boot.context.metrics.buffering.BufferedStartupStep$DefaultTag"); + + private static final TypeReference BUFFERED_STARTUP_STEP = TypeReference + .of("org.springframework.boot.context.metrics.buffering.BufferedStartupStep"); + + private static final TypeReference FLIGHT_RECORDER_TAG = TypeReference + .of("org.springframework.core.metrics.jfr.FlightRecorderStartupStep$FlightRecorderTag"); + + private static final TypeReference FLIGHT_RECORDER_STARTUP_STEP = TypeReference + .of("org.springframework.core.metrics.jfr.FlightRecorderStartupStep"); + @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { - hints.reflection().registerType( - TypeReference - .of("org.springframework.boot.context.metrics.buffering.BufferedStartupStep$DefaultTag"), - (hint) -> hint - .onReachableType(TypeReference - .of("org.springframework.boot.context.metrics.buffering.BufferedStartupStep")) - .withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)); - hints.reflection().registerType( - TypeReference - .of("org.springframework.core.metrics.jfr.FlightRecorderStartupStep$FlightRecorderTag"), - (hint) -> hint - .onReachableType( - TypeReference.of("org.springframework.core.metrics.jfr.FlightRecorderStartupStep")) - .withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)); + hints.reflection().registerType(DEFAULT_TAG, (hint) -> hint.onReachableType(BUFFERED_STARTUP_STEP) + .withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)); + hints.reflection().registerType(FLIGHT_RECORDER_TAG, (hint) -> hint + .onReachableType(FLIGHT_RECORDER_STARTUP_STEP).withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProviderTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProviderTests.java index 6d5dcac3185..f184c801284 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProviderTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProviderTests.java @@ -40,7 +40,6 @@ class DispatcherHandlersMappingDescriptionProviderTests { assertThat(RuntimeHintsPredicates.reflection().onType(DispatcherHandlerMappingDescription.class) .withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS)) .accepts(runtimeHints); - } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/mappings/servlet/FiltersMappingDescriptionProviderTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/mappings/servlet/FiltersMappingDescriptionProviderTests.java index 62fb4e8846c..ec1c3298087 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/mappings/servlet/FiltersMappingDescriptionProviderTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/mappings/servlet/FiltersMappingDescriptionProviderTests.java @@ -39,7 +39,6 @@ class FiltersMappingDescriptionProviderTests { assertThat(RuntimeHintsPredicates.reflection().onType(FilterRegistrationMappingDescription.class) .withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS)) .accepts(runtimeHints); - } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootAotPlugin.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootAotPlugin.java index 0ee159bf1cb..f2cfc956587 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootAotPlugin.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootAotPlugin.java @@ -22,6 +22,7 @@ import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.attributes.AttributeContainer; import org.gradle.api.attributes.LibraryElements; import org.gradle.api.file.Directory; import org.gradle.api.plugins.JavaPlugin; @@ -77,12 +78,17 @@ public class SpringBootAotPlugin implements Plugin { aotImplementation.extendsFrom(configurations.getByName(main.getImplementationConfigurationName())); aotImplementation.extendsFrom(configurations.getByName(main.getRuntimeOnlyConfigurationName())); configurations.getByName(aot.getCompileClasspathConfigurationName()) - .attributes((attributes) -> attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, - project.getObjects().named(LibraryElements.class, LibraryElements.CLASSES_AND_RESOURCES))); + .attributes((attributes) -> addLibraryElementsAttribute(project, attributes)); }); return aotSourceSet; } + private AttributeContainer addLibraryElementsAttribute(Project project, AttributeContainer attributes) { + LibraryElements libraryElements = project.getObjects().named(LibraryElements.class, + LibraryElements.CLASSES_AND_RESOURCES); + return attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, libraryElements); + } + private void registerGenerateAotSourcesTask(Project project, SourceSet aotSourceSet) { TaskProvider resolveMainClassName = project.getTasks() .named(SpringBootPlugin.RESOLVE_MAIN_CLASS_NAME_TASK_NAME, ResolveMainClassName.class); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractDependencyFilterMojo.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractDependencyFilterMojo.java index cdec21f6018..9de8ca7577f 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractDependencyFilterMojo.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractDependencyFilterMojo.java @@ -157,9 +157,9 @@ public abstract class AbstractDependencyFilterMojo extends AbstractMojo { return cleaned.toString(); } - static class TestArtifactFilter extends AbstractArtifactFeatureFilter { + protected static class TestScopeArtifactFilter extends AbstractArtifactFeatureFilter { - TestArtifactFilter() { + TestScopeArtifactFilter() { super("", Artifact.SCOPE_TEST); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractRunMojo.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractRunMojo.java index d7565a23d02..2e588d3b3e2 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractRunMojo.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractRunMojo.java @@ -371,7 +371,7 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo { private void addDependencies(List urls) throws MalformedURLException, MojoExecutionException { Set artifacts = (this.useTestClasspath) ? filterDependencies(this.project.getArtifacts()) - : filterDependencies(this.project.getArtifacts(), new TestArtifactFilter()); + : filterDependencies(this.project.getArtifacts(), new TestScopeArtifactFilter()); for (Artifact artifact : artifacts) { if (artifact.getFile() != null) { urls.add(artifact.getFile().toURI().toURL()); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AotGenerateMojo.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AotGenerateMojo.java index 2c6a66dd8c0..732e98a66cd 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AotGenerateMojo.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AotGenerateMojo.java @@ -154,6 +154,18 @@ public class AotGenerateMojo extends AbstractDependencyFilterMojo { private void generateAotAssets() throws MojoExecutionException { String applicationClass = (this.mainClass != null) ? this.mainClass : SpringBootApplicationClassFinder.findSingleClass(this.classesDirectory); + List command = CommandLineBuilder.forMainClass(AOT_PROCESSOR_CLASS_NAME) + .withSystemProperties(this.systemPropertyVariables) + .withJvmArguments(new RunArguments(this.jvmArguments).asArray()).withClasspath(getClassPathUrls()) + .withArguments(getAotArguments(applicationClass)).build(); + if (getLog().isDebugEnabled()) { + getLog().debug("Generating AOT assets using command: " + command); + } + JavaProcessExecutor processExecutor = new JavaProcessExecutor(this.session, this.toolchainManager); + processExecutor.run(this.project.getBasedir(), command, Collections.emptyMap()); + } + + private String[] getAotArguments(String applicationClass) { List aotArguments = new ArrayList<>(); aotArguments.add(applicationClass); aotArguments.add(this.generatedSources.toString()); @@ -164,25 +176,13 @@ public class AotGenerateMojo extends AbstractDependencyFilterMojo { if (!ObjectUtils.isEmpty(this.profiles)) { aotArguments.add("--spring.profiles.active=" + String.join(",", this.profiles)); } - // @formatter:off - List args = CommandLineBuilder.forMainClass(AOT_PROCESSOR_CLASS_NAME) - .withSystemProperties(this.systemPropertyVariables) - .withJvmArguments(new RunArguments(this.jvmArguments).asArray()) - .withClasspath(getClassPathUrls()) - .withArguments(aotArguments.toArray(String[]::new)) - .build(); - // @formatter:on - if (getLog().isDebugEnabled()) { - getLog().debug("Generating AOT assets using command: " + args); - } - JavaProcessExecutor processExecutor = new JavaProcessExecutor(this.session, this.toolchainManager); - processExecutor.run(this.project.getBasedir(), args, Collections.emptyMap()); + return aotArguments.toArray(String[]::new); } private URL[] getClassPathUrls() throws MojoExecutionException { List urls = new ArrayList<>(); urls.add(toURL(this.classesDirectory)); - urls.addAll(getDependencyURLs(new TestArtifactFilter())); + urls.addAll(getDependencyURLs(new TestScopeArtifactFilter())); return urls.toArray(URL[]::new); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesReflectionHintsProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesReflectionHintsProcessor.java index 7cfab17c67b..a29e83cdb95 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesReflectionHintsProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesReflectionHintsProcessor.java @@ -109,11 +109,14 @@ public final class ConfigurationPropertiesReflectionHintsProcessor { private void handleConstructor(ReflectionHints reflectionHints) { if (this.bindConstructor != null) { reflectionHints.registerConstructor(this.bindConstructor); + return; } - else { - Arrays.stream(this.type.getDeclaredConstructors()).filter((candidate) -> candidate.getParameterCount() == 0) - .findFirst().ifPresent(reflectionHints::registerConstructor); - } + Arrays.stream(this.type.getDeclaredConstructors()).filter(this::hasNoParameters).findFirst() + .ifPresent(reflectionHints::registerConstructor); + } + + private boolean hasNoParameters(Constructor candidate) { + return candidate.getParameterCount() == 0; } private void handleValueObjectProperties(ReflectionHints reflectionHints) { @@ -138,26 +141,6 @@ public final class ConfigurationPropertiesReflectionHintsProcessor { } } - private void handleProperty(ReflectionHints reflectionHints, String propertyName, ResolvableType propertyType) { - Class propertyClass = propertyType.resolve(); - if (propertyClass == null) { - return; - } - if (propertyClass.equals(this.type)) { - return; // Prevent infinite recursion - } - Class componentType = getComponentType(propertyType); - if (componentType != null) { - // Can be a list of simple types - if (!isJavaType(componentType)) { - processNestedType(componentType, reflectionHints); - } - } - else if (isNestedType(propertyName, propertyClass)) { - processNestedType(propertyClass, reflectionHints); - } - } - private boolean isSetterMandatory(String propertyName, ResolvableType propertyType) { Class propertyClass = propertyType.resolve(); if (propertyClass == null) { @@ -169,39 +152,61 @@ public final class ConfigurationPropertiesReflectionHintsProcessor { return !isNestedType(propertyName, propertyClass); } - private Class getComponentType(ResolvableType propertyType) { - Class propertyClass = propertyType.toClass(); - ResolvableType componentType = null; - if (propertyType.isArray()) { - componentType = propertyType.getComponentType(); + private void handleProperty(ReflectionHints reflectionHints, String propertyName, ResolvableType propertyType) { + Class propertyClass = propertyType.resolve(); + if (propertyClass == null) { + return; } - else if (Collection.class.isAssignableFrom(propertyClass)) { - componentType = propertyType.asCollection().getGeneric(0); + if (propertyClass.equals(this.type)) { + return; // Prevent infinite recursion } - else if (Map.class.isAssignableFrom(propertyClass)) { - componentType = propertyType.asMap().getGeneric(1); + Class componentType = getComponentClass(propertyType); + if (componentType != null) { + // Can be a list of simple types + if (!isJavaType(componentType)) { + processNestedType(componentType, reflectionHints); + } } + else if (isNestedType(propertyName, propertyClass)) { + processNestedType(propertyClass, reflectionHints); + } + } + + private Class getComponentClass(ResolvableType type) { + ResolvableType componentType = getComponentType(type); if (componentType == null) { return null; } if (isContainer(componentType)) { // Resolve nested generics like Map> - return getComponentType(componentType); + return getComponentClass(componentType); } return componentType.toClass(); } - private boolean isContainer(ResolvableType type) { + private ResolvableType getComponentType(ResolvableType type) { if (type.isArray()) { - return true; + return type.getComponentType(); } - if (Collection.class.isAssignableFrom(type.toClass())) { - return true; + if (isCollection(type)) { + return type.asCollection().getGeneric(); } - else if (Map.class.isAssignableFrom(type.toClass())) { - return true; + if (isMap(type)) { + return type.asMap().getGeneric(1); } - return false; + return null; + } + + private boolean isContainer(ResolvableType type) { + return type.isArray() || isCollection(type) || isMap(type); + } + + private boolean isCollection(ResolvableType type) { + return Collection.class.isAssignableFrom(type.toClass()); + } + + private boolean isMap(ResolvableType type) { + return Map.class.isAssignableFrom(type.toClass()); } /**