matrei commented on code in PR #16184:
URL: https://github.com/apache/grails-core/pull/16184#discussion_r4016151675


##########
grails-core/src/main/groovy/grails/boot/config/GrailsEarlyPluginRegistrationPostProcessor.java:
##########
@@ -120,6 +127,20 @@ public void 
postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) t
             return;
         }
 
+        // Two things make a context a Grails application: GrailsApp launched 
it, which it records by
+        // stashing the sources it was given, or one of its sources is a 
Grails application class.
+        // This initializer is registered for every Spring Boot application 
with grails-core on its
+        // class path, and the plugin lifecycle is not something the rest of 
them asked for: it would
+        // contribute a GrailsApplication, a plugin manager and the beans of 
every plugin found, over
+        // the top of whatever the libraries they did ask for auto-configure 
for themselves.
+        boolean launchedByGrails =
+                
applicationContext.getBeanFactory().getSingleton(APPLICATION_SOURCE_CLASSES_BEAN_NAME)
 != null;
+        Class<?>[] applicationSources = 
resolveApplicationSourceClasses(registry);
+        if (!launchedByGrails && 
!containsApplicationClass(applicationSources)) {

Review Comment:
   @jdaugherty 
   
   Yes, and I have now run it rather than read it, because the question 
deserved that. I added four `@SpringBootTest` specs to the app3 example (which 
has the `loadafter` plugin registering `earlyPluginProbe` in `doWithSpring`, so 
the lifecycle is observable) and ran them on this branch and on the 8.0.x merge 
base 3067d0a855.
   
   | Spec | 8.0.x (before) | this PR |
   |---|---|---|
   | `@SpringBootTest` | context fails: `BeanDefinitionOverrideException` on 
`grailsDomainClassMappingContext` | same failure, unchanged |
   | `@SpringBootTest(properties = [allow-bean-definition-overriding=true, 
allow-circular-references=true])` | passes, full lifecycle | passes, full 
lifecycle: `grailsApplication`, `pluginManager`, plugin `doWithSpring` bean, 
`beanRegistrar` bean, application artefacts |
   | `@SpringBootTest(classes = SbtLifecycleProbeConfig)` | context fails: 
circular reference creating `hibernateDatastore` from 
`openSessionInViewInterceptor` | passes, no Grails lifecycle |
   | `@SpringBootTest(classes = [Application, SbtLifecycleProbeConfig])` | not 
run | fails like the bare form (same override exception) |
   
   So, on what the gate does:
   
   - The application class is not required for `@SpringBootTest` as such. The 
compiler puts `@SpringBootConfiguration` on `Application` 
(`ApplicationClassInjector`, same in 7.x), Boot finds it, and 
`resolveApplicationSourceClasses` recovers it from the registry. The second row 
shows the lifecycle then runs in full. `@Integration` names it outright.
   - Only `@SpringBootTest(classes = SomeConfig)` is gated out. That form never 
had the lifecycle in 7.x: the lifecycle was the 
`grailsApplicationPostProcessor` `@Bean` on the application class, and 7.0.x 
registers no `ApplicationContextInitializer` for it. And in the M3 to M6 window 
where the early phase ran for every context, the third row shows what that form 
actually got in an application with GORM: a context that could not start, 
because plugin beans were registered into a plain `SpringApplication` that 
disallows circular references. This PR is what makes that test load.
   
   Two things the runs turned up that I would ask for before this is settled:
   
   1. The upgrade note is wrong where it says "Grails 7: the plugin lifecycle 
ran, and this context had a GrailsApplication and every plugin's beans" and 
"the test was given the whole plugin runtime". 7.x gave that form nothing 
Grails, and the milestones gave it a context that failed to start. The note 
should say so, and drop the "Grails 7" label.
   2. Separate from this PR, and already on 8.0.x: a bare `@SpringBootTest` in 
a Grails application with Hibernate does not start unless the test sets 
`spring.main.allow-bean-definition-overriding=true` and 
`spring.main.allow-circular-references=true`, the two defaults `GrailsApp` 
applies and a plain `SpringApplication` does not. `@Integration` avoids it via 
`useMainMethod = ALWAYS`. The override is the Hibernate plugin's 
`grailsDomainClassMappingContext` (`hibernateDatastore.getMappingContext`) 
against the generated `DomainClassAutoConfiguration` `@Bean` of the same name. 
That is the real "SpringBootTest usage" gap in 8.0, and it deserves its own 
issue: either the generated auto-configuration guards that bean with 
`@ConditionalOnMissingBean`, or `GrailsPluginLifecycleInitializer` applies the 
two `GrailsApp` defaults when it decides the context is a Grails application.
   
   A classpath scan for a `GrailsApplicationClass` would be the only way to 
give `@SpringBootTest(classes = SomeConfig)` the lifecycle anyway, since 
`grails.build.info` is written for plugin projects too. That would be new 
behaviour relative to 7.x, added to every Boot context with grails-core on the 
class path, for a form Boot itself treats as "use these sources only". I would 
rather not.
   



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