I have a generic rest controller (with @RestController and empty mapping) with a method that accepts a generic @RequestBody. I then register this controller multiple times using ImportBeanDefinitionRegistrar and assign dynamic mappings inside my own customized RequestMappingHandlerMapping. The problem I'm having is that when the request comes to the controller it's not of the resoved non-generic type, but rather a LinkedHashMap.
I don't have the exact code with me, but it goes something as follows
@RestController
@RequiredArgsConstructor
class DynamicGenericController<T> {
private final Service<T> service;
@PostMapping
void execute(@RequestBody T request) {
//request is always LinkedHashMap and not the actual generic type
service.execute(request);
}
}
class DynamicRegistrar implements ImportBeanDefinitionRegistrar {
void registerBeanDefinitions(...) {
//this is actually a variable sequence of registrations based on the app configuration
//here are well known classes just for the sake of simplicity
registerController(registry, new ServiceA(), A.class);
registerController(registry, new ServiceB(), B.class);
}
<T> void registerController(BeanRegistry registry, Service<T> service, Class<T> clazz) {
var bean = (RootBeanDefinition)BeanDefinitionBuilder.rootBeanDefinition()
.beanClass(DynamicGenericController.class)
.addConstrutorArgValue(service)
.build();
bean.setTargetType(ResolvableType.forClassWithGenerics(DynamicGenericController.class, clazz));
registry.register("controller" + clazz.getSimpleName(), bean);
}
}
@Bean
RequestMappingHandlerMapping requestMappingHandlerMapping(BeanFactory beanFactory) {
return new RequestMappingHandlerMapping() {
@Override
void registerMapping(...) {
...
var bean = beanFactory.getBean(beanName);
if(bean instanceof DynamicGenericController) {
super.registerMapping(..., mapping.mutate().paths(...).build());
} else {
super.registerMapping(...);
}
}
}
}
I more or less understand why this is the case and I'm looking for a simple and elegant way of keeping this as generic as possible without modyfing the request classes or introducing my own ArgumentResolver or ObjectMapper for Jackson - I would like to utilize the beans that are already registered there and not overwrite what application has set itself. Potentially I will have more controllers like this in the future with more methods, so I wouldn't like to create a boiler plate code for each of them.