From cefb734763a8a83a22123789f52cb5cc31283eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Fri, 28 Apr 2023 09:57:22 +0200 Subject: [PATCH] Restore native support for record beans After b3748243199fe809f177bb7922c69f7eb37ab6d2 related to gh-29246, `"queryAllDeclaredMethods": true` is now added on all registered beans. This legit change triggers oracle/graal#6510. This commit workarounds this GraalVM bug, and should be removed once the GraalVM fix has reached a wide enough audience. Closes gh-30383 --- .../aot/BeanRegistrationsAotContribution.java | 12 +++++++-- ...BeanRegistrationsAotContributionTests.java | 27 ++++++++++++++----- .../beans/testfixture/beans/RecordBean.java | 19 +++++++++++++ 3 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/RecordBean.java diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContribution.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContribution.java index 93f8967ed7e6..110d55568957 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContribution.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContribution.java @@ -27,6 +27,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.ReflectionHints; import org.springframework.aot.hint.RuntimeHints; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.javapoet.ClassName; @@ -109,8 +110,15 @@ private void generateRegisterAliasesMethod(MethodSpec.Builder method) { } private void generateRegisterHints(RuntimeHints runtimeHints, Map registrations) { - registrations.keySet().forEach(beanRegistrationKey -> runtimeHints.reflection() - .registerType(beanRegistrationKey.beanClass(), MemberCategory.INTROSPECT_DECLARED_METHODS)); + registrations.keySet().forEach(beanRegistrationKey -> { + ReflectionHints hints = runtimeHints.reflection(); + Class beanClass = beanRegistrationKey.beanClass(); + hints.registerType(beanClass, MemberCategory.INTROSPECT_DECLARED_METHODS); + // Workaround for https://github.com/oracle/graal/issues/6510 + if (beanClass.isRecord()) { + hints.registerType(beanClass, MemberCategory.INVOKE_DECLARED_METHODS); + } + }); } /** diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java index 72c6bbc10e4b..48a3d1a217f7 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java @@ -36,6 +36,7 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.testfixture.beans.RecordBean; import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.beans.testfixture.beans.factory.aot.MockBeanFactoryInitializationCode; import org.springframework.core.test.io.support.MockSpringFactoriesLoader; @@ -75,7 +76,7 @@ void applyToAppliesContribution() { RegisteredBean registeredBean = registerBean(new RootBeanDefinition(TestBean.class)); BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(this.methodGeneratorFactory, registeredBean, null, List.of()); - BeanRegistrationsAotContribution contribution = createContribution(generator); + BeanRegistrationsAotContribution contribution = createContribution(TestBean.class, generator); contribution.applyTo(this.generationContext, this.beanFactoryInitializationCode); compile((consumer, compiled) -> { DefaultListableBeanFactory freshBeanFactory = new DefaultListableBeanFactory(); @@ -89,7 +90,7 @@ void applyToAppliesContributionWithAliases() { RegisteredBean registeredBean = registerBean(new RootBeanDefinition(TestBean.class)); BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(this.methodGeneratorFactory, registeredBean, null, List.of()); - BeanRegistrationsAotContribution contribution = createContribution(generator, "testAlias"); + BeanRegistrationsAotContribution contribution = createContribution(TestBean.class, generator, "testAlias"); contribution.applyTo(this.generationContext, this.beanFactoryInitializationCode); compile((consumer, compiled) -> { DefaultListableBeanFactory freshBeanFactory = new DefaultListableBeanFactory(); @@ -106,7 +107,7 @@ void applyToWhenHasNameGeneratesPrefixedFeatureName() { RegisteredBean registeredBean = registerBean(new RootBeanDefinition(TestBean.class)); BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(this.methodGeneratorFactory, registeredBean, null, List.of()); - BeanRegistrationsAotContribution contribution = createContribution(generator); + BeanRegistrationsAotContribution contribution = createContribution(TestBean.class, generator); contribution.applyTo(this.generationContext, this.beanFactoryInitializationCode); compile((consumer, compiled) -> { SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions"); @@ -129,7 +130,7 @@ MethodReference generateBeanDefinitionMethod(GenerationContext generationContext } }; - BeanRegistrationsAotContribution contribution = createContribution(generator); + BeanRegistrationsAotContribution contribution = createContribution(TestBean.class, generator); contribution.applyTo(this.generationContext, this.beanFactoryInitializationCode); assertThat(beanRegistrationsCodes).hasSize(1); BeanRegistrationsCode actual = beanRegistrationsCodes.get(0); @@ -141,13 +142,25 @@ void applyToRegisterReflectionHints() { RegisteredBean registeredBean = registerBean(new RootBeanDefinition(TestBean.class)); BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(this.methodGeneratorFactory, registeredBean, null, List.of()); - BeanRegistrationsAotContribution contribution = createContribution(generator); + BeanRegistrationsAotContribution contribution = createContribution(TestBean.class, generator); contribution.applyTo(this.generationContext, this.beanFactoryInitializationCode); assertThat(reflection().onType(TestBean.class) .withMemberCategory(MemberCategory.INTROSPECT_DECLARED_METHODS)) .accepts(this.generationContext.getRuntimeHints()); } + @Test + void applyToRegisterReflectionHintsOnRecordBean() { + RegisteredBean registeredBean = registerBean(new RootBeanDefinition(RecordBean.class)); + BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(this.methodGeneratorFactory, + registeredBean, null, List.of()); + BeanRegistrationsAotContribution contribution = createContribution(RecordBean.class, generator); + contribution.applyTo(this.generationContext, this.beanFactoryInitializationCode); + assertThat(reflection().onType(RecordBean.class) + .withMemberCategories(MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.INVOKE_DECLARED_METHODS)) + .accepts(this.generationContext.getRuntimeHints()); + } + private RegisteredBean registerBean(RootBeanDefinition rootBeanDefinition) { String beanName = "testBean"; this.beanFactory.registerBeanDefinition(beanName, rootBeanDefinition); @@ -177,10 +190,10 @@ private void compile(BiConsumer, Compiled> result.accept(compiled.getInstance(Consumer.class), compiled)); } - private BeanRegistrationsAotContribution createContribution( + private BeanRegistrationsAotContribution createContribution(Class beanClass, BeanDefinitionMethodGenerator methodGenerator,String... aliases) { return new BeanRegistrationsAotContribution( - Map.of(new BeanRegistrationKey("testBean", TestBean.class), new Registration(methodGenerator, aliases))); + Map.of(new BeanRegistrationKey("testBean", beanClass), new Registration(methodGenerator, aliases))); } } diff --git a/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/RecordBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/RecordBean.java new file mode 100644 index 000000000000..75419ab1cb73 --- /dev/null +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/RecordBean.java @@ -0,0 +1,19 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.beans.testfixture.beans; + +public record RecordBean(String name) { }