Можно ли динамически устанавливать RequestMappings в Spring MVC?



Я использую Spring MVC уже три месяца. Я рассматривал хороший способ динамического добавления RequestMapping. Это происходит из-за необходимости поместить части контроллера в библиотеку, а затем добавить их динамически. В любом случае, единственный способ, который я могу придумать, это объявить контроллер следующим образом:



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

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

}


Что не очень хорошо, потому что в основном я не использую весну. Тогда я не могу использовать привязку формы, аннотации и т. д.. Я бы хотел, чтобы динамически добавить requestMappings для методов классов, которые могли бы быть аннотированы, как обычные контроллеры MVC, с автообвинением, чтобы я мог избежать обработки HttpServletRequest вручную.



Есть идеи?
}

476   4  

4 ответов:

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

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

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

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

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

В моем конкретном случае использования я создал экземпляр новых объектов @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());

Пожалуйста, посмотрите на мое решение. Он не создает динамические @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

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

}

Comments

    Ничего не найдено.