亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SpringCloud中怎么聲明式調用Feign

發布時間:2021-06-24 15:49:33 來源:億速云 閱讀:198 作者:Leah 欄目:大數據

今天就跟大家聊聊有關SpringCloud中怎么聲明式調用Feign,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

Feign聲明式調用

一、Feign簡介

使用Ribbon和RestTemplate消費服務的時候,有一個最麻煩的點在于,每次都要拼接URL,組織參數,所以有了Feign聲明式調用,Feign的首要目標是將Java HTTP客戶端的調用過程非常簡單。它采用聲明式API接口的風格,將Java Http客戶端綁定到它的內部,以此來方便調用。

二、Feign實踐

1、項目組織

從前面Ribbon中拿到項目整體,然后再整改成如下目錄
帖子地址:https://my.oschina.net/devilsblog/blog/3115061
碼云地址:https://gitee.com/devilscode/cloud-practice/tree/ribbon-test

  1. 修改項目名稱為feign-test

  2. 修改原來的子Module ribbon-service為feign-service
    SpringCloud中怎么聲明式調用Feign

2、核心pom


feign-test/pom.xml
<modelVersion>4.0.0</modelVersion>

<groupId>com.calvin.feigb</groupId>
<artifactId>feign-test</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>

<modules>
    <module>common-service</module>
    <module>eureka-server</module>
    <module>feign-service</module>
</modules>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.3.RELEASE</version>
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Dalston.SR4</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

common-service/pom.xml
<parent>
    <groupId>com.calvin.feigb</groupId>
    <artifactId>feign-test</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>common-service</artifactId>

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

eureka-service/pom.xml
<parent>
    <groupId>com.calvin.feigb</groupId>
    <artifactId>feign-test</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka-server</artifactId>
<properties>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka-server</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

feign-service/pom.xml
<parent>
    <groupId>com.calvin.feigb</groupId>
    <artifactId>feign-test</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>feign-service</artifactId>

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-feign</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

3、feign-service項目改造

(1)修改服務名稱

這樣就可以從eureka注冊中心中區別出我們的服務是feign-service

server:
  port: 8082
spring:
  application:
    name: feign-service
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8080/eureka/
(2)增加配置注解EurekaFeignClient

服務啟動的時候,此注解的作用會使得所有使用了注解FeignClient的類,被掃描解析,然后注冊到IoC容器中。

/**
 * <p> 
 *     啟動類 
 * </p>
 *
 * @author Calvin
 * @date 2019/10/09
 * @since
 */
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients
public class FeignServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(FeignServerApplication.class);
    }
}
(3)新建FeignConfiguration.java
/**
 * <p> FeignClient的配置類 </p>
 *
 * @author Calvin
 * @date 2019/10/21
 * @since
 */
@Configuration
public class FeignConfiguration {

    /**
     * 覆蓋默認的重試
     * 重試間隔100ms
     * 最大重試時間1s
     * 最大重試次數5次
     */
    @Bean
    public Retryer feignRetryer(){
        return new Retryer.Default(100,SECONDS.toMillis(1), 5);
    }
}

關于FeignClient的相關配置,如果我們不主動去配置,都有默認配置,配置類為FeignClientsConfiguration.class,詳細內容后續分析

(4)增加CommonFeignClient.java
/**
 * <p> 
 *     遠程調用公共服務消費端
 * </p>
 *
 * @author Calvin
 * @date 2019/10/21
 * @since
 */
@FeignClient(value = "common-service", configuration = FeignConfiguration.class)
public interface CommonFeignClient {

    /**
     * 調用 common-service/hello接口
     * @return
     */
    @GetMapping(value = "/hello")
    String sayHi();

}
(5)改造SayHiController.java
/**
 * <p>
 *     測試接口
 * </p>
 *
 * @author Calvin
 * @date 2019/10/09
 * @since
 */
@RestController
public class SayHiController {

//    @Autowired
//    private RemoteCommonService remoteCommonService;

