2015-04-20 13 views
13

Tôi cần mở rộng bộ điều khiển hiện tại và thêm một số chức năng vào bộ điều khiển. Nhưng như một yêu cầu dự án tôi không thể chạm vào bộ điều khiển gốc, vấn đề là bộ điều khiển này có chú thích @RequestMapping trên đó. Vì vậy, câu hỏi của tôi là làm thế nào tôi có thể yêu cầu để /someUrl đi đến bộ điều khiển mới của tôi thay vì cũ.Cách ghi đè @RequestMapping trong bộ điều khiển khác?

đây là một ví dụ chỉ để làm rõ những gì tôi đang nói về:

điều khiển gốc:

@Controller 
public class HelloWorldController { 

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

điều khiển mới:

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

làm thế nào tôi có thể ghi đè lên các bản đồ gốc mà không chỉnh sửa HelloWorldController?

+0

Không chắc liệu [' Order'] (http://docs.spring.io /spring-framework/docs/3.2.3.RELEASE/javadoc-api/org/springframework/core/annotation/Order.html) chú thích sẽ ảnh hưởng đến logic lập bản đồ xử lý (bạn có thể thử). Nếu không, bạn có thể ánh xạ tới một URL khác và triển khai một số loại viết lại (ví dụ: thông qua [URLRewriteFilter] (http://tuckey.org/urlrewrite/)). –

+0

Phải sửa bản thân - 'Order' sẽ không hoạt động vì ánh xạ trùng lặp không được phép trong Spring (như Jordi đã chỉ ra trong câu trả lời của anh ấy). –

Trả lời

9

Url lập bản đồ như chú thích không thể được ghi đè. Bạn sẽ gặp lỗi nếu hai hoặc nhiều Bộ điều khiển được cấu hình với cùng một url yêu cầu và phương thức yêu cầu.

Những gì bạn có thể làm là để mở rộng bản đồ theo yêu cầu:

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

} 

Ví dụ: Bây giờ nếu bạn gọi yourhost/HelloWorld type = 42MyHelloWorldController sẽ đáp ứng yêu cầu


Bằng? cách. Bộ điều khiển không phải là nhà cung cấp nội dung động. Bạn cần một phiên bản @Service. Vì vậy, bạn có thể triển khai Controller một lần và sử dụng nhiều dịch vụ. Đây là ý tưởng chính của 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"; 
    } 

} 
3

Mỗi ánh xạ phải là duy nhất.. Không có cách nào để ghi đè một số hiện tại @RequestMapping.



NHƯNG Bạn luôn có thể làm một số cách giải quyết:

Sử dụng một param trong yêu cầu như thế này sẽ tạo ra một mới @RequestMapping rằng sẽ khác với một hiện có.

@RequestMapping("/helloWorld/{someDataId}", method = RequestMethod.GET) 
public String helloWorld(@PathVariable("someDataId") final long id, Model model) { 
/* your code here */ 
} 

Hoặc tạo @Controller khác mở rộng một hiện:

public class YourController extends BaseController { 

    @Override 
    @RequestMapping("/helloWorld") 
    public void renderDashboard(Model model){ 
     // Call to default functionallity (if you want...) 
     super.renderDashboard(patientId, map); 
    } 
} 
+0

Thật không may khi tôi cố gắng ghi đè lên một mùa xuân aproach ném một AmbiguousMappingException. –

+0

Bạn đã thử tùy chọn thứ nhất với thuộc tính * virtual * mới chưa? –

+2

@ JordiCastilla sự hiểu biết của tôi là bạn không thể sử dụng ánh xạ Ghi đè trừ khi các phương thức có cùng tên và chữ ký. Làm thế nào lựa chọn thứ hai sẽ làm việc sau đó? –

2

Dưới đây là một cách giải quyết, có thể có hoặc không có thể nguy hiểm.

Tạo lớp bên dưới "MyRequestMappingHandler", sau đó dây nó lên trong MvcConfig bạn

@Bean 
public RequestMappingHandlerMapping requestMappingHandlerMapping() { 

    return new MyRequestMappingHandler(); 
} 

RequestMappingHandlerMapping: * NÀY KHÔNG PHẢI LÀ SẢN XUẤT MÃ - tùy thuộc vào bạn *

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

Điều này cho phép bạn làm là, mở rộng một lớp @Controller và đánh dấu nó bằng @Primary, và ghi đè lên một phương thức trên lớp đó, lớp mới của bạn bây giờ sẽ được nạp khi mùa xuân bắt đầu thay vì thổi lên với "nhiều bean/reques t ánh xạ vv"

Ví dụ về "siêu" Bộ điều khiển:

@Controller 
public class Foobar { 

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

} 

Ví dụ về thực hiện:

@Primary 
@Controller 
public class MyFoobar extends Foobar { 

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

} 
Các vấn đề liên quan