Skip to content

Commit

Permalink
Restore native support for record beans
Browse files Browse the repository at this point in the history
After b374824 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
  • Loading branch information
sdeleuze committed Apr 28, 2023
1 parent 82e5464 commit cefb734
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -109,8 +110,15 @@ private void generateRegisterAliasesMethod(MethodSpec.Builder method) {
}

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);
}
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand All @@ -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");
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -177,10 +190,10 @@ private void compile(BiConsumer<Consumer<DefaultListableBeanFactory>, 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)));
}

}
Original file line number Diff line number Diff line change
@@ -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) { }

0 comments on commit cefb734

Please sign in to comment.