關於@FeignClient的屬性源碼分析


  這段時間一直在用RestTemplate做restful服務的調度,與新同事交流后學到了另外的方式用Feign來調用。以前用dubbo多了,確實對spring-cloud全家桶的認識不足。今天用feign的調用方式將文件服務的相關接口做了改造。但是對@FeignClient注解的相關屬性不是很清楚。同時在不指定url的情況下,feign是如何找到服務地址的?帶着這兩個問題,做了今天的源碼解讀。接下來做個總結

一、@FeignClient的各屬性解讀

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {
    /**
     * 說明:
     * 1、value 與 name 互為別名,兩者二選一即可
     * 2、當 contextId 沒有值的時候,會默認獲取(value/name)的值
     * 3、當未指定 url 請求地址的時候,最終會通過 ribbon-loadbalancer工具,從consul注冊節點中選取 service-id 等於 value的服務作為請求地址
     * @return
     */
    @AliasFor("name")
    String value() default "";
    /**
     * 說明:
     * 1、serviceId 已經作廢,其目的與 value一致。如果設置了 serviceId,則value/name 皆以 serviceId 為准
     */
    /** @deprecated */
    @Deprecated
    String serviceId() default "";
    /**
     * 說明:
     * 1、當 contextId 沒有值的時候,會默認獲取(value/name)的值
     * 2、當 qualifier 沒有值的時候,會將 '${contextId}FeignClient'作為 feign 的bean組件別名
     */
    String contextId() default "";
    /**
     * 說明:同 value, 二選一
     */
    @AliasFor("value")
    String name() default "";
    /**
     * 說明:
     * 1、feign的bean組件別名,擁有最高優先級;
     * 2、當 qualifier 為空時,取 '${contextId}FeignClient' 作為 bean 的名稱
     */
    String qualifier() default "";
    /**
     * 說明:
     * 1、設置 url 以后,后續發起http調用時,直接讀取該地址作為請求目標;
     * 2、未設置 url 時,借助 ribbon-loadbalancer 組件,根據 (value/name)從consul的服務列表中選中service-id匹配的目標服務;
     */
    String url() default "";

    boolean decode404() default false;

    Class<?>[] configuration() default {};

    Class<?> fallback() default void.class;

    Class<?> fallbackFactory() default void.class;
    /**
     * 說明:
     * 目標服務器 對應的資源 uri (FeignClient下所有方法相同的path路徑)
     */
    String path() default "";

    boolean primary() default true;
}

核心源碼截圖如下

org\springframework\cloud\spring-cloud-openfeign-core\2.1.5.RELEASE\spring-cloud-openfeign-core-2.1.5.RELEASE.jar!\org\springframework\cloud\openfeign\FeignClientsRegistrar.class

  

 二、當FeignClient未設置url時,通過ribbon查找服務的核心方法

\com\netflix\ribbon\ribbon-loadbalancer\2.3.0\ribbon-loadbalancer-2.3.0.jar!\com\netflix\client\AbstractLoadBalancerAwareClient.class

 

 三、使用Feign做restful服務調用的簡單示例

3.1、開啟FeignClient的掃描(EnableFeignClients)

@Configuration
@EnableFeignClients(basePackages = "com.simm")
public class FeignConfiguration {
}

3.2、設置 RequestInterceptor,統一添加授權的Header

@Slf4j
@Component(value = "core_feign_interceptor")
@AllArgsConstructor
public class FeignInterceptor implements RequestInterceptor {
    private final ClientTokenTemplate clientTokenTemplate;

    /**
     * fengin請求添加header
     * @param requestTemplate
     */
    @Override
    public void apply(RequestTemplate requestTemplate) {
        // 如果token中的類型加入了驗證, 則設置rdc token
        Collection<String> authHeaders = requestTemplate.headers().get(TokenConstant.AUTH_TYPE);
        if (CollectionUtils.isNotEmpty(authHeaders) && authHeaders.contains(TokenConstant.CLIENT_AUTH)) {
            requestTemplate.header("Authorization", clientTokenTemplate.getRedisToken().getAccess_token());
        } else {
            AuthInfo authInfo = BizContext.getValue(Constants.BizContextKey.AUTH);
            if(authInfo!=null && !StringUtils.isEmpty(authInfo.getAccess_token())){
                requestTemplate.header("Authorization", authInfo.getAccess_token());
            }
        }
    }
}
View Code

3.3、文件服務的客戶端實現

/**
 * 文件服務客戶端
 *
 * @author simm
 */
@ConditionalOnProperty(name = {"gateway.host", "gateway.apis.file-service"})
@FeignClient(value = "rdc-file-service", url = "${gateway.host}/${gateway.apis.file-service}", path = "/api/v2/files")
public interface RdcFileClient {
    /**
     * 獲取文件信息
     *
     * @param fileId 文件ID
     * @return 文件內容
     */
    @GetMapping(value = "/{fileId}", headers = TokenConstant.GATEWAY_AUTH_HEADER)
    PluginFileInfoResponse getFileInfo(@PathVariable String fileId);

    /**
     * 上傳文件測試
     *
     * @param file 文件
     * @return 文件注冊信息
     */
    @PostMapping(value = "/", headers = TokenConstant.GATEWAY_AUTH_HEADER, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    PluginFileInfoResponse uploadFile(MultipartFile file);

    /**
     * 獲取下載地址
     *
     * @param fileId 文件ID
     * @return
     */
    @GetMapping(value = "/{fileId}/download-url", headers = TokenConstant.GATEWAY_AUTH_HEADER)
    String getDownloadUrl(@PathVariable String fileId);

    /**
     * 批量獲取文件詳情
     *
     * @param object 信息
     * @return
     */
    @PostMapping(value = "/batchInfos", headers = TokenConstant.GATEWAY_AUTH_HEADER,
            produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    List<PluginFileInfoResponse> batchGetFileDetail(JSONObject object);

    /**
     * 刪除緩存的文件
     *
     * @param fileId 文件ID
     * @return
     */
    @DeleteMapping(value = "/{fileId}", headers = TokenConstant.GATEWAY_AUTH_HEADER)
    Object delFile(@PathVariable String fileId);
}
View Code


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM