Restore native support for record beans

After b374824319 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
This commit is contained in:
Sébastien Deleuze 2023-04-28 09:57:22 +02:00
parent 82e5464972
commit cefb734763
3 changed files with 49 additions and 9 deletions

View File

@ -27,6 +27,7 @@ import org.springframework.aot.generate.GenerationContext;
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 @@ class BeanRegistrationsAotContribution
}
private void generateRegisterHints(RuntimeHints runtimeHints, Map<BeanRegistrationKey, Registration> 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);
}
});
}
/**

View File

@ -36,6 +36,7 @@ import org.springframework.beans.factory.aot.BeanRegistrationsAotContribution.Re
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 @@ class BeanRegistrationsAotContributionTests {
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 @@ class BeanRegistrationsAotContributionTests {
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 @@ class BeanRegistrationsAotContributionTests {
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 @@ class BeanRegistrationsAotContributionTests {
}
};
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 @@ class BeanRegistrationsAotContributionTests {
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 @@ class BeanRegistrationsAotContributionTests {
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)));
}
}

View File

@ -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) { }