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]
