SpringCloud分散式微服務b2b2c電子商務-Gateway的基本入門
SpringCloud微服務專案之間呼叫是透過httprest請求來進行服務呼叫的,之前我們會用到HttpClient等工具來進行服務請求,Spring對這種請求進行了處理,封裝成了可宣告式的web客戶端,使得編寫web客戶端更容易,feign還支援可插拔的編碼器和解碼器,Spring在用的時候增加了對@requestMapping的處理,同時,SpringCloud還對feign整合了註冊中心(eureka)和客戶端負載均衡(ribbon),瞭解springcloud架構可以加求求:三五三六二四七二五九,使得我們擁有一個客戶端負載均衡的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 = " } 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 = " } 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; } } }
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69952307/viewspace-2672063/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- JAVA b2b2c電子商務SpringCloud分散式微服務spring-security-基本JavaSpringGCCloud分散式微服務
- java b2b2c電子商務 springcloud分散式微服務-Session共享JavaSpringGCCloud分散式微服務Session
- SpringCloud分散式微服務b2b2c電子商務分散式微服務-docker-feign-hystrix(七)SpringGCCloud分散式微服務Docker
- Springcloud分散式微服務b2b2c電子商務一整合Hystrix(二)SpringGCCloud分散式微服務
- SpringCloud分散式微服務b2b2c電子商務(五)springboot整合 beatlsqlGCCloud分散式微服務Spring BootTLSSQL
- SpringCloud分散式微服務b2b2c電子商務docker-feign配置(六)SpringGCCloud分散式微服務Docker
- SpringCloud分散式微服務b2b2c電子商務-hystrix引數詳解(十)SpringGCCloud分散式微服務
- SpringCloud分散式微服務b2b2c電子商務(十三)Springboot整合RabbitMQGCCloud分散式微服務Spring BootMQ
- SpringCloud分散式微服務b2b2c電子商務(一)元件和概念介紹SpringGCCloud分散式微服務元件
- SpringCloud分散式微服務b2b2c電子商務-SpringBoot 整合JPA(四)GCCloud分散式微服務Spring Boot
- SpringCloud分散式微服務b2b2c電子商務-定時任務(Scheduling Tasks)SpringGCCloud分散式微服務
- SpringCloud分散式微服務b2b2c電子商務(三)註冊中心叢集篇SpringGCCloud分散式微服務
- SpringCloud分散式微服務b2b2c電子商務-Spring Boot配置檔案詳解GCCloud分散式微服務Spring Boot
- SpringCloud分散式微服務b2b2c電子商務docker-feign-hystrix-ribbon(八)SpringGCCloud分散式微服務Docker
- SpringCloud分散式微服務b2b2c電子商務-docker部署spring cloud專案(十一)SpringGCCloud分散式微服務Docker
- spring cloud微服務分散式雲架構-Gateway入門SpringCloud微服務分散式架構Gateway
- Java b2b2c電子商務 SpringCloud分散式微服務-config的簡單配置shh方式JavaSpringGCCloud分散式微服務
- Springcloud分散式微服務b2b2c電子商務一Spring Cloud常見問題(一)SpringGCCloud分散式微服務
- SpringCloud分散式微服務b2b2c電子商務(二)Eureka(服務註冊和服務發現基礎篇)SpringGCCloud分散式微服務
- 微服務、分散式、雲架構構建電子商務平臺微服務分散式架構
- JAVA spring cloud boot b2b2c電子商務分散式微服務JavaSpringCloudboot分散式微服務
- (十四)JAVA springboot微服務b2b2c電子商務系統- Spring Cloud構建分散式電子商務平臺JavaSpring Boot微服務Cloud分散式
- SpringCloud分散式微服務b2b2c電子商務-Spring Cloud自定義引導屬性源SpringGCCloud分散式微服務
- 分散式微服務雲架構電子商務平臺分散式微服務架構
- 分散式微服務雲架構構建電子商務分散式微服務架構
- SpringCloud分散式微服務b2b2c電子商務(十二)在springboot中用redis實現訊息佇列GCCloud分散式微服務Spring BootRedis佇列
- 關於SpringCloud微服務雲架構構建B2B2C電子商務平臺之-(SpringGCCloud微服務架構
- 構建分散式微服務雲架構電子商務平臺分散式微服務架構
- 分散式微服務雲架構構建電子商務平臺分散式微服務架構
- java B2B2C Springcloud電子商務平臺原始碼-Ribbon 基本使用JavaSpringGCCloud原始碼
- SpringCloud微服務雲架構構建B2B2C電子商務平臺分析之-服務消費(Ribbon)SpringGCCloud微服務架構
- java B2B2C Springcloud電子商務平臺原始碼JavaSpringGCCloud原始碼
- SpringCloud微服務雲架構構建B2B2C電子商務平臺之-服務的註冊與發現EurekaSpringGCCloud微服務架構
- SpringCloud微服務雲架構構建B2B2C電子商務平臺之-(十)高可用的服務註冊中心SpringGCCloud微服務架構
- 微服務雲架構電子商務平臺微服務架構
- SpringCloud 微服務閘道器 Gateway 元件SpringGCCloud微服務Gateway元件
- ZooKeeper分散式專題與Dubbo微服務入門分散式微服務
- SpringCloud微服務系列- 分散式能力建設之微服務閘道器SpringGCCloud微服務分散式