From f6875b11ff97e3622521e51ec3dd913b6953e32d Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 25 Apr 2023 16:15:58 +0200 Subject: [PATCH 1/2] Generate missing hints for custom PropertySource factories Closes gh-30175 --- .../ConfigurationClassPostProcessor.java | 10 +++++++ ...lassPostProcessorAotContributionTests.java | 26 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) 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..65305db42c 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 @@ -27,6 +27,7 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; 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; @@ -650,6 +652,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo @Override public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) { + registerRuntimeHints(generationContext.getRuntimeHints()); GeneratedMethod generatedMethod = beanFactoryInitializationCode .getMethods() .add("processPropertySources", this::generateAddPropertySourceProcessorMethod); @@ -657,6 +660,13 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo .addInitializer(generatedMethod.toMethodReference()); } + private void registerRuntimeHints(RuntimeHints hints) { + this.descriptors.stream().map(PropertySourceDescriptor::propertySourceFactory) + .filter(Objects::nonNull).distinct() + .forEach(factory -> hints.reflection() + .registerType(factory, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)); + } + 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..efd9a97cb4 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. @@ -29,7 +29,9 @@ 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.predicate.RuntimeHintsPredicates; import org.springframework.aot.test.generate.TestGenerationContext; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; @@ -50,6 +52,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; @@ -291,6 +294,16 @@ 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()); + } + @SuppressWarnings("unchecked") private void compile(BiConsumer, Compiled> result) { MethodReference methodReference = beanFactoryInitializationCode.getInitializers().get(0); @@ -332,6 +345,13 @@ class ConfigurationClassPostProcessorAotContributionTests { } + @Configuration(proxyBeanMethods = false) + @PropertySource(value = "classpath:org/springframework/context/annotation/p1.properties", + factory = CustomPropertySourcesFactory.class) + static class PropertySourceWithCustomFactoryConfiguration { + + } + } @Nested @@ -379,4 +399,8 @@ class ConfigurationClassPostProcessorAotContributionTests { .containsExactly(entry(key.getName(), value.getName())); } + static class CustomPropertySourcesFactory extends DefaultPropertySourceFactory { + + } + } From 02132bd060040c399c9f00ac7d8d83e1e54480b2 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 26 Apr 2023 14:13:19 +0200 Subject: [PATCH 2/2] Infer resource hints for PropertySource#value This commit adds a resource hints for the target(s) of a @PropertySource declaration. Closes gh-30376 --- .../ConfigurationClassPostProcessor.java | 40 +++++++++++++++---- ...lassPostProcessorAotContributionTests.java | 11 +++++ 2 files changed, 44 insertions(+), 7 deletions(-) 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 65305db42c..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 @@ -27,8 +27,8 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; +import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; @@ -84,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; @@ -328,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); @@ -338,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. @@ -646,8 +661,11 @@ 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 @@ -661,10 +679,18 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo } private void registerRuntimeHints(RuntimeHints hints) { - this.descriptors.stream().map(PropertySourceDescriptor::propertySourceFactory) - .filter(Objects::nonNull).distinct() - .forEach(factory -> hints.reflection() - .registerType(factory, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)); + 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) { 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 efd9a97cb4..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 @@ -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; @@ -31,6 +32,7 @@ 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; @@ -249,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(); @@ -265,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(); @@ -304,6 +311,10 @@ class ConfigurationClassPostProcessorAotContributionTests { .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);