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 7a367ce384a..6d5e3e2f93e 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 @@ -280,7 +280,8 @@ class SpringBootJoranConfigurator extends JoranConfigurator { private Class loadComponentType(String componentType) { try { - return ClassUtils.forName(componentType, getClass().getClassLoader()); + return ClassUtils.forName(this.modelInterpretationContext.subst(componentType), + getClass().getClassLoader()); } catch (Throwable ex) { throw new RuntimeException("Failed to load component type '" + componentType + "'", ex); 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 5681a08019d..4714728fd48 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 @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; @@ -190,6 +191,18 @@ class LogbackConfigurationAotContributionTests { .accepts(generationContext.getRuntimeHints()); } + @Test + void placeholdersInComponentClassAttributeAreReplaced() { + ComponentModel component = new ComponentModel(); + 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()); + assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(Implementation.class)) + .accepts(generationContext.getRuntimeHints()); + } + private Predicate invokePublicConstructorsOf(String name) { return RuntimeHintsPredicates.reflection() .onType(TypeReference.of(name)) @@ -220,7 +233,13 @@ class LogbackConfigurationAotContributionTests { } private TestGenerationContext applyContribution(Model model) { + return this.applyContribution(model, (context) -> { + }); + } + + private TestGenerationContext applyContribution(Model model, Consumer contextCustomizer) { LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + contextCustomizer.accept(context); SpringBootJoranConfigurator configurator = new SpringBootJoranConfigurator(null); configurator.setContext(context); withSystemProperty("spring.aot.processing", "true", () -> configurator.processModel(model));