SpringCloud Feign基于Netflix Feign實(shí)現(xiàn)隧期,整合SpringCloud Ribbon和SpringCloud Hystrix
我們?cè)谑褂梦⒎?wù)框架的時(shí)候刽严,一般都會(huì)在項(xiàng)目中同時(shí)使用Ribbon和Hystrix,所以可以考慮直接使用Feign來整合
1.Feign的使用
我們現(xiàn)在需要?jiǎng)?chuàng)建一個(gè)服務(wù)消費(fèi)者應(yīng)用勾邦,消費(fèi)服務(wù)提供者的服務(wù)
1)引入maven依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
2)創(chuàng)建接口FeignService,提供消費(fèi)服務(wù)
@FeignClient(name="part-1-sms-interface", fallback=FeginFallbackService.class)
public interface FeignService {
@RequestMapping("/sms/test")
String test();
}
// 服務(wù)降級(jí)類
@Component
public class FeginFallbackService {
public String test(){
return "fallback error";
}
}
注意:
@FeignClient注解中的name為服務(wù)提供者所在應(yīng)用的spring.application.name,fallback所對(duì)應(yīng)的類為服務(wù)調(diào)用異常時(shí)服務(wù)降級(jí)的類
@RequestMapping("/sms/test")為服務(wù)提供者應(yīng)用中的方法路徑
@FeignClient還有一個(gè)關(guān)鍵的configuration參數(shù)舞痰,可以讓用戶自定義配置bean
注解默認(rèn)的配置bean所在類為FeignClientsConfiguration弃酌,用戶可參考其中的bean實(shí)現(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)測(cè)試驗(yàn)證
調(diào)用/feign/test方法氨菇,可以看到其調(diào)用了服務(wù)提供者part-1-sms-interface提供的/sms/test方法,驗(yàn)證成功
2.寫在源碼分析之前
經(jīng)過上面的關(guān)于Feign的使用分析妓湘,可以看到使用的時(shí)候還是非常簡(jiǎn)單的查蓉,只需要兩個(gè)簡(jiǎn)單的注解就實(shí)現(xiàn)了Ribbon+Hystrix的功能。
那具體是如何實(shí)現(xiàn)的呢榜贴?
關(guān)鍵的一步就是如何把對(duì)接口的請(qǐng)求映射為對(duì)真正服務(wù)的請(qǐng)求(也就是一個(gè)HTTP請(qǐng)求)豌研,同時(shí)還要加上Hystrix的功能。
映射為HTTP請(qǐng)求唬党、Hystrix的功能都是每個(gè)服務(wù)共同的需求鹃共,所以肯定是被抽象出來的。而且我們沒有對(duì)接口有任何具體實(shí)現(xiàn)驶拱,那么應(yīng)該是用了反射的功能了霜浴,實(shí)現(xiàn)具體接口的實(shí)現(xiàn)類,并注冊(cè)到Spring容器中蓝纲,這樣才能在Controller層中使用@Autowired
3.分析注解@EnableFeignClients
通過上面的使用過程分析阴孟,@EnableFeignClients和@FeignClient兩個(gè)注解就實(shí)現(xiàn)了Feign的功能晌纫,那我們重點(diǎn)分析下@EnableFeignClients注解
1)@EnableFeignClients
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)// 重點(diǎn)在這里,注入FeignClientsRegistrar類
public @interface EnableFeignClients {}
2)FeignClientsRegistrar.java分析
通過其類結(jié)構(gòu)可知永丝,其實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口锹漱,那么在registerBeanDefinitions()中就會(huì)注冊(cè)一些bean到Spring中
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware{
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 1.針對(duì)那些在@EnableFeignClients中添加了defaultConfiguration屬性的進(jìn)行操作
// 將這些類定義的bean添加到容器中
registerDefaultConfiguration(metadata, registry);
// 2.注冊(cè)那些添加了@FeignClient的類或接口
// 重點(diǎn)就在這里
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.針對(duì)所有帶有@FeignClient注解的類或接口分別封裝
// 注冊(cè)其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 注冊(cè)其Configuration類(如果有的話)
// 本例中使用的是默認(rèn)的Configuration,就不再分析該段代碼
registerClientConfiguration(registry, name,
attributes.get("configuration"));
// 2.2 并將類或接口本身注入到Spring中
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
總結(jié):從3)的分析可知,@EnableFeignClients注解的主要功能就是把帶有@FeignClient注解的類或接口注冊(cè)到Spring中
關(guān)于具體是如何注冊(cè)的碉钠、請(qǐng)求時(shí)如何轉(zhuǎn)換的累贤,暫時(shí)還不清楚,但是代碼結(jié)構(gòu)已經(jīng)很清晰了。接下來我們繼續(xù)分析registerFeignClient()方法
電子商務(wù)社交平臺(tái)源碼請(qǐng)加企鵝求求:三五三六二四七二五九