我已经使用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。
有什么想法吗? }
答案 0 :(得分:24)
Spring MVC使用HandlerMapping
接口的实现来执行URL映射。通常用于开箱即用的是默认实现,即SimpleUrlHandlerMapping
,BeanNameUrlHandlerMapping
和DefaultAnnotationHandlerMapping
。
如果你想实现自己的映射机制,这很容易做到 - 只需实现该接口(或者,更有可能扩展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
,但会提供处理所有请求的HandlerMapping
和Controller
。如果您运行该应用程序,您将在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;
}
}
答案 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));
}
}