是否可以在Spring MVC中动态设置RequestMappings?

时间:2011-04-22 18:09:08

标签: spring-mvc

我已经使用Spring MVC三个月了。我正在考虑一种动态添加RequestMapping的好方法。这需要将控制器部件放入库中,然后以动态方式添加它们。无论如何,我能想到的唯一方法是声明一个这样的控制器:

@Controller
@RequestMapping("/mypage")
public class MyController {

@RequestMapping(method = RequestMethod.GET)
    public ModelAndView mainHandler(HttpServletRequest req) {
        return handleTheRest(req);
    }

}

哪个不好,因为基本上我不使用Spring。然后我不能使用表单绑定,注释等。我想将requestMappings动态添加到可以像通常的MVC控制器一样注释的类的方法,并使用自动绑定,这样我就可以避免手动处理HttpServletRequest。

有什么想法吗? }

6 个答案:

答案 0 :(得分:24)

Spring MVC使用HandlerMapping接口的实现来执行URL映射。通常用于开箱即用的是默认实现,即SimpleUrlHandlerMappingBeanNameUrlHandlerMappingDefaultAnnotationHandlerMapping

如果你想实现自己的映射机制,这很容易做到 - 只需实现该接口(或者,更有可能扩展AbstractUrlHandlerMapping),在上下文中将类声明为bean,并且当需要映射请求时,DispatcherServlet将查询它。

请注意,您可以在一个上下文中拥有任意数量的HandlerMapping实现。他们将被轮流咨询,直到其中一人有匹配。

答案 1 :(得分:4)

我花了很长时间试图让这个工作,但最终设法找到一个返回ResponseEntity而不是旧版ModelAndView的解决方案。此解决方案还具有避免与Application Context明确交互的额外好处。

终端服务

@Service
public class EndpointService {

  @Autowired
  private QueryController queryController;

  @Autowired
  private RequestMappingHandlerMapping requestMappingHandlerMapping;

  public void addMapping(String urlPath) throws NoSuchMethodException {

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

    requestMappingHandlerMapping.
            registerMapping(requestMappingInfo, queryController,
                    QueryController.class.getDeclaredMethod("handleRequests")
            );
  }

}

控制器处理新映射的请求

@Controller
public class QueryController {

  public ResponseEntity<String> handleRequests() throws Exception {

    //Do clever stuff here

    return new ResponseEntity<>(HttpStatus.OK);
  }

}

答案 2 :(得分:3)

我知道这已经很老了,但我想我会把它扔进去,以防其他人有同样的粗暴经历,我试图让这项工作。我最终利用了Spring的两个特性:在上下文启动后动态注册bean的能力和afterPropertiesSet()对象上的RequestMappingHandlerMapping方法。

初始化RequestMappingHandlerMapping时,它会扫描上下文并创建需要提供的所有@RequestMapping的地图(可能是出于性能原因)。如果您动态注册使用@Controller注释的bean,则不会将它们选中。要重新触发此扫描,您只需在添加了bean后调用afterPropertiesSet()

在我的特定用例中,我在一个单独的Spring上下文中实例化了新的@Controller对象,并且需要将它们连接到我的WebMvc上下文中。尽管这些物体对此无关紧要的细节,但您只需要一个对象参考:

//register all @Controller beans from separateContext into webappContext
separateContext.getBeansWithAnnotation(Controller.class)
   .forEach((k, v) -> webappContext.getBeanFactory().registerSingleton(k, v));

//find all RequestMappingHandlerMappings in webappContext and refresh them
webappContext.getBeansOfType(RequestMappingHandlerMapping.class)
   .forEach((k, v) -> v.afterPropertiesSet());

例如,你也可以这样做:

//class annotated with @Controller
MyController controller = new MyController

//register new controller object
webappContext.getBeanFactory().registerSingleton("myController", controller);

//find all RequestMappingHandlerMappings in webappContext and refresh them
webappContext.getBeansOfType(RequestMappingHandlerMapping.class)
   .forEach((k, v) -> v.afterPropertiesSet());

答案 3 :(得分:3)

请看我的解决方案。它不会在您的代码中创建动态@RequestMapping,但会提供处理所有请求的HandlerMappingController。如果您运行该应用程序,您将在json中收到hello world消息。

申请类:

@SpringBootApplication
public class Application {
  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  @Bean
  public MyCustomHandler myCustomHandler(MyCustomController myCustomController) {
    MyCustomHandler myCustomHandler = new MyCustomHandler(myCustomController);
    myCustomHandler.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return myCustomHandler;
  }
}

<强> MyCustomController

@Component
public class MyCustomController extends AbstractController {

  @Override
  protected ModelAndView handleRequestInternal(HttpServletRequest request,
      HttpServletResponse response) throws Exception {
    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
    response.getWriter().println("{\"hello\":\"world\"}");
    return null;
  }
}

<强> MyCustomHandler

public class MyCustomHandler extends AbstractHandlerMapping {

  private MyCustomController myCustomController;

  public MyCustomHandler(MyCustomController myCustomController) {
    this.myCustomController = myCustomController;
  }

  @Override
  protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
    return myCustomController;
  }
}

https://github.com/nowszy94/spring-mvc-dynamic-controller

答案 4 :(得分:1)

@RequestMapping(value = "/bla/{myParam1}", method = RequestMethod.GET)
public String media(@PathVariable("myParam1") String myParam1, HttpServletRequest request, HttpServletResponse response) {
    return "bla/" + myParam1;
}

答案 5 :(得分:0)

以下构造可在单个类中配置和实现处理程序方法。

它是动态映射和静态映射的组合-所有MVC注释都可以使用,例如@RequestParam@PathVariable@RequestBody等。

@RestController注释会创建bean,并将@ResponseBody添加到每个处理程序方法中。

@RestController
public class MyController {

    @Inject
    private RequestMappingHandlerMapping handlerMapping;

    /***
     * Register controller methods to various URLs.
     */
    @PostConstruct
    public void init() throws NoSuchMethodException {

        /**
         * When "GET /simpleHandler" is called, invoke, parametrizedHandler(String,
         * HttpServletRequest) method.
         */
        handlerMapping.registerMapping(
                RequestMappingInfo.paths("/simpleHandler").methods(RequestMethod.GET)
                .produces(MediaType.APPLICATION_JSON_VALUE).build(),
                this,
                // Method to be executed when above conditions apply, i.e.: when HTTP
                // method and URL are called)
                MyController.class.getDeclaredMethod("simpleHandler"));

        /**
         * When "GET /x/y/z/parametrizedHandler" is called invoke
         * parametrizedHandler(String, HttpServletRequest) method.
         */
        handlerMapping.registerMapping(
                RequestMappingInfo.paths("/x/y/z/parametrizedHandler").methods(RequestMethod.GET)
                .produces(MediaType.APPLICATION_JSON_VALUE).build(),
                this,
                // Method to be executed when above conditions apply, i.e.: when HTTP
                // method and URL are called)
                MyController.class.getDeclaredMethod("parametrizedHandler", String.class, HttpServletRequest.class));
    }

    // GET /simpleHandler
    public List<String> simpleHandler() {
        return Arrays.asList("simpleHandler called");
    }

    // GET /x/y/z/parametrizedHandler
    public ResponseEntity<List<String>> parametrizedHandler(
            @RequestParam(value = "param1", required = false) String param1,
            HttpServletRequest servletRequest) {
        return ResponseEntity.ok(Arrays.asList("parametrizedHandler called", param1));
    }
}