codeconsole opened a new pull request, #16179:
URL: https://github.com/apache/grails-core/pull/16179

   In development, every message bundle contributed by a plugin, and every base 
name an application configured itself, silently fails to resolve. Spring Boot's 
`messageSource` is built with only the default `messages` base name, and 
nothing warns:
   
   ```
   NoSuchMessageException: No message found under code 'my.plugin.message' for 
locale 'en'
   ```
   
   Deployed applications are unaffected — only reload mode builds the holder as 
a proxy — so this is `grails run-app`, every Gradle `JavaExec`, and every 
integration run.
   
   ## Cause
   
   A factory bean's produced type has to be answerable from the bean 
*definition*, because a property value cannot be. Asked for the type of 
`grailsUrlMappingsHolder`, Spring builds a constructor-only `ProxyFactoryBean` 
— no property values applied, so no `targetSource` and no `proxyInterfaces` — 
whose `getObjectType()` returns `null`. Spring then falls back to creating the 
factory bean in full.
   
   That resolves the target source and the inner 
`UrlMappingsHolderFactoryBean`, evaluates every URL mapping, reaches the 
constraints machinery and its `List<MessageSource>` injection, and so creates 
Spring Boot's message source — all while bean definition registry 
post-processors are still running. Anything created that early misses 
`ConfigurationPropertiesBindingPostProcessor`, so `MessageSourceProperties` is 
never bound and keeps its constructor defaults.
   
   It is not only the base names: `encoding`, `cache-duration` and 
`fallback-to-system-locale` are lost with them.
   
   ## Fix
   
   Each factory-bean definition declares what it produces through 
`FactoryBean.OBJECT_TYPE_ATTRIBUTE`, which Spring consults before instantiating 
anything:
   
   ```groovy
   proxy.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, UrlMappings)
   ```
   
   By-type autowiring of `UrlMappingsHolder` keeps working — that is why these 
definitions live in a post-processor rather than the `BeanRegistry` API — and 
the hot-swap target source is untouched, so reload still works.
   
   Reload mode is additionally switched off when running generated bean 
definitions: AOT code generation drops custom definition attributes, and 
hot-swapping mappings is meaningless in an image.
   
   ## Test
   
   The regression test issues the eager by-type lookup directly against a bare 
`DefaultListableBeanFactory`, so it reproduces with no Grails runtime and no 
ordering luck. It fails without the fix.
   
   ## Notes
   
   - `setLazyInit(true)` cannot help here: the lazy-init gate is bypassed when 
a by-type scan runs with `allowEagerInit`, and these definitions were already 
lazy.
   - Promoting the inner holder to a named top-level bean does not work — it 
adds a second `UrlMappings` candidate and the application fails to start with 
`required a single bean, but 2 were found`.
   


-- 
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]

Reply via email to