Java 如何在另一个控制器中覆盖@RequestMapping?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29751416/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to override @RequestMapping in another controller?
提问by Victor Ribeiro da Silva Eloy
I need to extend an existing controller and add some functionality to it. But as a project requirement I can't touch in the original controller, the problem is that this controller have an @RequestMapping
annotation on it. So my question is how can I make requests to /someUrl
go to my new controller instead of the old one.
我需要扩展现有的控制器并为其添加一些功能。但是作为一个项目需求,我在原来的控制器中无法触及,问题是这个控制器上有一个@RequestMapping
注释。所以我的问题是我怎样才能请求/someUrl
去我的新控制器而不是旧控制器。
here is a example just to clarify what I'm talking about:
这是一个例子,只是为了澄清我在说什么:
Original controller:
原控制器:
@Controller
public class HelloWorldController {
@RequestMapping("/helloWorld")
public String helloWorld(Model model) {
model.addAttribute("message", "Hello World!");
return "helloWorld";
}
}
new Controller:
新控制器:
@Controller
public class MyHelloWorldController {
@RequestMapping("/helloWorld")
public String helloWorld(Model model) {
model.addAttribute("message", "Hello World from my new controller");
// a lot of new logic
return "helloWorld";
}
}
how can I override the original mapping without editing HelloWorldController
?
如何在不编辑的情况下覆盖原始映射HelloWorldController
?
采纳答案by dieter
Url mapping as annotation can not be overridden. You will get an error if two or more Controllers are configured with the same request url and request method.
作为注释的 URL 映射不能被覆盖。如果两个或多个 Controller 配置了相同的请求 url 和请求方法,则会出现错误。
What you can do is to extend the request mapping:
您可以做的是扩展请求映射:
@Controller
public class MyHelloWorldController {
@RequestMapping("/helloWorld", params = { "type=42" })
public String helloWorld(Model model) {
model.addAttribute("message", "Hello World from my new controller");
return "helloWorld";
}
}
Example: Now if you call yourhost/helloWorld?type=42MyHelloWorldController
will response the request
示例:现在如果你调用yourhost/helloWorld?type=42MyHelloWorldController
将响应请求
By the way.
Controller should not be a dynamic content provider. You need a @Service
instance. So you can implement Controller once and use multiple Service implementation. This is the main idea of Spring MVC and DI
顺便一提。控制器不应是动态内容提供者。你需要一个@Service
实例。所以你可以实现一次 Controller 并使用多个 Service 实现。这是Spring MVC和DI的主要思想
@Controller
public class HelloWorldController {
@Autowired
private MessageService _messageService; // -> new MessageServiceImpl1() or new MessageServiceImpl2() ...
@RequestMapping("/helloWorld")
public String helloWorld(Model model) {
model.addAttribute("message", messageService.getMessage());
return "helloWorld";
}
}
回答by Jordi Castilla
Each mapping must be unique.. There is no way to overrule an existing @RequestMapping
.
每个映射必须是唯一的。. 没有办法推翻现有的@RequestMapping
.
BUTYou can always do some workarounds:
但是你总是可以做一些解决方法:
Use a param in the request like this will create a new @RequestMapping
that will differ from the existing one.
在请求中使用这样的参数将创建一个@RequestMapping
与现有参数不同的新参数。
@RequestMapping("/helloWorld/{someDataId}", method = RequestMethod.GET)
public String helloWorld(@PathVariable("someDataId") final long id, Model model) {
/* your code here */
}
Or creating another @Controller
extending the existing one:
或者创建另一个@Controller
扩展现有的:
public class YourController extends BaseController {
@Override
@RequestMapping("/helloWorld")
public void renderDashboard(Model model){
// Call to default functionallity (if you want...)
super.renderDashboard(patientId, map);
}
}
回答by Bruce Grobler
Here is another workaround, that may or may not be dangerous.
这是另一种解决方法,可能危险也可能不危险。
Create the below class "MyRequestMappingHandler", then wire it up in your MvcConfig
创建以下类“MyRequestMappingHandler”,然后将其连接到您的 MvcConfig
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new MyRequestMappingHandler();
}
RequestMappingHandlerMapping: * THIS IS NOT PRODUCTION CODE - UP TO YOU *
RequestMappingHandlerMapping: * 这不是生产代码 - 由你决定 *
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class MyRequestMappingHandler extends RequestMappingHandlerMapping {
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo mappingForMethod = super.getMappingForMethod(method, handlerType);
// Check if this class extends a super. and that super is annotated with @Controller.
Class superClass = handlerType.getSuperclass();
if (superClass.isAnnotationPresent(Controller.class)) {
// We have a super class controller.
if (handlerType.isAnnotationPresent(Primary.class)) {
// We have a @Primary on the child.
return mappingForMethod;
}
} else {
// We do not have a super class, therefore we need to look for other implementations of this class.
Map<String, Object> controllerBeans = getApplicationContext().getBeansWithAnnotation(Controller.class);
List<Map.Entry<String, Object>> classesExtendingHandler = controllerBeans.entrySet().stream().filter(e ->
AopUtils.getTargetClass(e.getValue()).getSuperclass().getName().equalsIgnoreCase(handlerType
.getName()) &&
!AopUtils.getTargetClass(e.getValue()).getName().equalsIgnoreCase(handlerType.getName()))
.collect(Collectors.toList());
if (classesExtendingHandler == null || classesExtendingHandler.isEmpty()) {
// No classes extend this handler, therefore it is the only one.
return mappingForMethod;
} else {
// Classes extend this handler,
// If this handler is marked with @Primary and no others are then return info;
List<Map.Entry<String, Object>> classesWithPrimary = classesExtendingHandler
.stream()
.filter(e -> e.getValue().getClass().isAnnotationPresent(Primary.class) &&
!AopUtils.getTargetClass(e.getValue().getClass()).getName().equalsIgnoreCase
(handlerType.getName()))
.collect(Collectors.toList());
if (classesWithPrimary == null || classesWithPrimary.isEmpty()) {
// No classes are marked with primary.
return null;
} else {
// One or more classes are marked with @Primary,
if (classesWithPrimary.size() == 1 && AopUtils.getTargetClass(classesWithPrimary.get(0).getValue
()).getClass().getName().equalsIgnoreCase(handlerType.getName())) {
// We have only one and it is this one, return it.
return mappingForMethod;
} else if (classesWithPrimary.size() == 1 && !AopUtils.getTargetClass(classesWithPrimary.get(0)
.getValue()).getClass().getName().equalsIgnoreCase(handlerType.getName())) {
// Nothing.
} else {
// nothing.
}
}
}
}
// If it does, and it is marked with @Primary, then return info.
// else If it does not extend a super with @Controller and there are no children, then return info;
return null;
}
}
What this allows you to do is, extend a @Controller class, and mark it with @Primary, and override a method on that class, your new class will now be loaded up when spring starts up instead of blowing up with "multiple beans / request mappings etc"
这允许你做的是,扩展一个@Controller 类,并用@Primary 标记它,并覆盖该类上的一个方法,你的新类现在将在 spring 启动时加载,而不是用“多个 bean /请求映射等”
Example of "super" Controller :
“超级”控制器示例:
@Controller
public class Foobar {
@RequestMapping(method = "GET")
private String index() {
return "view";
}
}
Example of implementation :
实施示例:
@Primary
@Controller
public class MyFoobar extends Foobar {
@Override
private String index() {
return "myView";
}
}