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