codeconsole commented on PR #16019:
URL: https://github.com/apache/grails-core/pull/16019#issuecomment-5095501387
The `grails-databinding` conversion (623ce9748d), which drove a DSL
addition: `bean(Type)` no longer requires a factory closure (4dab384019). Five
of these seven beans were nothing but their own no-argument construction, and
spelling that out as `bean('xmlDataBindingSourceCreator',
XmlDataBindingSourceCreator) { new XmlDataBindingSourceCreator() }` restates
the type three times to say nothing. Imports and comments are trimmed here;
each linked filename is the verbatim, commit-pinned source.
Before,
[`DataBindingConfiguration.java`](https://github.com/codeconsole/grails-core/blob/700cf9c329a16e67179566a3879aeb29c3d7beb1/grails-databinding/src/main/groovy/org/grails/plugins/databinding/DataBindingConfiguration.java#L49-L132)
(since deleted):
```java
@AutoConfiguration
@AutoConfigureOrder
@EnableConfigurationProperties(DataBindingConfigurationProperties.class)
@ImportAutoConfiguration(DefaultConvertersConfiguration.class)
public class DataBindingConfiguration {
private final DataBindingConfigurationProperties configurationProperties;
public DataBindingConfiguration(DataBindingConfigurationProperties
configurationProperties) {
this.configurationProperties = configurationProperties;
}
@Lazy
@Bean("grailsWebDataBinder")
protected GrailsWebDataBinder grailsWebDataBinder(
GrailsApplication grailsApplication,
ValueConverter[] valueConverters,
FormattedValueConverter[] formattedValueConverters,
TypedStructuredBindingEditor[] structuredBindingEditors,
DataBindingListener[] dataBindingListeners) {
GrailsWebDataBinder dataBinder = new
GrailsWebDataBinder(grailsApplication);
dataBinder.setConvertEmptyStringsToNull(configurationProperties.isConvertEmptyStringsToNull());
// ... converter/editor/listener merging with the main context,
elided ...
return dataBinder;
}
@Bean("xmlDataBindingSourceCreator")
protected XmlDataBindingSourceCreator xmlDataBindingSourceCreator() {
return new XmlDataBindingSourceCreator();
}
@Bean("jsonDataBindingSourceCreator")
protected JsonDataBindingSourceCreator jsonDataBindingSourceCreator() {
return new JsonDataBindingSourceCreator();
}
@Bean("halJsonDataBindingSourceCreator")
protected HalJsonDataBindingSourceCreator
halJsonDataBindingSourceCreator() {
return new HalJsonDataBindingSourceCreator();
}
@Bean("halXmlDataBindingSourceCreator")
protected HalXmlDataBindingSourceCreator
halXmlDataBindingSourceCreator() {
return new HalXmlDataBindingSourceCreator();
}
@Bean("jsonApiDataBindingSourceCreator")
protected JsonApiDataBindingSourceCreator
jsonApiDataBindingSourceCreator() {
return new JsonApiDataBindingSourceCreator();
}
@Bean("dataBindingSourceRegistry")
protected DataBindingSourceRegistry
dataBindingSourceRegistry(DataBindingSourceCreator... creators) {
final DefaultDataBindingSourceRegistry registry = new
DefaultDataBindingSourceRegistry();
registry.setDataBindingSourceCreators(creators);
registry.initialize();
return registry;
}
}
```
After —
[`DataBindingGrailsPlugin.groovy`](https://github.com/codeconsole/grails-core/blob/bab7b058f3527c311c6da2464a72fdaab269c185/grails-databinding/src/main/groovy/org/grails/plugins/databinding/DataBindingGrailsPlugin.groovy#L60-L130)'s
class declaration and `beans` block. The plugin previously held nothing but a
`version` property, so this is the whole file:
```groovy
@CompileStatic
@GrailsBeans
@AutoConfiguration
@AutoConfigureOrder
@EnableConfigurationProperties(DataBindingConfigurationProperties)
@ImportAutoConfiguration(DefaultConvertersConfiguration)
class DataBindingGrailsPlugin extends Plugin {
def version = GrailsUtil.getGrailsVersion()
def beans = {
bean(GrailsWebDataBinder).lazy() { GrailsApplication
grailsApplication,
DataBindingConfigurationProperties configurationProperties,
ValueConverter[] valueConverters,
FormattedValueConverter[] formattedValueConverters,
TypedStructuredBindingEditor[] structuredBindingEditors,
DataBindingListener[] dataBindingListeners ->
GrailsWebDataBinder dataBinder = new
GrailsWebDataBinder(grailsApplication)
dataBinder.convertEmptyStringsToNull =
configurationProperties.convertEmptyStringsToNull
// ... converter/editor/listener merging with the main context,
elided ...
dataBinder
}
bean(XmlDataBindingSourceCreator)
bean(JsonDataBindingSourceCreator)
bean(HalJsonDataBindingSourceCreator)
bean(HalXmlDataBindingSourceCreator)
bean(JsonApiDataBindingSourceCreator)
bean(DataBindingSourceRegistry) { DataBindingSourceCreator[]
creators ->
DefaultDataBindingSourceRegistry registry = new
DefaultDataBindingSourceRegistry()
registry.dataBindingSourceCreators = creators
registry.initialize()
registry
}
}
}
```
**The bodyless `bean(Type)` form.** The trailing closure is now optional:
`bean(Type)` and `bean('name', Type)` generate a parameterless factory method
returning `new Type()`, and chain the existing qualifiers exactly as the
closure form does — `bean(Widget).primary().lazy().conditionalOnMissingBean()`
compiles the same way it would with an empty body. Because it constructs the
*declared* type, it is rejected at compile time for an interface or abstract
class with a message pointing at the fix, rather than deferring to a confusing
error inside generated code. That is precisely why `dataBindingSourceRegistry`
above keeps its closure: it declares the `DataBindingSourceRegistry` interface
while constructing `DefaultDataBindingSourceRegistry` and then initializing it.
**No explicit names.** Each of these seven types decapitalizes to exactly
the bean name the hand-written class declared, so passing the name would only
restate it. Reading the `@Bean` values back out of the bytecode confirms all
seven are unchanged. The same cleanup was applied to the earlier `grails-core`
and `grails-cache` conversions (bab7b058f3), which had carried the same
redundancy; the names that remain across all conversions are the ones a type
genuinely does not produce — `grailsUrlConverter` from `UrlConverter`,
`mailSession` from `JndiObjectFactoryBean`, `grailsDomainClassMappingContext`
from `DefaultMappingContextFactoryBean`, and so on.
**Two structural notes.** `configurationProperties` was a
constructor-injected field, and the generated sibling always has a no-arg
constructor; only `grailsWebDataBinder` reads it, so that one bean takes it as
a parameter. And `dataBindingSourceRegistry`'s `DataBindingSourceCreator...`
parameter is written as an array — Spring resolves both identically, and javap
shows Groovy hands the generated method the varargs flag back anyway, since a
trailing array parameter on a closure compiles to varargs.
The class is renamed `DataBindingConfiguration` →
`DataBindingAutoConfiguration` by the `*GrailsPlugin` → `*AutoConfiguration`
convention. Beyond dropping the inconsistency, it corrects a class that was
registered in `AutoConfiguration.imports` while named only `*Configuration`.
The imports entry was its only reference in the repository; the 7.0 upgrade
guide's note about the original `DataBindingGrailsPlugin` →
`DataBindingConfiguration` move is left alone as an accurate record of the 7.x
change.
One imperfection worth stating rather than glossing: the generated
`grailsWebDataBinder` body keeps four explicit array casts on
`GrailsArrayUtils.concat` that the Java original did not need. Groovy's
`@CompileStatic` cannot infer `T[]` from `concat`'s `<T> T[]` signature the way
javac does; removing the casts produces *more* runtime coercion, not less (nine
`invokedynamic` sites rather than five), and an explicit type witness does not
help either. The five that remain are all `invokedynamic cast` — array
coercion, not dynamic dispatch — in a `@Lazy` bean method.
--
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]