美文网首页
java版spring cloud微服务架构b2b2c电子商务平

java版spring cloud微服务架构b2b2c电子商务平

作者: ITsupuerlady | 来源:发表于2019-08-13 10:22 被阅读0次

    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()方法
    电子商务社交平台源码请加企鹅求求:三五三六二四七二五九

    相关文章

      网友评论

          本文标题:java版spring cloud微服务架构b2b2c电子商务平

          本文链接:https://www.haomeiwen.com/subject/hxqvjctx.html