Refine ConfigurationPropertiesReflectionHintsProcessor
This commit refines ConfigurationPropertiesReflectionHintsProcessor Java bean properties handling in order to register reflection hints only for getters and setters, not for all methods. It avoids including unconditionally method like SpringApplication#load which in turn avoids shipping BeanDefinitionLoader and related transitively used classes in the native image. The gain is significant: it allows to remove up to 700 classes (when no XML parser is used elsewhere) and to reduce the memory footprint by 2M of RSS. Closes gh-32186
This commit is contained in:
parent
49d3bf1d7f
commit
138c55ee11
|
|
@ -23,7 +23,6 @@ import com.google.gson.Gson;
|
|||
import jakarta.json.bind.Jsonb;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
|
|
@ -67,6 +66,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
* @author Sebastien Deleuze
|
||||
* @author Eddú Meléndez
|
||||
* @author Moritz Halbritter
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class HttpMessageConvertersAutoConfigurationTests {
|
||||
|
||||
|
|
@ -281,9 +281,12 @@ class HttpMessageConvertersAutoConfigurationTests {
|
|||
void shouldRegisterHints() {
|
||||
RuntimeHints hints = new RuntimeHints();
|
||||
new HttpMessageConvertersAutoConfigurationRuntimeHints().registerHints(hints, getClass().getClassLoader());
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(Encoding.class)
|
||||
.withMemberCategories(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS))
|
||||
.accepts(hints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(Encoding.class)).accepts(hints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onMethod(Encoding.class, "getCharset")).accepts(hints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onMethod(Encoding.class, "setCharset")).accepts(hints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onMethod(Encoding.class, "isForce")).accepts(hints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onMethod(Encoding.class, "setForce")).accepts(hints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onMethod(Encoding.class, "shouldForce")).rejects(hints);
|
||||
}
|
||||
|
||||
private ApplicationContextRunner allOptionsRunner() {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import java.util.HashSet;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.ReflectionHints;
|
||||
import org.springframework.beans.BeanInfoFactory;
|
||||
import org.springframework.beans.ExtendedBeanInfoFactory;
|
||||
|
|
@ -44,6 +44,7 @@ import org.springframework.util.ReflectionUtils;
|
|||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Moritz Halbritter
|
||||
* @author Sebastien Deleuze
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public final class ConfigurationPropertiesReflectionHintsProcessor {
|
||||
|
|
@ -95,8 +96,6 @@ public final class ConfigurationPropertiesReflectionHintsProcessor {
|
|||
return;
|
||||
}
|
||||
this.seen.add(this.type);
|
||||
reflectionHints.registerType(this.type, (hint) -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
handleConstructor(reflectionHints);
|
||||
if (this.bindConstructor != null) {
|
||||
handleValueObjectProperties(reflectionHints);
|
||||
|
|
@ -129,14 +128,19 @@ public final class ConfigurationPropertiesReflectionHintsProcessor {
|
|||
|
||||
private void handleJavaBeanProperties(ReflectionHints reflectionHints) {
|
||||
for (PropertyDescriptor propertyDescriptor : this.beanInfo.getPropertyDescriptors()) {
|
||||
Method writeMethod = propertyDescriptor.getWriteMethod();
|
||||
if (writeMethod != null) {
|
||||
reflectionHints.registerMethod(writeMethod, ExecutableMode.INVOKE);
|
||||
}
|
||||
Method readMethod = propertyDescriptor.getReadMethod();
|
||||
if (readMethod != null) {
|
||||
ResolvableType propertyType = ResolvableType.forMethodReturnType(readMethod, this.type);
|
||||
String propertyName = propertyDescriptor.getName();
|
||||
if (isSetterMandatory(propertyName, propertyType) && propertyDescriptor.getWriteMethod() == null) {
|
||||
if (isSetterMandatory(propertyName, propertyType) && writeMethod == null) {
|
||||
continue;
|
||||
}
|
||||
handleProperty(reflectionHints, propertyName, propertyType);
|
||||
reflectionHints.registerMethod(readMethod, ExecutableMode.INVOKE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ import org.mockito.InOrder;
|
|||
import org.mockito.Mockito;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
|
||||
import org.springframework.beans.CachedIntrospectionResults;
|
||||
|
|
@ -158,7 +157,7 @@ import static org.mockito.Mockito.spy;
|
|||
* @author Marten Deinum
|
||||
* @author Nguyen Bao Sach
|
||||
* @author Chris Bono
|
||||
* @author Brian Clozel
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class SpringApplicationTests {
|
||||
|
|
@ -1309,9 +1308,12 @@ class SpringApplicationTests {
|
|||
void shouldRegisterHints() {
|
||||
RuntimeHints hints = new RuntimeHints();
|
||||
new SpringApplicationRuntimeHints().registerHints(hints, getClass().getClassLoader());
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(SpringApplication.class)
|
||||
.withMemberCategories(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS))
|
||||
.accepts(hints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(SpringApplication.class)).accepts(hints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onMethod(SpringApplication.class, "setBannerMode"))
|
||||
.accepts(hints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onMethod(SpringApplication.class, "getSources")).accepts(hints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onMethod(SpringApplication.class, "setSources")).accepts(hints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onMethod(SpringApplication.class, "load")).rejects(hints);
|
||||
}
|
||||
|
||||
private <S extends AvailabilityState> ArgumentMatcher<ApplicationEvent> isAvailabilityChangeEventWithState(
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ import org.springframework.aot.generate.DefaultGenerationContext;
|
|||
import org.springframework.aot.generate.GenerationContext;
|
||||
import org.springframework.aot.generate.InMemoryGeneratedFiles;
|
||||
import org.springframework.aot.hint.ExecutableHint;
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.TypeHint;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
|
|
@ -57,6 +56,7 @@ import static org.mockito.Mockito.mock;
|
|||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
|
||||
|
||||
|
|
@ -233,26 +233,17 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
|
|||
@Test
|
||||
void processConfigurationPropertiesWithNestedGenerics() {
|
||||
RuntimeHints runtimeHints = process(NestedGenerics.class);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(NestedGenerics.class)
|
||||
.withMemberCategories(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS))
|
||||
.accepts(runtimeHints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(NestedGenerics.Nested.class)
|
||||
.withMemberCategories(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS))
|
||||
.accepts(runtimeHints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(NestedGenerics.class)).accepts(runtimeHints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(NestedGenerics.Nested.class)).accepts(runtimeHints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void processConfigurationPropertiesWithMultipleNestedClasses() {
|
||||
RuntimeHints runtimeHints = process(TripleNested.class);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(TripleNested.class)
|
||||
.withMemberCategories(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS))
|
||||
.accepts(runtimeHints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(TripleNested.DoubleNested.class)
|
||||
.withMemberCategories(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS))
|
||||
.accepts(runtimeHints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(TripleNested.DoubleNested.Nested.class)
|
||||
.withMemberCategories(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS))
|
||||
.accepts(runtimeHints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(TripleNested.class)).accepts(runtimeHints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(TripleNested.DoubleNested.class)).accepts(runtimeHints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(TripleNested.DoubleNested.Nested.class))
|
||||
.accepts(runtimeHints);
|
||||
}
|
||||
|
||||
private Consumer<TypeHint> javaBeanBinding(Class<?> type) {
|
||||
|
|
@ -263,8 +254,9 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
|
|||
return (entry) -> {
|
||||
assertThat(entry.getType()).isEqualTo(TypeReference.of(type));
|
||||
assertThat(entry.constructors()).singleElement().satisfies(match(constructor));
|
||||
assertThat(entry.getMemberCategories()).containsOnly(MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_PUBLIC_METHODS);
|
||||
assertThat(entry.getMemberCategories()).isEmpty();
|
||||
assertThat(entry.methods()).allMatch((t) -> t.getName().startsWith("set") || t.getName().startsWith("get")
|
||||
|| t.getName().startsWith("is"));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -272,8 +264,8 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests {
|
|||
return (entry) -> {
|
||||
assertThat(entry.getType()).isEqualTo(TypeReference.of(type));
|
||||
assertThat(entry.constructors()).singleElement().satisfies(match(constructor));
|
||||
assertThat(entry.getMemberCategories()).containsOnly(MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_PUBLIC_METHODS);
|
||||
assertThat(entry.getMemberCategories()).isEmpty();
|
||||
assertThat(entry.methods()).isEmpty();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue