【问题标题】:Is it possible to dynamically set RequestMappings in Spring MVC?是否可以在 Spring MVC 中动态设置 RequestMappings?
【发布时间】:2011-04-22 18:09:08
【问题描述】:

我已经使用 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。

有什么想法吗? }

【问题讨论】:

    标签: spring-mvc


    【解决方案1】:

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

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

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

    【讨论】:

    • 感谢斯卡夫,您总是提供很棒的建议。无论如何,我如何通过代码操作上下文,我的意思是,有没有办法动态添加 HandlerMapping bean 或任何其他 bean?
    • @gotch4:您不需要动态添加HandlerMapping。您配置一个 自定义HandlerMapping,然后动态添加映射到它。由于您是自己编写HandlerMapping,因此其工作方式取决于您。
    【解决方案2】:

    我花了很长时间试图让它工作,但最终设法找到一个返回 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);
      }
    
    }
    

    【讨论】:

    • 能否补充一下addMapping 的调用时间和地点?
    • 它可以从任何你喜欢的地方调用。就我而言,我的应用程序发布到解析新请求的单独端点,并最终调用addMapping
    【解决方案3】:

    我知道这真的很老了,但我想我会把它扔掉,以防其他人有我在尝试完成这项工作时的粗略经历。我最终利用了 Spring 的两个特性:在上下文启动后动态注册 bean 的能力以及 RequestMappingHandlerMapping 对象上的 afterPropertiesSet() 方法。

    RequestMappingHandlerMapping 初始化时,它会扫描上下文并创建一个它需要服务的所有@RequestMappings 的映射(可能是出于性能原因)。如果您动态注册带有@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());
    

    【讨论】:

      【解决方案4】:

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

      它是动态和静态映射的组合——所有的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));
          }
      }
      

      【讨论】:

        【解决方案5】:

        请看我的解决方案。它不会在您的代码中创建动态@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

        【讨论】:

          【解决方案6】:
          @RequestMapping(value = "/bla/{myParam1}", method = RequestMethod.GET)
          public String media(@PathVariable("myParam1") String myParam1, HttpServletRequest request, HttpServletResponse response) {
              return "bla/" + myParam1;
          }
          

          【讨论】:

            猜你喜欢
            • 2012-01-25
            • 1970-01-01
            • 2019-11-30
            • 1970-01-01
            • 2019-03-22
            • 2014-12-18
            • 2020-01-31
            • 1970-01-01
            相关资源
            最近更新 更多