Browse Source

Add support for BeanRegistrar registration on GenericApplicationContext

Closes gh-34574
pull/34585/head
Juergen Hoeller 9 months ago
parent
commit
86b21d9b5c
  1. 5
      spring-beans/src/main/java/org/springframework/beans/factory/BeanRegistrar.java
  2. 13
      spring-beans/src/main/java/org/springframework/beans/factory/support/BeanRegistryAdapter.java
  3. 22
      spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigRegistry.java
  4. 17
      spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java
  5. 30
      spring-web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java

5
spring-beans/src/main/java/org/springframework/beans/factory/BeanRegistrar.java

@ -60,9 +60,10 @@ import org.springframework.core.env.Environment; @@ -60,9 +60,10 @@ import org.springframework.core.env.Environment;
public interface BeanRegistrar {
/**
* Register beans in a programmatic way.
* @param registry the bean registry
* Register beans on the given {@link BeanRegistry} in a programmatic way.
* @param registry the bean registry to operate on
* @param env the environment that can be used to get the active profile or some properties
*/
void register(BeanRegistry registry, Environment env);
}

13
spring-beans/src/main/java/org/springframework/beans/factory/support/BeanRegistryAdapter.java

@ -56,6 +56,12 @@ public class BeanRegistryAdapter implements BeanRegistry { @@ -56,6 +56,12 @@ public class BeanRegistryAdapter implements BeanRegistry {
private final @Nullable MultiValueMap<String, BeanDefinitionCustomizer> customizers;
public BeanRegistryAdapter(DefaultListableBeanFactory beanFactory, Environment environment,
Class<? extends BeanRegistrar> beanRegistrarClass) {
this(beanFactory, beanFactory, environment, beanRegistrarClass, null);
}
public BeanRegistryAdapter(BeanDefinitionRegistry beanRegistry, ListableBeanFactory beanFactory,
Environment environment, Class<? extends BeanRegistrar> beanRegistrarClass) {
@ -73,6 +79,7 @@ public class BeanRegistryAdapter implements BeanRegistry { @@ -73,6 +79,7 @@ public class BeanRegistryAdapter implements BeanRegistry {
this.customizers = customizers;
}
@Override
public <T> String registerBean(Class<T> beanClass) {
String beanName = BeanDefinitionReaderUtils.uniqueBeanName(beanClass.getName(), this.beanRegistry);
@ -154,7 +161,8 @@ public class BeanRegistryAdapter implements BeanRegistry { @@ -154,7 +161,8 @@ public class BeanRegistryAdapter implements BeanRegistry {
}
}
static class BeanSpecAdapter<T> implements Spec<T> {
private static class BeanSpecAdapter<T> implements Spec<T> {
private final RootBeanDefinition beanDefinition;
@ -239,7 +247,8 @@ public class BeanRegistryAdapter implements BeanRegistry { @@ -239,7 +247,8 @@ public class BeanRegistryAdapter implements BeanRegistry {
}
}
static class SupplierContextAdapter implements SupplierContext {
private static class SupplierContextAdapter implements SupplierContext {
private final ListableBeanFactory beanFactory;

22
spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigRegistry.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2025 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.
@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
package org.springframework.context.annotation;
import org.springframework.beans.factory.BeanRegistrar;
/**
* Common interface for annotation config application contexts,
* defining {@link #register} and {@link #scan} methods.
@ -26,17 +28,33 @@ package org.springframework.context.annotation; @@ -26,17 +28,33 @@ package org.springframework.context.annotation;
public interface AnnotationConfigRegistry {
/**
* Register one or more component classes to be processed.
* Invoke the given registrars for registering their beans with this
* application context.
* <p>This can be used to register custom beans without inferring
* annotation-based characteristics for primary/fallback/lazy-init,
* rather specifying those programmatically if needed.
* @param registrars one or more {@link BeanRegistrar} instances
* @since 7.0
* @see #register(Class[])
*/
void register(BeanRegistrar... registrars);
/**
* Register one or more component classes to be processed, inferring
* annotation-based characteristics for primary/fallback/lazy-init
* just like for scanned component classes.
* <p>Calls to {@code register} are idempotent; adding the same
* component class more than once has no additional effect.
* @param componentClasses one or more component classes,
* for example, {@link Configuration @Configuration} classes
* @see #scan(String...)
*/
void register(Class<?>... componentClasses);
/**
* Perform a scan within the specified base packages.
* @param basePackages the packages to scan for component classes
* @see #register(Class[])
*/
void scan(String... basePackages);

17
spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java

@ -30,6 +30,7 @@ import org.springframework.aot.hint.support.ClassHintUtils; @@ -30,6 +30,7 @@ import org.springframework.aot.hint.support.ClassHintUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanRegistrar;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
@ -38,6 +39,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -38,6 +39,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanRegistryAdapter;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
@ -594,6 +596,21 @@ public class GenericApplicationContext extends AbstractApplicationContext implem @@ -594,6 +596,21 @@ public class GenericApplicationContext extends AbstractApplicationContext implem
registerBeanDefinition(nameToUse, beanDefinition);
}
/**
* Invoke the given registrars for registering their beans with this
* application context.
* <p>This can be used to apply encapsulated pieces of programmatic
* bean registration to this application context without relying on
* individual calls to its context-level {@code registerBean} methods.
* @param registrars one or more {@link BeanRegistrar} instances
* @since 7.0
*/
public void register(BeanRegistrar... registrars) {
for (BeanRegistrar registrar : registrars) {
new BeanRegistryAdapter(this.beanFactory, getEnvironment(), registrar.getClass()).register(registrar);
}
}
/**
* {@link RootBeanDefinition} subclass for {@code #registerBean} based

30
spring-web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2025 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.
@ -22,7 +22,9 @@ import java.util.Set; @@ -22,7 +22,9 @@ import java.util.Set;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.BeanRegistrar;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.BeanRegistryAdapter;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigRegistry;
@ -104,6 +106,8 @@ public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWe @@ -104,6 +106,8 @@ public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWe
private @Nullable ScopeMetadataResolver scopeMetadataResolver;
private final Set<BeanRegistrar> beanRegistrars = new LinkedHashSet<>();
private final Set<Class<?>> componentClasses = new LinkedHashSet<>();
private final Set<String> basePackages = new LinkedHashSet<>();
@ -148,6 +152,20 @@ public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWe @@ -148,6 +152,20 @@ public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWe
}
/**
* Invoke the given registrars for registering their beans with this
* application context.
* <p>Note that {@link #refresh()} must be called in order for the context
* to fully process the new classes.
* @param registrars one or more {@link BeanRegistrar} instances
* @since 7.0
*/
@Override
public void register(BeanRegistrar... registrars) {
Assert.notEmpty(registrars, "At least one BeanRegistrar must be specified");
Collections.addAll(this.beanRegistrars, registrars);
}
/**
* Register one or more component classes to be processed.
* <p>Note that {@link #refresh()} must be called in order for the context
@ -222,6 +240,16 @@ public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWe @@ -222,6 +240,16 @@ public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWe
scanner.setScopeMetadataResolver(scopeMetadataResolver);
}
if (!this.beanRegistrars.isEmpty()) {
if (logger.isDebugEnabled()) {
logger.debug("Applying bean registrars: [" +
StringUtils.collectionToCommaDelimitedString(this.beanRegistrars) + "]");
}
for (BeanRegistrar registrar : this.beanRegistrars) {
new BeanRegistryAdapter(beanFactory, getEnvironment(), registrar.getClass()).register(registrar);
}
}
if (!this.componentClasses.isEmpty()) {
if (logger.isDebugEnabled()) {
logger.debug("Registering component classes: [" +

Loading…
Cancel
Save