-
Notifications
You must be signed in to change notification settings - Fork 38.2k
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
AOT/native support when registering beans with the Kotlin DSL #29555
Comments
Thanks for the detailed report and the sample. FTR this should have been reported against the core Spring Framework as the exception you've shared doesn't contain anything Spring Boot-specifc (I've transferred it). Unfortunately, this is something that we don't support at the moment. The AOT engine works on bean definitions and they need to define the necessary metadata so that we can translate this to generated code. The exception you're getting is because the DSL you use relies on an instance supplier (lambda style). There's no way for us to analyse the lambda and figure out what to write. I think we should fail with a different exception though. We know it will never work and yet we attempt to locate the metadata. I've created #29556. For this particular issue at hand, we'll need to give it some thoughts. |
Understood. Is it fair to say that in order to generate a native image in 6.0.0 it is necessary to completely abandon What would be an alternate way to supply instances without lambdas that would provide the necessary metadata? In my use-case the routes are created at startup depending on application configuration parameters(application.yml). Thank you for looking into this issue. |
That's indeed something we don't support yet, and I understand how it can be an issue for our users that were leveraging those functional constructs because they are naturally better supported on native via the static analysis and with less hints involved. It is currently possible to make it work with the pretty ugly code below: fun beans() = ApplicationContextInitializer<GenericApplicationContext> {
it.registerBean(RouterFunction::class.java, { customizer ->
customizer.beanClassName = "com.example.demo.DemoApplicationKt"
customizer.factoryMethodName = "writeEndpoint"
})
} We could potentially update fun beans() = beans {
bean<RouterFunction<*>>(
beanClassName = "com.example.demo.DemoApplicationKt",
factoryMethodName = "writeEndpoint")
} Not amazing, but at least it provides a reasonable workaround, and I think it does not hurt to add those 2 parameters with default values since we have a use case here. Depending on the feedback I may create a related issue for that refinement. The best way to support this use case would be IMO to allow using beans instance suppliers as they are, with inference mechanism disable which IMO could make sense with this kind of functional constructs where much less hints are needed, and where having full control on what is done without hint inference could be a feature and the desired behavior. I think this could be explored as part of #29553. |
I was able to verify the workaround. Thank you @sdeleuze. Allow me to add that in our applications we make use of all the available DSLs and the only spring annotations we use are As one example, the security and routing requirements of our APIs are different between mobile/desktop(X-Auth-Tokens, routes, etc) and browser(cookies, csrf, etc) applications while the APIs themselves are practically the same. With the functional DSL we are able to conditionally accommodate all requirements in a cross-cutting way without having to break apart services or introduce gateways. At our early stage, these APIs save cloud resources, cognitive overhead and development time. Additionally, we are finding that the functional model facilitates onboarding of developers coming in from different ecosystem as the code (wiring) is explicit and IDE-assisted. Anyhow, thank you for providing functional DSLs and I look forward to having first-class support of native images for them. |
We successfully migrated a few of our microservices (Bean DSL, functional routing) to spring-native - it works now in production. spring-native is practically deprecated but Spring Boot 3 is unable to run our services in native mode. So it looks like regression to me. The idiomatic programming model is more essential for us than benefits from native runtime so we start to 'denativate' our apps. Fortunately, native-build services are not so many... Maybe raise some epic-type ticket for first-class functional support in AOT mode (bean definitions (#29556), routing, security, and so on)? And clearly says these limitations in the documentation: want to go functional (Kotlin or Java) - AOT is not an option. Sample bean: bean {
val kafkaConsumerProps =
Binder.get(env).bindOrCreate("kafka.consumer.properties", mapOf(String::class.java, String::class.java))
val options = ReceiverOptions.create<String, ApiEvent>(kafkaConsumerProps as Map<String, Any>)
.consumerProperty(
ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,
env.getRequiredProperty("kafka.bootstrap-servers")
)
.commitBatchSize(25)
// 10 minutes of commit retry
.maxCommitAttempts(1200)
.withValueDeserializer(ApiEventDeserializer(ref()))
.subscription(setOf("risk-engine.api.events"))
KafkaCloudEventReceiver<ApiEvent>(options, provider(), ref())
} Produces error:
|
Another workaround is to use
|
@vladimirfx I understand the pain, we are going to document this issue. |
https://stackoverflow.com/questions/21887358/reflection-type-inference-on-java-8-lambdas provides interesting background why instance supplier callback can't be supported in AOT code generation. |
In addition to the short term documentation of this limitation, there could be potentially way to tackle that middle term, by skipping AOT processing for some It requires to be able to identify them (by class name, method name, annotation as suggested in #29484 or use cases like all Since there is a potential way to solve that issue, but that's not straightforward, I will create a distinct issue for fixing the documentation, restore the initial purpose of that issue (how to allow functional constructs in |
ApplicationContextInitializer
using lambdas or method references with AOT/native
It is by design not possible to generate code that handles bean definitions with user-provided instance suppliers because the JVM does not allow to get a stable reference reusable at runtime on the lambda or method reference in the code generated AOT. Before this commit, such instance supplier was ignored. After this commit, an IllegalArgumentException is thrown, allowing projects to be aware this is not supported and enforce related refactorings. The related issue spring-projectsgh-29555 describes how this limitation could be relaxed in the future. Closes spring-projectsgh-29556
Any chance of seeing implementation in 6.1.x? Use case: |
With 6.1 RC around the corner, I am sorry to say that we won't have the time to craft the necessary API change for this. Keep in mind that using bean instance supplier really goes against the first principle of AOT of detecting components and their stereotype. As such, offering a support to suppress AOT processing on those (as we can't infer what's behind a lambda) will probably other side effects in terms of hints generation typically. |
ApplicationContextInitializer
using lambdas or method references with AOT/native
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
I've narrowed the scope of this issue to the use of the Kotin DSL although anybody writing beans with a lambda can make use of the option that has been added in #33243. Since we have a DSL for Kotlin, we can do that on behalf of the user and that's what we'll do here. |
This commit review the support for AOT by only ignoring beans that are using an instance supplier. The Kotlin DSL has a way to register a bean by type where all inferences should happen as usual and that was previously ignored. This commit no longer ignores those beans so AOT can optimize them, and makes sure that they are not registered again when running with AOT optimizations. This change makes it so that the order in which beans are registered is now different when running with AOT optimizations, and we'll have to find a solution for that. See gh-29555
An early exception is thrown by the "bootBuildImage" Gradle task when attempting to create a native image for a WebFlux application using a functional router.
Exception:
Issue Observed in Spring Boot Version: >= 3.x.x
Key Dependencies: Spring WebFlux
Reproducer: https://github.com/PedroAlvarado/boot-issue-33318
Steps to Reproduce
The text was updated successfully, but these errors were encountered: