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]