<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      原理 | dubbo [與 springboot 整合時服務導出的觸發(fā)]

      @

      §1 接入方式

      dubbo 與 springboot 整合后的接入方式可以概括為 3 種

      • Dubbo api 方式,通過 @Bean 的方式手動完成 dubbo 相關(guān)對象的聲明
        • ApplicationConfig
        • RegistryConfig
        • ProtocolConfig
        • ServiceBean
      • xml,原始方式
      • 注解,常用方式,使用下述注解,配合
        • 原生 dubbo2:@Service/@Reference
        • 原生 dubbo3:@DubboService/@DubboReference

      不管使用哪種方式,整體思路應該是一致的

      • 讓 spring 掃描到相關(guān)類,注冊 BeanDefinition(后簡稱為 BD)
      • Spring 容器 context.refresh() 階段通過 BeanDefinition 生成 SpringBean
      • 最后由這些 SpringBean 完成 dubbo 的相關(guān)動作

      單說服務導出這個事,其核心類是 ServiceBean,dubbo 相關(guān)動作即導出,即 export() 方法


      §2 原生注解方式接入

      入口 @EnableDubbo

      dubbo2 原生注解生效的入口其實是 @EnableDubbo

      @Target({ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      @Inherited
      @Documented
      @EnableDubboConfig
      @DubboComponentScan //1
      public @interface EnableDubbo {}
      

      其上注解 @DubboComponentScan 導入了 DubboComponentScanRegistrar

      @Target(ElementType.TYPE)
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      @Import(DubboComponentScanRegistrar.class) //1
      public @interface DubboComponentScan {}
      

      掃描 BeanDefinition 階段

      DubboComponentScanRegistrar 用于負責 BeanDefinition 級的注冊,與本帖討論話題相關(guān)的代碼如下

      public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
          @Override
          public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
              Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
              //1
              registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
              registerCommonBeans(registry);
          }
          private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
              //2
              BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
              builder.addConstructorArgValue(packagesToScan);
              builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
              AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
              BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
          }
      

      DubboComponentScanRegistrar 注冊了一個 ServiceAnnotationBeanPostProcessor,可以看到它是 BeanFactoryPostProcessor 的實現(xiàn)
      其關(guān)鍵方法 registerServiceBeans 就是字面意思的功能,負責掃描并注冊 ServiceBean 的 BD

      public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {}
      
      public class ServiceAnnotationBeanPostProcessor 
          implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ResourceLoaderAware, BeanClassLoaderAware {
          @Override
          public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
              registerBeans(registry, DubboBootstrapApplicationListener.class);  
              Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
              if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
                  //1
                  registerServiceBeans(resolvedPackagesToScan, registry);
              } else {
                  if (logger.isWarnEnabled()) {
                      logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
                  }
              }
          }
      }  
      

      registerServiceBeans 進行了如下 3 步

      • //1:聲明對 @Service 注解的掃描
      • //2:執(zhí)行掃描,這里已經(jīng)獲取了相關(guān)的 BeanDefinition,但是這是原始類的 BeanDefinition
      • //3:生成并注冊 ServiceBeanBeanDefinition
      private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
          DubboClassPathBeanDefinitionScanner scanner = new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
          BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
          scanner.setBeanNameGenerator(beanNameGenerator);
          //1
          scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));
          scanner.addIncludeFilter(new AnnotationTypeFilter(com.alibaba.dubbo.config.annotation.Service.class));
          for (String packageToScan : packagesToScan) {
              scanner.scan(packageToScan);
              //2
              Set<BeanDefinitionHolder> beanDefinitionHolders =
                      findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
              if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
                  for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                  	//3
                      registerServiceBean(beanDefinitionHolder, registry, scanner); 
                  }
                  // ......
          }
      }
      

      //2 處,會掃描指定包,按注解過濾其下所有 /**/*.class 文件,包裝成 BeanDefinition 進而包裝成 BeanDefinitionHolder 集合,如下

      private Set<BeanDefinitionHolder> findServiceBeanDefinitionHolders(
              ClassPathBeanDefinitionScanner scanner, String packageToScan, BeanDefinitionRegistry registry,
              BeanNameGenerator beanNameGenerator) {
          //2.1
          Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(packageToScan);
          Set<BeanDefinitionHolder> beanDefinitionHolders = new LinkedHashSet<>(beanDefinitions.size());
          for (BeanDefinition beanDefinition : beanDefinitions) {
              String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);
              BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);
              beanDefinitionHolders.add(beanDefinitionHolder);
          }
          return beanDefinitionHolders;
      }
      

      //2.1 處,就已經(jīng)掃描出了所有 BD,其核心邏輯如下
      即遍歷 registerServiceBeans //1 處聲明的注解過濾器,逐個匹配 //2 處指定包下的所有類型

      protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
          //...
          for (TypeFilter tf : this.includeFilters) {
             if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return isConditionMatch(metadataReader);
             }
          }
          return false;
      }
      

      //3 處,registerServiceBean 方法負責注冊 ServiceBean 的 BD

      為什么會有兩種BD ?
      因為這倆是針對不同類的 BD。
      假設有個類叫 A,它被標注了 @Service 注解。則,前者是 A 的實例定義信息,而后者是對應 A 的 ServiceBean 的實例定義信息


      BD 是對 spring 里的 bean 的描述,其抽象實現(xiàn) AbstractBeanDefinition 中,有一個成員 Object beanClass; 用于描述被定義的 Bean 的類型
      這兩個 BD 之間的邏輯說白了就是通過輸入流讀取指定包下 class 文件,然后篩選出帶有對應注解的,包裝成第一種 BD
      這些 BD,都需要讓 Spring 聲明為 dubbo 的提供者,于是用原 BD 聲明出 dubbo 使用的 ServiceBean 的 BD(第二種),并注冊到 Spring 容器
      最后在 Spring 的 refresh 環(huán)節(jié)完成 BD 到 SpringBean 的轉(zhuǎn)換

      private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
                                       DubboClassPathBeanDefinitionScanner scanner) {
          Class<?> beanClass = resolveClass(beanDefinitionHolder);
          Annotation service = findServiceAnnotation(beanClass);
          AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
          Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
          String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
          //3.1
          AbstractBeanDefinition serviceBeanDefinition =
                  buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
          String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
          if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
              //3.2
              registry.registerBeanDefinition(beanName, serviceBeanDefinition);
              //...
          }
      }
      

      //3.1 組裝 ServiceBean 的 BD,組裝用的信息從原 BD 及其注解上獲取
      //3.2 將新的 BD 注冊到 Spring 容器,默認由 DefaultListableBeanFactory 實現(xiàn),核心代碼就是下面兩句
      會同時加入兩個集合:beanDefinitionMap / beanDefinitionNames

      public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
          @Override
          public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
              //...
              this.beanDefinitionMap.put(beanName, beanDefinition);
              this.beanDefinitionNames.add(beanName); // 可以視為 beanDefinitionMap 的索引
              //...
          }
      }
      

      生成 ServiceBean

      生成 ServiceBean 是在 applicationContext.refresh() 中實現(xiàn)的,具體流程可以參考下面的脈絡

      • 入口是 Springboot 啟動類 SpringApplication.run(XxxApplication.class, args);
      • 觸發(fā)容器刷新的方法也在 SpringApplication
        • refreshContext(context);
        • applicationContext.refresh();
      • 容器刷新的功能默認由 AbstractApplicationContext 實現(xiàn)
        • finishBeanFactoryInitialization(beanFactory);
        • beanFactory.preInstantiateSingletons();
      • preInstantiateSingletons 的實現(xiàn)在 DefaultListableBeanFactory

      這個類剛剛提到過,最終 ServiceBean 的 BD 就在這個類的成員集合中
      //1 處,就是上文提到的集合之一, preInstantiateSingletons 會遍歷此集合
      //2 處,beanDefinitionNames 中記錄的每個 BD 都會通過此方法創(chuàng)建對象,本文討論的話題中,最終生成的就是 ServiceBean

      public void preInstantiateSingletons() throws BeansException {  
          List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); //1
          for (String beanName : beanNames) {
             RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
             if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                if (isFactoryBean(beanName)) { //... 
                } else {
                   getBean(beanName); //2
                }
             }
          }
          // smart 初始化邏輯,忽略
      }
      

      getBean 中相關(guān)的核心邏輯如下

      // 從前文所屬集合獲取 BD,此方法最終是從 beanDefinitionMap 獲取 BD
      RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
      // ...
      //處理依賴(遞歸 getBean)
      String[] dependsOn = mbd.getDependsOn();
      // ... 
      // 真正創(chuàng)建實例
      if (mbd.isSingleton()) {
          sharedInstance = getSingleton(beanName, () -> {  
                //在這里實際創(chuàng)建,會使用前面生成的 beanDefinition 
                return createBean(beanName, mbd, args);
          });
          bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
      }
      

      服務導出

      服務導出最終由 ServiceConfig.export() 實現(xiàn)(ServiceBeanServiceConfig 的子類),這就好辦了,打個斷點,可見如下
      在這里插入圖片描述
      原生注解的服務導出由 DubboBootstrapApplicationListener 監(jiān)聽(實現(xiàn)了 ApplicationListener),由 Spring 的 ContextRefreshedEvent 事件觸發(fā)

      public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener implements Ordered {
          @Override
          public void onApplicationContextEvent(ApplicationContextEvent event) {
              if (event instanceof ContextRefreshedEvent) {
                  // 事件觸發(fā)
                  onContextRefreshedEvent((ContextRefreshedEvent) event);
              } else if (event instanceof ContextClosedEvent) {
                  onContextClosedEvent((ContextClosedEvent) event);
              }
          }
          private void onContextRefreshedEvent(ContextRefreshedEvent event) {
              dubboBootstrap.start(); // dubbo 啟動
          }
      }
      

      比較關(guān)鍵的代碼摘要如下

      public DubboBootstrap start() {
          if (started.compareAndSet(false, true)) {
              initialize();
              if (logger.isInfoEnabled()) {
                  logger.info(NAME + " is starting...");
              }
              exportServices(); //從這里導出服務
              //...
          }
          return this;
      }
      

      里面的邏輯很好找重點,從 configManager 遍歷,挨個執(zhí)行 ServiceConfig.export()

      private void exportServices() {
          //1 遍歷
          configManager.getServices().forEach(sc -> {
              ServiceConfig serviceConfig = (ServiceConfig) sc;
              serviceConfig.setBootstrap(this);
      
              if (exportAsync) {
                  //...挨個異步導出
              } else {
                  //挨個同步導出
                  sc.export();
                  exportedServices.add(sc);
              }
          });
      }
      

      唯一的問題是 configManager 里面的東西是怎么來的。
      答案是在 ServiceConfig/ServiceBean 的父類 AbstractConfig 上定義的初始化方法(繼承關(guān)系有點復雜,如下圖)
      ServiceBean 初始化完成后會通過此方法把自己加入 ConfigManager,至于為什么這里叫做 ConfigManager,這是因為 dubbo 的各種對象都叫 xxxConfig

      @PostConstruct
      public void addIntoConfigManager() {
          ApplicationModel.getConfigManager().addConfig(this);
      }
      

      在這里插入圖片描述



      §3 原生 xml 方式接入

      xml 方式接入時,可以推測其整個流程的最大不同在 BeanDefinition 階段
      原生注解接入時,dubbo 的服務是由注解標注生效,服務也是由注解標注聲明,因此我們從注解開始找入口(如果找不到應該結(jié)合 Spring 的機制找相關(guān)的類)
      xml 方式接入時,服務是從 xml 里聲明的,入口也應該從 xml 開始找,尤其是其對應的解析器

      入口 xmlns:dubbo

      xml 方式入口位置很容易忽略,其實是 xml 父標簽的 xmlns。
      xmlns 用于聲明 xml 的 namespace,用于定義其中的標簽,以及其解析器,如下

      <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" (here)
             xmlns="http://www.springframework.org/schema/beans"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
             http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
      </beans>
      

      http://dubbo.apache.org/schema/dubbo 直接訪問是訪問不了的,它實際上定義在 dubbo-config-spring 項目下的 spring.handlers

      http\://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
      http\://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandle
      

      可見對應的是 DubboNamespaceHandler,作為 Dubbo 標簽的解析器,解析器中明顯可見如下代碼
      其初始化方法中注冊了 BD 解析器,針對每種標簽注冊了一種,以 registry 為例,就對應 <dubbo:registry /> 標簽
      從下面代碼可見,xml 依賴的解析器基本都是 DubboBeanDefinitionParser

      從這里也可以看到,dubbo 解析后的類,基本全叫 xxxConfig

      public class DubboNamespaceHandler extends NamespaceHandlerSupport implements ConfigurableSourceBeanMetadataElement {
          @Override
          public void init() {
              registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
              registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
              registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
              registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));
              registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));
              registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
              registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));
              registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.class, true));
              registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
              registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
              registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
              //1
              registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
              registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
              registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
          }
      }
      

      掃描 BeanDefinition 階段

      DubboBeanDefinitionParser 見名知意,是用于將標簽解析為 dubbo 的 BD 的解析器,既然是解析器其核心方法必然是 parse,入口斷點調(diào)試可見
      在這里插入圖片描述

      解析由 spring 觸發(fā) XmlBeanDefinitionReader, 在 DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions() 中執(zhí)行
      此方法的有效邏輯最終由 DubboBeanDefinitionParser.parse() 全權(quán)實現(xiàn):解析標簽為 BD 并完成注冊

      本帖相關(guān)核心代碼如下
      //1:這里的 beanClass 直接使用的成員變量,即 DubboBeanDefinitionParser 實例化時傳來的 ServiceBean.class
      //2:直接定義結(jié)果 BD,parse 方法的作用就是將標簽 <dubbo:service/> 解析為 ServiceBean 的 BD,并完成 BD 的注冊
      //3:按 Dubbo 的規(guī)則生成 id,這個 id 會作為 ServiceBean 的 BD 的名字注冊
      //4:注冊 ServiceBean 的 BD,parserContext.getRegistry() 實際返回的是 DefaultListableBeanFactory,與注解方式接入時一致
      //5/6:與注解方式接入的一個區(qū)別在于此,xml 方式是現(xiàn)有 ServiceBean 的 BD,在注冊時再去生成實際服務的 BD,而注解方式是相反的

      public BeanDefinition parse(Element element, ParserContext parserContext) {
          registerDubboConfigAliasPostProcessor(parserContext.getRegistry());
          //1
          return parse(element, parserContext, beanClass, required); 
      }
      
      private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
          //2
          RootBeanDefinition beanDefinition = new RootBeanDefinition(); 
          beanDefinition.setBeanClass(beanClass); 
          beanDefinition.setLazyInit(false);
          String id = element.getAttribute("id");
          //3
          if (StringUtils.isEmpty(id) && required) {//...} 
          if (StringUtils.isNotEmpty(id)) {
              if (parserContext.getRegistry().containsBeanDefinition(id)) {
                  throw new IllegalStateException("Duplicate spring bean id " + id);
              }
              //4
              parserContext.getRegistry().registerBeanDefinition(id, beanDefinition); 
              beanDefinition.getPropertyValues().addPropertyValue("id", id);
          }
          if (ProtocolConfig.class.equals(beanClass)) {
          } else if (ServiceBean.class.equals(beanClass)) {
              String className = element.getAttribute("class");
              if (StringUtils.isNotEmpty(className)) {
                  //5
                  RootBeanDefinition classDefinition = new RootBeanDefinition(); 
                  classDefinition.setBeanClass(ReflectUtils.forName(className));
                  classDefinition.setLazyInit(false);
                  parseProperties(element.getChildNodes(), classDefinition);
                  //6
                  beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl")); 
              }
              //...
          }
          //...
      }
      

      生成 ServiceBean

      與原生注解方式一致


      服務導出

      與原生注解方式一致

      posted @ 2025-05-21 11:12  問仙長何方蓬萊  閱讀(13)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 日韩有码中文字幕第一页| 蜜臀av性久久久久蜜臀aⅴ麻豆| 久久天天躁夜夜躁一区| 国产精品十八禁一区二区| 亚洲成a人片在线视频| 国产三级国产精品久久成人| 永久无码天堂网小说区| 国产免费播放一区二区三区| 又长又粗又爽又高潮的视频| 午夜夜福利一区二区三区| 在线播放国产女同闺蜜| 南开区| 最新午夜男女福利片视频| 欧美成人看片黄A免费看| 爆乳2把你榨干哦ova在线观看| 亚洲国产成人无码影片在线播放 | 国产色悠悠综合在线观看| 色综合热无码热国产| 国产高清在线男人的天堂| 风韵丰满熟妇啪啪区老熟熟女| 好男人好资源WWW社区| 屏边| 亚洲中文字幕无码爆乳| 无码视频伊人| 女人色熟女乱| 亚洲最大有声小说AV网| 欧美激情内射喷水高潮| 国产人妻大战黑人第1集| 亚洲永久视频| 精品中文字幕人妻一二| 四虎影视www在线播放| 国产精品一区二区国产主播| 欧美乱码伦视频免费| 成人一区二区三区久久精品| 亚洲一区二区精品偷拍| 斗六市| 精品三级在线| 中文字幕无码中文字幕有码a| 日韩精品区一区二区三vr| 国产精品亚洲综合久久小说| 被喂春药蹂躏的欲仙欲死视频|