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

   Makes a Grails application processable by Spring AOT. A stock web 
application now completes `processAot` and starts with 
`spring.aot.enabled=true`:
   
   ```
   $ ./gradlew bootJar
   $ java -Dspring.aot.enabled=true -jar build/libs/app.jar
   Starting AOT-processed Application v0.1 using Java 21.0.9
   Started Application in 1.475 seconds
   ```
   
   Apply the Spring Boot AOT plugin to opt in:
   
   ```groovy
   apply plugin: 'org.springframework.boot.aot'
   ```
   
   AOT is also the gate on GraalVM native images: Boot refuses to start a 
native image without a build-time generated initializer, so nothing native was 
reachable before this.
   
   ## What was blocking it
   
   Four independent mechanisms, each only visible once the previous was cleared.
   
   **Live object instances in bean definitions.** `GroovyPagesGrailsPlugin` 
passed the live `GrailsApplication` as a constructor argument and a `new 
GroovyPagesServlet()` instance. A bean definition is a recipe; 
`ValueCodeGenerator` cannot emit source that reconstructs an arbitrary object:
   
   ```
   UnsupportedTypeValueCodeGenerationException:
       Code generation does not support grails.core.DefaultGrailsApplication
   ```
   
   Both now use forms the generator understands — a reference by bean name and 
an inner bean definition. This was the first `processAot` failure on 7.2.1, 
8.0.0-M4 and 8.0.0-SNAPSHOT alike.
   
   **A second `ConfigurationClassPostProcessor`.** `CoreGrailsPlugin` registers 
one so that `@Configuration` beans contributed by plugins through 
`doWithSpring` — which arrive after Spring's own processor has finished — still 
get parsed. An AOT context has none of its own by design, and adding one back 
parses the configuration classes a second time, colliding with what AOT already 
emitted:
   
   ```
   BeanDefinitionStoreException: Invalid bean definition with name
   'propertySourcesPlaceholderConfigurer' defined in 
org.grails.plugins.CoreAutoConfiguration:
   Bean name derived from @Bean method 'propertySourcesPlaceholderConfigurer' 
clashes with
   bean name for containing configuration class
   ```
   
   For a static `@Bean` method AOT emits the definition with the configuration 
class as its bean class, so the re-parse rediscovers `CoreAutoConfiguration` 
under the bean's own name and collides with itself.
   
   **Abstract bean definitions.** `BeanDefinitionPropertiesCodeGenerator` emits 
`lazyInit`, `primary`, `scope`, `role` and `synthetic` but not the abstract 
flag, and nothing filters abstract definitions out beforehand — a template is 
silently regenerated as a concrete `Object` bean still carrying the template's 
properties. `CoreGrailsPlugin` contributes exactly such a template 
(`abstractGrailsResourceLocator`), and it is public surface: third-party 
plugins inherit from it with `bean.parent`, asset-pipeline's 
`assetResourceLocator` among them. A `BeanRegistrationExcludeFilter` keeps them 
out of generation; children are unaffected because AOT generates them from 
their merged definition.
   
   **A servlet context wired as a bean reference.** SiteMesh's bean-definition 
wrap referenced the `servletContext` bean, which the container registers only 
once the web server starts and which has no bean definition at all.
   
   ## Requires a SiteMesh release
   
   The last item is fixed upstream in `spring-webmvc-sitemesh` — 
`SiteMeshViewResolver` now implements `ServletContextAware` instead of taking 
the context as a constructor argument. `dependencies.gradle` therefore moves to 
`3.3.0-SNAPSHOT`, and **must be pinned to a released version before this 
merges**.
   
   ## Limitations
   
   - `processAot` must run in production mode. In development `reloadEnabled` 
is true and the URL mappings holder becomes a `ProxyFactoryBean` whose lazy 
object type cannot be predicted without instantiating it. Set it on the task:
     ```groovy
     tasks.named('processAot') { systemProperty 'grails.env', 'production' }
     ```
   - This makes an application AOT-processable, not native-image ready. Native 
images additionally need reachability metadata for plugin descriptor scanning 
and Groovy class loading.
   - 13 `beanRegistrar()`-contributed beans are still excluded from generation 
by Spring's own `aotProcessingIgnoreRegistration` flag and re-registered at 
runtime when `GrailsApplicationPostProcessor` re-runs. Correct, but it repeats 
the plugin scan on every boot — the work AOT exists to precompute.
   - `GroovyPagesServlet` is now created by the container rather than with 
`new`, so it passes through bean post-processing. Its `pluginManager` is 
unaffected (`initFrameworkServlet` already autowires by type), but a 
post-processor whose pointcut matched it would hand `ServletRegistrationBean` a 
proxy. No pointcut in the framework does.
   
   ## Verification
   
   An AOT-processed application's bean definitions differ from a normal boot 
only by the annotation-processing infrastructure AOT replaces — 410 beans 
against 405, the difference being the configuration, autowired and 
common-annotation processors, Boot's shared metadata reader factory, and 
`grailsConfigurationClassPostProcessor`. No application bean is missing and 
none is added.
   


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