mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 16:39:29 +00:00
Add support for package-private BeanRegistrar
Closes gh-35803
This commit is contained in:
+58
-9
@@ -42,16 +42,20 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
|
||||
import org.springframework.aot.generate.AccessControl;
|
||||
import org.springframework.aot.generate.GeneratedClass;
|
||||
import org.springframework.aot.generate.GeneratedMethod;
|
||||
import org.springframework.aot.generate.GeneratedMethods;
|
||||
import org.springframework.aot.generate.GenerationContext;
|
||||
import org.springframework.aot.generate.MethodReference;
|
||||
import org.springframework.aot.generate.MethodReference.ArgumentCodeGenerator;
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.ReflectionHints;
|
||||
import org.springframework.aot.hint.ResourceHints;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
@@ -872,11 +876,14 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
@Override
|
||||
public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) {
|
||||
GeneratedMethod generatedMethod = beanFactoryInitializationCode.getMethods().add(
|
||||
"applyBeanRegistrars", builder -> this.generateApplyBeanRegistrarsMethod(builder, generationContext));
|
||||
"applyBeanRegistrars", builder -> this.generateApplyBeanRegistrarsMethod(builder,
|
||||
generationContext, beanFactoryInitializationCode.getClassName()));
|
||||
beanFactoryInitializationCode.addInitializer(generatedMethod.toMethodReference());
|
||||
}
|
||||
|
||||
private void generateApplyBeanRegistrarsMethod(MethodSpec.Builder method, GenerationContext generationContext) {
|
||||
private void generateApplyBeanRegistrarsMethod(MethodSpec.Builder method, GenerationContext generationContext,
|
||||
ClassName className) {
|
||||
|
||||
ReflectionHints reflectionHints = generationContext.getRuntimeHints().reflection();
|
||||
method.addJavadoc("Apply bean registrars.");
|
||||
method.addModifiers(Modifier.PRIVATE);
|
||||
@@ -915,7 +922,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
}
|
||||
}
|
||||
}
|
||||
method.addCode(generateRegisterCode());
|
||||
method.addCode(generateRegisterCode(className, generationContext));
|
||||
}
|
||||
|
||||
private void checkUnsupportedFeatures(AbstractBeanDefinition beanDefinition) {
|
||||
@@ -937,37 +944,79 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
return code.build();
|
||||
}
|
||||
|
||||
private CodeBlock generateRegisterCode() {
|
||||
private CodeBlock generateRegisterCode(ClassName className, GenerationContext generationContext) {
|
||||
Builder code = CodeBlock.builder();
|
||||
Builder metadataReaderFactoryCode = null;
|
||||
NameAllocator nameAllocator = new NameAllocator();
|
||||
for (Map.Entry<String, List<BeanRegistrar>> beanRegistrarEntry : this.beanRegistrars.entrySet()) {
|
||||
for (BeanRegistrar beanRegistrar : beanRegistrarEntry.getValue()) {
|
||||
String beanRegistrarName = nameAllocator.newName(StringUtils.uncapitalize(beanRegistrar.getClass().getSimpleName()));
|
||||
code.addStatement("$T $L = new $T()", beanRegistrar.getClass(), beanRegistrarName, beanRegistrar.getClass());
|
||||
Constructor<?> constructor = BeanUtils.getResolvableConstructor(beanRegistrar.getClass());
|
||||
boolean visible = isVisible(constructor, className);
|
||||
if (visible) {
|
||||
code.addStatement("$T $L = new $T()", beanRegistrar.getClass(), beanRegistrarName, beanRegistrar.getClass());
|
||||
}
|
||||
else {
|
||||
try {
|
||||
Class<?> configClass = ClassUtils.forName(beanRegistrarEntry.getKey(), beanRegistrar.getClass().getClassLoader());
|
||||
GeneratedClass generatedClass = generationContext.getGeneratedClasses()
|
||||
.getOrAddForFeatureComponent("BeanRegistrars", configClass, type ->
|
||||
type.addJavadoc("Bean registrars for {@link $T}.", configClass)
|
||||
.addModifiers(Modifier.PUBLIC));
|
||||
GeneratedMethod generatedMethod = generatedClass.getMethods().add(
|
||||
"get" + beanRegistrar.getClass().getSimpleName(),
|
||||
method -> method
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.returns(BeanRegistrar.class)
|
||||
.addStatement("return new $T()", beanRegistrar.getClass()));
|
||||
code.addStatement("$T $L = $L", BeanRegistrar.class, beanRegistrarName,
|
||||
generatedMethod.toMethodReference().toInvokeCodeBlock(ArgumentCodeGenerator.none()));
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
if (beanRegistrar instanceof ImportAware) {
|
||||
if (metadataReaderFactoryCode == null) {
|
||||
metadataReaderFactoryCode = CodeBlock.builder();
|
||||
metadataReaderFactoryCode.addStatement("$T metadataReaderFactory = new $T()",
|
||||
MetadataReaderFactory.class, CachingMetadataReaderFactory.class);
|
||||
}
|
||||
CodeBlock setImportMetadataCode;
|
||||
if (visible) {
|
||||
setImportMetadataCode = CodeBlock.builder()
|
||||
.addStatement("$L.setImportMetadata(metadataReaderFactory.getMetadataReader($S).getAnnotationMetadata())",
|
||||
beanRegistrarName, beanRegistrarEntry.getKey()).build();
|
||||
}
|
||||
else {
|
||||
setImportMetadataCode = CodeBlock.builder()
|
||||
.addStatement("(($T)$L).setImportMetadata(metadataReaderFactory.getMetadataReader($S).getAnnotationMetadata())",
|
||||
ImportAware.class, beanRegistrarName, beanRegistrarEntry.getKey()).build();
|
||||
}
|
||||
code.beginControlFlow("try")
|
||||
.addStatement("$L.setImportMetadata(metadataReaderFactory.getMetadataReader($S).getAnnotationMetadata())",
|
||||
beanRegistrarName, beanRegistrarEntry.getKey())
|
||||
.add(setImportMetadataCode)
|
||||
.nextControlFlow("catch ($T ex)", IOException.class)
|
||||
.addStatement("throw new $T(\"Failed to read metadata for '$L'\", ex)",
|
||||
IllegalStateException.class, beanRegistrarEntry.getKey())
|
||||
.endControlFlow();
|
||||
}
|
||||
code.addStatement("$L.register(new $T(($T)$L, $L, $L, $T.class, $L), $L)", beanRegistrarName,
|
||||
code.addStatement("$L.register(new $T(($T)$L, $L, $L, $L.getClass(), $L), $L)", beanRegistrarName,
|
||||
BeanRegistryAdapter.class, BeanDefinitionRegistry.class, BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE,
|
||||
BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE, ENVIRONMENT_VARIABLE, beanRegistrar.getClass(),
|
||||
BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE, ENVIRONMENT_VARIABLE, beanRegistrarName,
|
||||
CUSTOMIZER_MAP_VARIABLE, ENVIRONMENT_VARIABLE);
|
||||
}
|
||||
}
|
||||
return (metadataReaderFactoryCode == null ? code.build() : metadataReaderFactoryCode.add(code.build()).build());
|
||||
}
|
||||
|
||||
private boolean isVisible(Constructor<?> ctor, ClassName className) {
|
||||
AccessControl classAccessControl = AccessControl.forClass(ctor.getDeclaringClass());
|
||||
AccessControl memberAccessControl = AccessControl.forMember(ctor);
|
||||
AccessControl.Visibility visibility = AccessControl.lowest(classAccessControl, memberAccessControl).getVisibility();
|
||||
return (visibility == AccessControl.Visibility.PUBLIC || (visibility != AccessControl.Visibility.PRIVATE &&
|
||||
ctor.getDeclaringClass().getPackageName().equals(className.packageName())));
|
||||
}
|
||||
|
||||
private CodeBlock generateInitDestroyMethods(String beanName, AbstractBeanDefinition beanDefinition,
|
||||
String[] methodNames, String method, ReflectionHints reflectionHints) {
|
||||
|
||||
|
||||
+71
-5
@@ -62,6 +62,7 @@ import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.DefaultPropertySourceFactory;
|
||||
import org.springframework.core.test.tools.CompileWithForkedClassLoader;
|
||||
import org.springframework.core.test.tools.Compiled;
|
||||
import org.springframework.core.test.tools.TestCompiler;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
@@ -503,7 +504,7 @@ public class ConfigurationClassPostProcessorAotContributionTests {
|
||||
@Test
|
||||
void applyToWhenIsImportAware() {
|
||||
BeanFactoryInitializationAotContribution contribution = getContribution(CommonAnnotationBeanPostProcessor.class,
|
||||
ImportAwareBeanRegistrarConfiguration.class);
|
||||
ImportAwareConfiguration.class);
|
||||
assertThat(contribution).isNotNull();
|
||||
contribution.applyTo(generationContext, beanFactoryInitializationCode);
|
||||
compile((initializer, compiled) -> {
|
||||
@@ -511,7 +512,42 @@ public class ConfigurationClassPostProcessorAotContributionTests {
|
||||
initializer.accept(freshContext);
|
||||
freshContext.refresh();
|
||||
assertThat(freshContext.getBean(ClassNameHolder.class).className())
|
||||
.isEqualTo(ImportAwareBeanRegistrarConfiguration.class.getName());
|
||||
.isEqualTo(ImportAwareConfiguration.class.getName());
|
||||
freshContext.close();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@CompileWithForkedClassLoader
|
||||
void applyToWhenIsPackagePrivate() throws NoSuchMethodException {
|
||||
BeanFactoryInitializationAotContribution contribution = getContribution(PackagePrivateConfiguration.class);
|
||||
assertThat(contribution).isNotNull();
|
||||
contribution.applyTo(generationContext, beanFactoryInitializationCode);
|
||||
Constructor<Foo> fooConstructor = Foo.class.getDeclaredConstructor();
|
||||
compile((initializer, compiled) -> {
|
||||
GenericApplicationContext freshContext = new GenericApplicationContext();
|
||||
initializer.accept(freshContext);
|
||||
freshContext.refresh();
|
||||
assertThat(freshContext.getBean(Foo.class)).isNotNull();
|
||||
assertThat(RuntimeHintsPredicates.reflection().onConstructorInvocation(fooConstructor))
|
||||
.accepts(generationContext.getRuntimeHints());
|
||||
freshContext.close();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@CompileWithForkedClassLoader
|
||||
void applyToWhenIsPackagePrivateAndImportAware() {
|
||||
BeanFactoryInitializationAotContribution contribution = getContribution(CommonAnnotationBeanPostProcessor.class,
|
||||
PackagePrivateAndImportAwareConfiguration.class);
|
||||
assertThat(contribution).isNotNull();
|
||||
contribution.applyTo(generationContext, beanFactoryInitializationCode);
|
||||
compile((initializer, compiled) -> {
|
||||
GenericApplicationContext freshContext = new GenericApplicationContext();
|
||||
initializer.accept(freshContext);
|
||||
freshContext.refresh();
|
||||
assertThat(freshContext.getBean(ClassNameHolder.class).className())
|
||||
.isEqualTo(PackagePrivateAndImportAwareConfiguration.class.getName());
|
||||
freshContext.close();
|
||||
});
|
||||
}
|
||||
@@ -578,7 +614,7 @@ public class ConfigurationClassPostProcessorAotContributionTests {
|
||||
}
|
||||
|
||||
@Import(ImportAwareBeanRegistrar.class)
|
||||
public static class ImportAwareBeanRegistrarConfiguration {
|
||||
public static class ImportAwareConfiguration {
|
||||
}
|
||||
|
||||
public static class ImportAwareBeanRegistrar implements BeanRegistrar, ImportAware {
|
||||
@@ -596,9 +632,39 @@ public class ConfigurationClassPostProcessorAotContributionTests {
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
this.importMetadata = importMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable AnnotationMetadata getImportMetadata() {
|
||||
return this.importMetadata;
|
||||
@Configuration
|
||||
@Import(PackagePrivateBeanRegistrar.class)
|
||||
static class PackagePrivateConfiguration {
|
||||
}
|
||||
|
||||
static class PackagePrivateBeanRegistrar implements BeanRegistrar {
|
||||
|
||||
@Override
|
||||
public void register(BeanRegistry registry, Environment env) {
|
||||
registry.registerBean(Foo.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Import(PackagePrivateAndImportAwareBeanRegistrar.class)
|
||||
static class PackagePrivateAndImportAwareConfiguration {
|
||||
}
|
||||
|
||||
static class PackagePrivateAndImportAwareBeanRegistrar implements BeanRegistrar, ImportAware {
|
||||
|
||||
@Nullable
|
||||
private AnnotationMetadata importMetadata;
|
||||
|
||||
@Override
|
||||
public void register(BeanRegistry registry, Environment env) {
|
||||
registry.registerBean(ClassNameHolder.class, spec -> spec.supplier(context ->
|
||||
new ClassNameHolder(this.importMetadata == null ? null : this.importMetadata.getClassName())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
this.importMetadata = importMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user