7

I would like to create custom child context from some configuration and additionally add some beans to it programmatically.

I read answer https://stackoverflow.com/a/4540762/258483 about BeanDefinitionRegistryPostProcessor but don't understand how to use it. If I write implementation of BeanDefinitionRegistryPostProcessor then what to do with it next? Add to context? But this is the question: how to add bean to context! If I would be able to add BeanDefinitionRegistryPostProcessor to context, then why I would ask how to add beans?

The problem is that I have context and want to add bean to it.

I know I can instantiate beans and autowire them with

Context#getAutowireCapableBeanFactory().createBean(klass);

but this apparently just wires class, but not adds it to context?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Dims
  • 47,675
  • 117
  • 331
  • 600
  • 1
    You basically have to add the bean to the context you get when creating a new Spring context, check this out: http://stackoverflow.com/a/15329777/3858121 – Japu_D_Cret Mar 25 '17 at 20:26
  • Does this answer your question? [Add Bean Programmatically to Spring Web App Context](https://stackoverflow.com/questions/4540713/add-bean-programmatically-to-spring-web-app-context) – akop Jan 05 '23 at 20:35

3 Answers3

17

From Spring 5.0 onwards, you can register your beans dynamically directly using the GenericApplicationContext.

GenericApplicationContext ac = ....;
// example
ac.registerBean("myspecialBean", Integer.class, () -> new Integer(100));
// using BeanDefinitionCustomizer
ac.registerBean("myLazySpecialBean", Integer.class, () -> new Integer(100), (bd) -> bd.setLazyInit(true));

See the javadoc here for different APIs for registerBean

Philipp-P
  • 45
  • 8
Neo
  • 4,640
  • 5
  • 39
  • 53
  • 2
    I am using Spring 5.3 (Spring Boot v2.7.1) and `registerBean()` is unknown to `ApplicationContext`. What am I doing wrong? – WebViewer Oct 30 '22 at 14:06
1

Originally answered here by myself, repeating it here as well.

Actually AnnotationConfigApplicationContext derived from AbstractApplicationContext, which has empty postProcessBeanFactory method left for override

/**
 * Modify the application context's internal bean factory after its standard
 * initialization. All bean definitions will have been loaded, but no beans
 * will have been instantiated yet. This allows for registering special
 * BeanPostProcessors etc in certain ApplicationContext implementations.
 * @param beanFactory the bean factory used by the application context
 */
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}

To leverage this, Create AnnotationConfigApplicationContextProvider class which may look like following(given for Vertx instance example, you can use MyClass instead)...

public class CustomAnnotationApplicationContextProvider {
private final Vertx vertx;

public CustomAnnotationApplicationContextProvider(Vertx vertx) {
    this.vertx = vertx;
}

/**
 * Register all beans to spring bean factory
 *
 * @param beanFactory, spring bean factory to register your instances
 */
private void configureBeans(ConfigurableListableBeanFactory beanFactory) {
    beanFactory.registerSingleton("vertx", vertx);
}

/**
 * Proxy method to create {@link AnnotationConfigApplicationContext} instance with no params
 *
 * @return {@link AnnotationConfigApplicationContext} instance
 */
public AnnotationConfigApplicationContext get() {
    return new AnnotationConfigApplicationContext() {

        @Override
        protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
            super.postProcessBeanFactory(beanFactory);
            configureBeans(beanFactory);
        }
    };
}

/**
 * Proxy method to call {@link AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(DefaultListableBeanFactory)} with our logic
 *
 * @param beanFactory bean factory for spring
 * @return
 * @see AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(DefaultListableBeanFactory)
 */
public AnnotationConfigApplicationContext get(DefaultListableBeanFactory beanFactory) {
    return new AnnotationConfigApplicationContext(beanFactory) {

        @Override
        protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
            super.postProcessBeanFactory(beanFactory);
            configureBeans(beanFactory);
        }
    };
}

/**
 * Proxy method to call {@link AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class[])} with our logic
 *
 * @param annotatedClasses, set of annotated classes for spring
 * @return
 * @see AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class[])
 */
public AnnotationConfigApplicationContext get(Class<?>... annotatedClasses) {
    return new AnnotationConfigApplicationContext(annotatedClasses) {

        @Override
        protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
            super.postProcessBeanFactory(beanFactory);
            configureBeans(beanFactory);
        }
    };
}

/**
 * proxy method to call {@link AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...)} with our logic
 *
 * @param basePackages set of base packages for spring
 * @return
 * @see AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...)
 */
public AnnotationConfigApplicationContext get(String... basePackages) {
    return new AnnotationConfigApplicationContext(basePackages) {

        @Override
        protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
            super.postProcessBeanFactory(beanFactory);
            configureBeans(beanFactory);
        }
    };
}
}

While creating ApplicationContext you can create it using

Vertx vertx = ...; // either create or for vertx, it'll be passed to main verticle
ApplicationContext context = new CustomAnnotationApplicationContextProvider(vertx).get(ApplicationSpringConfig.class);
yugandhar
  • 580
  • 7
  • 16
1

I'm using this in my current application, because I already have an object that I want to register. I can have applicationContext injected as a bean.

private void registerAsBean(
  ApplicationContext injectedApplicationContext, 
  MyClass objectToRegisterAsBean, 
  String beanName) {

    AutowireCapableBeanFactory beanFactory = 
      injectedApplicationContext.getAutowireCapableBeanFactory();
    beanFactory.initializeBean(objectToRegisterAsBean, beanName);
}
RobertG
  • 1,550
  • 1
  • 23
  • 42