-
Notifications
You must be signed in to change notification settings - Fork 40.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expose the functional bean registration API via SpringApplication #8115
Comments
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. |
Isn't that the definition of an |
I - as a user - would have been looking for something like
and having Boot delegate to an Initializer for me. |
As a user I'd expect to have something that worked in integration tests with |
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 |
Doesn't seem to take integration tests with |
+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 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. |
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 |
I think that @dsyer's approach is ok, can also be used through |
Sounds like this might be turning into a documentation issue |
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. |
I don't like adding this directly to private class MyApp implements BeanRegistrar {
@Override
public void registerBeans(BeanRegistry registry) {
registry.registerBean(Greeter.class, Greeter::new);
registry.registerBean(Foo.class, () -> new WhateverFoo());
}
public static void main(String... args) {
SpringApplication.run(MyApp.class);
}
} this assumes some new Spring Framework interface |
Spring Framework should do that itself then (easy to detect instances of its own interface being registered as |
While Functional bean registration API and |
Dropping the annotation model entirely opens another can of worms. We'd need to consider component scanning, the JPA base package stuff and how to do excludes. We'd also need to think about testing. I'm tempted to say we should park this until 2.0 is out. |
If there isn't a massive usage of this new model in the coming months, I am convinced we should tackle this one post 2.0. We need more use cases to make it right. |
+1 for waiting post 2.0 for this, I am just adding a link to this Spring Framework 5 functional bean declaration blog post in Kotlin with a SPR-13779 should also allow easier integration with Spring Boot 2. |
FYI, I have updated Kotlin functional bean definition DSL to implement Beans.kt fun beans() = beans {
bean<Greeter>()
bean<Foo> { WhateverFoo() }
// ...
} Application.kt SpringApplication(Application::class.java).apply {
addInitializers(beans())
run(*args)
} |
Since SPR-13779 has returned to 5.x backlog, I have given a new try to Boot + functional bean registration and have created a Boot 2.0 based branch of my The I guess the kind of experimental support we can provide with Spring Framework 5.0 and Spring Boot 2.0 for early adopters and people curious about experimenting on this. |
I think https://github.com/tgirard12/spring-webflux-kotlin-dsl shows a very nice example of what could look like a functional WebFlux application written in Kotlin (not sure it apply to Java). It could give us some ideas for this issue. |
Thanks a lot @sdeleuze I dived into this already and I like it very much. |
One thing I like about java-config it the ability to make my configuration very navigable following a pattern like this. This allows me to split up my configuration and navigate the configuration easily. I've been trying to do something like this using BeanDefinitionDsl and it's been more of a hassle than I expected. Are any of you playing with the possibility of pulling the equivalent of @import into BeanDefinitionDsl? I have a feeling this isn't the perfect spot to be posting this question. :( I posted a kind-of-related question on stackoverflow which makes me feel better about commenting here. :) |
@benjishults I have only found this question that I have just answered. Please post your other question to StackOverflow and I will answer you there. |
After more thoughts, I think What would be awesome is to support top level function reference in Kotlin (conceptually similar to static method reference in Java), in order to be able to define That would allow to just remove the need for Any thoughts? |
If possible, I would like to move forward on this issue for 2.1 by providing a PR implementing this improvement if we can come to an agreement about what needs to be done. Adding support for Kotlin top level functions references like These properties could be defined via I am not sure if we should reuse Last point, it would be super useful to be able to specify the initializer via an annotation attribute. So in my dreams, the improvement would allow to write something like bellow taken in account by application and tests: @SpringBootApplication(initializer="com.example.ApplicationKt.beans") // Could be @SpringBootApplication(initializer="com.example.Initializer") to specify a class that would make that useful in Java as well
class Application
fun main(args: Array<String>) {
runApplication<Application>(*args)
}
fun beans() = beans {
bean<UserHandler>()
// ...
} Any thoughts? |
After more thoughts and discussions on that topic, I think I have changed my mind and agree with @snicoll about the fact that adding more functional bean definition support to Boot raises a wide range of other questions. I plan to experiment on that to move forward, but I don't think this has to be a Spring Boot 2.1 topic anymore. IMO better to focus on clearly defined features like #8762 to polish existing scope than introducing dedicated support for something different to what people use in Boot currently. |
This issue was hijacked a bit by the Kotlin discussion. To set it back on course, here is a proof of concept for a way for Boot to support functional bean registration (along the lines @philwebb showed above): https://github.com/dsyer/spring-boot-initializer. Simple example: @SpringBootApplication
public class InitializerApplication
implements ApplicationContextInitializer<GenericApplicationContext> {
public static void main(String[] args) {
SpringApplication.run(InitializerApplication.class, args);
}
@Override
public void initialize(GenericApplicationContext context) {
context.registerBean(RouterFunction.class, this::userEndpoints);
}
private RouterFunction<?> userEndpoints() {
return route(GET("/"), request -> ok().body(Mono.just("Hello"), String.class));
}
} |
Fixing this means we'll also probably want to change the default context to not be an |
What we need I think is a way to externalize and configure the public static void main(String[] args) {
new SpringApplication(InitApplication.class) {
@Override
protected void load(ApplicationContext context, Object[] sources) {
};
}.run(args); Formalizing that as an option, or making the loader a strategy that can be manipulated, ideally through a listener so it can happen in integration tests for instance, would be ideal. |
FWIW I have found it relatively easy to override the WebApplicationType type = application.getWebApplicationType();
Class<?> contextType = getApplicationContextType(application);
if (type == WebApplicationType.NONE) {
if (contextType == AnnotationConfigApplicationContext.class
|| contextType == null) {
application
.setApplicationContextClass(GenericApplicationContext.class);
}
}
else if (type == WebApplicationType.REACTIVE) {
if (contextType == AnnotationConfigReactiveWebApplicationContext.class) {
application.setApplicationContextClass(
ReactiveWebServerApplicationContext.class);
}
}
else if (type == WebApplicationType.SERVLET) {
if (contextType == AnnotationConfigServletWebServerApplicationContext.class) {
application.setApplicationContextClass(
ServletWebServerApplicationContext.class);
}
} This works in a listener for |
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 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 This works correctly in tests - without modifying them in any way. However, tests annotated with Is it somehow possible to know from ApplicationContextInitializer (like Or I have to use general |
@daliborfilus I think you'll have to use |
@wilkinsona I thought so. It's not a big problem, really. Thank you for corfimation. |
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. |
Is this still being considered? Thanks |
@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. |
FWIW there are already quite a few options for using functional bean definitions via |
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 |
SpringApplication
makes it hard to get hold of the application context before it's refreshed. @michael-simons came up with this: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.
The text was updated successfully, but these errors were encountered: