1. 程式人生 > >將一個@RequestMapping定義的方法對映為兩個http服務----記一次有趣的排查問題過程

將一個@RequestMapping定義的方法對映為兩個http服務----記一次有趣的排查問題過程

小夥伴遇到個問題,某個controller釋出的http服務直接訪問沒問題,通過nginx轉發後就報404,此模組其他url訪問都正常。。   controller程式碼如下:
@RequestMapping("/addrExport.spr")
public class AddrExportController
@RequestMapping(params = "method=exportDynamicQueryData")
public void exportDynamicQueryData(HttpServletRequest request, HttpServletResponse response, String downLoadData) {
     System.out.println("hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh");
     .....
} 
springboot應用,未配置上下文根。所以此http服務未使用nginx轉發時的直接訪問地址為:http://localhost:8299/addrExport.spr?method=exportDynamicQueryData   前臺ajax呼叫url:   nginx對映路徑:
server {
        listen       8888;
   ......
       location /portal/space-addr/ {
            proxy_pass http://localhost:8299/space-addr/;
        }
    .....
}

 

使用nginx埠訪問的url如下,就是此url訪問404 http://localhost:8888/portal/space-addr/addrExport.spr?method=exportDynamicQueryData   一陣兵荒馬亂,小夥伴發現此url轉發到後臺的路徑為 http://localhost:8299/space-addr/addrExport.spr?method=exportDynamicQueryData 多了一個space-addr   此時有兩種方案, 方案1.在controller裡@RequestMapping時增加space-addr字首 方案2.修改nginx對映去掉proxy_pass中多餘的字首,注意紅色字型部分去掉了space-addr,見下文:

location /portal/space-addr/ {
    proxy_pass http://localhost:8299/;
}

採用方案2修改nginx配置驗證後發現確實能解決問題。但問題來了。。。。 修改nginx轉發規則後,此模組原來正常訪問的的功能應該報錯才對,但事實上這些請求順暢無比,彷彿世界從未發生改變,也就是說這些url在nginx轉發規則增加space-addr和去掉space-addr時都可以正常訪問。這真的不科學。。。   。。此處省略走過的彎路。。。   抓包查看了這些訪問正常的功能對應的請求,發現都是一樣的url,只是引數不同:http://localhost:8888/portal/space-addr/rescommon/service/callServerFunction 檢視此url對應的controller(ResCommonServiceController,隱藏特別深,由公共模組提供,見下圖)發現:類上無@RequestMapping註解,也無@Controller或者@RestController註解,方法上此註解的value=callServerFunction。而根據nginx轉發規則,此註解的value至少應該包含rescommon/service/callServerFunction。那麼缺失的rescommon/service是在哪裡被拼接的呢?

 

感謝spring的日誌輸出,偶然搜尋發現瞭如下內容,什麼鬼,竟然增加了兩個rescommon/service/callServerFunction相關的requestmapping:一個有space-addr字首,一個無字首。。   最終發現公共側對ResCommonServiceController做了特殊處理,具體見CommonConfig類的registerCommonServerMapping方法(見下圖),預設會生成兩個requestmapping: 1./rescommon/service/callServerFunction 2.${pub.commonservice.prefix}/rescommon/service/callServerFunction,對於排查問題的這個模組存在配置pub.commonservice.prefix=space-addr,最終效果為space-addr/rescommon/service/callServerFunction 正是因為生成了兩個requestmapping,所以才會出現nginx裡轉發規則配或不配space-addr都沒問題的情況,因為都能匹配到requestmapping。。

 

 類全文如下:

 1 @Configuration
 2 public class CommonConfig {
 3 
 4     @Value("${pub.commonservice.prefix}")
 5     String prefix;
 6 
 7     @Bean
 8     public ResCommonServiceController registerCommonServiceController() {
 9         return new ResCommonServiceController();
10     }
11 
12     @Autowired
13     public void registerCommonServerMapping(ResCommonServiceController registerCommonServiceController, RequestMappingHandlerMapping mapping) {
14         String uri = "/rescommon/service/";
15         RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths(uri).build();
16         ResRequestMappingHandlerMapping resRequestMappingHandlerMapping = new ResRequestMappingHandlerMapping(mapping, requestMappingInfo);
17         resRequestMappingHandlerMapping.detectHandlerMethods(registerCommonServiceController);
18         if (!("".equals(prefix) || prefix == null || "${pub.commonservice.prefix}".equals(prefix))) {
19             String[] split = prefix.split(",");
20             for (String prefix1 : split) {
21                 ResRequestMappingHandlerMapping resRequestMappingHandlerMapping1 = new ResRequestMappingHandlerMapping(mapping, RequestMappingInfo.paths(prefix1 + uri).build());
22                 resRequestMappingHandlerMapping1.detectHandlerMethods(registerCommonServiceController);
23             }
24         }
25     }
26 }
這個寫法很有趣,第一次見。。自定義的ResRequestMappingHandlerMapping類擴充套件了spring的RequestMappingHandlerMapping,覆寫了detectHandlerMethods方法,在registerMapping時將自定義的url字首和method上的url做拼接。
 1 public class ResRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
 2 
 3     /**
 4      * 代理物件
 5      */
 6     private RequestMappingHandlerMapping requestMappingHandlerMapping;
 7 
 8     /**
 9      * 存放類的RequestMapping資訊
10      */
11     RequestMappingInfo typeMapping;
12 
13     public ResRequestMappingHandlerMapping(RequestMappingHandlerMapping requestMappingHandlerMapping, RequestMappingInfo typeMapping) {
14         this.requestMappingHandlerMapping = requestMappingHandlerMapping;
15         this.typeMapping = typeMapping;
16     }
17 
18     @Override
19     public void detectHandlerMethods(final Object handler) {
20         if(handler == null){
21             return;
22         }
23         Class<?> handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
24         final Class<?> userType = ClassUtils.getUserClass(handlerType);
25 
26         Map<Method, RequestMappingInfo> methods = MethodIntrospector.selectMethods(userType, new MethodIntrospector.MetadataLookup<RequestMappingInfo>() {
27             public RequestMappingInfo inspect(Method method) {
28                 try {
29                     return getMappingForMethod(method, userType);
30                 }
31                 catch (Exception ex) {
32                     throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex);
33                 }
34             }
35         });
36 
37         if (logger.isDebugEnabled()) {
38             logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
39         }
40         for (Map.Entry<Method, RequestMappingInfo> entry : methods.entrySet()) {
41             Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
42             RequestMappingInfo mapping = entry.getValue();
43             if (typeMapping != null) {
44                 mapping = typeMapping.combine(mapping);
45             }
46             requestMappingHandlerMapping.registerMapping(mapping, handler, invocableMethod);
47         }
48     }

 

至此,謎題解開了,因為公共側對ResCommonServiceController類做了特殊定製,使其釋出的@RequestMapping方法釋出了兩個http服務。。所以nginx的轉發規則帶不帶字首都不影響功能使用。。