diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java index 3974df9681..f68c34ef0c 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java @@ -28,6 +28,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; @@ -40,6 +41,7 @@ import org.springframework.aop.framework.autoproxy.AutoProxyUtils; import org.springframework.aot.generate.GeneratedMethod; import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.hint.ExecutableMode; +import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.ResourceHints; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.TypeReference; @@ -82,7 +84,9 @@ import org.springframework.core.PriorityOrdered; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.StandardEnvironment; +import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.PropertySourceDescriptor; import org.springframework.core.io.support.PropertySourceProcessor; @@ -326,7 +330,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo if (hasPropertySourceDescriptors || hasImportRegistry) { return (generationContext, code) -> { if (hasPropertySourceDescriptors) { - new PropertySourcesAotContribution(this.propertySourceDescriptors).applyTo(generationContext, code); + new PropertySourcesAotContribution(this.propertySourceDescriptors, this::resolvePropertySourceLocation) + .applyTo(generationContext, code); } if (hasImportRegistry) { new ImportAwareAotContribution(beanFactory).applyTo(generationContext, code); @@ -336,6 +341,18 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo return null; } + @Nullable + private Resource resolvePropertySourceLocation(String location) { + try { + String resolvedLocation = (this.environment != null + ? this.environment.resolveRequiredPlaceholders(location) : location); + return this.resourceLoader.getResource(resolvedLocation); + } + catch (Exception ex) { + return null; + } + } + /** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. @@ -644,12 +661,16 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo private final List descriptors; - PropertySourcesAotContribution(List descriptors) { + private final Function resourceResolver; + + PropertySourcesAotContribution(List descriptors, Function resourceResolver) { this.descriptors = descriptors; + this.resourceResolver = resourceResolver; } @Override public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) { + registerRuntimeHints(generationContext.getRuntimeHints()); GeneratedMethod generatedMethod = beanFactoryInitializationCode .getMethods() .add("processPropertySources", this::generateAddPropertySourceProcessorMethod); @@ -657,6 +678,21 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo .addInitializer(generatedMethod.toMethodReference()); } + private void registerRuntimeHints(RuntimeHints hints) { + for (PropertySourceDescriptor descriptor : this.descriptors) { + Class factory = descriptor.propertySourceFactory(); + if (factory != null) { + hints.reflection().registerType(factory, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); + } + for (String location : descriptor.locations()) { + Resource resource = this.resourceResolver.apply(location); + if (resource != null && resource.exists() && resource instanceof ClassPathResource classpathResource) { + hints.resources().registerPattern(classpathResource.getPath()); + } + } + } + } + private void generateAddPropertySourceProcessorMethod(MethodSpec.Builder method) { method.addJavadoc("Apply known @PropertySources to the environment."); method.addModifiers(Modifier.PRIVATE); diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorAotContributionTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorAotContributionTests.java index 80a784c1b0..13cb21a2f3 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorAotContributionTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorAotContributionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Predicate; import javax.lang.model.element.Modifier; @@ -29,7 +30,10 @@ import org.junit.jupiter.api.Test; import org.springframework.aot.generate.MethodReference; import org.springframework.aot.generate.MethodReference.ArgumentCodeGenerator; +import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.ResourcePatternHint; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.aot.test.generate.TestGenerationContext; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; @@ -50,6 +54,7 @@ import org.springframework.context.testfixture.context.generator.SimpleComponent import org.springframework.core.Ordered; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.io.ResourceLoader; +import org.springframework.core.io.support.DefaultPropertySourceFactory; import org.springframework.core.test.tools.Compiled; import org.springframework.core.test.tools.TestCompiler; import org.springframework.core.type.AnnotationMetadata; @@ -246,6 +251,8 @@ class ConfigurationClassPostProcessorAotContributionTests { BeanFactoryInitializationAotContribution contribution = getContribution( PropertySourceConfiguration.class); contribution.applyTo(generationContext, beanFactoryInitializationCode); + assertThat(resource("org/springframework/context/annotation/p1.properties")) + .accepts(generationContext.getRuntimeHints()); compile((initializer, compiled) -> { GenericApplicationContext freshContext = new GenericApplicationContext(); ConfigurableEnvironment environment = freshContext.getEnvironment(); @@ -262,6 +269,9 @@ class ConfigurationClassPostProcessorAotContributionTests { BeanFactoryInitializationAotContribution contribution = getContribution( PropertySourceConfiguration.class, PropertySourceDependentConfiguration.class); contribution.applyTo(generationContext, beanFactoryInitializationCode); + assertThat(resource("org/springframework/context/annotation/p1.properties") + .and(resource("org/springframework/context/annotation/p2.properties"))) + .accepts(generationContext.getRuntimeHints()); compile((initializer, compiled) -> { GenericApplicationContext freshContext = new GenericApplicationContext(); ConfigurableEnvironment environment = freshContext.getEnvironment(); @@ -291,6 +301,20 @@ class ConfigurationClassPostProcessorAotContributionTests { }); } + @Test + void applyToWhenHasCustomFactoryRegistersHints() { + BeanFactoryInitializationAotContribution contribution = getContribution( + PropertySourceWithCustomFactoryConfiguration.class); + contribution.applyTo(generationContext, beanFactoryInitializationCode); + assertThat(RuntimeHintsPredicates.reflection().onType(CustomPropertySourcesFactory.class) + .withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)) + .accepts(generationContext.getRuntimeHints()); + } + + private Predicate resource(String location) { + return RuntimeHintsPredicates.resource().forResource(location); + } + @SuppressWarnings("unchecked") private void compile(BiConsumer, Compiled> result) { MethodReference methodReference = beanFactoryInitializationCode.getInitializers().get(0); @@ -332,6 +356,13 @@ class ConfigurationClassPostProcessorAotContributionTests { } + @Configuration(proxyBeanMethods = false) + @PropertySource(value = "classpath:org/springframework/context/annotation/p1.properties", + factory = CustomPropertySourcesFactory.class) + static class PropertySourceWithCustomFactoryConfiguration { + + } + } @Nested @@ -379,4 +410,8 @@ class ConfigurationClassPostProcessorAotContributionTests { .containsExactly(entry(key.getName(), value.getName())); } + static class CustomPropertySourcesFactory extends DefaultPropertySourceFactory { + + } + }