2

i would like to understand what is the programmatic equivalent of a @Bean annotated bean registration

Lets say i have a class like this:

@Configuration
public class SimpleConfiguration {
    @Bean
    public BigDecimal aDecimal( String example ) {
        return new BigDecimal( example );
    }
}

here is what i think happens here:

  1. somehow spring register this method as a factory for a bean named "aDecimal" of type BigDecimal, with a dependency on a bean of type String
  2. at some point this method will be called with the right bean as parameter and the result will be the instance associated to the "aDecimal" bean.

If i wanted to do the same with something like this:

@Configuration
public class DynamicConfiguration {

    public void registerDefinition() {
        /* i know it can't be like this but i hope it's clear what i mean */
        register( "aDecimal", (example) -> aDecimal( example ) );
    }

    public BigDecimal aDecimal( String example ) {
        /* this could be any kind of complex bean creation method */
        return new BigDecimal( example );
    }
}

what would be the right way to achieve this result?

i already researched a bit about this, and i found for example

How do I create beans programmatically in Spring Boot?

but this kind of registration doesn't seem as powerful as the annotation, and let's spring instatiate the bean, i want the bean to be instatied by a provided method

How to programmatically create bean definition with injected properties?

and this is missing the ability to call a method with injected bean parameters.

the reason i want to do this, is that i have some configuration classes that hold a lot of the same kind of beans with different qualifiers, based on a configuration file. now every time the configuration file expands, i need to add new beans and configurations ( many of these are spring SessionFactories and SpringIntegration flows so i need these things to be spring beans )

davide bubz
  • 1,321
  • 13
  • 31

2 Answers2

2

You need to consider to use IntegrationFlowContext:

@Autowired
private IntegrationFlowContext integrationFlowContext;

...


IntegrationFlow myFlow = f -> ...;

BeanFactoryHandler additionalBean = new BeanFactoryHandler();

IntegrationFlowRegistration flowRegistration =
            this.integrationFlowContext.registration(myFlow)
                    .addBean(additionalBean)
                    .register();

It provides for you hooks to register additional beans at runtime, not only IntegrationFlow structure.

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • This is interesting, i'll play with it a bit to see if i can make it work :) Anyway i was thinking there should also be a solution out there that doesn't require spring integration – davide bubz Oct 04 '17 at 17:19
  • ??? You question is marked with `spring-integration`, plus you mention integration flows in your question. So, I made an assumption that you definitely deal with dynamic flows, where `IntegrationFlowContext` comes to the rescue. The Spring Framework doesn't have clean solution for runtime bean registration, so in Spring Integration we decide to give it a shot. The solution is based on the `BeanFactory.registerSingleton()` though. I think you may take a look into its source code for some ideas if Spring Integration isn't your main goal. – Artem Bilan Oct 04 '17 at 17:35
  • i added spring-integration becouse im using it, and solutions that use it are ok for my use case, but i have other projects that don't have spring-integration as a dependency and if i can find a solution that just works with spring that would be even better – davide bubz Oct 04 '17 at 19:34
2

I found the way to solve my problem, it all happens in the BeanDefinition "phase" this way everything is managed by spring and works exactly the same as as a @Bean annotated method with injected parameters, it also cleanly bridge between annotated and programmatically registered beans.

here is what i did

@Configuration
@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( classes = { TestSpringDynamicConfiguration.class } )
public class TestSpringDynamicConfiguration implements BeanDefinitionRegistryPostProcessor {

    @Autowired
    @Qualifier( "qualified" )
    private String dynamicString;

    @Bean
    public Integer zero() {
        return 0;
    }

    public String zeroString( Integer aInteger ) {
        return aInteger.toString();
    }

    @Override
    public void postProcessBeanDefinitionRegistry( final BeanDefinitionRegistry registry ) throws BeansException {
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setAutowireMode( GenericBeanDefinition.AUTOWIRE_CONSTRUCTOR );
        beanDefinition.setScope( BeanDefinition.SCOPE_SINGLETON );

        beanDefinition.setFactoryBeanName( "testSpringDynamicConfiguration" );
        beanDefinition.setFactoryMethodName( "zeroString" );

        registry.registerBeanDefinition( "qualified", beanDefinition );
    }

    @Override public void postProcessBeanFactory( final ConfigurableListableBeanFactory beanFactory ) throws BeansException { }

    @Test
    public void testDynamicConfiguration() throws Exception {
        assertThat( dynamicString, is( "0" ) );
    }
}
davide bubz
  • 1,321
  • 13
  • 31