IntegrationRequestMappingHandlerMapping
has a special requirement to initialize on ContextRefreshedEvent
. To quote the code:
@Override
public void afterPropertiesSet() {
// No-op in favor of onApplicationEvent
}
/**
* {@link HttpRequestHandlingEndpointSupport}s may depend on auto-created
* {@code requestChannel}s, so MVC Handlers detection should be postponed
* as late as possible.
* @see RequestMappingHandlerMapping#afterPropertiesSet()
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (!this.initialized.getAndSet(true)) {
super.afterPropertiesSet();
}
}
The effect is that its mappingRegistry
is empty when other beans try to evaluate it during application startup, even when they implement SmartLifeCycle
with a phase of MAX_VALUE
.
In my case I am trying to implement a spring-integration plugin for Springfox. Its DocumentationPluginsBootstrapper
needs to access the request mappings to document them.
How can I be sure that IntegrationRequestMappingHandlerMapping
has been initialized before I start asking for its mappings? Would it be the correct approach to listen for ContextRefreshedEvent
, too, but with a high value for @Order
? Or would you advise to use a different event?
Update: AbstractHandlerMapping already uses Order.LOWEST_PRECEDENCE. I guess I can't use the context refreshed event to be safe.
Also see the related springfox issue.
The ContextRefreshedEvent
is really the last step in the application context initialization:
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
It is fired in that finishRefresh()
.
You indeed should consider an Ordered
for your own ApplicationListener<ContextRefreshedEvent>
with the Order.LOWEST_PRECEDENCE
. At the same time Framework register that IntegrationRequestMappingHandlerMapping
with the order == 0
:
private void registerRequestMappingHandlerMappingIfNecessary(BeanDefinitionRegistry registry) {
if (HttpContextUtils.WEB_MVC_PRESENT &&
!registry.containsBeanDefinition(HttpContextUtils.HANDLER_MAPPING_BEAN_NAME)) {
BeanDefinitionBuilder requestMappingBuilder =
BeanDefinitionBuilder.genericBeanDefinition(IntegrationRequestMappingHandlerMapping.class);
requestMappingBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
requestMappingBuilder.addPropertyValue(IntegrationNamespaceUtils.ORDER, 0);
registry.registerBeanDefinition(HttpContextUtils.HANDLER_MAPPING_BEAN_NAME,
requestMappingBuilder.getBeanDefinition());
}
}
So, you really are save to handle mapping in your own listener. Just because, thanks to order = 0
the IntegrationRequestMappingHandlerMapping
is going to be initialized already.
The same is applied for the WebFluxIntegrationRequestMappingHandlerMapping
:
private void registerReactiveRequestMappingHandlerMappingIfNecessary(BeanDefinitionRegistry registry) {
if (HttpContextUtils.WEB_FLUX_PRESENT &&
!registry.containsBeanDefinition(WebFluxContextUtils.HANDLER_MAPPING_BEAN_NAME)) {
BeanDefinitionBuilder requestMappingBuilder =
BeanDefinitionBuilder.genericBeanDefinition(WebFluxIntegrationRequestMappingHandlerMapping.class);
requestMappingBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
requestMappingBuilder.addPropertyValue(IntegrationNamespaceUtils.ORDER, 0);
registry.registerBeanDefinition(WebFluxContextUtils.HANDLER_MAPPING_BEAN_NAME,
requestMappingBuilder.getBeanDefinition());
BeanDefinitionReaderUtils.registerWithGeneratedName(
new RootBeanDefinition(IntegrationHandlerResultHandler.class), registry);
}
}