    @Autowired
    private CommonFeignClient commonFeignClient;

    @GetMapping("/hi")
    public String sayHi(){
        return commonFeignClient.sayHi() + ", this is feign service";
    }

}
(6)停用RemoteCommonService

刪除RemoteCommonService.java即可

3、啟動整體項目

  • step1. EurekaSeverApplicaton

  • step2. CommonServiceApplication

  • step3. CommonServiceApplication2

  • step4. FeignServerApplication

4、調用接口測試

Eureka管理界面 http://localhost:8080/
SpringCloud中怎么聲明式調用Feign

調用 http://localhost:8082/hi

SpringCloud中怎么聲明式調用Feign

刷新頁面

SpringCloud中怎么聲明式調用Feign

三、工作原理探索

1. FeignClientsConfiguration

/**
 * @author Dave Syer
 * @author Venil Noronha
 */
@Configuration
public class FeignClientsConfiguration {

	/**
	 * 配置消息轉換器
	 */
	@Autowired
	private ObjectFactory<HttpMessageConverters> messageConverters;

	/**
	 * 注入參數解析,用于解析@RequestParam
	 */
	@Autowired(required = false)
	private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>();
	
	/**
	 * 
	 */
	@Autowired(required = false)
	private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList<>();

	@Autowired(required = false)
	private Logger logger;

	
	/**
	 * 返回值解析
	 */
	@Bean
	@ConditionalOnMissingBean
	public Decoder feignDecoder() {
		return new ResponseEntityDecoder(new SpringDecoder(this.messageConverters));
	}

	/**
	 * url編碼
	 */
	@Bean
	@ConditionalOnMissingBean
	public Encoder feignEncoder() {
		return new SpringEncoder(this.messageConverters);
	}

	@Bean
	@ConditionalOnMissingBean
	public Contract feignContract(ConversionService feignConversionService) {
		return new SpringMvcContract(this.parameterProcessors, feignConversionService);
	}

	@Bean
	public FormattingConversionService feignConversionService() {
		FormattingConversionService conversionService = new DefaultFormattingConversionService();
		for (FeignFormatterRegistrar feignFormatterRegistrar : feignFormatterRegistrars) {
			feignFormatterRegistrar.registerFormatters(conversionService);
		}
		return conversionService;
	}

	@Configuration
	@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
	protected static class HystrixFeignConfiguration {
		@Bean
		@Scope("prototype")
		@ConditionalOnMissingBean
		@ConditionalOnProperty(name = "feign.hystrix.enabled", matchIfMissing = false)
		public Feign.Builder feignHystrixBuilder() {
			return HystrixFeign.builder();
		}
	}

	/**
	 * 配置重試,NEVER_RETRY代表從不重試
	 */
	@Bean
	@ConditionalOnMissingBean
	public Retryer feignRetryer() {
		return Retryer.NEVER_RETRY;
	}

	/**
	 * 給Feign綁定重試器
	 */
	@Bean
	@Scope("prototype")
	@ConditionalOnMissingBean
	public Feign.Builder feignBuilder(Retryer retryer) {
		return Feign.builder().retryer(retryer);
	}

	/**
	 * 日志工廠
	 */
	@Bean
	@ConditionalOnMissingBean(FeignLoggerFactory.class)
	public FeignLoggerFactory feignLoggerFactory() {
		return new DefaultFeignLoggerFactory(logger);
	}

}

2. Feign工作原理

(1)步驟
  1. 通過@EnableFeignClients開啟注解掃描

  2. 掃描@FeignClient注解修飾的元注解信息,使用BeanDefinitionBuilder解析成BeanDefinition,交給IoC容器中

  3. 通過JDK代理,當發現FeignClient被調用的時候,攔截該方法

  4. 攔截到該方法后,在SynchronousMethodHandler類中,使用生成的RequestTemplate,重新生成Request對象

  5. 使用HttpClient調用請求,獲取Response

