codeconsole opened a new pull request, #16292:
URL: https://github.com/apache/grails-core/pull/16292
Five changes to the `beans` DSL: two diagnostics for mistakes it currently
accepts in silence, one new qualifier, and two gaps in what the docs promise.
### 1. A partly DSL-shaped `beans` block is reported, not dropped
A block is claimed only when *every* top-level statement is a
`bean`/`field`/`method` call, so an unrelated `beans` property on a pre-8.0
plugin descriptor is left alone. That is right for a block with no declarations
in it — and wrong for one that has some, where today every declaration is
dropped without a word:
```groovy
def beans = {
bean('greeting', String) { 'hello' }
bea('typo', String) { 'oops' } // registers nothing, silently,
for all three
bean('farewell', String) { 'bye' }
}
```
```groovy
def beans = {
bean('greeting', String) { 'hello' }
if (dev) { // same: the whole block is
skipped
bean('devOnly', String) { 'dev' }
}
}
```
An application class now fails, naming the statement and pointing at
`.annotate(ConditionalOnProperty, ...)` / `method(...)`. A plugin descriptor
only warns and is still left alone, since its `beans` property may predate the
DSL. A block with no declarations at all stays silent in both.
### 2. A sibling bean call that cannot return the singleton is rejected
Getting the registered singleton back from a call to another `@Bean` method
is a CGLIB trick, and Spring only plays it for a full `@Configuration` class.
`@AutoConfiguration` is `@Configuration(proxyBeanMethods = false)`, the sibling
generated for a plugin descriptor carries exactly that, and a Grails
`Application` class is a configuration source without being `@Configuration` at
all — so the DSL's three main hosts are all unproxied:
```groovy
def beans = {
bean('targetUrlSuccessHandler',
SavedRequestAwareAuthenticationSuccessHandler) { ... }
bean('filterChain', SecurityFilterChain) { HttpSecurity http ->
http.formLogin { it.successHandler(targetUrlSuccessHandler()) } //
a SECOND handler
http.build()
}
}
```
Nothing about that is visible at runtime: the context starts, every bean
exists, and two objects live where one was meant. Inject it instead:
```groovy
bean('filterChain', SecurityFilterChain) { HttpSecurity http,
SavedRequestAwareAuthenticationSuccessHandler
targetUrlSuccessHandler ->
```
Proxying is resolved by walking the host's annotations for a reachable
`@Configuration`, skipping any branch that sets `proxyBeanMethods = false`, so
a composed annotation gets the same answer as `@AutoConfiguration`. The walk
stops at nested closures: inside `tap`/`with` an unqualified call resolves
against the delegate, which the AST cannot distinguish from `this`
(`DataBindingGrailsPlugin` writes exactly that shape).
### 3. New: `.typeArguments(...)`
The type in `bean(...)` is a class literal, and Groovy has no syntax for
type arguments on one, so a bean could only be declared raw. Spring resolves an
injection point by its full generic type, so a raw bean may not match a
consumer asking for `Handler<Order>`, and `ObjectProvider<Handler<Order>>` or
an injected `List<Handler<Order>>` cannot select it at all:
```groovy
def beans = {
bean('orderHandler', Handler).typeArguments(Order) { new
OrderHandler() }
bean('refundHandler', Handler).typeArguments(Refund) { new
RefundHandler() }
bean('dispatcher', Dispatcher) { Handler<Order> handler -> // gets
OrderHandler
new Dispatcher(handler)
}
}
```
Available on all three declaration forms, and the arity is checked against
the type's own parameters:
```groovy
field('cache', Map).typeArguments(String, Integer)
method('names', ArrayList).typeArguments(String) { new
ArrayList<String>() }
```
### 4. Parameter annotations are documented, and the optional case is tested
The javadoc said closure parameters become the method's parameters and
stopped there, so nothing promised their annotations survive. They do —
`@Qualifier` already relied on it — but `@Autowired(required = false)` is the
one that decides whether the context starts at all, and it had no test:
```groovy
bean('smsSender', SmsSender) { @Autowired(required = false) SmsTransport
transport ->
transport ? new SmsSender(transport) : null
}
```
### 5. `.annotate(...)` array attributes
The javadoc called it an escape hatch for "any other single-valued
annotation". It takes as many attributes as the annotation declares, and an
array-valued one takes either spelling — which worked, but was only covered
incidentally by a test about something else:
```groovy
bean('filterChain', SecurityFilterChain).annotate(DependsOn, value:
'webExpressionHandler') { ... }
bean('filterChain', SecurityFilterChain).annotate(DependsOn, value:
['a', 'b']) { ... }
```
---
`grails-beans-dsl` 196/196 and `grails-core`'s injector spec 48/48 (173 and
44 pre-existing). Every in-tree `beans = { }` user compiles — cache, core,
databinding, domain-class, i18n, mail, url-mappings, sitemesh3 — and the
`beans-dsl` / `beans-dsl-plugin` examples plus the full `grails-i18n` suite
pass.
--
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]