java版spring cloud微服務(wù)架構(gòu)b2b2c電子商務(wù)平臺-Feign使用及源碼深度解析

SpringCloud Feign基于Netflix Feign實現(xiàn),整合SpringCloud Ribbon和SpringCloud Hystrix

我們在使用微服務(wù)框架的時候,一般都會在項目中同時使用Ribbon和Hystrix,所以可以考慮直接使用Feign來整合

1.Feign的使用

我們現(xiàn)在需要創(chuàng)建一個服務(wù)消費者應(yīng)用,消費服務(wù)提供者的服務(wù)

1)引入maven依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

2)創(chuàng)建接口FeignService,提供消費服務(wù)

@FeignClient(name="part-1-sms-interface", fallback=FeginFallbackService.class)
public interface FeignService {
 
    @RequestMapping("/sms/test")
    String test();
}
 
// 服務(wù)降級類
@Component
public class FeginFallbackService {
 
    public String test(){
        return "fallback error";
    }
}

注意:

@FeignClient注解中的name為服務(wù)提供者所在應(yīng)用的spring.application.name,fallback所對應(yīng)的類為服務(wù)調(diào)用異常時服務(wù)降級的類

@RequestMapping("/sms/test")為服務(wù)提供者應(yīng)用中的方法路徑

@FeignClient還有一個關(guān)鍵的configuration參數(shù),可以讓用戶自定義配置bean

注解默認(rèn)的配置bean所在類為FeignClientsConfiguration,用戶可參考其中的bean實現(xiàn)來自定義

3)創(chuàng)建Controller

@RestController
@RequestMapping("/feign")
public class FeignController {
 
    @Autowired
    private FeignService feignService;
    
    @GetMapping(value="/test")
    public String test(){
        return feignService.test();
    }
}

4)測試驗證
調(diào)用/feign/test方法,可以看到其調(diào)用了服務(wù)提供者part-1-sms-interface提供的/sms/test方法,驗證成功

2.寫在源碼分析之前
經(jīng)過上面的關(guān)于Feign的使用分析,可以看到使用的時候還是非常簡單的,只需要兩個簡單的注解就實現(xiàn)了Ribbon+Hystrix的功能。
那具體是如何實現(xiàn)的呢?
關(guān)鍵的一步就是如何把對接口的請求映射為對真正服務(wù)的請求(也就是一個HTTP請求),同時還要加上Hystrix的功能。
映射為HTTP請求、Hystrix的功能都是每個服務(wù)共同的需求,所以肯定是被抽象出來的。而且我們沒有對接口有任何具體實現(xiàn),那么應(yīng)該是用了反射的功能了,實現(xiàn)具體接口的實現(xiàn)類,并注冊到Spring容器中,這樣才能在Controller層中使用@Autowired

3.分析注解@EnableFeignClients

通過上面的使用過程分析,@EnableFeignClients和@FeignClient兩個注解就實現(xiàn)了Feign的功能,那我們重點分析下@EnableFeignClients注解

1)@EnableFeignClients

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)// 重點在這里,注入FeignClientsRegistrar類
public @interface EnableFeignClients {}

2)FeignClientsRegistrar.java分析

通過其類結(jié)構(gòu)可知,其實現(xiàn)了ImportBeanDefinitionRegistrar接口,那么在registerBeanDefinitions()中就會注冊一些bean到Spring中

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
        ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware{
        
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        // 1.針對那些在@EnableFeignClients中添加了defaultConfiguration屬性的進(jìn)行操作
        // 將這些類定義的bean添加到容器中
        registerDefaultConfiguration(metadata, registry);
        
        // 2.注冊那些添加了@FeignClient的類或接口
        // 重點就在這里
        registerFeignClients(metadata, registry);
    }
}

3)registerFeignClients(metadata, registry)方法分析

public void registerFeignClients(AnnotationMetadata metadata,
                                 BeanDefinitionRegistry registry) {
    ClassPathScanningCandidateComponentProvider scanner = getScanner();
    scanner.setResourceLoader(this.resourceLoader);
 
    // 1.以下代碼的主要功能是掃描包下的所有帶有@FeignClient注解的類
    Set<String> basePackages;
 
    Map<String, Object> attrs = metadata
        .getAnnotationAttributes(EnableFeignClients.class.getName());
    // @FeignClient注解過濾器
    AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
        FeignClient.class);
    final Class<?>[] clients = attrs == null ? null
        : (Class<?>[]) attrs.get("clients");
    if (clients == null || clients.length == 0) {
        // 在這里獲取帶有@FeignClient注解的類,放在basePackages中
        scanner.addIncludeFilter(annotationTypeFilter);
        basePackages = getBasePackages(metadata);
    }
    else {
        final Set<String> clientClasses = new HashSet<>();
        basePackages = new HashSet<>();
        for (Class<?> clazz : clients) {
            basePackages.add(ClassUtils.getPackageName(clazz));
            clientClasses.add(clazz.getCanonicalName());
        }
        AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
            @Override
            protected boolean match(ClassMetadata metadata) {
                String cleaned = metadata.getClassName().replaceAll("\\$", ".");
                return clientClasses.contains(cleaned);
            }
        };
        scanner.addIncludeFilter(
            new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
    }
 
    // 2.針對所有帶有@FeignClient注解的類或接口分別封裝
    // 注冊其Configuration類(如果有的話)
    // 并將類或接口本身注入到Spring中
    for (String basePackage : basePackages) {
        Set<BeanDefinition> candidateComponents = scanner
            .findCandidateComponents(basePackage);
        for (BeanDefinition candidateComponent : candidateComponents) {
            if (candidateComponent instanceof AnnotatedBeanDefinition) {
                // verify annotated class is an interface
                AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                Assert.isTrue(annotationMetadata.isInterface(),
                              "@FeignClient can only be specified on an interface");
 
                Map<String, Object> attributes = annotationMetadata
                    .getAnnotationAttributes(
                    FeignClient.class.getCanonicalName());
 
                String name = getClientName(attributes);
                // 2.1 注冊其Configuration類(如果有的話)
                // 本例中使用的是默認(rèn)的Configuration,就不再分析該段代碼
                registerClientConfiguration(registry, name,
                                            attributes.get("configuration"));
 
                // 2.2 并將類或接口本身注入到Spring中
                registerFeignClient(registry, annotationMetadata, attributes);
            }
        }
    }
}

總結(jié):從3)的分析可知,@EnableFeignClients注解的主要功能就是把帶有@FeignClient注解的類或接口注冊到Spring中
關(guān)于具體是如何注冊的、請求時如何轉(zhuǎn)換的,暫時還不清楚,但是代碼結(jié)構(gòu)已經(jīng)很清晰了。接下來我們繼續(xù)分析registerFeignClient()方法
電子商務(wù)社交平臺源碼請加企鵝求求:三五三六二四七二五九

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ?通過前面兩章對Spring Cloud Ribbon和Spring Cloud Hystrix的介紹,我們已經(jīng)掌...
    Chandler_玨瑜閱讀 214,604評論 15 140
  • 5月2日我稱之為過渡的一日,上午在束河古鎮(zhèn)休閑半日后,下午要去大理了。 早上在民宿房頂拍了無數(shù)張玉龍雪山的照片,還...
    子弈媽媽閱讀 311評論 0 3
  • 文/居里社 01 真的不要低估你身邊的每一個人,你也并不知道究竟哪片云彩會下雨。 最近結(jié)交了一些新朋友,一些很時尚...
    居里葉閱讀 707評論 0 7
  • 大學(xué)四年,我收獲了許多,學(xué)會了許多,感謝大家的幫助和教導(dǎo)。 經(jīng)歷了很多事,也從中得到很多反思。 大一,滿滿的收獲,...
    桌子和1106閱讀 158評論 0 0
  • 內(nèi)鏈接:select * from t_student t inner join t_hours s on id ...
    無敵帥帥頭閱讀 352評論 0 0

友情鏈接更多精彩內(nèi)容