Skip to content

Expose the functional bean registration API via SpringApplication #8115

Open
@wilkinsona

Description

@wilkinsona
Member

SpringApplication makes it hard to get hold of the application context before it's refreshed. @michael-simons came up with this:

SpringApplication springApplication = new SpringApplication(Application.class);
springApplication.addInitializers((GenericApplicationContext ctx) -> {
    ctx.registerBean(Greeter.class, Greeter::new);
});
springApplication.run(args);

It would be nice if the initialiser wasn't needed and the API provided an easier way to access the context before it's refreshed.

Activity

michael-simons

michael-simons commented on Jan 26, 2017

@michael-simons
Contributor

Background was (again) creating an example for my book how to use Spring 5 enhancements out of the box with Spring Boot. I assume that this question will arise, especially with the functional router.

dsyer

dsyer commented on Jan 26, 2017

@dsyer
Member

way to access the context before it's refreshed

Isn't that the definition of an ApplicationContextInitializer? How could the API be any simpler without duplicating?

michael-simons

michael-simons commented on Jan 26, 2017

@michael-simons
Contributor

I - as a user - would have been looking for something like

springApplication
    .registerBean(Greeter.class, Greeter::new);
    .registerBean(Foo.class, () -> new WhateverFoo());

and having Boot delegate to an Initializer for me.

dsyer

dsyer commented on Jan 26, 2017

@dsyer
Member

As a user I'd expect to have something that worked in integration tests with @SpringBootTest as well as when I run my main method (changing the API of SpringApplication doesn't help there).

wilkinsona

wilkinsona commented on Jan 26, 2017

@wilkinsona
MemberAuthor

How could the API be any simpler without duplicating?

I'm not sure, however the pure Spring example in the Kotlin blog post involves less ceremony:

GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(Foo.class);
context.registerBean(Bar.class, () -> new 
	Bar(context.getBean(Foo.class))
);

It's a shame that SpringApplication gives you an extra hoop to jump through. Perhaps we'll have to live with that, but I think it's worth some thought.

michael-simons

michael-simons commented on Jan 26, 2017

@michael-simons
Contributor

Doesn't seem to take integration tests with @SpringBootTest into account as well… It's a good point!

sdeleuze

sdeleuze commented on Jan 26, 2017

@sdeleuze
Contributor

+1 for allowing using the functional bean registration API with Spring Boot, even if I am not sure yet exactly where should be the entry point. For the record, here is the Kotlin function bean registration I used before switching to the app to Boot. I found no proper wat to integrate with Boot so I had to remove it to use @Bean but I would be happy to restore it if you provide a way to do that easily.

If I remember well I had no way to integrate my functional bean registration at the right level in Spring Boot application context lifecycle even with @bclozel help.

dsyer

dsyer commented on Jan 26, 2017

@dsyer
Member

Just to complete that loop: if you define the initializer above as a standalone class and declare it in spring.factories it will work with @SpringBootTest and in a simple one-line main() method. I see no reason why that wouldn't work with Kotlin if anyone cares.

michael-simons

michael-simons commented on Jan 26, 2017

@michael-simons
Contributor

I think that @dsyer's approach is ok, can also be used through context.initializer.classes, but if people see those nice examples, they forget about IT tests… (like I did).

wilkinsona

wilkinsona commented on Jan 26, 2017

@wilkinsona
MemberAuthor

Sounds like this might be turning into a documentation issue

dsyer

dsyer commented on Jan 26, 2017

@dsyer
Member

What about an annotation instead of spring.factories, and a special component scan just for initializers and listeners? That's not even Spring 5 specific.

36 remaining items

daliborfilus

daliborfilus commented on Oct 23, 2020

@daliborfilus

I don't want to hijack this thread by something non-relevant, so please excuse me if it is.

I'm currently trying to migrate my application into BeanDefinitionDsl style and I stumbled upon this issue too.

Using runApplication(...) { beans() } doesn't work from tests, as some of you have already mentioned.

