Можно ли динамически устанавливать RequestMappings в Spring MVC? - PullRequest
10 голосов
/ 22 апреля 2011

Я использую 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 вручную.

Есть идеи?}

Ответы [ 5 ]

24 голосов
/ 22 апреля 2011

Spring MVC выполняет сопоставления URL-адресов с использованием реализаций интерфейса HandlerMapping.Те, которые обычно используются "из коробки", являются реализациями по умолчанию, а именно SimpleUrlHandlerMapping, BeanNameUrlHandlerMapping и DefaultAnnotationHandlerMapping.

Если вы хотите реализовать свой собственный механизм отображения, это довольно легко сделать - простореализовать этот интерфейс (или, что более вероятно, расширить AbstractUrlHandlerMapping), объявить класс как bean-компонент в вашем контексте, и с ним будет обращаться DispatcherServlet, когда запрос должен быть отображен.

Примечаниечто вы можете иметь столько реализаций HandlerMapping, сколько захотите в одном контексте.С ними будут консультироваться по очереди, пока у одного из них не будет совпадения.

4 голосов
/ 26 января 2018

Я потратил много времени, пытаясь заставить это работать, но в конце концов мне удалось найти решение, которое возвращает 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);
  }

}
3 голосов
/ 07 декабря 2016

Пожалуйста, посмотрите на мое решение.Он не создает динамический @RequestMapping в вашем коде, но предоставляет HandlerMapping и Controller, который обрабатывает все запросы.Если вы запустите это приложение, вы получите сообщение Hello World в json.

Класс приложения:

@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

3 голосов
/ 24 сентября 2016

Я знаю, что это действительно старо, но я решил, что я брошу это на тот случай, если у кого-то еще будет такой же грубый опыт, как у меня, когда я пытался сделать эту работу.В итоге я воспользовался двумя функциями Spring: возможностью динамической регистрации bean-компонентов после запуска контекста и методом afterPropertiesSet() объекта RequestMappingHandlerMapping.

Когда инициализируется RequestMappingHandlerMapping, он сканирует контекст и создает карту всех @RequestMapping с, которые он должен обслуживать (предположительно из соображений производительности).Если вы динамически регистрируете компоненты, помеченные @Controller, они не будут приняты.Чтобы повторно запустить это сканирование, вам просто нужно вызвать afterPropertiesSet() после того, как вы добавили свои bean-компоненты.

В моем конкретном случае использования я создал новые объекты @Controller в отдельном контексте Spring и должен был подключитьих в мой контекст 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());
0 голосов
/ 07 марта 2019

Следующая конструкция конфигурирует и реализует методы-обработчики в одном классе.

Это комбинация динамического и статического сопоставления - можно использовать все аннотации MVC, например @RequestParam, @PathVariable, @RequestBody и т. Д.

@RestController аннотация создает компонент и добавляет @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));
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...