68

Because of a plug-in architecture, I'm trying to add a bean programmatically to my webapp. I have a Spring bean created through the @Component annotation, and i am implementing the ApplicationContextAware interface.

My override function looks like this:

@Override
public void setApplicationContext(ApplicationContext applicationContext)
        throws BeansException {

    // this fails
    this.applicationContext = (GenericWebApplicationContext) applicationContext;
 }

Basically, I can't figure out how to add a bean to the applicationContext object given to setApplicationContext. Can anyone tell me how I am going about this the wrong way?

Ok, this is what i ended up with as the solution:

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry bdr)
        throws BeansException {
    BeanDefinition definition = new RootBeanDefinition(
            <My Class>.class);

    bdr.registerBeanDefinition("<my id>", definition);
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
user146714
  • 731
  • 2
  • 7
  • 9

6 Answers6

69

Here is a simple code:

ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
beanFactory.registerSingleton(bean.getClass().getCanonicalName(), bean);
sndyuk
  • 2,720
  • 2
  • 24
  • 32
  • 3
    can you elabore more on the answer? – Gaurav Sep 23 '19 at 07:34
  • Is `applicationContext` guaranteed to be instance of `ConfigurableApplicationContext`? (If not, this could unfortunately prevent the application from deploying) – fbastien Oct 26 '22 at 07:57
51

In Spring 3.0 you can make your bean implement BeanDefinitionRegistryPostProcessor and add new beans via BeanDefinitionRegistry.

In previous versions of Spring you can do the same thing in BeanFactoryPostProcessor (though you need to cast BeanFactory to BeanDefinitionRegistry, which may fail).

axtavt
  • 239,438
  • 41
  • 511
  • 482
  • 12
    thank you. i guess the hardest part is knowing what to look for. – user146714 Dec 27 '10 at 21:58
  • 4
    Please take care that as per [JavaDoc](http://static.springsource.org/spring/docs/3.0.x/javadoc-api/index.html?org/springframework/beans/factory/config/BeanPostProcessor.html): `A BeanFactoryPostProcessor may interact with and modify bean definitions, but never bean instances. Doing so may cause premature bean instantiation, violating the container and causing unintended side-effects. If bean instance interaction is required, consider implementing BeanPostProcessor instead.` – dgiffone May 06 '13 at 09:00
  • 1
    What to do with `BeanDefinitionRegistryPostProcessor`? Suppose I wrote this class. What next? How to activate it? – Dims Mar 25 '17 at 19:53
11

Why do you need it to be of type GenericWebApplicationContext?
I think you can probably work with any ApplicationContext type.

Usually you would use an init method (in addition to your setter method):

@PostConstruct
public void init(){
    AutowireCapableBeanFactory bf = this.applicationContext
        .getAutowireCapableBeanFactory();
    // wire stuff here
}

And you would wire beans by using either

AutowireCapableBeanFactory.autowire(Class, int mode, boolean dependencyInject)

or

AutowireCapableBeanFactory.initializeBean(Object existingbean, String beanName)

Beri
  • 11,470
  • 4
  • 35
  • 57
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
2

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

First initialize Property values

MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
mutablePropertyValues.add("hostName", details.getHostName());
mutablePropertyValues.add("port", details.getPort());

DefaultListableBeanFactory context = new DefaultListableBeanFactory();
GenericBeanDefinition connectionFactory = new GenericBeanDefinition();
connectionFactory.setBeanClass(Class);
connectionFactory.setPropertyValues(mutablePropertyValues);

context.registerBeanDefinition("beanName", connectionFactory);

Add to the list of beans

ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
beanFactory.registerSingleton("beanName", context.getBean("beanName"));
Tarun
  • 61
  • 6
0

If someone looks how to add bean to the spring application before running it and made it by programmatically way, then there is simple example:

Here some bean for health checks, which we want to register in application:

// you no need @Component stereotype annotations here. You go programmatically way
public class HikariPoolHealthIndicatorImpl implements HealthIndicator {
    @Override
    public Health health() {
        if (health < 0.05) {
            return Health.status(Status.DOWN).withDetail("health is not good", health).build();
        }
        return Health.up().build();
    }
}

And how we are registering it with application:

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        // Creating our application
        SpringApplication application = new SpringApplication(MyApplication.class);
        // Adding initializer for application context
        application.addInitializers((ApplicationContextInitializer) applicationContext -> {
            ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
            // Here goes our bean
            HikariPoolHealthIndicatorImpl  bean = new HikariPoolHealthIndicatorImpl ();
            // Registering it
            beanFactory.registerSingleton("Some name for our bean in one word", bean);
        });
        // Starting application, our bean in action 
        application.run(args);
    }
}
Yan Pak
  • 1,767
  • 2
  • 19
  • 15