So I currently need to have an initializer class, which delegates into the functional DSL (not a big deal):

class BeansInitializer : ApplicationContextInitializer<GenericApplicationContext> {
    override fun initialize(context: GenericApplicationContext) {
        beans().initialize(context)
    }
}

where beans() is a your average fun beans() = beans { /* ... */ }.
Then I need to have context.initializer.classes=app.BeansInitializer in application.properties.

This works correctly in tests - without modifying them in any way.

However, tests annotated with @JsonTest doesn't work, because in that beans { /* ... */ }, I'm creating beans that use ref<NamedParameterJdbcTemplate>() and that of course isn't present in the @JsonTest context.

Is it somehow possible to know from ApplicationContextInitializer (like beans {} is) if I have full context loaded, or if I'm in the slice test? I tried to use if (!context.containsBean("namedParameterJdbcTemplate")) { beans().initialize(context) }, but this 1) doesn't work (maybe ordering issue or the bean is named differently?) and 2) is fragile - it would require updating every time I change bean definitions.

Or I have to use general @SpringBootTest if I want to use BeanDefinitionDSL?

wilkinsona

wilkinsona commented on Oct 24, 2020

@wilkinsona
MemberAuthor

@daliborfilus I think you'll have to use @SpringBootTest to use the DSL for bean registration.

daliborfilus

daliborfilus commented on Oct 25, 2020

@daliborfilus

@wilkinsona I thought so. It's not a big problem, really. Thank you for corfimation.

dsyer

dsyer commented on Oct 25, 2020

@dsyer
Member

If you were prepared to put some conditional logic around all the bean definitions in the DSL I think maybe you could get it to work. Probably not what you want though, and not the “house style” with Spring Fu.

albertocavalcante

albertocavalcante commented on Mar 10, 2023

@albertocavalcante

Is this still being considered? Thanks

wilkinsona

wilkinsona commented on Mar 10, 2023

@wilkinsona
MemberAuthor

@albertocavalcante Yes. The issue would have been closed if that was not the case. We don't have any plans to work on it in the near future as we have other higher priority work on our plates. This is indicated by the issue being in the general backlog milestone.

dsyer

dsyer commented on Mar 10, 2023

@dsyer
Member

FWIW there are already quite a few options for using functional bean definitions via SpringApplication (e.g. add an initializer) or normal @Configuration (e.g. provide a BeanDefinitionRegistryPostProcessor). Changes to Spring Boot could make that more prominent in the programming model, I suppose, but it wouldn't change the user configuration code much. Most of my comments above were directed to work towards providing a reflection-free alternative, but if you don't care about that you can still write functional bean definitions.

wakingrufus

wakingrufus commented on Jul 3, 2024

@wakingrufus

I have published a way of solving this problem. Based on Spring-Fu, I incorporated some additional functionality that I needed to implement this approach at my day job, which allows it to be used within existing Autoconfiguration-based applications. Take a look: https://github.com/wakingrufus/spring-funk A good place to start to read more is here: https://wakingrufus.github.io/spring-funk/introduction.html Feel free to reach out to me @wakingrufus on the kotlin slack, or open a discussion on https://github.com/wakingrufus/spring-funk/discussions I welcome feedback. This is a project I am willing to work on long term if it has traction on making the functional approach to Spring Boot more accessible and mainstream

sdeleuze

sdeleuze commented on Mar 6, 2025

@sdeleuze
Contributor

Spring Framework 7 introduces first class support for programmatic bean registration with @Configuration + BeanRegistrar, see spring-projects/spring-framework#18353 (comment) for more details. Happy to explore with the Spring Boot team how that could be used for Boot use cases, and if this issue should be closed or if we are missing important use cases.

removed
for: team-meetingAn issue we'd like to discuss as a team to make progress
on Mar 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @GreyTeardrop@bclozel@dsyer@sdeleuze@daliborfilus

        Issue actions

          Expose the functional bean registration API via SpringApplication · Issue #8115 · spring-projects/spring-boot