java版spring cloud微服务架构b2b2c电子商务平台-Feign使用及源码深度解析

itautum · · 33 次点击 · 开始浏览   
SpringCloud Feign基于Netflix Feign实现,整合SpringCloud Ribbon和SpringCloud Hystrix 我们在使用微服务框架的时候,一般都会在项目中同时使用Ribbon和Hystrix,所以可以考虑直接使用Feign来整合 1.Feign的使用 我们现在需要创建一个服务消费者应用,消费服务提供者的服务 1)引入maven依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> 2)创建接口FeignService,提供消费服务 @FeignClient(name="part-1-sms-interface", fallback=FeginFallbackService.class) public interface FeignService { @RequestMapping("/sms/test") String test(); } // 服务降级类 @Component public 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<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类(如果有的话) // 本例中使用的是默认的Configuration,就不再分析该段代码 registerClientConfiguration(registry, name, attributes.get("configuration")); // 2.2 并将类或接口本身注入到Spring中 registerFeignClient(registry, annotationMetadata, attributes); } } } } 总结:从3)的分析可知,@EnableFeignClients注解的主要功能就是把带有@FeignClient注解的类或接口注册到Spring中 关于具体是如何注册的、请求时如何转换的,暂时还不清楚,但是代码结构已经很清晰了。接下来我们继续分析registerFeignClient()方法。电子商务社交平台源码请加企鹅求求:三五三六二四七二五九
33 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet