codeconsole commented on code in PR #16292:
URL: https://github.com/apache/grails-core/pull/16292#discussion_r3961352856
##########
grails-doc/src/en/guide/plugins/hookingIntoRuntimeConfiguration.adoc:
##########
@@ -193,6 +204,30 @@ bean('greeting', Greeting) {
}
----
+A third statement kind, `group(["name"]).<conditions> { ... }`, declares a
nested static `@Configuration(proxyBeanMethods = false)` class holding the
declarations in its body, with the chained conditions on the class rather than
on each bean:
+
+[source,groovy]
+----
+group('imageServing').conditionalOnClass(name:
'com.example.ManagedFileAccessProvider') {
+ bean('roomFileAccessProvider', RoomFileAccessProvider)
+ bean('chatFileAccessProvider', ChatFileAccessProvider)
+}
+----
+
+This is the shape real auto-configurations take — Spring Boot's own
`JacksonAutoConfiguration` carries four nested `@ConditionalOnClass`
configuration classes — and it is the only shape that works for a bean whose
*own signature* names a class that may be absent. A condition is read from the
bytecode before anything is loaded, but a `@Bean` method's parameter and return
types are resolved when its configuration class is parsed, so guarding such a
bean on the method is not reliably safe; moving it into a group moves the guard
onto a class that is never parsed when the condition fails. Spring finds the
nested class unaided, since `ConfigurationClassParser` processes a
configuration class's member classes. Groups take the condition qualifiers and
`.annotate(...)`; they do not nest, and the closure takes no parameters, since
it declares a class rather than a bean.
+
+=== Diagnostics
+
+Three mistakes the DSL used to accept are now compile errors, because each of
them fails at runtime in a way that is hard to trace back:
+
+* A `beans` closure containing any top-level `bean`/`field`/`method` call must
be entirely such calls. A stray statement among real declarations — a typo in a
call name, or an `if` wrapped around some beans — used to make the whole block
register *nothing*, silently, with the failure surfacing far away as beans that
are simply absent. To register a bean conditionally, put the condition on the
bean rather than around it; for state or logic shared between beans, use
`field(...)` or `method(...)`. A `beans` closure containing no such calls at
all is not the DSL and is left alone, as before.
+* Calling one bean from another does not return the registered singleton
unless the host is a proxied `@Configuration` class — and the DSL's usual hosts
are not: `@AutoConfiguration` is `@Configuration(proxyBeanMethods = false)`, a
generated plugin sibling carries exactly that, and an `Application` class is a
configuration source without being annotated at all. Such a call silently
constructs a second instance, so it is rejected; inject the bean as a closure
parameter instead. A static `@Bean` method is never intercepted even on a
proxied class, so `.staticMethod()` beans are checked everywhere.
+* A `BeanFactoryPostProcessor` or `BeanPostProcessor` bean must be
`.staticMethod()`.
+
+=== Seeing what a block compiled to
+
+Building with `-Dgrails.beans.dsl.dumpdir=<dir>` writes one `<qualified
name>.beans.txt` per host class, listing the generated members — bean names,
the annotations the qualifiers became, modifiers, declared types with any type
arguments they ended up carrying, and parameter annotations. Bodies are
omitted, being the closure bodies from the source. Nothing is written unless
the property is set.
Review Comment:
Valid, and the feature was effectively documented into uselessness — fixed
in 902f082.
You are right about why I missed it: the spec sets the property with
System.setProperty inside the test JVM, where compilation happens in-process,
so it never exercised the documented invocation. The guide now shows
tasks.withType(GroovyCompile).configureEach {
groovyOptions.forkOptions.jvmArgs +=
"-Dgrails.beans.dsl.dumpdir=/absolute/path/to/beans"
}
and says why: GroovyCompile forks, so -D on the Gradle command line sets the
property on the client and daemon rather than on the process that runs the
transform. The absolute-path point is in there too, with the reason — a
relative path resolves against that worker working directory, not the project
one.
--
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]