(2)相關代碼
掃描@EnableFeignClients
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
		ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
	/**
	 * <p>
	 *    檢查EnableFeignClients注解是否開啟,如果開啟,則開始注冊默認配置
	 * </p>
	 */
	private void registerDefaultConfiguration(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		Map<String, Object> defaultAttrs = metadata
				.getAnnotationAttributes(EnableFeignClients.class.getName(), true);

		if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
			String name;
			if (metadata.hasEnclosingClass()) {
				name = "default." + metadata.getEnclosingClassName();
			}
			else {
				name = "default." + metadata.getClassName();
			}
			registerClientConfiguration(registry, name,
					defaultAttrs.get("defaultConfiguration"));
		}
	}
}
掃描@FeignClient注解,獲取到元注解信息
public void registerFeignClients(AnnotationMetadata metadata,
		BeanDefinitionRegistry registry) {
	ClassPathScanningCandidateComponentProvider scanner = getScanner();
	scanner.setResourceLoader(this.resourceLoader);

	Set<String> basePackages;

	Map<String, Object> attrs = metadata
			.getAnnotationAttributes(EnableFeignClients.class.getName());
	AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
			FeignClient.class);
	final Class<?>[] clients = attrs == null ? null
			: (Class<?>[]) attrs.get("clients");
	if (clients == null || clients.length == 0) {
		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)));
	}
	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);
				registerClientConfiguration(registry, name,
						attributes.get("configuration"));

				registerFeignClient(registry, annotationMetadata, attributes);
			}
		}
	}
}
解析元注解內容,注入到IoC容器中
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);
	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 = name + "FeignClient";
	AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

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

	beanDefinition.setPrimary(primary);

	String qualifier = getQualifier(attributes);
	if (StringUtils.hasText(qualifier)) {
		alias = qualifier;
	}

	BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
			new String[] { alias });
	BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
JDK代理,攔截FeignClient的調用
public class ReflectiveFeign extends Feign {
@Override
  public <T> T newInstance(Target<T> target) {
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    for (Method method : target.type().getMethods()) {
      if (method.getDeclaringClass() == Object.class) {
        continue;
      } else if(Util.isDefault(method)) {
        DefaultMethodHandler handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
      } else {
        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    }
    InvocationHandler handler = factory.create(target, methodToHandler);
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);

    for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
  }
}

攔截進行處理,先解析RequestTemplate,然后使用HttpClient調用網絡請求

final class SynchronousMethodHandler implements MethodHandler {

  /**
   * 將傳遞過來的參數解析成RequestTemplate
   */
  @Override
  public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        return executeAndDecode(template);
      } catch (RetryableException e) {
        retryer.continueOrPropagate(e);
        if (logLevel != Logger.Level.NONE) {
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
  }
  /**
   * 將RequestTemplate轉換成一個Request,然后使用HttpClient調用請求,拿到返回結果
   */
  Object executeAndDecode(RequestTemplate template) throws Throwable {
  Request request = targetRequest(template);

  Response response;
  long start = System.nanoTime();
  try {
  response = client.execute(request, options);
  response.toBuilder().request(request).build();
  } catch (IOException e) {
  // 處理異常
  }
  //省略返回的代碼
}

看完上述內容,你們對SpringCloud中怎么聲明式調用Feign有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

全南县| 新乡市| 措勤县| 鲁山县| 阿拉善右旗| 文昌市| 奉贤区| 桦南县| 嘉峪关市| 阳东县| 谢通门县| 日照市| 洛宁县| 双辽市| 梁山县| 江油市| 合水县| 尖扎县| 白沙| 称多县| 南华县| 乌兰察布市| 中牟县| 东安县| 临朐县| 平原县| 淄博市| 松江区| 嘉义县| 霍城县| 鲁山县| 辽阳县| 新和县| 永修县| 岫岩| 高台县| 林州市| 崇阳县| 广平县| 星座| 湟源县|