Alex Pritchard
Alex Pritchard

Reputation: 4250

Choose Controller Bean instance by RequestMapping

I want Spring to create 2 instances of FooController. Requests to /foo should be handled by one of the instances and requests to /bar should be handled by the other instance. I want something like the below, but of course @RequestMapping doesn't work that way and also Spring gives me the ambiguous mapping error on FooController as well.

@RestController
public class FooController {
  String name;
  public FooController(String name) { this.name = name; }
}

@Configuration
public class FooControllerConfig {
  @Bean
  @RequestMapping("/foo")
  public FooController getFooFooController(){
    return new FooController("foo");
  }
  @Bean
  @RequestMapping("/bar")
  public FooController getBarFooController(){
    return new FooController("bar");
  }
}

Upvotes: 0

Views: 628

Answers (2)

CryptoFool
CryptoFool

Reputation: 23089

Don't try this at home. This code was performed by a bored, trained professional...

You can have multiple instances of the same controller class, each of which handles a different URL through the same or a different method in the controller. The only thing is, I don't know how to do it with just annotations. The way I just did it was to dynamically register each request mapping at initialization time. The FooController becomes a prototype bean (defined with annotations) so you can have Spring instantiate it multiple times, once for each mapping

FooController.java

@Controller
@Scope("prototype")
public class FooController {

    private String name;

    public FooController() {}

    public FooController(String name) {
        this.name = name;
    }

    public ResponseEntity<String> handleRequests() throws Exception {
        return new ResponseEntity<>("Yo: " + name + " " + this.hashCode(), HttpStatus.OK);
    }

EndpointService.java

@Service
public class EndpointService {

    @Autowired
    private BeanFactory beanFactory;

    @Autowired
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    public void addFooController(String urlPath, String name) throws NoSuchMethodException {

        RequestMappingInfo requestMappingInfo = RequestMappingInfo
                .paths(urlPath)
                .methods(RequestMethod.GET)
                .produces(MediaType.APPLICATION_JSON_VALUE)
                .build();

        requestMappingHandlerMapping.registerMapping(requestMappingInfo,
                beanFactory.getBean(FooController.class, name),
                FooController.class.getDeclaredMethod("handleRequests"));
    }

    @EventListener
    public void handleContextRefreshEvent(ContextRefreshedEvent ctxStartEvt) {
        try {
            addFooController("/blah1", "blahblah1");
            addFooController("/blah2", "blahblah2");
            addFooController("/blah3", "blahblah3");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

Results:

http://localhost:8080/blah1 returns: Yo: blahblah1 1391627345

http://localhost:8080/blah3 returns: Yo: blahblah3 2078995154

Upvotes: 1

James Williams
James Williams

Reputation: 71

I'm really confused by why you need this requirement? Can you please explain why this is required? Is it that each mapping requires a different name?

First you do not map Beans to a RequestMapping. While I am not even sure the spring application would start it would potentially create a new Bean with an identical name every time you access one of these mappings which would probably throw an error.

You could potentially overcome the duplicate names with your own annotation processing but that is way more work then this looks like it is worth.

Just looking at what you have there is there any reason why the following will not meet your requirements?

@RestController
public class FooController {

    private static final fooName = "fooName";
    private static final barName = "barName";

    @RequestMapping("/foo")
    public String getFoo(){
        return fooName;
    }

    @RequestMapping("/bar")
    public String getBar(){
      return barName;
    }
}

Upvotes: 2

Related Questions