溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

RequestMapping要寫在Controller類里嗎

發(fā)布時(shí)間:2021-12-16 09:23:17 來源:億速云 閱讀:141 作者:iii 欄目:大數(shù)據(jù)

本篇內(nèi)容介紹了“RequestMapping要寫在Controller類里嗎”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

使用Spring Cloud做項(xiàng)目的同學(xué)會(huì)使用Feign這個(gè)組件進(jìn)行遠(yuǎn)程服務(wù)的調(diào)用,F(xiàn)eign這個(gè)組件采用模板的方式,有著優(yōu)雅的代碼書寫規(guī)范。核心原理對(duì)Feign等相關(guān)注解進(jìn)行解析,并提取信息,在Spring Boot工程啟動(dòng)時(shí),通過反射生產(chǎn)Request的bean,并將提取的信息,設(shè)置到bean中,最后注入到ioc容器中。

現(xiàn)在有這樣的場(chǎng)景,服務(wù)A提高RestApi接口,服務(wù)B、C、D等服務(wù)需要調(diào)用服務(wù)A提供的RestApi接口,這時(shí)最常見的做法是在服務(wù)B、C、D分別寫一個(gè)FeignClient,并需要寫RestApi接口的接收參數(shù)的實(shí)體和接收響應(yīng)的實(shí)體DTo類。這樣的做法就是需要不停復(fù)制代碼。

有沒有辦法簡(jiǎn)潔上面的操作呢?有一種最常見的做法是將將服務(wù)A進(jìn)行模塊拆分,將FeignClient和常見的model、dto對(duì)外輸出的類單獨(dú)寫一個(gè)模塊,可以類似于取名a-service-open_share。這樣將服務(wù)A服務(wù)分為兩個(gè)模塊,即A服務(wù)的業(yè)務(wù)模塊和A服務(wù)需要被其他服務(wù)引用的公共類的模塊。服務(wù)B、C、D只需要引用服務(wù)A的a-service-open_share就具備調(diào)用服務(wù)A的能力。

筆者在這里遇到一個(gè)有趣的其問題。首先看問題:

寫一個(gè)FeignClient:

@FeignClient(name = "user-service")
public interface UserClient {

    @GetMapping("/users")
    List<User> getUsers();

寫一個(gè)實(shí)現(xiàn)類:

@RestController
public class UserController implements UserClient {
    @Autowired
    UserService      userService;

    @OverRide
    List<User> getUsers(){
       return userService.getUsers();
    }

啟動(dòng)工程,瀏覽器訪問接口localhost:8008/users,竟然能正確訪問?!明明我在UserController類的getUsers方法沒有加RequestMapping這樣的注解。為何能正確的映射?!

帶著這樣的疑問,我進(jìn)行了一番的分析和探索!

首先就是自己寫了一個(gè)demo,首先創(chuàng)建一個(gè)接口類:

public interface ITest {

    @GetMapping("/test/hi")
    public String hi();
}

寫一個(gè)Controller類TestController

@RestController
public class TestController implements ITest {
    @Override
    public String hi() {
        return "hi you !";
    }

啟動(dòng)工程,瀏覽器訪問:http://localhost:8762/test/hi,瀏覽器顯示:

hi you !

我去,TestController類的方法 hi()能夠得到ITest的方法hi()的   @GetMapping(“/test/hi”)注解嗎? 答案肯定是獲取不到的。

特意編譯了TestController字節(jié)碼文件:
javap -c TestController

 public class com.example.demo.web.TestController implements com.example.demo.web.ITest {
  public com.example.demo.web.TestController();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public java.lang.String hi();
    Code:
       0: ldc           #2                  // String hi you !
       2: areturn
}

上面的字節(jié)碼沒有任何關(guān)于@GetMapping(“/test/hi”)的信息,可見TestController直接獲取不到@GetMapping(“/test/hi”)的信息。

那應(yīng)該是Spring MVC在啟動(dòng)時(shí)在向容器注入Controller的Bean(HandlerAdapter)時(shí)做了處理。初步判斷應(yīng)該是通過反射獲取到這些信息,并組裝到Controller的Bean中。首先看通過反射能不能獲取ITest的注解信息:

 public static void main(String[] args) throws ClassNotFoundException {
    Class c = Class.forName("com.example.demo.web.TestController");
    Class[] i=c.getInterfaces();
    System.out.println("start interfaces.."  );
    for(Class clz:i){
        System.out.println(clz.getSimpleName());
        Method[] methods = clz.getMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(GetMapping.class)) {
                GetMapping w = method.getAnnotation(GetMapping.class);
                System.out.println("value:" + w.value()[0]  );
            }
        }
    }
    System.out.println("end interfaces.."  );

    Method[] methods = c.getMethods();
    for (Method method : methods) {
        if (method.isAnnotationPresent(GetMapping.class)) {
            GetMapping w = method.getAnnotation(GetMapping.class);
            System.out.println("value:" + w.value());
        }
    }
}

允運(yùn)行上面的代碼:

start interfaces..

ITest

value:/test/hi

end interfaces..

可見通過反射是TestController類是可以獲取其實(shí)現(xiàn)的接口的注解信息的。為了驗(yàn)證Spring Mvc 在注入Controller的bean時(shí)通過反射獲取了其實(shí)現(xiàn)的接口的注解信息,并作為urlMapping進(jìn)行了映射。于是查看了Spring Mvc 的源碼,經(jīng)過一系列的跟蹤在RequestMappingHandlerMapping.java類找到了以下的方法:

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
   RequestMappingInfo info = createRequestMappingInfo(method);
   if (info != null) {
      RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
      if (typeInfo != null) {
         info = typeInfo.combine(info);
      }
   }
   return info;
}

繼續(xù)跟蹤源碼在AnnotatedElementUtils 類的searchWithFindSemantics()方法中發(fā)現(xiàn)了如下代碼片段:

// Search on methods in interfaces declared locally
Class<?>[] ifcs = method.getDeclaringClass().getInterfaces();
result = searchOnInterfaces(method, annotationType, annotationName, containerType, processor,
      visited, metaDepth, ifcs);
if (result != null) {
   return result;
}

這就是我要尋找的代碼片段,驗(yàn)證了我的猜測(cè)。

“RequestMapping要寫在Controller類里嗎”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI