codeconsole commented on PR #16019: URL: https://github.com/apache/grails-core/pull/16019#issuecomment-5094209109
The `grails-core` conversion (6986f0cc12, with the follow-up fix in 100b9dbf77), same pattern. This is the first one where the plugin keeps its legacy `doWithSpring()` — the bean builder closure is isolated with `@CompileDynamic` so the class itself can carry `@CompileStatic`, which the transform copies onto the generated sibling — and the first where the sibling does not land on the deleted class's exact package. Imports and comments are trimmed here; each linked filename is the verbatim, commit-pinned source. Before, [`CoreAutoConfiguration.java`](https://github.com/codeconsole/grails-core/blob/5c71d9e88ceae522071f724681b5de2baafdb43a/grails-core/src/main/groovy/org/grails/plugins/core/CoreAutoConfiguration.java#L41-L69) (since deleted): ```java @AutoConfiguration(before = { PropertyPlaceholderAutoConfiguration.class }) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) public class CoreAutoConfiguration { @Value("${" + Settings.SPRING_PLACEHOLDER_PREFIX + ":#{null}}") private String placeholderPrefix; @Bean @Primary public ClassLoader classLoader(GrailsApplication grailsApplication) { return grailsApplication.getClassLoader(); } @Bean @Primary public ConfigProperties grailsConfigProperties(GrailsApplication grailsApplication) { return new ConfigProperties(grailsApplication.getConfig()); } @Bean @Primary PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { GrailsPlaceholderConfigurer grailsPlaceholderConfigurer = new GrailsPlaceholderConfigurer(); if (placeholderPrefix != null) { grailsPlaceholderConfigurer.setPlaceholderPrefix(placeholderPrefix); } return grailsPlaceholderConfigurer; } } ``` After — [`CoreGrailsPlugin.groovy`](https://github.com/codeconsole/grails-core/blob/100b9dbf77d65a32cef2f1698113052fa9f59ac7/grails-core/src/main/groovy/org/grails/plugins/CoreGrailsPlugin.groovy#L67-L103)'s class declaration and `beans` block (the plugin's metadata properties between them are elided below, unchanged by the conversion; `doWithSpring()` and `onChange()` further down the file are pre-existing plugin logic, untouched): ```groovy @CompileStatic @GrailsBeans @AutoConfiguration(before = [PropertyPlaceholderAutoConfiguration]) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) class CoreGrailsPlugin extends Plugin { // ... pre-existing plugin metadata properties, unchanged ... def beans = { bean('classLoader', ClassLoader).primary() { GrailsApplication grailsApplication -> grailsApplication.classLoader } bean('grailsConfigProperties', ConfigProperties).primary() { GrailsApplication grailsApplication -> new ConfigProperties(grailsApplication.config) } bean('propertySourcesPlaceholderConfigurer', PropertySourcesPlaceholderConfigurer).primary().staticMethod() { Environment environment -> def configurer = new GrailsPlaceholderConfigurer() String prefix = environment.getProperty(Settings.SPRING_PLACEHOLDER_PREFIX) if (prefix != null) { configurer.placeholderPrefix = prefix } configurer } } ``` `CoreGrailsPlugin` lives in `org.grails.plugins` while the deleted class was in `org.grails.plugins.core`, and the sibling is always emitted into the plugin's own package, so the class becomes `org.grails.plugins.CoreAutoConfiguration` and the single hand-maintained `AutoConfiguration.imports` entry moves with it. Nothing else in the repository referenced the old package, and the `org.grails` prefix filter in `grails-testing-support-core` still matches. `@AutoConfiguration(before = ...)` and `@AutoConfigureOrder` move onto the sibling at compile time, and javap confirms the rest is equivalent to the deleted Java class: the same three method signatures, the same `@Bean`/`@Primary` pairs, and no `invokedynamic` call sites, with the `beans` closure not surviving on the plugin class. The `.staticMethod()` on the placeholder configurer is a behaviour fix rather than a like-for-like port, and it is worth calling out because the DSL made it visible. `grails.spring.placeholder.prefix` never had any effect: the configurer is a `BeanFactoryPostProcessor`, so its configuration class is instantiated during `invokeBeanFactoryPostProcessors`, before `AutowiredAnnotationBeanPostProcessor` is registered, and the `@Value` field the original instance method read was therefore always `null` — confirmed by reflection against a refreshed context, where the field held `null` and the configurer kept the default `${` prefix. A `static` factory method needs no enclosing instance and can take the `Environment` directly, which is the shape Boot's own `PropertyPlaceholderAutoConfiguration` uses, so the configured prefix now actually applies. The auto-configuration previously had no test coverage at all; the new `CoreAutoConfigurationSpec` asserts the three beans and their types, the class loader identity, `@Primary` winning against a competing `ClassLoader` candidate, the config properties read-through to the application config, Boot's `PropertyPlaceholderAutoConfiguration` ordering after this one and backing off, default-prefix placeholder resolution in bean definitions, an unresolvable placeholder being left in place rather than failing the context, and — newly possible — a configured prefix both resolving `@{foo.bar}` and displacing the default so `${foo.bar}` is left literal. One dependency note for anyone writing similar specs: `spring-boot-test` alone is not enough, because `ApplicationContextRunner`'s signatures reach AssertJ through `ApplicationContextAssertProvider`; without `assertj-core` on the test classpath Groovy cannot introspect the runner at all — its metaclass degrades to `Object`'s methods and every call fails to dispatch with `MissingMethodException`. -- 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]
