Programmatic Bean registration

Viewed 1465

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 )

2 Answers

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.

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" ) );
    }
}
Related