如何在另一个控制器中覆盖@RequestMapping?

时间:2015-04-20 14:58:52

标签: java spring-mvc

我需要扩展现有的控制器并为其添加一些功能。但是作为项目要求我无法触及原始控制器,问题是该控制器上有一个@RequestMapping注释。所以我的问题是如何向/someUrl请求我的新控制器而不是旧控制器。

这是一个例子,只是为了澄清我在说什么:

原始控制器:

@Controller
public class HelloWorldController {

    @RequestMapping("/helloWorld")
    public String helloWorld(Model model) {
        model.addAttribute("message", "Hello World!");
        return "helloWorld";
    }
}

新控制器:

@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";
    }
}

如何在不编辑HelloWorldController的情况下覆盖原始地图?

3 个答案:

答案 0 :(得分:12)

无法覆盖作为注释的Url映射。如果两个或更多控制器配置了相同的请求URL和请求方法,您将收到错误。

您可以做的是扩展请求映射:

@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";
    }

}

示例:现在,如果您调用 yourhost / helloWorld?type = 42 MyHelloWorldController将响应请求


顺便说一下。 控制器不应该是动态内容提供者。您需要一个@Service实例。因此,您可以实现一次Controller并使用多个Service实现。这是Spring MVC and 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";
    }

}

答案 1 :(得分:3)

每个映射必须是唯一的。。没有办法否决现有的@RequestMapping



但是您可以随时做一些解决方法:

在请求中使用param会创建一个与现有的@RequestMapping不同的新@RequestMapping("/helloWorld/{someDataId}", method = RequestMethod.GET) public String helloWorld(@PathVariable("someDataId") final long id, Model model) { /* your code here */ }

@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); } } 扩展现有的:{/ p>

{{1}}

答案 2 :(得分:3)

这是另一种解决方法,可能有危险,也可能没有危险。

创建以下类" MyRequestMappingHandler",然后将其连接到您的MvcConfig

@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {

    return new MyRequestMappingHandler();
}

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;
    }
}

这允许你做的是,扩展一个@Controller类,并用@Primary标记它,并覆盖该类的方法,你的新类现在将在spring启动时加载,而不是炸掉& #34;多个bean /请求映射等&#34;

&#34; super&#34;的例子控制器:

@Controller 
public class Foobar {

    @RequestMapping(method = "GET")
    private String index() {
      return "view";
    }

} 

实施示例:

@Primary
@Controller 
public class MyFoobar extends Foobar {

    @Override
    private String index() {
      return "myView";
    }

}