1

I'm looking for help at Spring Controller definitions. I tried to google couple of times, but found no straight explanation how to acomplish my goal.

I'd like to define generic controller:

public abstract class AbstractController<E extends AbstractEntity, 
                                         P extends IRequestParams> {
  @RequestMapping(method = GET)
  public @ResponseBody
  abstract List<E> list(@RequestParam P params);
}

after that I would declare controllers like:

public class CatController 
          extends AbstractController<Cat, ICatSearchParams> {
  @RequestMapping(method = GET)
  public @ResponseBody
  List<Cat> list(@RequestParam ICatSearchParams params) {
    // do something and return list of results
  }
}

I have service that transforms @RequestParam Map<String, String[]> params into IRequestParams in a generic way. I'm able to resolve generic type via Spring.

But I don't know how to plug into Spring to automatically convert request params into my params interface and use it in method invocation. I would like this transformation to be transparent for developer after configuring it.

Thank you in advance for any help!

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
tunguski
  • 763
  • 7
  • 12
  • I am not sure if the complete construct will work in the end, because I do not know if spring `Converter` or `WebArgrumentResolver` can resolve the generic type correctly. – Ralph Nov 22 '13 at 17:54

2 Answers2

1

You can implement a HandlerMethodArgumentResolver but then you will need to implement the complete object mapping by your self.

Registration is easy:

<mvc:annotation-driven>
      <mvc:argument-resolvers>
           <bean class="YourMethodArgumentResolver"/>         
      </mvc:argument-resolvers>
 </mvc:annotation-driven>

The other way would be implementing a custom Converter. The main problem is, that a Converter can only convert one single String/Request attribute. But if your IRequestParams is in the http-request with more than one value this will not work. - On the other hand if your Converter is designed to load an entity by its ID, then a Converter is the way to go.

If you want to build a way automatically register a Converter then have a look at this answer https://stackoverflow.com/a/13778502/280244

Community
  • 1
  • 1
Ralph
  • 118,862
  • 56
  • 287
  • 383
1

I've acomplished question's goal. @Ralph's answer was very very helpfull, but to be precise I'll write what was needed more.

I've created HandlerMethodArgumentResolver, name it CustomHandlerMethodArgumentResolver. To map all request parameters to result object, I had to mark controller's method's param as @RequestBody.

That was not enough because Spring's default HandlerMethodArgumentResolvers try to map it first and my resolver wasn't executed. But this answer pointed how to set priority for my resolver.

@PostConstruct did not work (not shure why), so I've modified my @Configuration class to implement ApplicationListener<ContextRefreshedEvent>. Here is the code:

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
  RequestMappingHandlerAdapter adapter = 
      event.getApplicationContext().getBean(
          RequestMappingHandlerAdapter.class);
  CustomHandlerMethodArgumentResolver resolver =
      event.getApplicationContext().getBean(
          CustomHandlerMethodArgumentResolver.class);

  List<HandlerMethodArgumentResolver> argumentResolvers = 
      new ArrayList<>(
          adapter.getArgumentResolvers().getResolvers());
  List<HandlerMethodArgumentResolver> customResolvers = 
      adapter.getCustomArgumentResolvers();
  argumentResolvers.remove(resolver);
  argumentResolvers.add(0, resolver);
  adapter.setArgumentResolvers(argumentResolvers);
}

Of course CustomHandlerMethodArgumentResolver .supportsParameter(MethodParameter) checks if actual situation should be processed in custom or default way - that depends on requirements.

Thank you @Ralph for lightning fast help!

Community
  • 1
  • 1
tunguski
  • 763
  • 7
  • 12