diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java index 2945e2bc7edc..24cd20fa827f 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2025 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. @@ -231,12 +231,12 @@ private Set> serializationTypes(Model model) { return modelClasses; } - private Set reflectionTypes(Model model) { + private Set> reflectionTypes(Model model) { return reflectionTypes(model, () -> null); } - private Set reflectionTypes(Model model, Supplier parent) { - Set reflectionTypes = new HashSet<>(); + private Set> reflectionTypes(Model model, Supplier parent) { + Set> reflectionTypes = new HashSet<>(); Class componentType = determineType(model, parent); if (componentType != null) { processComponent(componentType, reflectionTypes); @@ -306,15 +306,15 @@ private Object instantiate(Class type) { } } - private void processComponent(Class componentType, Set reflectionTypes) { + private void processComponent(Class componentType, Set> reflectionTypes) { BeanDescription beanDescription = this.modelInterpretationContext.getBeanDescriptionCache() .getBeanDescription(componentType); reflectionTypes.addAll(parameterTypesNames(beanDescription.getPropertyNameToAdder().values())); reflectionTypes.addAll(parameterTypesNames(beanDescription.getPropertyNameToSetter().values())); - reflectionTypes.add(componentType.getCanonicalName()); + reflectionTypes.add(componentType); } - private Collection parameterTypesNames(Collection methods) { + private Collection> parameterTypesNames(Collection methods) { return methods.stream() .filter((method) -> !method.getDeclaringClass().equals(ContextAware.class) && !method.getDeclaringClass().equals(ContextAwareBase.class)) @@ -322,7 +322,6 @@ private Collection parameterTypesNames(Collection methods) { .flatMap(Stream::of) .filter((type) -> !type.isPrimitive() && !type.equals(String.class)) .map((type) -> type.isArray() ? type.getComponentType() : type) - .map(Class::getName) .toList(); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackConfigurationAotContributionTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackConfigurationAotContributionTests.java index 7d2e4944cfc2..6cd9d6f29b62 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackConfigurationAotContributionTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackConfigurationAotContributionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,6 +54,7 @@ import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.SerializationHints; +import org.springframework.aot.hint.TypeHint; import org.springframework.aot.hint.TypeReference; import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.aot.test.generate.TestGenerationContext; @@ -149,15 +150,18 @@ void componentModelClassAndSetterParametersAreRegisteredForReflection() { Model model = new Model(); model.getSubModels().add(component); TestGenerationContext generationContext = applyContribution(model); + RuntimeHints runtimeHints = generationContext.getRuntimeHints(); assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(SizeAndTimeBasedRollingPolicy.class)) - .accepts(generationContext.getRuntimeHints()); + .accepts(runtimeHints); assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(FileAppender.class)) - .accepts(generationContext.getRuntimeHints()); - assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(FileSize.class)) - .accepts(generationContext.getRuntimeHints()); + .accepts(runtimeHints); + assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(FileSize.class)).accepts(runtimeHints); assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf( TimeBasedFileNamingAndTriggeringPolicy.class)) - .accepts(generationContext.getRuntimeHints()); + .accepts(runtimeHints); + assertThat(runtimeHints).satisfies(hasValidTypeName(SizeAndTimeBasedRollingPolicy.class)); + assertThat(runtimeHints).satisfies(hasValidTypeName(FileSize.class)); + assertThat(runtimeHints).satisfies(hasValidTypeName(FileAppender.class)); } @Test @@ -167,12 +171,14 @@ void implicitModelClassAndSetterParametersAreRegisteredForReflection() { Model model = new Model(); model.getSubModels().add(implicit); TestGenerationContext generationContext = applyContribution(model); + RuntimeHints runtimeHints = generationContext.getRuntimeHints(); assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(PatternLayoutEncoder.class)) - .accepts(generationContext.getRuntimeHints()); - assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(Layout.class)) - .accepts(generationContext.getRuntimeHints()); - assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(Charset.class)) - .accepts(generationContext.getRuntimeHints()); + .accepts(runtimeHints); + assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(Layout.class)).accepts(runtimeHints); + assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(Charset.class)).accepts(runtimeHints); + assertThat(runtimeHints).satisfies(hasValidTypeName(PatternLayoutEncoder.class)); + assertThat(runtimeHints).satisfies(hasValidTypeName(Layout.class)); + assertThat(runtimeHints).satisfies(hasValidTypeName(Charset.class)); } @Test @@ -186,6 +192,8 @@ void componentModelReferencingImportedClassNameIsRegisteredForReflection() { TestGenerationContext generationContext = applyContribution(model); assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(SizeAndTimeBasedRollingPolicy.class)) .accepts(generationContext.getRuntimeHints()); + assertThat(generationContext.getRuntimeHints()) + .satisfies(hasValidTypeName(SizeAndTimeBasedRollingPolicy.class)); } @Test @@ -196,10 +204,12 @@ void typeFromParentsSetterIsRegisteredForReflection() { component.setClassName(Outer.class.getName()); component.getSubModels().add(implementation); TestGenerationContext generationContext = applyContribution(component); - assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(Outer.class)) - .accepts(generationContext.getRuntimeHints()); + RuntimeHints runtimeHints = generationContext.getRuntimeHints(); + assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(Outer.class)).accepts(runtimeHints); assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(Implementation.class)) - .accepts(generationContext.getRuntimeHints()); + .accepts(runtimeHints); + assertThat(runtimeHints).satisfies(hasValidTypeName(Outer.class)); + assertThat(runtimeHints).satisfies(hasValidTypeName(Implementation.class)); } @Test @@ -210,10 +220,16 @@ void typeFromParentsDefaultClassAnnotatedSetterIsRegisteredForReflection() { component.setClassName(OuterWithDefaultClass.class.getName()); component.getSubModels().add(contract); TestGenerationContext generationContext = applyContribution(component); + RuntimeHints runtimeHints = generationContext.getRuntimeHints(); assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(OuterWithDefaultClass.class)) - .accepts(generationContext.getRuntimeHints()); + .accepts(runtimeHints); assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(Implementation.class)) - .accepts(generationContext.getRuntimeHints()); + .accepts(runtimeHints); + assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(BaseImplementation.Details.class)) + .accepts(runtimeHints); + assertThat(runtimeHints).satisfies(hasValidTypeName(OuterWithDefaultClass.class)); + assertThat(runtimeHints).satisfies(hasValidTypeName(Implementation.class)); + assertThat(runtimeHints).satisfies(hasValidTypeName(BaseImplementation.Details.class)); } @Test @@ -221,8 +237,11 @@ void componentTypesOfArraysAreRegisteredForReflection() { ComponentModel component = new ComponentModel(); component.setClassName(ArrayParameters.class.getName()); TestGenerationContext generationContext = applyContribution(component); + RuntimeHints runtimeHints = generationContext.getRuntimeHints(); assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(InetSocketAddress.class)) - .accepts(generationContext.getRuntimeHints()); + .accepts(runtimeHints); + assertThat(runtimeHints).satisfies(hasValidTypeName(InetSocketAddress.class)); + assertThat(runtimeHints).satisfies(hasValidTypeName(ArrayParameters.class)); } @Test @@ -231,10 +250,12 @@ void placeholdersInComponentClassAttributeAreReplaced() { component.setClassName("${VARIABLE_CLASS_NAME}"); TestGenerationContext generationContext = applyContribution(component, (context) -> context.putProperty("VARIABLE_CLASS_NAME", Outer.class.getName())); - assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(Outer.class)) - .accepts(generationContext.getRuntimeHints()); + RuntimeHints runtimeHints = generationContext.getRuntimeHints(); + assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(Outer.class)).accepts(runtimeHints); assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(Implementation.class)) - .accepts(generationContext.getRuntimeHints()); + .accepts(runtimeHints); + assertThat(runtimeHints).satisfies(hasValidTypeName(Outer.class)); + assertThat(runtimeHints).satisfies(hasValidTypeName(Implementation.class)); } private Predicate invokePublicConstructorsOf(String name) { @@ -250,6 +271,12 @@ private Predicate invokePublicConstructorsAndInspectAndInvokePubli MemberCategory.INVOKE_PUBLIC_METHODS); } + private Consumer hasValidTypeName(Class type) { + return (runtimeHints) -> assertThat(runtimeHints.reflection().getTypeHint(type)).extracting(TypeHint::getType) + .extracting(TypeReference::getName) + .isEqualTo(type.getName()); + } + private Properties load(InputStreamSource source) { try (InputStream inputStream = source.getInputStream()) { Properties properties = new Properties(); @@ -323,7 +350,18 @@ public void setContract(Contract contract) { } - public static class Implementation implements Contract { + public static class BaseImplementation implements Contract { + + public void setDetails(Details details) { + } + + public static final class Details { + + } + + } + + public static class Implementation extends BaseImplementation { }