温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

SpringCloud的Gateway怎么使用

发布时间:2021-12-29 09:43:32 来源:亿速云 阅读:177 作者:iii 栏目:软件技术

这篇文章主要讲解了“SpringCloud的Gateway怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“SpringCloud的Gateway怎么使用”吧!

SpringCloud微服务项目之间调用是通过httprest请求来进行服务调用的,之前我们会用到HttpClient等工具来进行服务请求,Spring对这种请求进行了处理,封装成了可声明式的web客户端,使得编写web客户端更容易,feign还支持可插拔的编码器和解码器,Spring在用的时候增加了对@requestMapping的处理,同时,SpringCloud还对feign集成了注册中心(eureka)和客户端负载均衡(ribbon),使得我们拥有一个客户端负载均衡的web请求客户端。

Feign源码分析

  @Override     public void refresh() throws BeansException, IllegalStateException {         synchronized (this.startupShutdownMonitor) {             // 扫描本项目里面的java文件,把bean对象封装成BeanDefinitiaon对象,然后调用DefaultListableBeanFactory#registerBeanDefinition()方法把beanName放到DefaultListableBeanFactory 的 List<String> beanDefinitionNames 中去             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();             // Prepare the bean factory for use in this context.             prepareBeanFactory(beanFactory);             try {                 postProcessBeanFactory(beanFactory);                 // 在这里调用到FeignClientsRegistrar对象的registerBeanDefinitions()方法                 invokeBeanFactoryPostProcessors(beanFactory);                 //从DefaultListableBeanFactory里面的beanDefinitionNames中找到所有实现了BeanPostProcessor接口的方法,如果有排序进行排序后放到list中                 registerBeanPostProcessors(beanFactory);                 //Spring的国际化                 initMessageSource();                 //                  initApplicationEventMulticaster();                 // Initialize other special beans in specific context subclasses.                 onRefresh();                 //                  registerListeners();                 // Spring的IOC、ID处理。Spring的AOP。事务都是在IOC完成之后调用了BeanPostProcessor#postProcessBeforeInitialization()和postProcessBeforeInitialization()方法,AOP(事务)就是在这里处理的                 finishBeanFactoryInitialization(beanFactory);                 // 执行完之后调用实现了所有LifecycleProcessor接口的类的onRefresh()方法,同时调用所有观察了ApplicationEvent接口的事件(观察者模式)                 finishRefresh();             }             catch (BeansException ex) {                 // 找到所有实现了DisposableBean接口的方法,调用了destroy()方法,这就是bean的销毁                 destroyBeans();                 // Reset 'active' flag.                 cancelRefresh(ex);                 throw ex;             }             finally {                 resetCommonCaches();             }         }     }

根据上面整理的代码发现,FeignClientsRegistrar#registerBeanDefinitions()方法是在扫描完bean之后,只放了一个beanname的情况下, 并没有进行IOC注册的时候调用的,这就是Spring动态扩展Bean,实现BeanDefinitionRegistryPostProcessor接口的所有方法也会在这里调用下postProcessBeanDefinitionRegistry()方法。关于Spring的东西就分析到这里。下面回到正题,分析FeignClientsRegistrar#registerBeanDefinitions()方法:

 @Override     public void registerBeanDefinitions(AnnotationMetadata metadata,             BeanDefinitionRegistry registry) {         registerDefaultConfiguration(metadata, registry);//扫描EnableFeignClients标签里配置的信息,注册到beanDefinitionNames中。         registerFeignClients(metadata, registry);     }      public void registerFeignClients(AnnotationMetadata metadata,             BeanDefinitionRegistry registry) {         AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(                 FeignClient.class);         //省略代码...根据EnableFeignClients配置的basePackages找到包下所有FeignClient注解的类,Spring的Commponet也是这么干的         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);                    /**                      * 关键地方:Feign子容器概念:                      * 在注入FeignAutoConfiguration类的时候,注入了一个FeignContext对象,这个就是Feign的子容器。                      * 这里面装了List<FeignClientSpecification>对象,FeignClientSpecification对象的实质就是在@feignClient上配置的name为key,value为configuration对象的值                      * 比如feignclient 这样配置的@FeignClient(url="https://api.weixin.qq.com",name="${usercenter.name}", configuration = UserCenterFeignConfiguration.class, primary= false)                      * 那么在FeignContext中就会出现一个FeignClientSpecification{name='sms-server', configuration=[class com.jfbank.sms.configuration.FeignConfiguration]}这样的数据。                      *  这个地方比较关键,主要是因为后期对feign客户端的编码解码会用到自定义的类                      */                     //这个方法就是在ioc容器中塞入一个FeignClientSpecification对象,从而构建FeignContext子容器。                     registerClientConfiguration(registry, name,                             attributes.get("configuration"));                            //重点分析这个                     registerFeignClient(registry, annotationMetadata, attributes);                 }             }         }     }     private void registerFeignClient(BeanDefinitionRegistry registry,             AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {         String className = annotationMetadata.getClassName();         BeanDefinitionBuilder definition = BeanDefinitionBuilder                 .genericBeanDefinition(FeignClientFactoryBean.class);//对FeignClientFactoryBean对象生成一个BeanDefinition对象         ...读取配置         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 });         //注册到beanDefinitionNames中对象         BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);//     }

读过Dubbo源码的同学都知道,当在DubboNamespaceHandler中解析reference标签的时候,传入了一个ReferenceBean对象,把xml中配置的属性都塞到这个对象上,也是装到了beanDefinitionNames中,然后发现ReferenceBean类和FeignClientFactoryBean都实现了FactoryBean的接口,并且里面都有getObject()和getObjectType()方法。当接口调用到这个feign客户端的时候,会从IOC中读取这个FeignClientFactoryBean并且调用getObject方法。下面就是分析getObject方法:

 @Override     public Object getObject() throws Exception {         FeignContext context = applicationContext.getBean(FeignContext.class);         //从上文中的子容器中获取编码器,解码器等自定义类,然后封装一个Feign.Builder类         Feign.Builder builder = feign(context);         if (!StringUtils.hasText(this.url)) {//当@FeignClient没有配置url的时候             String url;             if (!this.name.startsWith("http")) {                 url = "http://" + this.name;             }             else {                 url = this.name;             }             url += cleanPath();             return loadBalance(builder, context, new HardCodedTarget<>(this.type,                     this.name, url));//集成了ribbon客户端负载均衡,下一篇分析         }         //当@FeignClient配置了url的时候         if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {             this.url = "http://" + this.url;         }         String url = this.url + cleanPath();         Client client = getOptional(context, Client.class);         if (client != null) {             if (client instanceof LoadBalancerFeignClient) {                 // not lod balancing because we have a url,                 // but ribbon is on the classpath, so unwrap                 client = ((LoadBalancerFeignClient)client).getDelegate();             }             builder.client(client);         }         Targeter targeter = get(context, Targeter.class);         return targeter.target(this, builder, context, new HardCodedTarget<>(                 this.type, this.name, url));     }

首先看配置了url的,指定了url的feignclient解析,一直跟着代码跟到了Feign.Builder#target()方法:

   public <T> T target(Target<T> target) {       return build().newInstance(target);     }     public Feign build() {       SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =           new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,                                                logLevel, decode404);       ParseHandlersByName handlersByName =           new ParseHandlersByName(contract, options, encoder, decoder,                                   errorDecoder, synchronousMethodHandlerFactory);       return new ReflectiveFeign(handlersByName, invocationHandlerFactory);     }

直接看ReflectiveFeign#newInstance()方法:

//ReflectiveFeign#newInstance()     public <T> T newInstance(Target<T> target) {                                                 //动态代理的handler类目前穿进来的是ParseHandlersByName类,所以这里要看ParseHandlersByName#apply()直接看下一个方法       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)) {//默认方法会走到这里,比如toString(),hashCode()等方法           DefaultMethodHandler handler = new DefaultMethodHandler(method);           defaultMethodHandlers.add(handler);           methodToHandler.put(method, handler);         } else {//这里才是装配的调用类,上文分析到计息的handler是SynchronousMethodHandler#invoke()           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);//jdk动态代理       for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {         defaultMethodHandler.bindTo(proxy);       }       return proxy;     }     //ParseHandlersByName#apply类,构建动态代理的handler     public Map<String, MethodHandler> apply(Target key) {         List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());         Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();         for (MethodMetadata md : metadata) {           BuildTemplateByResolvingArgs buildTemplate;           if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {             buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder);//通过自定义的encoder去解析参数           } else if (md.bodyIndex() != null) {             buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder);//通过自定义的encoder去解析参数           } else {             buildTemplate = new BuildTemplateByResolvingArgs(md);           }           //创建handler,再看Factory#create()方法,下一个方法           result.put(md.configKey(),                      factory.create(key, md, buildTemplate, options, decoder, errorDecoder));         }         return result;       }     //Factory#create(),构建一个SynchronousMethodHandler去处理请求,调用invoke方法     public MethodHandler create(Target<?> target, MethodMetadata md,             RequestTemplate.Factory buildTemplateFromArgs,             Options options, Decoder decoder, ErrorDecoder errorDecoder) {         return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,                       logLevel, md, buildTemplateFromArgs, options, decoder,                       errorDecoder, decode404);     }     //SynchronousMethodHandler#invoke()方法:实际调用的方法     //@Override     public Object invoke(Object[] argv) throws Throwable {         RequestTemplate template = buildTemplateFromArgs.create(argv);//构建requestTemplate对象         Retryer retryer = this.retryer.clone();         while (true) {           try {             return executeAndDecode(template);//下面不分析了,就是执行execute方法并且解码饭后返回值           } catch (RetryableException e) {             retryer.continueOrPropagate(e);             if (logLevel != Logger.Level.NONE) {               logger.logRetry(metadata.configKey(), logLevel);             }             continue;           }         }       }

感谢各位的阅读,以上就是“SpringCloud的Gateway怎么使用”的内容了,经过本文的学习后,相信大家对SpringCloud的Gateway怎么使用这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI