最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringCloud之@FeignClient()注解的使用詳解

 更新時間:2024年11月13日 16:18:49   作者:狂奔的小棕棕  
@FeignClient是SpringCloud中用于聲明一個Feign客戶端的注解,用于解決模塊方法互相調(diào)用的問題,Feign是一個聲明式的WebService客戶端,通過Feign,只需要創(chuàng)建一個接口,并使用注解來描述請求,就可以直接執(zhí)行HTTP請求了

@FeignClient介紹

@FeignClient 是 Spring Cloud 中用于聲明一個 Feign 客戶端的注解。由于SpringCloud采用分布式微服務(wù)架構(gòu),難免在各個子模塊下存在模塊方法互相調(diào)用的情況。

比如訂單服務(wù)要調(diào)用庫存服務(wù)的方法,@FeignClient()注解就是為了解決這個問題的。

Feign 是一個聲明式的 Web Service 客戶端,它的目的是讓編寫 HTTP 客戶端變得更簡單。通過 Feign,只需要創(chuàng)建一個接口,并使用注解來描述請求,就可以直接執(zhí)行 HTTP 請求了。

@FeignClient()注解的源碼要求它必須在Interface接口上使用( FeignClient注解被@Target(ElementType.TYPE)修飾,表示FeignClient注解的作用目標(biāo)在接口上)

SpringBoot服務(wù)的啟動類必須要有@EnableFeignClients 注解才能使@FeginClient注解生效。

@FeignClient工作原理及整體流程

Feign服務(wù)調(diào)用的工作原理可以總結(jié)為以下幾個步驟

  1. 首先通過@EnableFeignCleints注解開啟FeignCleint。
  2. 根據(jù)Feign的規(guī)則實(shí)現(xiàn)接口,添加@FeignCleint注解。程序啟動后,會掃描所有有@FeignCleint的類,并將這些信息注入到ioc容器中。
  3. 注入時從FeignClientFactoryBean.class獲取FeignClient。
  4. 當(dāng)接口的方法被調(diào)用時,通過jdk的代理,來生成具體的RequesTemplate,RequesTemplate生成http的Request。
  5. Request交給Client去處理,其中Client可以是HttpUrlConnection、HttpClient也可以是Okhttp。
  6. Client被封裝到LoadBalanceClient類,這個類結(jié)合類Ribbon做到了負(fù)載均衡。

整體流程:

@FeignClient常用屬性

name、value

指定FeignClient的名稱,如果項(xiàng)目使用了Ribbon,name屬性會作為微服務(wù)的名稱,用于服務(wù)發(fā)現(xiàn)

這兩個屬性的作用是一樣的,如果沒有配置url,那么配置的值將作為服務(wù)的名稱,用于服務(wù)的發(fā)現(xiàn),反之只是一個名稱。

@FeignClient(name = "order-server")
public interface OrderRemoteClient {
	
	@GetMapping("/order/detail")
	public Order detail(@RequestParam("orderId") String orderId);
}

注意

  • 這里寫的是你要調(diào)用的那個服務(wù)的名稱(spring.application.name屬性配置),而不是你自己的那個服務(wù)的名稱。
  • 如果同一個工程中出現(xiàn)兩個接口使用一樣的服務(wù)名稱會報錯。原因是Client名字注冊到容器中重復(fù)了。除非指定不同的contextId參數(shù)。

Description:
The bean ‘order-server.FeignClientSpecification’, defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

兩種解決方案:

  • 增加配置 spring.main.allow-bean-definition-overriding=true
  • 為每個FeignClient手動指定不同的contextId

contextId

比如我們有個user服務(wù),但user服務(wù)中有很多個接口,我們不想將所有的調(diào)用接口都定義在一個類中,那就可以給不同的client指定contextId,不然就會報異常。

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

注意:contextId不能帶_等符號。

@FeignClient(name = "order-server")
public interface OrderRemoteClient {
	
	@GetMapping("/api/order/detail", contextId = "OrderRemoteClient")
	public Order detail(@RequestParam("orderId") String orderId);
}

上面給出了Bean名稱沖突后的解決方案,下面來分析下contextId在Feign Client的作用,在注冊Feign Client Configuration的時候需要一個名稱,名稱是通過getClientName方法獲取的:

String name = getClientName(attributes);

registerClientConfiguration(registry, name,
attributes.get("configuration"));
private String getClientName(Map<String, Object> client) {
    if (client == null) {
      return null;
    }
    String value = (String) client.get("contextId");
    if (!StringUtils.hasText(value)) {
      value = (String) client.get("value");
    }
    if (!StringUtils.hasText(value)) {
      value = (String) client.get("name");
    }
    if (!StringUtils.hasText(value)) {
      value = (String) client.get("serviceId");
    }
    if (StringUtils.hasText(value)) {
      return value;
    }
    
    throw new IllegalStateException("Either 'name' or 'value' must be provided in @"
        + FeignClient.class.getSimpleName());
  }

可以看到如果配置了contextId就會用contextId,如果沒有配置就會去value然后是name最后是serviceId。默認(rèn)都沒有配置,當(dāng)出現(xiàn)一個服務(wù)有多個Feign Client的時候就會報錯了。

其次的作用是在注冊FeignClient中,contextId會作為Client 別名的一部分,如果配置了qualifier優(yōu)先用qualifier作為別名。

private void registerFeignClient(BeanDefinitionRegistry registry,
      AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    String className = annotationMetadata.getClassName();
    BeanDefinitionBuilder definition = BeanDefinitionBuilder
        .genericBeanDefinition(FeignClientFactoryBean.class);
    validate(attributes);
    definition.addPropertyValue("url", getUrl(attributes));
    definition.addPropertyValue("path", getPath(attributes));
    String name = getName(attributes);
    definition.addPropertyValue("name", name);
    String contextId = getContextId(attributes);
    definition.addPropertyValue("contextId", contextId);
    definition.addPropertyValue("type", className);
    definition.addPropertyValue("decode404", attributes.get("decode404"));
    definition.addPropertyValue("fallback", attributes.get("fallback"));
    definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
    definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

    // 拼接別名
    String alias = contextId + "FeignClient";
    AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

    boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be null
    beanDefinition.setPrimary(primary);

    // 配置了qualifier優(yōu)先用qualifier
    String qualifier = getQualifier(attributes);
    if (StringUtils.hasText(qualifier)) {
      alias = qualifier;
    }

    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
        new String[] { alias });
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
  }

url

url用于配置指定服務(wù)的地址,相當(dāng)于直接請求這個服務(wù)。像調(diào)試等場景可以使用。

@FeignClient(name = "order-server", url = "http://localhost:8085")
public interface OrderRemoteClient {
	
	@GetMapping("/api/order/detail")
	public Order detail(@RequestParam("orderId") String orderId);
}

path

path定義當(dāng)前FeignClient訪問接口時的統(tǒng)一前綴。

比如接口地址是/order/detail, 如果你定義了前綴是order, 那么具體方法上的路徑就只需要寫/detail即可。

@FeignClient(name = "order-server", url = "http://localhost:8085", path = "/api/order")
public interface OrderRemoteClient {
	
	@GetMapping("/detail")
	public Order detail(@RequestParam("orderId") String orderId);
}

primary

primary對應(yīng)的是@Primary注解,默認(rèn)為true,官方這樣設(shè)置也是有原因的。當(dāng)我們的Feign實(shí)現(xiàn)了fallback后,也就意味著Feign Client有多個相同的Bean在Spring容器中,當(dāng)我們在使用@Autowired進(jìn)行注入的時候,不知道注入哪個,所以我們需要設(shè)置一個優(yōu)先級高的,@Primary注解就是干這件事情的。

qualifier

qualifier對應(yīng)的是@Qualifier注解,使用場景跟上面的primary關(guān)系很淡,一般場景直接@Autowired直接注入就可以了。

如果我們的Feign Client有fallback實(shí)現(xiàn),默認(rèn)@FeignClient注解的primary=true, 意味著我們使用@Autowired注入是沒有問題的,會優(yōu)先注入你的Feign Client。

如果你鬼斧神差的把primary設(shè)置成false了,直接用@Autowired注入的地方就會報錯,不知道要注入哪個對象。

解決方案很明顯,你可以將primary設(shè)置成true即可,如果由于某些特殊原因,你必須得去掉primary=true的設(shè)置,這種情況下我們怎么進(jìn)行注入,我們可以配置一個qualifier,然后使用@Qualifier注解進(jìn)行注入。
Feign Client 定義

@FeignClient(name = "order-server", path = "/api/order", qualifier="orderRemoteClient")
public interface OrderRemoteClient {
	
	@GetMapping("/detail")
	public Order detail(@RequestParam("orderId") String orderId);
}

Feign Client注入

@Autowired
@Qualifier("orderRemoteClient")
private OrderRemoteClient orderRemoteClient;

configuration

configuration是配置Feign配置類,在配置類中可以自定義Feign的Encoder、Decoder、LogLevel、Contract等。

configuration定義

public class FeignConfiguration {
	@Bean
	public Logger.Level getLoggerLevel() {
		return Logger.Level.FULL;
	}
	@Bean
	public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
		return new BasicAuthRequestInterceptor("user", "password");
	}
	
	@Bean
	public CustomRequestInterceptor customRequestInterceptor() {
		return new CustomRequestInterceptor();
	}
	// Contract,feignDecoder,feignEncoder.....
}

使用示列

@FeignClient(value = "order-server", configuration = FeignConfiguration.class)
public interface OrderRemoteClient {
	
	@GetMapping("/api/order/detail")
	public Order detail(@RequestParam("orderId") String orderId);
	
}

fallback

定義容錯的處理類,也就是回退邏輯,當(dāng)調(diào)用遠(yuǎn)程接口失敗或超時時,會調(diào)用對應(yīng)接口的容錯邏輯,fallback指定的類必須實(shí)現(xiàn)@FeignClient標(biāo)記的接口,無法知道熔斷的異常信息。

fallback定義

@Component
public class OrderRemoteClientFallback implements OrderRemoteClient {
	@Override
	public Order detail(String orderId) {
		return new Order("order-998", "默認(rèn)fallback");
	}
	
}

使用示列

@FeignClient(value = "order-server", fallback = OrderRemoteClientFallback.class)
public interface OrderRemoteClient {
	
	@GetMapping("/api/order/detail")
	public Order detail(@RequestParam("orderId") String orderId);
	
}

fallbackFactory

也是容錯的處理,可以知道熔斷的異常信息。工廠類,用于生成fallback類示例,通過這個屬性我們可以實(shí)現(xiàn)每個接口通用的容錯邏輯,減少重復(fù)的代碼。

fallbackFactory定義

@Component
public class OrderRemoteClientFallbackFactory implements FallbackFactory<OrderRemoteClient> {
	private Logger logger = LoggerFactory.getLogger(OrderRemoteClientFallbackFactory.class);
	
	@Override
	public OrderRemoteClient create(Throwable cause) {
		return new OrderRemoteClient() {
			@Override
			public Order detail(String id) {
				logger.error("OrderRemoteClient.detail 異常", cause);
				return new Order("order-998", "默認(rèn)");
			}
		};
	}
}

使用示列

@FeignClient(value = "order-server", fallbackFactory = OrderRemoteClientFallbackFactory.class)
public interface OrderRemoteClient {
	
	@GetMapping("/order/detail")
	public Order detail(@RequestParam("orderId") String orderId);
	
}

@FeignClient添加Header信息

在@RequestMapping中添加

@FeignClient(
		url = "${orderServer_domain:http://order:8082}",
        value = "order-server",
        contextId = "OrderServerClient",
        path = "/api/order"
        )
public interface OrderRemoteClient {
	@RequestMapping(value="/detail", method = RequestMethod.POST,
		headers = {"Content-Type=application/json;charset=UTF-8"})
    Order detail(@RequestParam("orderId") String orderId);
}

使用@RequestHeader注解添加

@FeignClient(
		url = "${orderServer_domain:http://order:8082}",
        value = "order-server",
        contextId = "OrderServerClient",
        path = "/api/order"
        )
public interface OrderRemoteClient {
	@RequestMapping(value="/detail", method = RequestMethod.POST)
    List<String> detail(@RequestHeader Map<String, String> headerMap, @RequestParam("orderId") String orderId);
}

使用@Headers注解添加

@FeignClient(
		url = "${orderServer_domain:http://order:8082}",
        value = "order-server",
        contextId = "OrderServerClient",
        path = "/api/order"
        )
public interface OrderRemoteClient {
	@RequestMapping(value="/detail", method = RequestMethod.POST)
	@Headers({"Content-Type: application/json;charset=UTF-8"})
    List<String> detail(@RequestHeader Map<String, String> headerMap, @RequestParam("orderId") String orderId);
}

實(shí)現(xiàn)RequestInterceptor接口

@Configuration
public class FeignRequestInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate temp) {
        temp.header(HttpHeaders.AUTHORIZATION, "XXXXX");
    }

}

FeignClient 源碼

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.cloud.openfeign;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface FeignClient {
    @AliasFor("name")
    String value() default "";

    String contextId() default "";

    @AliasFor("value")
    String name() default "";

    String[] qualifiers() default {};

    String url() default "";

    boolean dismiss404() default false;

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

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

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

    String path() default "";

    boolean primary() default true;
}

總結(jié)

以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot中使用監(jiān)聽器的方法詳解

    SpringBoot中使用監(jiān)聽器的方法詳解

    這篇文章主要為大家詳細(xì)介紹了SpringBoot中使用監(jiān)聽器的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • IDEA報錯Caused by: java.lang.ClassNotFoundException: com.mysql.jdbc.Driver com.mysql.jdbc.Driver爆紅

    IDEA報錯Caused by: java.lang.ClassNotFoundException:

    這篇文章主要介紹了IDEA報錯Caused by: java.lang.ClassNotFoundException: com.mysql.jdbc.Driver com.mysql.jdbc.Driver爆紅,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-10-10
  • Java用三元運(yùn)算符判斷奇數(shù)和偶數(shù)的簡單實(shí)現(xiàn)

    Java用三元運(yùn)算符判斷奇數(shù)和偶數(shù)的簡單實(shí)現(xiàn)

    這篇文章主要介紹了Java用三元運(yùn)算符判斷奇數(shù)和偶數(shù)的簡單實(shí)現(xiàn),需要的朋友可以參考下
    2014-02-02
  • java自定義異常打印內(nèi)容詳解

    java自定義異常打印內(nèi)容詳解

    這篇文章主要為大家詳細(xì)介紹了java自定義異常打印內(nèi)容的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • springboot+springmvc實(shí)現(xiàn)登錄攔截

    springboot+springmvc實(shí)現(xiàn)登錄攔截

    這篇文章主要介紹了springboot+springmvc實(shí)現(xiàn)登錄攔截,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10
  • Java中的synchronized關(guān)鍵字

    Java中的synchronized關(guān)鍵字

    這篇文章主要介紹了Java中的synchronized關(guān)鍵字,synchronized可以保證方法或代碼塊在運(yùn)行時,同一時刻只有一個線程可以進(jìn)入到臨界區(qū)(互斥性),同時它還保證了共享變量的內(nèi)存可見性,下面我們就來看看你文章對synchronized鎖的介紹,需要的朋友也可以參考一下
    2021-12-12
  • JavaWeb基于Session實(shí)現(xiàn)的用戶登陸注銷方法示例

    JavaWeb基于Session實(shí)現(xiàn)的用戶登陸注銷方法示例

    為了安全起見,session常常用來保存用戶的登錄信息。那么服務(wù)器是怎么來實(shí)現(xiàn)的呢?下面這篇文章就來給大家介紹了關(guān)于JavaWeb基于Session實(shí)現(xiàn)的用戶登陸注銷的相關(guān)資料,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-12-12
  • Java打亂ArrayList生成一個隨機(jī)序列列表

    Java打亂ArrayList生成一個隨機(jī)序列列表

    有時候會需要將一個ArrayList或者數(shù)組中的數(shù)字打亂,方便后續(xù)使用,比如隨機(jī)出題、答案選項(xiàng)打亂、連線題打亂、抽獎號碼打亂等等,把我自己寫的一段代碼貼出來分享給大家。
    2016-08-08
  • Java Web用戶登錄實(shí)例代碼

    Java Web用戶登錄實(shí)例代碼

    這篇文章主要介紹了Java Web用戶登錄實(shí)例代碼的相關(guān)資料,非常不錯具有參考借鑒價值,感興趣的朋友一起看看吧
    2016-05-05
  • mybatisplus自動填充屬性值的實(shí)現(xiàn)步驟

    mybatisplus自動填充屬性值的實(shí)現(xiàn)步驟

    MyBatis-Plus提供自動填充的功能,幫助自定設(shè)置這些字段的值,提升開發(fā)效率,本文就來介紹一下如何使用,感興趣的可以了解一下
    2023-12-12

最新評論

色达县| 开原市| 玉屏| 阿坝| 兴山县| 赣州市| 卢龙县| 冕宁县| 巴南区| 灵丘县| 陆川县| 高淳县| 育儿| 泗水县| 宁陕县| 安远县| 万盛区| 宁南县| 墨竹工卡县| 崇礼县| 长垣县| 同德县| 博罗县| 绥江县| 武邑县| 昭苏县| 专栏| 盐源县| 开远市| 丹凤县| 郁南县| 萨嘎县| 甘泉县| 荣成市| 宁河县| 永年县| 绥德县| 林口县| 永清县| 改则县| 义马市|