博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java B2B2C Springcloud仿淘宝电子商城系统-Feign使用及源码深度解析
阅读量:6094 次
发布时间:2019-06-20

本文共 8441 字,大约阅读时间需要 28 分钟。

SpringCloud Feign基于Netflix Feign实现,整合SpringCloud Ribbon和SpringCloud Hystrix

需要JAVA Spring Cloud大型企业分布式微服务云构建的B2B2C电子商务平台源码 一零三八七七四六二六

我们在使用微服务框架的时候,一般都会在项目中同时使用Ribbon和Hystrix,所以可以考虑直接使用Feign来整合

1.Feign的使用

我们现在需要创建一个服务消费者应用,消费服务提供者的服务

1)引入maven依赖

org.springframework.cloud
spring-cloud-starter-feign
复制代码

2)创建接口FeignService,提供消费服务

@FeignClient(name="part-1-sms-interface", fallback=FeginFallbackService.class)public interface FeignService { 	@RequestMapping("/sms/test")	String test();} // 服务降级类@Componentpublic class FeginFallbackService { 	public String test(){		return "fallback error";	}}复制代码

注意:

  • @FeignClient注解中的name为服务提供者所在应用的spring.application.name,fallback所对应的类为服务调用异常时服务降级的类

  • @RequestMapping("/sms/test")为服务提供者应用中的方法路径

  • @FeignClient还有一个关键的configuration参数,可以让用户自定义配置bean

  • 注解默认的配置bean所在类为FeignClientsConfiguration,用户可参考其中的bean实现来自定义

    3)创建Controller

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

4)测试验证 调用/feign/test方法,可以看到其调用了服务提供者part-1-sms-interface提供的/sms/test方法,验证成功

2.写在源码分析之前

经过上面的关于Feign的使用分析,可以看到使用的时候还是非常简单的,只需要两个简单的注解就实现了Ribbon+Hystrix的功能。 那具体是如何实现的呢?

关键的一步就是如何把对接口的请求映射为对真正服务的请求(也就是一个HTTP请求),同时还要加上Hystrix的功能。

映射为HTTP请求、Hystrix的功能都是每个服务共同的需求,所以肯定是被抽象出来的。而且我们没有对接口有任何具体实现,那么应该是用了反射的功能了,实现具体接口的实现类,并注册到Spring容器中,这样才能在Controller层中使用@Autowired

3.分析注解@EnableFeignClients

通过上面的使用过程分析,@EnableFeignClients和@FeignClient两个注解就实现了Feign的功能,那我们重点分析下@EnableFeignClients注解

1)@EnableFeignClients

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(FeignClientsRegistrar.class)// 重点在这里,注入FeignClientsRegistrar类public @interface EnableFeignClients {}复制代码

2)FeignClientsRegistrar.java分析

通过其类结构可知,其实现了ImportBeanDefinitionRegistrar接口,那么在registerBeanDefinitions()中就会注册一些bean到Spring中

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,		ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware{        	@Override	public void registerBeanDefinitions(AnnotationMetadata metadata,			BeanDefinitionRegistry registry) {        // 1.针对那些在@EnableFeignClients中添加了defaultConfiguration属性的进行操作        // 将这些类定义的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
basePackages; Map
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
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
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
attributes = annotationMetadata .getAnnotationAttributes( FeignClient.class.getCanonicalName()); String name = getClientName(attributes); // 2.1 注册其Configuration类(如果有的话) // 本例中使用的是默认的Configuration,就不再分析该段代码 registerClientConfiguration(registry, name, attributes.get("configuration")); // 2.2 并将类或接口本身注入到Spring中 registerFeignClient(registry, annotationMetadata, attributes); } } }}复制代码

总结:从3)的分析可知,@EnableFeignClients注解的主要功能就是把带有@FeignClient注解的类或接口注册到Spring中 关于具体是如何注册的、请求时如何转换的,暂时还不清楚,但是代码结构已经很清晰了。接下来我们继续分析registerFeignClient()方法

4.registerFeignClient()方法的分析

private void registerFeignClient(BeanDefinitionRegistry registry,                                 AnnotationMetadata annotationMetadata, Map
attributes) { // 1.获取类名称,也就是本例中的FeignService接口 String className = annotationMetadata.getClassName(); // 2.BeanDefinitionBuilder的主要作用就是构建一个AbstractBeanDefinition // AbstractBeanDefinition类最终被构建成一个BeanDefinitionHolder // 然后注册到Spring中 // 注意:beanDefinition类为FeignClientFactoryBean,故在Spring获取类的时候实际返回的是 // FeignClientFactoryBean类 BeanDefinitionBuilder definition = BeanDefinitionBuilder .genericBeanDefinition(FeignClientFactoryBean.class); validate(attributes); // 3.添加FeignClientFactoryBean的属性, // 这些属性也都是我们在@FeignClient中定义的属性 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); // 4.设置别名 name就是我们在@FeignClient中定义的name属性 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; } // 5.定义BeanDefinitionHolder, // 在本例中 名称为FeignService,类为FeignClientFactoryBean BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[] { alias }); BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);}复制代码

总结4:通过分析可知:我们最终是向Spring中注册了一个bean,bean的名称就是类或接口的名称(也就是本例中的FeignService),bean的实现类是FeignClientFactoryBean,其属性设置就是我们在@FeignClient中定义的属性

那么下面我们在Controller中对FeignService的的引入,实际就是引入了FeignClientFactoryBean类

5.FeignClientFactoryBean的分析

这个类有什么神通广大的地方呢?

我们目前只知道,做了这么多工作之后,就是为了把这个类与我们的接口对应起来并注册到容器中,@FeignClient的属性也被添加到该类中,那么具体的工作都应该是在这个类中实现了。

本文暂时先分析到这,在 下篇中我们会详细分析下该类的具体功能实现

总结:

1)@EnableFeignClients注解将类FeignClientsRegistrar注册到Spring中

2)FeignClientsRegistrar类主要是扫描包路径下的所有类,将带有@FeignClient注解的类或接口注册到Spring中

3)如何注册带有@FeignClient的类或接口呢?就是生成一个BeanDefinitionHolder类,beanName为@FeignClient所在接口的名称,beanDefinition为FeignClientFactoryBean,并将@FeignClient的属性添加到FeignClientFactoryBean中

4)至此,我们已经把带有@FeignClient注解的类或接口注册到Spring中。

转载于:https://juejin.im/post/5c4e691ef265da616a48037d

你可能感兴趣的文章
ViewPager+Fragment取消预加载(延迟加载)
查看>>
Confluence 6 缓存性能优化
查看>>
八评腾讯:解密腾讯的中年危机
查看>>
我的友情链接
查看>>
iOS NSFileManeger 计算文件是否超时,和计算文件夹下文件的总大小
查看>>
Oracle DG 之--DG Broker 配置
查看>>
AIX删除多余的默认网关
查看>>
爬山为什么要穿登山鞋
查看>>
nutz4ror 代码生成器尝鲜
查看>>
51CTO微博常见问题解答【Q&A帮助】
查看>>
IIS 7站点管理入门在08服务器的管理
查看>>
Java 初始化与清理
查看>>
C#计算数组的算术平均数、几何平均数、调和平均数、平方平均数和中位数
查看>>
JQuery Select控件操作汇总
查看>>
E媒体|APP必死?!——阿里百川项目总监承渊有话说
查看>>
Android 游戏之三人对战源码
查看>>
Lua与C++交互机制
查看>>
MDT2012制作模板机
查看>>
我的友情链接
查看>>
关于海量数据的数据模型
查看>>