codeconsole opened a new pull request, #16019:
URL: https://github.com/apache/grails-core/pull/16019
## Summary
This is a proof-of-concept / discussion-stage spike, **not a request to
merge as-is**. It explores whether Grails-authored bean wiring can get
`doWithSpring()`-style DSL ergonomics while compiling down to a genuine Spring
Boot `@AutoConfiguration` class — no closures, no custom runtime, nothing but
real `@Bean` methods in the compiled bytecode.
Two pieces:
1. **`grails-beans-dsl`** — a `@GrailsBeans` annotation + local Groovy AST
transformation (`GrailsBeansASTTransformation`) that rewrites a `beans = {
bean(Type[, "name"]) { ... } }` closure into public `@Bean` factory methods,
with `.conditionalOnMissingBean(Type...)` chaining and typed closure parameters
becoming constructor-style-injected method parameters.
2. **`org.apache.grails.buildsrc.autoconfiguration-imports`** — a new
build-logic convention plugin that generates
`META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`
by scanning a module's own compiled classes for `@AutoConfiguration`, the same
way `META-INF/grails-plugin.xml` is already generated from scanned
`*GrailsPlugin` classes elsewhere in this build — so the imports file never has
to be hand-maintained.
## Why this, given `beanRegistrar()` already exists
`Plugin.beanRegistrar()` (#15994) plus the before-autoconfiguration retiming
(#15934) already give Grails a Spring-native, statically-compilable
bean-registration mechanism that runs before Boot evaluates its
`@ConditionalOnMissingBean` defaults. That solves the *coarse* half of what
this spike targets.
It does **not** solve the *fine-grained* half: `beanRegistrar()` output is
inserted as a single hard cut point ahead of *all* Boot auto-configuration,
ordered only relative to other Grails plugins by name — it has no way to
express "run before this specific `@AutoConfiguration` but after that one," the
way `@AutoConfiguration(before = ..., after = ...)` lets a real
autoconfiguration class do against *any* other autoconfiguration on the
classpath, Grails-authored or third-party. That's the open question this spike
is raising: is it worth Grails-authored beans compiling down to genuine
`@AutoConfiguration` classes so they can fully participate in Boot's own
ordering graph, with DSL ergonomics comparable to `beanRegistrar()`?
## Usage example
Author:
```groovy
package myapp
import org.grails.compiler.beans.GrailsBeans
import org.springframework.boot.autoconfigure.AutoConfiguration
@GrailsBeans
@AutoConfiguration
class MyBeans {
def beans = {
bean(Greeter, 'greeter') {
new Greeter('hello')
}
bean(FancyGreeter,
'fancyGreeter').conditionalOnMissingBean(FancyGreeter) {
new FancyGreeter()
}
bean(LoudGreeter, 'loudGreeter') { Greeter greeter ->
new LoudGreeter(greeter)
}
}
}
```
Compiles to the equivalent of (verified directly via `javap` — zero closures
survive into the bytecode):
```java
@AutoConfiguration
public class MyBeans {
@Bean("greeter")
public Greeter greeter() { return new Greeter("hello"); }
@Bean("fancyGreeter")
@ConditionalOnMissingBean(FancyGreeter.class)
public FancyGreeter fancyGreeter() { return new FancyGreeter(); }
@Bean("loudGreeter")
public LoudGreeter loudGreeter(Greeter greeter) { return new
LoudGreeter(greeter); }
}
```
Register it by applying the new convention plugin (see
`grails-beans-dsl-example/build.gradle`):
```groovy
plugins {
id 'org.apache.grails.buildsrc.autoconfiguration-imports'
}
```
which scans the module's compiled output and writes
`META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`
automatically at build time.
`grails-beans-dsl-example/src/test/groovy/beandsl/example/ExampleBeansAutoDiscoverySpec.groovy`
is the end-to-end proof this actually works as a whole pipeline: it boots a
bare `@EnableAutoConfiguration` application with **zero explicit reference** to
`ExampleBeans`, and the beans are still discovered and correctly wired — purely
from the generated imports file.
## What's here
- `grails-beans-dsl` — the annotation + transform, with unit tests that
compile fixture sources and inspect the generated bytecode directly.
- `grails-beans-dsl-example` — a worked example (`Greeter` / `FancyGreeter`
/ `LoudGreeter`), including the conditional-back-off test and the
auto-discovery end-to-end test above.
- `build-logic` — the new `autoconfiguration-imports` convention plugin,
with its own fixture-compiling unit tests.
## Test plan
- [x] `:grails-beans-dsl:test` — AST transform unit tests
- [x] `:grails-beans-dsl-example:test` — conditional back-off + end-to-end
classpath auto-discovery
- [x] `:build-logic:test` / `:build-logic:check` — imports-generation task
unit tests
- [x] `checkstyleMain` / `codenarcMain` clean on all new source
- [ ] Full-repo `aggregateViolations` / RAT / SBOM / doc-guide integration —
deliberately **not** run; this is a discussion-stage spike, not merge-ready
## Deliberately not done yet (first-look spike)
- No `grails-doc` guide chapter
- DSL surface is intentionally narrow — single-type conditional only, no
property values / destroy methods / parent bean definitions (comparable
limitations to `BeanRegistry.Spec`)
- Not reconciled with `beanRegistrar()` beyond the comparison above —
looking for feedback on whether the fine-grained ordering capability is worth a
second bean-wiring mechanism, or whether that capability should be added to
`beanRegistrar()` / the plugin ordering system instead
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]