blob: efb6973c77c8eb10e707e5b6d8d246b6dc3adc60 [file] [log] [blame]
<!doctype html><html lang=zh-cn class=no-js><head><meta name=ROBOTS content="INDEX, FOLLOW"><link rel=canonical href=https://dubbo.apache.org/zh-cn/docs/references/spis/dubbo-spi/><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=generator content="Hugo 0.117.0"><link rel="shortcut icon" type=image/png href=/imgs/favicon.png><link rel=apple-touch-icon href=/favicons/apple-touch-icon-180x180.png sizes=180x180><link rel=manifest href=/manifest.webmanifest><title>扩展点开发指南 | Apache Dubbo</title><meta property="og:title" content="扩展点开发指南"><meta property="og:description" content="本文介绍了 Dubbo SPI 的原理和实现细节"><meta property="og:type" content="article"><meta property="og:url" content="https://dubbo.apache.org/zh-cn/docs/references/spis/dubbo-spi/"><meta property="article:section" content="docs"><meta property="article:modified_time" content="2023-03-01T15:24:06+08:00"><meta itemprop=name content="扩展点开发指南"><meta itemprop=description content="本文介绍了 Dubbo SPI 的原理和实现细节"><meta itemprop=dateModified content="2023-03-01T15:24:06+08:00"><meta itemprop=wordCount content="1936"><meta itemprop=keywords content><meta name=twitter:card content="summary"><meta name=twitter:title content="扩展点开发指南"><meta name=twitter:description content="本文介绍了 Dubbo SPI 的原理和实现细节"><script async src="https://www.googletagmanager.com/gtag/js?id=G-1TFHM5YBH0"></script>
<script>var doNotTrack=!1;if(!doNotTrack){window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag("js",new Date),gtag("config","G-1TFHM5YBH0",{anonymize_ip:!1})}</script><link rel=preload href=/scss/main.min.fe7176cbe3102a33d3e8c0c9cec61eb52508abd24a2cc1ae23ccf535a481ffde.css as=style><link href=/scss/main.min.fe7176cbe3102a33d3e8c0c9cec61eb52508abd24a2cc1ae23ccf535a481ffde.css rel=stylesheet integrity><script src=/js/jquery-3.5.1.min.js integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin=anonymous></script>
<link rel=stylesheet href=https://cdn.jsdelivr.net/npm/@docsearch/css@3><meta name=theme-color content="#326ce5"><link rel=stylesheet href=/css/feature-states.css><meta name=description content="本文介绍了 Dubbo SPI 的原理和实现细节"><meta property="og:description" content="本文介绍了 Dubbo SPI 的原理和实现细节"><meta name=twitter:description content="本文介绍了 Dubbo SPI 的原理和实现细节"><meta property="og:url" content="https://dubbo.apache.org/zh-cn/docs/references/spis/dubbo-spi/"><meta property="og:title" content="扩展点开发指南"><meta name=twitter:title content="扩展点开发指南"><meta name=twitter:image:alt content="Apache Dubbo"><meta property="og:type" content="article"><meta name=viewport content="width=device-width"><script async defer src=/js/github-buttons.js></script>
<link href=/css/community.css rel=stylesheet><link href=/css/contactus.css rel=stylesheet><link href=/css/language.css rel=stylesheet><script src=/js/script.js></script></head><body class="td-page td-documentation"><header><nav class="js-navbar-scroll navbar navbar-expand navbar-dark flex-column flex-md-row td-navbar" data-auto-burger=primary><a class=navbar-brand href=/zh-cn/><span class=navbar-logo></span><span class="text-uppercase font-weight-bold">Apache Dubbo</span></a><div class="td-navbar-nav-scroll ml-md-auto" id=main_navbar><ul class="navbar-nav mt-2 mt-lg-0"><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=/zh-cn/overview/home/><span>文档</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=/zh-cn/blog/><span>博客</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=/zh-cn/download/><span>版本发布</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=/zh-cn/contact/><span>联系社区</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=https://start.dubbo.apache.org/bootstrap.html target=_blank><span>Initializer</span><i class='fas fa-external-link-alt'></i></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=/zh-cn/github/><span>Github</span><i class='fa-brands fa-github'></i></a></li><li class="nav-item dropdown mr-4 d-none d-lg-block"><a class="nav-link dropdown-toggle" href=# id=navbarDropdown role=button data-toggle=dropdown aria-haspopup=true aria-expanded=false>版本列表</a><div class="dropdown-menu dropdown-menu-right" aria-labelledby=navbarDropdownMenuLink><a class=dropdown-item href=/releases>Release Information</a>
<a class=dropdown-item href=https://dubbo.apache.org/zh-cn/docs/references/spis/dubbo-spi/>Java-3.3</a>
<a class=dropdown-item href=https://v3-2.dubbo.apache.org/zh-cn/docs/references/spis/dubbo-spi/>Java-3.2</a>
<a class=dropdown-item href=https://dubbo.apache.org/zh-cn/docs/references/spis/dubbo-spi/>Go-3.1</a>
<a class=dropdown-item href=https://dubbo.apache.org/zh-cn/docs/references/spis/dubbo-spi/>Nodejs-0.1</a>
<a class=dropdown-item href=https://dubbo.apache.org/zh-cn/docs/references/spis/dubbo-spi/>Web-0.1</a>
<a class=dropdown-item href=https://dubbo.apache.org/zh-cn/docs/references/spis/dubbo-spi/>Rust-0.1</a></div></li><li class="nav-item dropdown d-lg-block"><a class="nav-link dropdown-toggle" href=# id=navbarDropdownMenuLink role=button data-toggle=dropdown aria-haspopup=true aria-expanded=false>中文</a><div class="dropdown-menu dropdown-menu-right" aria-labelledby=navbarDropdownMenuLink><a class=dropdown-item href=/en/>English</a></div></li><li class="nav-item dropdown d-lg-block"><div class="nav-item d-none d-lg-block"><div id=docsearch></div></div></li></ul></div></nav><div lang=zh-cn id=announcement style='background-color:#3371e3;color:#fff; "background-image: linear-gradient(to right, #073476, #002b76, #022274, #0b1772, #16066e);"'><aside><div class=announcement-main data-nosnippet><h4><img alt=ApacheCon-Asia src=/imgs/contacts/wechat-account.jpg style=float:right;width:10%;height:auto>
<a href=/zh-cn/blog/2023/08/25/coc-asia-2023-大会精彩回顾/>Dubbo 微服务专题论坛</a> 精彩回顾!</h4><p>Community Over Code 2023 暨 ApacheCon Asia 2023 大会在 8月18 - 8月20日在北京举办,让我们一起回顾一下可观测、Native、协议、Kubernetes部署、企业微服务实践等 8 个议题的精彩分享!</p></div></aside></div><section class="header-hero text-white pb-0 light-text"></section></header><div class="container-fluid td-outer"><div class=td-main><div class="row flex-md-nowrap"><div class="col-12 col-md-3 col-xl-2 td-sidebar d-print-none"><script>$(function(){$("#td-section-nav a").removeClass("active"),$("#td-section-nav #m-zh-cndocsreferencesspisdubbo-spi").addClass("active"),$("#td-section-nav #m-zh-cndocsreferencesspisdubbo-spi-li span").addClass("td-sidebar-nav-active-item"),$("#td-section-nav #m-zh-cndocsreferencesspisdubbo-spi").parents("li").addClass("active-path"),$("#td-section-nav li.active-path").addClass("show"),$("#td-section-nav li.active-path").children("input").prop("checked",!0),$("#td-section-nav #m-zh-cndocsreferencesspisdubbo-spi-li").siblings("li").addClass("show"),$("#td-section-nav #m-zh-cndocsreferencesspisdubbo-spi-li").children("ul").children("li").addClass("show"),$("#td-sidebar-menu").toggleClass("d-none")})</script><div id=td-sidebar-menu class="td-sidebar__inner d-none"><div id=content-mobile><form class="td-sidebar__search d-flex align-items-center"><div id=docsearch></div><button class="btn btn-link td-sidebar__toggle d-md-none p-0 ml-3 fas fa-bars" type=button data-toggle=collapse data-target=#td-section-nav aria-controls=td-docs-nav aria-expanded=false aria-label="Toggle section navigation"></button></form></div><div id=content-desktop></div><nav class="collapse td-sidebar-nav foldable-nav" id=td-section-nav><ul class="td-sidebar-nav__section pr-md-3 ul-0"><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocs-li><ul class=ul-1><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsintroduction-li><input type=checkbox id=m-zh-cndocsintroduction-check>
<label for=m-zh-cndocsintroduction-check><a href=/zh-cn/docs/introduction/ title="Dubbo3 简介" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsintroduction><span>简介</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsnew-in-dubbo3-li><input type=checkbox id=m-zh-cndocsnew-in-dubbo3-check>
<label for=m-zh-cndocsnew-in-dubbo3-check><a href=/zh-cn/docs/new-in-dubbo3/ title="What's New in Dubbo3" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsnew-in-dubbo3><span>新版本特性速览</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsquick-start-li><input type=checkbox id=m-zh-cndocsquick-start-check>
<label for=m-zh-cndocsquick-start-check><a href=/zh-cn/docs/quick-start/ title="Spring Boot 快速开发 Dubbo 服务" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsquick-start><span>快速开始</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsconcepts-li><input type=checkbox id=m-zh-cndocsconcepts-check>
<label for=m-zh-cndocsconcepts-check><a href=/zh-cn/docs/concepts/ title=概念与架构 class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsconcepts><span>概念&架构</span></a></label><ul class="ul-2 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsconceptsservice-discovery-li><input type=checkbox id=m-zh-cndocsconceptsservice-discovery-check>
<label for=m-zh-cndocsconceptsservice-discovery-check><a href=/zh-cn/docs/concepts/service-discovery/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsconceptsservice-discovery><span>服务发现</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsconceptsrpc-protocol-li><input type=checkbox id=m-zh-cndocsconceptsrpc-protocol-check>
<label for=m-zh-cndocsconceptsrpc-protocol-check><a href=/zh-cn/docs/concepts/rpc-protocol/ title="RPC 通信协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsconceptsrpc-protocol><span>协议</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsconceptstraffic-management-li><input type=checkbox id=m-zh-cndocsconceptstraffic-management-check>
<label for=m-zh-cndocsconceptstraffic-management-check><a href=/zh-cn/docs/concepts/traffic-management/ title=服务流量管理 class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsconceptstraffic-management><span>流量管理</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsconceptsconfiguration-li><input type=checkbox id=m-zh-cndocsconceptsconfiguration-check>
<label for=m-zh-cndocsconceptsconfiguration-check><a href=/zh-cn/docs/concepts/configuration/ title=配置管理 class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsconceptsconfiguration><span>配置</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsconceptsregistry-configcenter-metadata-li><input type=checkbox id=m-zh-cndocsconceptsregistry-configcenter-metadata-check>
<label for=m-zh-cndocsconceptsregistry-configcenter-metadata-check><a href=/zh-cn/docs/concepts/registry-configcenter-metadata/ title="部署架构(注册中心 配置中心 元数据中心)" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsconceptsregistry-configcenter-metadata><span>部署架构</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsconceptsextensibility-li><input type=checkbox id=m-zh-cndocsconceptsextensibility-check>
<label for=m-zh-cndocsconceptsextensibility-check><a href=/zh-cn/docs/concepts/extensibility/ title="如何扩展 Dubbo" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsconceptsextensibility><span>扩展性</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsexamples-li><input type=checkbox id=m-zh-cndocsexamples-check>
<label for=m-zh-cndocsexamples-check><a href=/zh-cn/docs/examples/ title=基本功能介绍与示例 class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsexamples><span>介绍与示例</span></a></label><ul class="ul-2 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsexamplesrouting-li><input type=checkbox id=m-zh-cndocsexamplesrouting-check>
<label for=m-zh-cndocsexamplesrouting-check><a href=/zh-cn/docs/examples/routing/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsexamplesrouting><span>路由规则</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsexamplesroutingdynamic-rule-deployment-li><input type=checkbox id=m-zh-cndocsexamplesroutingdynamic-rule-deployment-check>
<label for=m-zh-cndocsexamplesroutingdynamic-rule-deployment-check><a href=/zh-cn/docs/examples/routing/dynamic-rule-deployment/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsexamplesroutingdynamic-rule-deployment><span>动态路由</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsexamplesroutingweight-rule-deployment-li><input type=checkbox id=m-zh-cndocsexamplesroutingweight-rule-deployment-check>
<label for=m-zh-cndocsexamplesroutingweight-rule-deployment-check><a href=/zh-cn/docs/examples/routing/weight-rule-deployment/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsexamplesroutingweight-rule-deployment><span>权重路由</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsexamplesroutingdemo-rule-deployment-li><input type=checkbox id=m-zh-cndocsexamplesroutingdemo-rule-deployment-check>
<label for=m-zh-cndocsexamplesroutingdemo-rule-deployment-check><a href=/zh-cn/docs/examples/routing/demo-rule-deployment/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsexamplesroutingdemo-rule-deployment><span>使用案例</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsexamplesroutingblue-green-deployment-li><input type=checkbox id=m-zh-cndocsexamplesroutingblue-green-deployment-check>
<label for=m-zh-cndocsexamplesroutingblue-green-deployment-check><a href=/zh-cn/docs/examples/routing/blue-green-deployment/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsexamplesroutingblue-green-deployment><span>蓝绿部署</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsexamplesroutingab-testing-deployment-li><input type=checkbox id=m-zh-cndocsexamplesroutingab-testing-deployment-check>
<label for=m-zh-cndocsexamplesroutingab-testing-deployment-check><a href=/zh-cn/docs/examples/routing/ab-testing-deployment/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsexamplesroutingab-testing-deployment><span>Ab测试</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsexamplesroutingcanary-deployment-li><input type=checkbox id=m-zh-cndocsexamplesroutingcanary-deployment-check>
<label for=m-zh-cndocsexamplesroutingcanary-deployment-check><a href=/zh-cn/docs/examples/routing/canary-deployment/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsexamplesroutingcanary-deployment><span>金丝雀部署</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsexamplesservice-discovery-li><input type=checkbox id=m-zh-cndocsexamplesservice-discovery-check>
<label for=m-zh-cndocsexamplesservice-discovery-check><a href=/zh-cn/docs/examples/service-discovery/ title=应用级服务发现 class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsexamplesservice-discovery><span>服务发现</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsexamplesconfiguration-override-li><input type=checkbox id=m-zh-cndocsexamplesconfiguration-override-check>
<label for=m-zh-cndocsexamplesconfiguration-override-check><a href=/zh-cn/docs/examples/configuration-override/ title=动态修改运行态配置项 class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsexamplesconfiguration-override><span>动态配置</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsadvanced-li><input type=checkbox id=m-zh-cndocsadvanced-check>
<label for=m-zh-cndocsadvanced-check><a href=/zh-cn/docs/advanced/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsadvanced><span>高级用法</span></a></label><ul class="ul-2 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedpreflight-check-li><input type=checkbox id=m-zh-cndocsadvancedpreflight-check-check>
<label for=m-zh-cndocsadvancedpreflight-check-check><a href=/zh-cn/docs/advanced/preflight-check/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedpreflight-check><span>启动时检查</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedserialization-security-li><input type=checkbox id=m-zh-cndocsadvancedserialization-security-check>
<label for=m-zh-cndocsadvancedserialization-security-check><a href=/zh-cn/docs/advanced/serialization-security/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedserialization-security><span>序列化协议安全</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedfault-tolerent-strategy-li><input type=checkbox id=m-zh-cndocsadvancedfault-tolerent-strategy-check>
<label for=m-zh-cndocsadvancedfault-tolerent-strategy-check><a href=/zh-cn/docs/advanced/fault-tolerent-strategy/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedfault-tolerent-strategy><span>集群容错</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedloadbalance-li><input type=checkbox id=m-zh-cndocsadvancedloadbalance-check>
<label for=m-zh-cndocsadvancedloadbalance-check><a href=/zh-cn/docs/advanced/loadbalance/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedloadbalance><span>负载均衡</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedthread-model-li><input type=checkbox id=m-zh-cndocsadvancedthread-model-check>
<label for=m-zh-cndocsadvancedthread-model-check><a href=/zh-cn/docs/advanced/thread-model/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedthread-model><span>线程模型</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedexplicit-target-li><input type=checkbox id=m-zh-cndocsadvancedexplicit-target-check>
<label for=m-zh-cndocsadvancedexplicit-target-check><a href=/zh-cn/docs/advanced/explicit-target/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedexplicit-target><span>直连提供者</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedsubscribe-only-li><input type=checkbox id=m-zh-cndocsadvancedsubscribe-only-check>
<label for=m-zh-cndocsadvancedsubscribe-only-check><a href=/zh-cn/docs/advanced/subscribe-only/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedsubscribe-only><span>只订阅</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedmulti-protocols-li><input type=checkbox id=m-zh-cndocsadvancedmulti-protocols-check>
<label for=m-zh-cndocsadvancedmulti-protocols-check><a href=/zh-cn/docs/advanced/multi-protocols/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedmulti-protocols><span>多协议</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedmulti-registry-li><input type=checkbox id=m-zh-cndocsadvancedmulti-registry-check>
<label for=m-zh-cndocsadvancedmulti-registry-check><a href=/zh-cn/docs/advanced/multi-registry/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedmulti-registry><span>多注册中心</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedservice-group-li><input type=checkbox id=m-zh-cndocsadvancedservice-group-check>
<label for=m-zh-cndocsadvancedservice-group-check><a href=/zh-cn/docs/advanced/service-group/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedservice-group><span>服务分组</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedstatic-service-li><input type=checkbox id=m-zh-cndocsadvancedstatic-service-check>
<label for=m-zh-cndocsadvancedstatic-service-check><a href=/zh-cn/docs/advanced/static-service/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedstatic-service><span>静态服务</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedmulti-versions-li><input type=checkbox id=m-zh-cndocsadvancedmulti-versions-check>
<label for=m-zh-cndocsadvancedmulti-versions-check><a href=/zh-cn/docs/advanced/multi-versions/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedmulti-versions><span>多版本</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedgroup-merger-li><input type=checkbox id=m-zh-cndocsadvancedgroup-merger-check>
<label for=m-zh-cndocsadvancedgroup-merger-check><a href=/zh-cn/docs/advanced/group-merger/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedgroup-merger><span>分组聚合</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedparameter-validation-li><input type=checkbox id=m-zh-cndocsadvancedparameter-validation-check>
<label for=m-zh-cndocsadvancedparameter-validation-check><a href=/zh-cn/docs/advanced/parameter-validation/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedparameter-validation><span>参数验证</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedresult-cache-li><input type=checkbox id=m-zh-cndocsadvancedresult-cache-check>
<label for=m-zh-cndocsadvancedresult-cache-check><a href=/zh-cn/docs/advanced/result-cache/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedresult-cache><span>结果缓存</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedgeneric-reference-li><input type=checkbox id=m-zh-cndocsadvancedgeneric-reference-check>
<label for=m-zh-cndocsadvancedgeneric-reference-check><a href=/zh-cn/docs/advanced/generic-reference/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedgeneric-reference><span>使用泛化调用</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedprotobuf-idl-li><input type=checkbox id=m-zh-cndocsadvancedprotobuf-idl-check>
<label for=m-zh-cndocsadvancedprotobuf-idl-check><a href=/zh-cn/docs/advanced/protobuf-idl/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedprotobuf-idl><span>Protobuf</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedpb-generic-reference-li><input type=checkbox id=m-zh-cndocsadvancedpb-generic-reference-check>
<label for=m-zh-cndocsadvancedpb-generic-reference-check><a href=/zh-cn/docs/advanced/pb-generic-reference/ title="GoogleProtobuf 对象泛化调用" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedpb-generic-reference><span>Protobuf 泛化调用</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedgeneric-service-li><input type=checkbox id=m-zh-cndocsadvancedgeneric-service-check>
<label for=m-zh-cndocsadvancedgeneric-service-check><a href=/zh-cn/docs/advanced/generic-service/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedgeneric-service><span>实现泛化调用</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedecho-service-li><input type=checkbox id=m-zh-cndocsadvancedecho-service-check>
<label for=m-zh-cndocsadvancedecho-service-check><a href=/zh-cn/docs/advanced/echo-service/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedecho-service><span>回声测试</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedcontext-li><input type=checkbox id=m-zh-cndocsadvancedcontext-check>
<label for=m-zh-cndocsadvancedcontext-check><a href=/zh-cn/docs/advanced/context/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedcontext><span>上下文信息</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedattachment-li><input type=checkbox id=m-zh-cndocsadvancedattachment-check>
<label for=m-zh-cndocsadvancedattachment-check><a href=/zh-cn/docs/advanced/attachment/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedattachment><span>隐式参数</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedasync-call-li><input type=checkbox id=m-zh-cndocsadvancedasync-call-check>
<label for=m-zh-cndocsadvancedasync-call-check><a href=/zh-cn/docs/advanced/async-call/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedasync-call><span>异步调用</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedasync-execute-on-provider-li><input type=checkbox id=m-zh-cndocsadvancedasync-execute-on-provider-check>
<label for=m-zh-cndocsadvancedasync-execute-on-provider-check><a href=/zh-cn/docs/advanced/async-execute-on-provider/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedasync-execute-on-provider><span>异步执行</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedlocal-call-li><input type=checkbox id=m-zh-cndocsadvancedlocal-call-check>
<label for=m-zh-cndocsadvancedlocal-call-check><a href=/zh-cn/docs/advanced/local-call/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedlocal-call><span>本地调用</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedcallback-parameter-li><input type=checkbox id=m-zh-cndocsadvancedcallback-parameter-check>
<label for=m-zh-cndocsadvancedcallback-parameter-check><a href=/zh-cn/docs/advanced/callback-parameter/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedcallback-parameter><span>参数回调</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedauth-li><input type=checkbox id=m-zh-cndocsadvancedauth-check>
<label for=m-zh-cndocsadvancedauth-check><a href=/zh-cn/docs/advanced/auth/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedauth><span>服务鉴权</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedevents-notify-li><input type=checkbox id=m-zh-cndocsadvancedevents-notify-check>
<label for=m-zh-cndocsadvancedevents-notify-check><a href=/zh-cn/docs/advanced/events-notify/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedevents-notify><span>事件通知</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedlocal-stub-li><input type=checkbox id=m-zh-cndocsadvancedlocal-stub-check>
<label for=m-zh-cndocsadvancedlocal-stub-check><a href=/zh-cn/docs/advanced/local-stub/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedlocal-stub><span>本地存根</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedlocal-mock-li><input type=checkbox id=m-zh-cndocsadvancedlocal-mock-check>
<label for=m-zh-cndocsadvancedlocal-mock-check><a href=/zh-cn/docs/advanced/local-mock/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedlocal-mock><span>本地伪装</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvanceddelay-publish-li><input type=checkbox id=m-zh-cndocsadvanceddelay-publish-check>
<label for=m-zh-cndocsadvanceddelay-publish-check><a href=/zh-cn/docs/advanced/delay-publish/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvanceddelay-publish><span>延迟暴露</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedconcurrency-control-li><input type=checkbox id=m-zh-cndocsadvancedconcurrency-control-check>
<label for=m-zh-cndocsadvancedconcurrency-control-check><a href=/zh-cn/docs/advanced/concurrency-control/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedconcurrency-control><span>并发控制</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedconfig-connections-li><input type=checkbox id=m-zh-cndocsadvancedconfig-connections-check>
<label for=m-zh-cndocsadvancedconfig-connections-check><a href=/zh-cn/docs/advanced/config-connections/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedconfig-connections><span>连接控制</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedlazy-connect-li><input type=checkbox id=m-zh-cndocsadvancedlazy-connect-check>
<label for=m-zh-cndocsadvancedlazy-connect-check><a href=/zh-cn/docs/advanced/lazy-connect/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedlazy-connect><span>延迟连接</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedstickiness-li><input type=checkbox id=m-zh-cndocsadvancedstickiness-check>
<label for=m-zh-cndocsadvancedstickiness-check><a href=/zh-cn/docs/advanced/stickiness/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedstickiness><span>粘滞连接</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedtls-li><input type=checkbox id=m-zh-cndocsadvancedtls-check>
<label for=m-zh-cndocsadvancedtls-check><a href=/zh-cn/docs/advanced/tls/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedtls><span>TLS</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedtoken-authorization-li><input type=checkbox id=m-zh-cndocsadvancedtoken-authorization-check>
<label for=m-zh-cndocsadvancedtoken-authorization-check><a href=/zh-cn/docs/advanced/token-authorization/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedtoken-authorization><span>令牌验证</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedrouting-rule-li><input type=checkbox id=m-zh-cndocsadvancedrouting-rule-check>
<label for=m-zh-cndocsadvancedrouting-rule-check><a href=/zh-cn/docs/advanced/routing-rule/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedrouting-rule><span>路由规则</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedrouting-rule-deprecated-li><input type=checkbox id=m-zh-cndocsadvancedrouting-rule-deprecated-check>
<label for=m-zh-cndocsadvancedrouting-rule-deprecated-check><a href=/zh-cn/docs/advanced/routing-rule-deprecated/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedrouting-rule-deprecated><span>旧路由规则</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedconfig-rule-li><input type=checkbox id=m-zh-cndocsadvancedconfig-rule-check>
<label for=m-zh-cndocsadvancedconfig-rule-check><a href=/zh-cn/docs/advanced/config-rule/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedconfig-rule><span>配置规则</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedservice-downgrade-li><input type=checkbox id=m-zh-cndocsadvancedservice-downgrade-check>
<label for=m-zh-cndocsadvancedservice-downgrade-check><a href=/zh-cn/docs/advanced/service-downgrade/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedservice-downgrade><span>服务降级</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedconfig-rule-deprecated-li><input type=checkbox id=m-zh-cndocsadvancedconfig-rule-deprecated-check>
<label for=m-zh-cndocsadvancedconfig-rule-deprecated-check><a href=/zh-cn/docs/advanced/config-rule-deprecated/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedconfig-rule-deprecated><span>旧配置规则</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedconsumer-threadpool-li><input type=checkbox id=m-zh-cndocsadvancedconsumer-threadpool-check>
<label for=m-zh-cndocsadvancedconsumer-threadpool-check><a href=/zh-cn/docs/advanced/consumer-threadpool/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedconsumer-threadpool><span>消费端线程池模型</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedgraceful-shutdown-li><input type=checkbox id=m-zh-cndocsadvancedgraceful-shutdown-check>
<label for=m-zh-cndocsadvancedgraceful-shutdown-check><a href=/zh-cn/docs/advanced/graceful-shutdown/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedgraceful-shutdown><span>优雅停机</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedhostname-binding-li><input type=checkbox id=m-zh-cndocsadvancedhostname-binding-check>
<label for=m-zh-cndocsadvancedhostname-binding-check><a href=/zh-cn/docs/advanced/hostname-binding/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedhostname-binding><span>主机绑定</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedset-host-li><input type=checkbox id=m-zh-cndocsadvancedset-host-check>
<label for=m-zh-cndocsadvancedset-host-check><a href=/zh-cn/docs/advanced/set-host/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedset-host><span>主机配置</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedsimplify-registry-data-li><input type=checkbox id=m-zh-cndocsadvancedsimplify-registry-data-check>
<label for=m-zh-cndocsadvancedsimplify-registry-data-check><a href=/zh-cn/docs/advanced/simplify-registry-data/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedsimplify-registry-data><span>注册信息简化</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedlogger-strategy-li><input type=checkbox id=m-zh-cndocsadvancedlogger-strategy-check>
<label for=m-zh-cndocsadvancedlogger-strategy-check><a href=/zh-cn/docs/advanced/logger-strategy/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedlogger-strategy><span>日志适配</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedaccesslog-li><input type=checkbox id=m-zh-cndocsadvancedaccesslog-check>
<label for=m-zh-cndocsadvancedaccesslog-check><a href=/zh-cn/docs/advanced/accesslog/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedaccesslog><span>访问日志</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsadvancedservice-container-li><input type=checkbox id=m-zh-cndocsadvancedservice-container-check>
<label for=m-zh-cndocsadvancedservice-container-check><a href=/zh-cn/docs/advanced/service-container/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsadvancedservice-container><span>服务容器</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsmigration-li><input type=checkbox id=m-zh-cndocsmigration-check>
<label for=m-zh-cndocsmigration-check><a href=/zh-cn/docs/migration/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsmigration><span>升级与兼容性</span></a></label><ul class="ul-2 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsmigrationmigration-and-compatibility-guide-li><input type=checkbox id=m-zh-cndocsmigrationmigration-and-compatibility-guide-check>
<label for=m-zh-cndocsmigrationmigration-and-compatibility-guide-check><a href=/zh-cn/docs/migration/migration-and-compatibility-guide/ title="3.x 升级与兼容性指南" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsmigrationmigration-and-compatibility-guide><span>总结</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsmigrationmigration-service-discovery-li><input type=checkbox id=m-zh-cndocsmigrationmigration-service-discovery-check>
<label for=m-zh-cndocsmigrationmigration-service-discovery-check><a href=/zh-cn/docs/migration/migration-service-discovery/ title=应用级地址发现迁移指南 class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsmigrationmigration-service-discovery><span>应用级地址发现</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsmigrationmigration-triple-li><input type=checkbox id=m-zh-cndocsmigrationmigration-triple-check>
<label for=m-zh-cndocsmigrationmigration-triple-check><a href=/zh-cn/docs/migration/migration-triple/ title=Triple协议迁移指南 class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsmigrationmigration-triple><span>Triple</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsmigrationmigration-routingrule-li><input type=checkbox id=m-zh-cndocsmigrationmigration-routingrule-check>
<label for=m-zh-cndocsmigrationmigration-routingrule-check><a href=/zh-cn/docs/migration/migration-routingrule/ title=统一路由规则升级指南 class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsmigrationmigration-routingrule><span>路由规则</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocslanguages-li><input type=checkbox id=m-zh-cndocslanguages-check>
<label for=m-zh-cndocslanguages-check><a href=/zh-cn/docs/languages/ title=多语言实现 class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocslanguages><span>多语言</span></a></label><ul class="ul-2 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocslanguageserlang-li><input type=checkbox id=m-zh-cndocslanguageserlang-check>
<label for=m-zh-cndocslanguageserlang-check><a href=/zh-cn/docs/languages/erlang/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocslanguageserlang><span>Erlang</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguageserlangquick-start-li><input type=checkbox id=m-zh-cndocslanguageserlangquick-start-check>
<label for=m-zh-cndocslanguageserlangquick-start-check><a href=/zh-cn/docs/languages/erlang/quick-start/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguageserlangquick-start><span>快速开始</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguageserlangreference-li><input type=checkbox id=m-zh-cndocslanguageserlangreference-check>
<label for=m-zh-cndocslanguageserlangreference-check><a href=/zh-cn/docs/languages/erlang/reference/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguageserlangreference><span>消费者配置</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguageserlangservice-li><input type=checkbox id=m-zh-cndocslanguageserlangservice-check>
<label for=m-zh-cndocslanguageserlangservice-check><a href=/zh-cn/docs/languages/erlang/service/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguageserlangservice><span>提供者配置</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguageserlangserialization-li><input type=checkbox id=m-zh-cndocslanguageserlangserialization-check>
<label for=m-zh-cndocslanguageserlangserialization-check><a href=/zh-cn/docs/languages/erlang/serialization/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguageserlangserialization><span>序列化配置项</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocslanguagesgolang-li><input type=checkbox id=m-zh-cndocslanguagesgolang-check>
<label for=m-zh-cndocslanguagesgolang-check><a href=/zh-cn/docs/languages/golang/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocslanguagesgolang><span>golang</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangquick-start-li><input type=checkbox id=m-zh-cndocslanguagesgolangquick-start-check>
<label for=m-zh-cndocslanguagesgolangquick-start-check><a href=/zh-cn/docs/languages/golang/quick-start/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangquick-start><span>Go 快速开始</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolanggo-specific-li><input type=checkbox id=m-zh-cndocslanguagesgolanggo-specific-check>
<label for=m-zh-cndocslanguagesgolanggo-specific-check><a href=/zh-cn/docs/languages/golang/go-specific/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolanggo-specific><span>Go 语言定义服务</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocslanguagesgolangdubbo-go-15-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-15-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-15-check><a href=/zh-cn/docs/languages/golang/dubbo-go-1.5/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocslanguagesgolangdubbo-go-15><span>Dubbo-go 1.5</span></a></label><ul class="ul-4 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-15configurationclient-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-15configurationclient-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-15configurationclient-check><a href=/zh-cn/docs/languages/golang/dubbo-go-1.5/configuration/client/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-15configurationclient><span>client</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-15configurationprovider-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-15configurationprovider-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-15configurationprovider-check><a href=/zh-cn/docs/languages/golang/dubbo-go-1.5/configuration/provider/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-15configurationprovider><span>service providers</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-15quick-start-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-15quick-start-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-15quick-start-check><a href=/zh-cn/docs/languages/golang/dubbo-go-1.5/quick-start/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-15quick-start><span>快速开始</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocslanguagesgolangdubbo-go-30-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocslanguagesgolangdubbo-go-30><span>Dubbo-go 3.0</span></a></label><ul class="ul-4 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocslanguagesgolangdubbo-go-30samples-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30samples-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30samples-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/samples/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocslanguagesgolangdubbo-go-30samples><span>高级使用</span></a></label><ul class="ul-5 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30samplescustom-filter-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30samplescustom-filter-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30samplescustom-filter-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/samples/custom-filter/ title="Dubbo-go 3.0 自定义filter" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30samplescustom-filter><span>Filter</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesgo_java_interactive-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30samplesgo_java_interactive-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30samplesgo_java_interactive-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/samples/go_java_interactive/ title="go-java 3.0 互通示例" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesgo_java_interactive><span>Go-Java 互通</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesmetrics-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30samplesmetrics-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30samplesmetrics-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/samples/metrics/ title="Dubbo-go 数据上报" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesmetrics><span>Metrics 数据上报</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30samplessamples_repo-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30samplessamples_repo-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30samplessamples_repo-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/samples/samples_repo/ title="dubbo-go 3.0 示例仓库" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30samplessamples_repo><span>Samples 仓库介绍</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesexception_response-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30samplesexception_response-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30samplesexception_response-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/samples/exception_response/ title="Dubbo-go 3.0 异常回传" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesexception_response><span>Triple 异常回传</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesgeneric-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30samplesgeneric-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30samplesgeneric-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/samples/generic/ title="Dubbo-go 泛化调用" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesgeneric><span>泛化调用</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesmesh_router-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30samplesmesh_router-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30samplesmesh_router-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/samples/mesh_router/ title="Dubbo-go v3 统一路由规则" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesmesh_router><span>路由规则</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesconfig-center-dynamic-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30samplesconfig-center-dynamic-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30samplesconfig-center-dynamic-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/samples/config-center-dynamic/ title="Dubbo-go 3.0 动态配置" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesconfig-center-dynamic><span>配置中心和配置监听</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesstart-check-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30samplesstart-check-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30samplesstart-check-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/samples/start-check/ title="Dubbo-go 3.0 启动时检查" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesstart-check><span>启动时检查</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30samplescustom-logger-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30samplescustom-logger-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30samplescustom-logger-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/samples/custom-logger/ title="Dubbo-go 3.0 自定义日志" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30samplescustom-logger><span>日志</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesservice-discovery-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30samplesservice-discovery-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30samplesservice-discovery-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/samples/service-discovery/ title="Dubbo-go 3.0 应用级服务发现" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesservice-discovery><span>应用级服务发现</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesregistry-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30samplesregistry-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30samplesregistry-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/samples/registry/ title="Dubbo-go 3.0 注册中心" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30samplesregistry><span>注册中心</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocslanguagesgolangdubbo-go-30concept-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30concept-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30concept-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/concept/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocslanguagesgolangdubbo-go-30concept><span>基本概念</span></a></label><ul class="ul-5 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30conceptapp_and_interface-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30conceptapp_and_interface-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30conceptapp_and_interface-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/concept/app_and_interface/ title="Dubbo-go 的应用和接口" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30conceptapp_and_interface><span>服务层级</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30conceptconfiguration-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30conceptconfiguration-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30conceptconfiguration-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/concept/configuration/ title=Dubbo-go的配置 class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30conceptconfiguration><span>框架配置</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30conceptprotocol-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30conceptprotocol-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30conceptprotocol-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/concept/protocol/ title="Dubbo-go 的网络协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30conceptprotocol><span>网络协议</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30conceptregistry-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30conceptregistry-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30conceptregistry-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/concept/registry/ title="Dubbo-go 的注册中心" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30conceptregistry><span>注册中心</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocslanguagesgolangdubbo-go-30preface-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30preface-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30preface-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/preface/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocslanguagesgolangdubbo-go-30preface><span>简介</span></a></label><ul class="ul-5 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30preface30_feature-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30preface30_feature-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30preface30_feature-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/preface/3.0_feature/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30preface30_feature><span>dubbo-go 3.0 新特性</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30prefacearchitecture-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30prefacearchitecture-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30prefacearchitecture-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/preface/architecture/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30prefacearchitecture><span>架构</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocslanguagesgolangdubbo-go-30developer-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30developer-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30developer-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/developer/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocslanguagesgolangdubbo-go-30developer><span>开发者指南</span></a></label><ul class="ul-5 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30developerdesign-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30developerdesign-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30developerdesign-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/developer/design/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30developerdesign><span>架构设计</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocslanguagesgolangdubbo-go-30quickstart-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30quickstart-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30quickstart-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/quickstart/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocslanguagesgolangdubbo-go-30quickstart><span>快速开始</span></a></label><ul class="ul-5 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30quickstartquickstart_dubbo-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30quickstartquickstart_dubbo-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30quickstartquickstart_dubbo-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/quickstart/quickstart_dubbo/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30quickstartquickstart_dubbo><span>Dubbo-go 协议快速开始</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesgolangdubbo-go-30quickstartquickstart_triple-li><input type=checkbox id=m-zh-cndocslanguagesgolangdubbo-go-30quickstartquickstart_triple-check>
<label for=m-zh-cndocslanguagesgolangdubbo-go-30quickstartquickstart_triple-check><a href=/zh-cn/docs/languages/golang/dubbo-go-3.0/quickstart/quickstart_triple/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesgolangdubbo-go-30quickstartquickstart_triple><span>Triple 协议快速开始</span></a></label></li></ul></li></ul></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocslanguagesjava-li><input type=checkbox id=m-zh-cndocslanguagesjava-check>
<label for=m-zh-cndocslanguagesjava-check><a href=/zh-cn/docs/languages/java/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocslanguagesjava><span>java</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesjavajava-specific-li><input type=checkbox id=m-zh-cndocslanguagesjavajava-specific-check>
<label for=m-zh-cndocslanguagesjavajava-specific-check><a href=/zh-cn/docs/languages/java/java-specific/ title="Java 语言定义服务" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesjavajava-specific><span>Java 定义服务</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocslanguagesjavaquick-start-li><input type=checkbox id=m-zh-cndocslanguagesjavaquick-start-check>
<label for=m-zh-cndocslanguagesjavaquick-start-check><a href=/zh-cn/docs/languages/java/quick-start/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocslanguagesjavaquick-start><span>Java 快速开始</span></a></label></li></ul></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsreferences-li><input type=checkbox id=m-zh-cndocsreferences-check>
<label for=m-zh-cndocsreferences-check><a href=/zh-cn/docs/references/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsreferences><span>参考手册</span></a></label><ul class="ul-2 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsreferencesprotobuf-li><input type=checkbox id=m-zh-cndocsreferencesprotobuf-check>
<label for=m-zh-cndocsreferencesprotobuf-check><a href=/zh-cn/docs/references/protobuf/ title=Protobuf class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsreferencesprotobuf><span>protobuf</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesprotobufidl-li><input type=checkbox id=m-zh-cndocsreferencesprotobufidl-check>
<label for=m-zh-cndocsreferencesprotobufidl-check><a href=/zh-cn/docs/references/protobuf/idl/ title="使用 IDL 开发服务" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesprotobufidl><span>idl</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsreferencesxml-li><input type=checkbox id=m-zh-cndocsreferencesxml-check>
<label for=m-zh-cndocsreferencesxml-check><a href=/zh-cn/docs/references/xml/ title="Schema 配置参考手册" class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsreferencesxml><span>XML 配置</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesxmldubbo-application-li><input type=checkbox id=m-zh-cndocsreferencesxmldubbo-application-check>
<label for=m-zh-cndocsreferencesxmldubbo-application-check><a href=/zh-cn/docs/references/xml/dubbo-application/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesxmldubbo-application><span>dubbo:application</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesxmldubbo-argument-li><input type=checkbox id=m-zh-cndocsreferencesxmldubbo-argument-check>
<label for=m-zh-cndocsreferencesxmldubbo-argument-check><a href=/zh-cn/docs/references/xml/dubbo-argument/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesxmldubbo-argument><span>dubbo:argument</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesxmldubbo-config-center-li><input type=checkbox id=m-zh-cndocsreferencesxmldubbo-config-center-check>
<label for=m-zh-cndocsreferencesxmldubbo-config-center-check><a href=/zh-cn/docs/references/xml/dubbo-config-center/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesxmldubbo-config-center><span>dubbo:config-center</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesxmldubbo-consumer-li><input type=checkbox id=m-zh-cndocsreferencesxmldubbo-consumer-check>
<label for=m-zh-cndocsreferencesxmldubbo-consumer-check><a href=/zh-cn/docs/references/xml/dubbo-consumer/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesxmldubbo-consumer><span>dubbo:consumer</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesxmldubbo-method-li><input type=checkbox id=m-zh-cndocsreferencesxmldubbo-method-check>
<label for=m-zh-cndocsreferencesxmldubbo-method-check><a href=/zh-cn/docs/references/xml/dubbo-method/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesxmldubbo-method><span>dubbo:method</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesxmldubbo-module-li><input type=checkbox id=m-zh-cndocsreferencesxmldubbo-module-check>
<label for=m-zh-cndocsreferencesxmldubbo-module-check><a href=/zh-cn/docs/references/xml/dubbo-module/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesxmldubbo-module><span>dubbo:module</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesxmldubbo-monitor-li><input type=checkbox id=m-zh-cndocsreferencesxmldubbo-monitor-check>
<label for=m-zh-cndocsreferencesxmldubbo-monitor-check><a href=/zh-cn/docs/references/xml/dubbo-monitor/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesxmldubbo-monitor><span>dubbo:monitor</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesxmldubbo-parameter-li><input type=checkbox id=m-zh-cndocsreferencesxmldubbo-parameter-check>
<label for=m-zh-cndocsreferencesxmldubbo-parameter-check><a href=/zh-cn/docs/references/xml/dubbo-parameter/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesxmldubbo-parameter><span>dubbo:parameter</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesxmldubbo-protocol-li><input type=checkbox id=m-zh-cndocsreferencesxmldubbo-protocol-check>
<label for=m-zh-cndocsreferencesxmldubbo-protocol-check><a href=/zh-cn/docs/references/xml/dubbo-protocol/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesxmldubbo-protocol><span>dubbo:protocol</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesxmldubbo-provider-li><input type=checkbox id=m-zh-cndocsreferencesxmldubbo-provider-check>
<label for=m-zh-cndocsreferencesxmldubbo-provider-check><a href=/zh-cn/docs/references/xml/dubbo-provider/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesxmldubbo-provider><span>dubbo:provider</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesxmldubbo-reference-li><input type=checkbox id=m-zh-cndocsreferencesxmldubbo-reference-check>
<label for=m-zh-cndocsreferencesxmldubbo-reference-check><a href=/zh-cn/docs/references/xml/dubbo-reference/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesxmldubbo-reference><span>dubbo:reference</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesxmldubbo-registry-li><input type=checkbox id=m-zh-cndocsreferencesxmldubbo-registry-check>
<label for=m-zh-cndocsreferencesxmldubbo-registry-check><a href=/zh-cn/docs/references/xml/dubbo-registry/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesxmldubbo-registry><span>dubbo:registry</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesxmldubbo-service-li><input type=checkbox id=m-zh-cndocsreferencesxmldubbo-service-check>
<label for=m-zh-cndocsreferencesxmldubbo-service-check><a href=/zh-cn/docs/references/xml/dubbo-service/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesxmldubbo-service><span>dubbo:service</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesconfig-center-li><input type=checkbox id=m-zh-cndocsreferencesconfig-center-check>
<label for=m-zh-cndocsreferencesconfig-center-check><a href=/zh-cn/docs/references/config-center/ title=配置中心参考手册 class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesconfig-center><span>配置中心</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsreferencesregistry-li><input type=checkbox id=m-zh-cndocsreferencesregistry-check>
<label for=m-zh-cndocsreferencesregistry-check><a href=/zh-cn/docs/references/registry/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsreferencesregistry><span>注册中心参考手册</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesregistrynacos-li><input type=checkbox id=m-zh-cndocsreferencesregistrynacos-check>
<label for=m-zh-cndocsreferencesregistrynacos-check><a href=/zh-cn/docs/references/registry/nacos/ title="Nacos 注册中心" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesregistrynacos><span>Nacos</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesregistryzookeeper-li><input type=checkbox id=m-zh-cndocsreferencesregistryzookeeper-check>
<label for=m-zh-cndocsreferencesregistryzookeeper-check><a href=/zh-cn/docs/references/registry/zookeeper/ title="Zookeeper 注册中心" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesregistryzookeeper><span>Zookeeper</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesregistrymulticast-li><input type=checkbox id=m-zh-cndocsreferencesregistrymulticast-check>
<label for=m-zh-cndocsreferencesregistrymulticast-check><a href=/zh-cn/docs/references/registry/multicast/ title="Multicast 注册中心" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesregistrymulticast><span>Multicast</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesregistryredis-li><input type=checkbox id=m-zh-cndocsreferencesregistryredis-check>
<label for=m-zh-cndocsreferencesregistryredis-check><a href=/zh-cn/docs/references/registry/redis/ title="Redis 注册中心" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesregistryredis><span>Redis</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesregistrysimple-li><input type=checkbox id=m-zh-cndocsreferencesregistrysimple-check>
<label for=m-zh-cndocsreferencesregistrysimple-check><a href=/zh-cn/docs/references/registry/simple/ title="Simple 注册中心" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesregistrysimple><span>Simple</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsreferencesspis-li><input type=checkbox id=m-zh-cndocsreferencesspis-check>
<label for=m-zh-cndocsreferencesspis-check><a href=/zh-cn/docs/references/spis/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsreferencesspis><span>SPI 扩展实现</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisprotocol-li><input type=checkbox id=m-zh-cndocsreferencesspisprotocol-check>
<label for=m-zh-cndocsreferencesspisprotocol-check><a href=/zh-cn/docs/references/spis/protocol/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisprotocol><span>协议扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisfilter-li><input type=checkbox id=m-zh-cndocsreferencesspisfilter-check>
<label for=m-zh-cndocsreferencesspisfilter-check><a href=/zh-cn/docs/references/spis/filter/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisfilter><span>调用拦截扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisinvoker-listener-li><input type=checkbox id=m-zh-cndocsreferencesspisinvoker-listener-check>
<label for=m-zh-cndocsreferencesspisinvoker-listener-check><a href=/zh-cn/docs/references/spis/invoker-listener/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisinvoker-listener><span>引用监听扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisexporter-listener-li><input type=checkbox id=m-zh-cndocsreferencesspisexporter-listener-check>
<label for=m-zh-cndocsreferencesspisexporter-listener-check><a href=/zh-cn/docs/references/spis/exporter-listener/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisexporter-listener><span>暴露监听扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspiscluster-li><input type=checkbox id=m-zh-cndocsreferencesspiscluster-check>
<label for=m-zh-cndocsreferencesspiscluster-check><a href=/zh-cn/docs/references/spis/cluster/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspiscluster><span>集群扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisrouter-li><input type=checkbox id=m-zh-cndocsreferencesspisrouter-check>
<label for=m-zh-cndocsreferencesspisrouter-check><a href=/zh-cn/docs/references/spis/router/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisrouter><span>路由扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisload-balance-li><input type=checkbox id=m-zh-cndocsreferencesspisload-balance-check>
<label for=m-zh-cndocsreferencesspisload-balance-check><a href=/zh-cn/docs/references/spis/load-balance/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisload-balance><span>负载均衡扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspismerger-li><input type=checkbox id=m-zh-cndocsreferencesspismerger-check>
<label for=m-zh-cndocsreferencesspismerger-check><a href=/zh-cn/docs/references/spis/merger/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspismerger><span>合并结果扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisregistry-li><input type=checkbox id=m-zh-cndocsreferencesspisregistry-check>
<label for=m-zh-cndocsreferencesspisregistry-check><a href=/zh-cn/docs/references/spis/registry/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisregistry><span>注册中心扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspismonitor-li><input type=checkbox id=m-zh-cndocsreferencesspismonitor-check>
<label for=m-zh-cndocsreferencesspismonitor-check><a href=/zh-cn/docs/references/spis/monitor/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspismonitor><span>监控中心扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisextension-factory-li><input type=checkbox id=m-zh-cndocsreferencesspisextension-factory-check>
<label for=m-zh-cndocsreferencesspisextension-factory-check><a href=/zh-cn/docs/references/spis/extension-factory/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisextension-factory><span>扩展点加载扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisproxy-factory-li><input type=checkbox id=m-zh-cndocsreferencesspisproxy-factory-check>
<label for=m-zh-cndocsreferencesspisproxy-factory-check><a href=/zh-cn/docs/references/spis/proxy-factory/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisproxy-factory><span>动态代理扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspiscompiler-li><input type=checkbox id=m-zh-cndocsreferencesspiscompiler-check>
<label for=m-zh-cndocsreferencesspiscompiler-check><a href=/zh-cn/docs/references/spis/compiler/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspiscompiler><span>编译器扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisconfig-center-li><input type=checkbox id=m-zh-cndocsreferencesspisconfig-center-check>
<label for=m-zh-cndocsreferencesspisconfig-center-check><a href=/zh-cn/docs/references/spis/config-center/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisconfig-center><span>配置中心扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisdispatcher-li><input type=checkbox id=m-zh-cndocsreferencesspisdispatcher-check>
<label for=m-zh-cndocsreferencesspisdispatcher-check><a href=/zh-cn/docs/references/spis/dispatcher/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisdispatcher><span>消息派发扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisthreadpool-li><input type=checkbox id=m-zh-cndocsreferencesspisthreadpool-check>
<label for=m-zh-cndocsreferencesspisthreadpool-check><a href=/zh-cn/docs/references/spis/threadpool/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisthreadpool><span>线程池扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisserialize-li><input type=checkbox id=m-zh-cndocsreferencesspisserialize-check>
<label for=m-zh-cndocsreferencesspisserialize-check><a href=/zh-cn/docs/references/spis/serialize/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisserialize><span>序列化扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisremoting-li><input type=checkbox id=m-zh-cndocsreferencesspisremoting-check>
<label for=m-zh-cndocsreferencesspisremoting-check><a href=/zh-cn/docs/references/spis/remoting/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisremoting><span>网络传输扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisexchanger-li><input type=checkbox id=m-zh-cndocsreferencesspisexchanger-check>
<label for=m-zh-cndocsreferencesspisexchanger-check><a href=/zh-cn/docs/references/spis/exchanger/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisexchanger><span>信息交换扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspispage-li><input type=checkbox id=m-zh-cndocsreferencesspispage-check>
<label for=m-zh-cndocsreferencesspispage-check><a href=/zh-cn/docs/references/spis/page/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspispage><span>对等网络节点组网器扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisnetworker-li><input type=checkbox id=m-zh-cndocsreferencesspisnetworker-check>
<label for=m-zh-cndocsreferencesspisnetworker-check><a href=/zh-cn/docs/references/spis/networker/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisnetworker><span>组网扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspistelnet-handler-li><input type=checkbox id=m-zh-cndocsreferencesspistelnet-handler-check>
<label for=m-zh-cndocsreferencesspistelnet-handler-check><a href=/zh-cn/docs/references/spis/telnet-handler/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspistelnet-handler><span>Telnet 命令扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisstatus-checker-li><input type=checkbox id=m-zh-cndocsreferencesspisstatus-checker-check>
<label for=m-zh-cndocsreferencesspisstatus-checker-check><a href=/zh-cn/docs/references/spis/status-checker/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisstatus-checker><span>状态检查扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspiscontainer-li><input type=checkbox id=m-zh-cndocsreferencesspiscontainer-check>
<label for=m-zh-cndocsreferencesspiscontainer-check><a href=/zh-cn/docs/references/spis/container/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspiscontainer><span>容器扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspiscache-li><input type=checkbox id=m-zh-cndocsreferencesspiscache-check>
<label for=m-zh-cndocsreferencesspiscache-check><a href=/zh-cn/docs/references/spis/cache/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspiscache><span>缓存扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisvalidation-li><input type=checkbox id=m-zh-cndocsreferencesspisvalidation-check>
<label for=m-zh-cndocsreferencesspisvalidation-check><a href=/zh-cn/docs/references/spis/validation/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisvalidation><span>验证扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspislogger-adapter-li><input type=checkbox id=m-zh-cndocsreferencesspislogger-adapter-check>
<label for=m-zh-cndocsreferencesspislogger-adapter-check><a href=/zh-cn/docs/references/spis/logger-adapter/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspislogger-adapter><span>日志适配扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesspisdubbo-spi-li><input type=checkbox id=m-zh-cndocsreferencesspisdubbo-spi-check>
<label for=m-zh-cndocsreferencesspisdubbo-spi-check><a href=/zh-cn/docs/references/spis/dubbo-spi/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesspisdubbo-spi><span>扩展点开发指南</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsreferencesconfiguration-li><input type=checkbox id=m-zh-cndocsreferencesconfiguration-check>
<label for=m-zh-cndocsreferencesconfiguration-check><a href=/zh-cn/docs/references/configuration/ title="Dubbo 配置" class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsreferencesconfiguration><span>配置</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesconfigurationoverview-li><input type=checkbox id=m-zh-cndocsreferencesconfigurationoverview-check>
<label for=m-zh-cndocsreferencesconfigurationoverview-check><a href=/zh-cn/docs/references/configuration/overview/ title=配置概述 class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesconfigurationoverview><span>概述</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesconfigurationapi-li><input type=checkbox id=m-zh-cndocsreferencesconfigurationapi-check>
<label for=m-zh-cndocsreferencesconfigurationapi-check><a href=/zh-cn/docs/references/configuration/api/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesconfigurationapi><span>API 配置</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesconfigurationxml-li><input type=checkbox id=m-zh-cndocsreferencesconfigurationxml-check>
<label for=m-zh-cndocsreferencesconfigurationxml-check><a href=/zh-cn/docs/references/configuration/xml/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesconfigurationxml><span>XML 配置</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesconfigurationannotation-li><input type=checkbox id=m-zh-cndocsreferencesconfigurationannotation-check>
<label for=m-zh-cndocsreferencesconfigurationannotation-check><a href=/zh-cn/docs/references/configuration/annotation/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesconfigurationannotation><span>注解配置</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesconfigurationproperties-li><input type=checkbox id=m-zh-cndocsreferencesconfigurationproperties-check>
<label for=m-zh-cndocsreferencesconfigurationproperties-check><a href=/zh-cn/docs/references/configuration/properties/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesconfigurationproperties><span>属性配置</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesconfigurationexternal-config-li><input type=checkbox id=m-zh-cndocsreferencesconfigurationexternal-config-check>
<label for=m-zh-cndocsreferencesconfigurationexternal-config-check><a href=/zh-cn/docs/references/configuration/external-config/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesconfigurationexternal-config><span>外部化配置</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesconfigurationenvironment-variables-li><input type=checkbox id=m-zh-cndocsreferencesconfigurationenvironment-variables-check>
<label for=m-zh-cndocsreferencesconfigurationenvironment-variables-check><a href=/zh-cn/docs/references/configuration/environment-variables/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesconfigurationenvironment-variables><span>自动加载环境变量</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesmetadata-li><input type=checkbox id=m-zh-cndocsreferencesmetadata-check>
<label for=m-zh-cndocsreferencesmetadata-check><a href=/zh-cn/docs/references/metadata/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesmetadata><span>元数据参考手册</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesapi-li><input type=checkbox id=m-zh-cndocsreferencesapi-check>
<label for=m-zh-cndocsreferencesapi-check><a href=/zh-cn/docs/references/api/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesapi><span>API 参考手册</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsreferenceslifecycle-li><input type=checkbox id=m-zh-cndocsreferenceslifecycle-check>
<label for=m-zh-cndocsreferenceslifecycle-check><a href=/zh-cn/docs/references/lifecycle/ title="Kubernetes 生命周期对齐探针" class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsreferenceslifecycle><span>探针</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferenceslifecyclebrief-li><input type=checkbox id=m-zh-cndocsreferenceslifecyclebrief-check>
<label for=m-zh-cndocsreferenceslifecyclebrief-check><a href=/zh-cn/docs/references/lifecycle/brief/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferenceslifecyclebrief><span>使用方法</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferenceslifecycleliveness-li><input type=checkbox id=m-zh-cndocsreferenceslifecycleliveness-check>
<label for=m-zh-cndocsreferenceslifecycleliveness-check><a href=/zh-cn/docs/references/lifecycle/liveness/ title="Liveness 存活探针" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferenceslifecycleliveness><span>存活探针</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferenceslifecyclereadiness-li><input type=checkbox id=m-zh-cndocsreferenceslifecyclereadiness-check>
<label for=m-zh-cndocsreferenceslifecyclereadiness-check><a href=/zh-cn/docs/references/lifecycle/readiness/ title="Readiness 就绪探针" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferenceslifecyclereadiness><span>就绪探针</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferenceslifecyclestartup-li><input type=checkbox id=m-zh-cndocsreferenceslifecyclestartup-check>
<label for=m-zh-cndocsreferenceslifecyclestartup-check><a href=/zh-cn/docs/references/lifecycle/startup/ title="Startup 启动探针" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferenceslifecyclestartup><span>启动探针</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferenceslifecyclerest-li><input type=checkbox id=m-zh-cndocsreferenceslifecyclerest-check>
<label for=m-zh-cndocsreferenceslifecyclerest-check><a href=/zh-cn/docs/references/lifecycle/rest/ title="开发 REST 应用" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferenceslifecyclerest><span>REST 支持</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesqos-li><input type=checkbox id=m-zh-cndocsreferencesqos-check>
<label for=m-zh-cndocsreferencesqos-check><a href=/zh-cn/docs/references/qos/ title=在线运维命令参考手册 class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesqos><span>QOS 手册</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencestelnet-li><input type=checkbox id=m-zh-cndocsreferencestelnet-check>
<label for=m-zh-cndocsreferencestelnet-check><a href=/zh-cn/docs/references/telnet/ title="Telnet 命令参考手册" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencestelnet><span>Telnet 手册</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesmaven-li><input type=checkbox id=m-zh-cndocsreferencesmaven-check>
<label for=m-zh-cndocsreferencesmaven-check><a href=/zh-cn/docs/references/maven/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesmaven><span>Maven 插件参考手册</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsreferencesprotocols-li><input type=checkbox id=m-zh-cndocsreferencesprotocols-check>
<label for=m-zh-cndocsreferencesprotocols-check><a href=/zh-cn/docs/references/protocols/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsreferencesprotocols><span>RPC协议</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesprotocolsdubbo-li><input type=checkbox id=m-zh-cndocsreferencesprotocolsdubbo-check>
<label for=m-zh-cndocsreferencesprotocolsdubbo-check><a href=/zh-cn/docs/references/protocols/dubbo/ title="dubbo 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesprotocolsdubbo><span>dubbo://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesprotocolshttp-li><input type=checkbox id=m-zh-cndocsreferencesprotocolshttp-check>
<label for=m-zh-cndocsreferencesprotocolshttp-check><a href=/zh-cn/docs/references/protocols/http/ title="http 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesprotocolshttp><span>http://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesprotocolshessian-li><input type=checkbox id=m-zh-cndocsreferencesprotocolshessian-check>
<label for=m-zh-cndocsreferencesprotocolshessian-check><a href=/zh-cn/docs/references/protocols/hessian/ title="hessian 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesprotocolshessian><span>hessian://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesprotocolsredis-li><input type=checkbox id=m-zh-cndocsreferencesprotocolsredis-check>
<label for=m-zh-cndocsreferencesprotocolsredis-check><a href=/zh-cn/docs/references/protocols/redis/ title="redis 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesprotocolsredis><span>redis://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesprotocolsthrift-li><input type=checkbox id=m-zh-cndocsreferencesprotocolsthrift-check>
<label for=m-zh-cndocsreferencesprotocolsthrift-check><a href=/zh-cn/docs/references/protocols/thrift/ title="thrift 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesprotocolsthrift><span>thrift://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesprotocolsgrpc-li><input type=checkbox id=m-zh-cndocsreferencesprotocolsgrpc-check>
<label for=m-zh-cndocsreferencesprotocolsgrpc-check><a href=/zh-cn/docs/references/protocols/grpc/ title="gRPC 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesprotocolsgrpc><span>grpc://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesprotocolsmemcached-li><input type=checkbox id=m-zh-cndocsreferencesprotocolsmemcached-check>
<label for=m-zh-cndocsreferencesprotocolsmemcached-check><a href=/zh-cn/docs/references/protocols/memcached/ title="memcached 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesprotocolsmemcached><span>memcached://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesprotocolsrmi-li><input type=checkbox id=m-zh-cndocsreferencesprotocolsrmi-check>
<label for=m-zh-cndocsreferencesprotocolsrmi-check><a href=/zh-cn/docs/references/protocols/rmi/ title="rmi 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesprotocolsrmi><span>rmi://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesprotocolswebservice-li><input type=checkbox id=m-zh-cndocsreferencesprotocolswebservice-check>
<label for=m-zh-cndocsreferencesprotocolswebservice-check><a href=/zh-cn/docs/references/protocols/webservice/ title="webservice 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesprotocolswebservice><span>webservice://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesprotocolstri-li><input type=checkbox id=m-zh-cndocsreferencesprotocolstri-check>
<label for=m-zh-cndocsreferencesprotocolstri-check><a href=/zh-cn/docs/references/protocols/tri/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesprotocolstri><span>Triple 协议</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesprotocolsrest-li><input type=checkbox id=m-zh-cndocsreferencesprotocolsrest-check>
<label for=m-zh-cndocsreferencesprotocolsrest-check><a href=/zh-cn/docs/references/protocols/rest/ title="开发 REST 应用" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesprotocolsrest><span>REST 支持</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsreferencesgraalvm-li><input type=checkbox id=m-zh-cndocsreferencesgraalvm-check>
<label for=m-zh-cndocsreferencesgraalvm-check><a href=/zh-cn/docs/references/graalvm/ title=支持graalvm class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsreferencesgraalvm><span>support graalvm</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesgraalvmsupport-graalvm-li><input type=checkbox id=m-zh-cndocsreferencesgraalvmsupport-graalvm-check>
<label for=m-zh-cndocsreferencesgraalvmsupport-graalvm-check><a href=/zh-cn/docs/references/graalvm/support-graalvm/ title="使用 GraalVM 打包 Native Image" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesgraalvmsupport-graalvm><span>Native Image</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsreferencesrouters-li><input type=checkbox id=m-zh-cndocsreferencesrouters-check>
<label for=m-zh-cndocsreferencesrouters-check><a href=/zh-cn/docs/references/routers/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsreferencesrouters><span>路由规则</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesroutersvirtualservice-li><input type=checkbox id=m-zh-cndocsreferencesroutersvirtualservice-check>
<label for=m-zh-cndocsreferencesroutersvirtualservice-check><a href=/zh-cn/docs/references/routers/virtualservice/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesroutersvirtualservice><span>VirtualService</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsreferencesroutersdestination-rule-li><input type=checkbox id=m-zh-cndocsreferencesroutersdestination-rule-check>
<label for=m-zh-cndocsreferencesroutersdestination-rule-check><a href=/zh-cn/docs/references/routers/destination-rule/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsreferencesroutersdestination-rule><span>DestinationRule</span></a></label></li></ul></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsperformance-li><input type=checkbox id=m-zh-cndocsperformance-check>
<label for=m-zh-cndocsperformance-check><a href=/zh-cn/docs/performance/ title="性能优化 & 基准测试" class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsperformance><span>性能优化</span></a></label><ul class="ul-2 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsperformancebenchmarking-li><input type=checkbox id=m-zh-cndocsperformancebenchmarking-check>
<label for=m-zh-cndocsperformancebenchmarking-check><a href=/zh-cn/docs/performance/benchmarking/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsperformancebenchmarking><span>基准测试</span></a></label></li></ul></li></ul></li></ul></nav></div></div><main class="col-12 col-md-9 col-xl-8 pl-md-5" role=main><nav aria-label=breadcrumb class=td-breadcrumbs><ol class=breadcrumb><li class=breadcrumb-item><a href=https://dubbo.apache.org/zh-cn/docs/>文档</a></li><li class=breadcrumb-item><a href=https://dubbo.apache.org/zh-cn/docs/references/>参考手册</a></li><li class=breadcrumb-item><a href=https://dubbo.apache.org/zh-cn/docs/references/spis/>SPI 扩展实现</a></li><li class="breadcrumb-item active" aria-current=page><a href=https://dubbo.apache.org/zh-cn/docs/references/spis/dubbo-spi/ aria-disabled=true class="btn-link disabled">扩展点开发指南</a></li></ol></nav><div class=td-content><h1>扩展点开发指南</h1><div class=lead>本文介绍了 Dubbo SPI 的原理和实现细节</div><header class=article-meta></header><div class="pageinfo pageinfo-primary"><p>此文档已经不再维护。您当前查看的是快照版本。如果想要查看最新版本的文档,请参阅<a href=/zh-cn/overview/mannual/java-sdk/reference-manual/spi/overview/>最新版本</a></p></div><h2 id=1简介>1.简介</h2><p>SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。SPI 机制在第三方框架中也有所应用,比如 Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。在 Dubbo 中,SPI 是一个非常重要的模块。基于 SPI,我们可以很容易的对 Dubbo 进行拓展。
Dubbo 中,SPI 主要有两种用法,一种是加载固定的扩展类,另一种是加载自适应扩展类。这两种方式会在下面详细的介绍。
需要特别注意的是: 在 Dubbo 中,基于 SPI 扩展加载的类是单例的。</p><h3 id=11-加载固定的扩展类>1.1 加载固定的扩展类</h3><p>如果让你来设计加载固定扩展类,你会怎么做了?
一种常见思路是读取特定目录下的配置文件,然后解析出全类名,通过反射机制来实例化这个类,然后将这个类放在集合中存起来,如果有需要的时候,直接从集合中取。Dubbo 中的实现也是这么一个思路。
不过在 Dubbo 中,实现的更加完善,它实现类了 IOC 和 AOP 的功能。IOC 就是说如果这个扩展类依赖其他属性,Dubbo 会自动的将这个属性进行注入。这个功能如何实现了?一个常见思路是获取这个扩展类的 setter
方法,调用 setter 方法进行属性注入。AOP 指的是什么了?这个说的是 Dubbo 能够为扩展类注入其包装类。比如 DubboProtocol 是 Protocol 的扩展类,ProtocolListenerWrapper 是 DubboProtocol 的包装类。</p><h3 id=12-加载自适应扩展类>1.2 加载自适应扩展类</h3><p>先说明下自适应扩展类的使用场景。比如我们有需求,在调用某一个方法时,基于参数选择调用到不同的实现类。和工厂方法有些类似,基于不同的参数,构造出不同的实例对象。
在 Dubbo 中实现的思路和这个差不多,不过 Dubbo 的实现更加灵活,它的实现和策略模式有些类似。每一种扩展类相当于一种策略,基于 URL 消息总线,将参数传递给 ExtensionLoader,通过 ExtensionLoader 基于参数加载对应的扩展类,实现运行时动态调用到目标实例上。</p><h2 id=2-dubbo-spi-源码分析>2. Dubbo SPI 源码分析</h2><h3 id=21-加载固定的扩展类>2.1 加载固定的扩展类</h3><p>Dubbo 中,SPI 加载固定扩展类的入口是 ExtensionLoader 的 getExtension 方法,下面我们对拓展类对象的获取过程进行详细的分析。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>public</span> T <span style=color:#268bd2>getExtension</span><span style=color:#719e07>(</span>String name<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>name <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>||</span> name<span style=color:#719e07>.</span>length<span style=color:#719e07>()</span> <span style=color:#719e07>==</span> 0<span style=color:#719e07>)</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalArgumentException<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;Extension name == null&#34;</span><span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span><span style=color:#2aa198>&#34;true&#34;</span><span style=color:#719e07>.</span>equals<span style=color:#719e07>(</span>name<span style=color:#719e07>))</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取默认的拓展实现类
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>return</span> getDefaultExtension<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// Holder,顾名思义,用于持有目标对象
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> Holder<span style=color:#719e07>&lt;</span>Object<span style=color:#719e07>&gt;</span> holder <span style=color:#719e07>=</span> cachedInstances<span style=color:#719e07>.</span>get<span style=color:#719e07>(</span>name<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 这段逻辑保证了只有一个线程能够创建 Holder 对象
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>holder <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> cachedInstances<span style=color:#719e07>.</span>putIfAbsent<span style=color:#719e07>(</span>name<span style=color:#719e07>,</span> <span style=color:#719e07>new</span> Holder<span style=color:#719e07>&lt;</span>Object<span style=color:#719e07>&gt;());</span>
</span></span><span style=display:flex><span> holder <span style=color:#719e07>=</span> cachedInstances<span style=color:#719e07>.</span>get<span style=color:#719e07>(</span>name<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> Object instance <span style=color:#719e07>=</span> holder<span style=color:#719e07>.</span>get<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 双重检查
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>instance <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>synchronized</span> <span style=color:#719e07>(</span>holder<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> instance <span style=color:#719e07>=</span> holder<span style=color:#719e07>.</span>get<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>instance <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 创建拓展实例
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> instance <span style=color:#719e07>=</span> createExtension<span style=color:#719e07>(</span>name<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 设置实例到 holder 中
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> holder<span style=color:#719e07>.</span>set<span style=color:#719e07>(</span>instance<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> <span style=color:#719e07>(</span>T<span style=color:#719e07>)</span> instance<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><p>上面代码的逻辑比较简单,首先检查缓存,缓存未命中则创建拓展对象。下面我们来看一下创建拓展对象的过程是怎样的。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> T <span style=color:#268bd2>createExtension</span><span style=color:#719e07>(</span>String name<span style=color:#719e07>,</span> <span style=color:#dc322f>boolean</span> wrap<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 从配置文件中加载所有的拓展类,可得到“配置项名称”到“配置类”的映射关系表
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> Class<span style=color:#719e07>&lt;?&gt;</span> clazz <span style=color:#719e07>=</span> getExtensionClasses<span style=color:#719e07>().</span>get<span style=color:#719e07>(</span>name<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 如果没有该接口的扩展,或者该接口的实现类不允许重复但实际上重复了,直接抛出异常
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>clazz <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>||</span> unacceptableExceptions<span style=color:#719e07>.</span>contains<span style=color:#719e07>(</span>name<span style=color:#719e07>))</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> findException<span style=color:#719e07>(</span>name<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>try</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> T instance <span style=color:#719e07>=</span> <span style=color:#719e07>(</span>T<span style=color:#719e07>)</span> EXTENSION_INSTANCES<span style=color:#719e07>.</span>get<span style=color:#719e07>(</span>clazz<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 这段代码保证了扩展类只会被构造一次,也就是单例的.
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>instance <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> EXTENSION_INSTANCES<span style=color:#719e07>.</span>putIfAbsent<span style=color:#719e07>(</span>clazz<span style=color:#719e07>,</span> clazz<span style=color:#719e07>.</span>getDeclaredConstructor<span style=color:#719e07>().</span>newInstance<span style=color:#719e07>());</span>
</span></span><span style=display:flex><span> instance <span style=color:#719e07>=</span> <span style=color:#719e07>(</span>T<span style=color:#719e07>)</span> EXTENSION_INSTANCES<span style=color:#719e07>.</span>get<span style=color:#719e07>(</span>clazz<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 向实例中注入依赖
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> injectExtension<span style=color:#719e07>(</span>instance<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 如果启用包装的话,则自动为进行包装.
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#586e75>// 比如我基于 Protocol 定义了 DubboProtocol 的扩展,但实际上在 Dubbo 中不是直接使用的 DubboProtocol, 而是其包装类
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#586e75>// ProtocolListenerWrapper
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>wrap<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> List<span style=color:#719e07>&lt;</span>Class<span style=color:#719e07>&lt;?&gt;&gt;</span> wrapperClassesList <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> ArrayList<span style=color:#719e07>&lt;&gt;();</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>cachedWrapperClasses <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> wrapperClassesList<span style=color:#719e07>.</span>addAll<span style=color:#719e07>(</span>cachedWrapperClasses<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> wrapperClassesList<span style=color:#719e07>.</span>sort<span style=color:#719e07>(</span>WrapperComparator<span style=color:#719e07>.</span>COMPARATOR<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> Collections<span style=color:#719e07>.</span>reverse<span style=color:#719e07>(</span>wrapperClassesList<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 循环创建 Wrapper 实例
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>CollectionUtils<span style=color:#719e07>.</span>isNotEmpty<span style=color:#719e07>(</span>wrapperClassesList<span style=color:#719e07>))</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> <span style=color:#719e07>(</span>Class<span style=color:#719e07>&lt;?&gt;</span> wrapperClass <span style=color:#719e07>:</span> wrapperClassesList<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> Wrapper wrapper <span style=color:#719e07>=</span> wrapperClass<span style=color:#719e07>.</span>getAnnotation<span style=color:#719e07>(</span>Wrapper<span style=color:#719e07>.</span>class<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>wrapper <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>||</span> <span style=color:#719e07>(</span>ArrayUtils<span style=color:#719e07>.</span>contains<span style=color:#719e07>(</span>wrapper<span style=color:#719e07>.</span>matches<span style=color:#719e07>(),</span> name<span style=color:#719e07>)</span> <span style=color:#719e07>&amp;&amp;</span> <span style=color:#719e07>!</span>ArrayUtils<span style=color:#719e07>.</span>contains<span style=color:#719e07>(</span>wrapper<span style=color:#719e07>.</span>mismatches<span style=color:#719e07>(),</span> name<span style=color:#719e07>)))</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 将当前 instance 作为参数传给 Wrapper 的构造方法,并通过反射创建 Wrapper 实例。
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#586e75>// 然后向 Wrapper 实例中注入依赖,最后将 Wrapper 实例再次赋值给 instance 变量
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> instance <span style=color:#719e07>=</span> injectExtension<span style=color:#719e07>((</span>T<span style=color:#719e07>)</span> wrapperClass<span style=color:#719e07>.</span>getConstructor<span style=color:#719e07>(</span>type<span style=color:#719e07>).</span>newInstance<span style=color:#719e07>(</span>instance<span style=color:#719e07>));</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 初始化
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> initExtension<span style=color:#719e07>(</span>instance<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> instance<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span> <span style=color:#719e07>catch</span> <span style=color:#719e07>(</span>Throwable t<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;Extension instance (name: &#34;</span> <span style=color:#719e07>+</span> name <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;, class: &#34;</span> <span style=color:#719e07>+</span>
</span></span><span style=display:flex><span> type <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;) couldn&#39;t be instantiated: &#34;</span> <span style=color:#719e07>+</span> t<span style=color:#719e07>.</span>getMessage<span style=color:#719e07>(),</span> t<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><p>createExtension 方法的逻辑稍复杂一下,包含了如下的步骤:</p><ol><li>通过 getExtensionClasses 获取所有的拓展类</li><li>通过反射创建拓展对象</li><li>向拓展对象中注入依赖</li><li>将拓展对象包裹在相应的 Wrapper 对象中</li><li>初始化拓展对象</li></ol><p>以上步骤中,第一个步骤是加载拓展类的关键,第三和第四个步骤是 Dubbo IOC 与 AOP 的具体实现。在接下来的章节中,将会重点分析 getExtensionClasses 方法的逻辑,以及简单介绍 Dubbo IOC 的具体实现。</p><h3 id=211-获取所有的拓展类>2.1.1 获取所有的拓展类</h3><p>我们在通过名称获取拓展类之前,首先需要根据配置文件解析出拓展项名称到拓展类的映射关系表(Map&lt;名称, 拓展类>),之后再根据拓展项名称从映射关系表中取出相应的拓展类即可。相关过程的代码分析如下:</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> Map<span style=color:#719e07>&lt;</span>String<span style=color:#719e07>,</span> Class<span style=color:#719e07>&lt;?&gt;&gt;</span> getExtensionClasses<span style=color:#719e07>()</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 从缓存中获取已加载的拓展类
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> Map<span style=color:#719e07>&lt;</span>String<span style=color:#719e07>,</span> Class<span style=color:#719e07>&lt;?&gt;&gt;</span> classes <span style=color:#719e07>=</span> cachedClasses<span style=color:#719e07>.</span>get<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 双重检查
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>classes <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>synchronized</span> <span style=color:#719e07>(</span>cachedClasses<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> classes <span style=color:#719e07>=</span> cachedClasses<span style=color:#719e07>.</span>get<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>classes <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 加载拓展类
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> classes <span style=color:#719e07>=</span> loadExtensionClasses<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> cachedClasses<span style=color:#719e07>.</span>set<span style=color:#719e07>(</span>classes<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> classes<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><p>这里也是先检查缓存,若缓存未命中,则通过 synchronized 加锁。加锁后再次检查缓存,并判空。此时如果 classes 仍为 null,则通过 loadExtensionClasses 加载拓展类。下面分析 loadExtensionClasses 方法的逻辑。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> Map<span style=color:#719e07>&lt;</span>String<span style=color:#719e07>,</span> Class<span style=color:#719e07>&lt;?&gt;&gt;</span> loadExtensionClasses<span style=color:#719e07>()</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 缓存默认的 SPI 扩展名
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> cacheDefaultExtensionName<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> Map<span style=color:#719e07>&lt;</span>String<span style=color:#719e07>,</span> Class<span style=color:#719e07>&lt;?&gt;&gt;</span> extensionClasses <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> HashMap<span style=color:#719e07>&lt;&gt;();</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 基于策略来加载指定文件夹下的文件
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#586e75>// 目前有四种策略,分别读取 META-INF/services/ META-INF/dubbo/ META-INF/dubbo/internal/ META-INF/dubbo/external/ 这四个目录下的配置文件
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>for</span> <span style=color:#719e07>(</span>LoadingStrategy strategy <span style=color:#719e07>:</span> strategies<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> loadDirectory<span style=color:#719e07>(</span>extensionClasses<span style=color:#719e07>,</span> strategy<span style=color:#719e07>.</span>directory<span style=color:#719e07>(),</span> type<span style=color:#719e07>.</span>getName<span style=color:#719e07>(),</span> strategy<span style=color:#719e07>.</span>preferExtensionClassLoader<span style=color:#719e07>(),</span> strategy<span style=color:#719e07>.</span>overridden<span style=color:#719e07>(),</span> strategy<span style=color:#719e07>.</span>excludedPackages<span style=color:#719e07>());</span>
</span></span><span style=display:flex><span> loadDirectory<span style=color:#719e07>(</span>extensionClasses<span style=color:#719e07>,</span> strategy<span style=color:#719e07>.</span>directory<span style=color:#719e07>(),</span> type<span style=color:#719e07>.</span>getName<span style=color:#719e07>().</span>replace<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;org.apache&#34;</span><span style=color:#719e07>,</span> <span style=color:#2aa198>&#34;com.alibaba&#34;</span><span style=color:#719e07>),</span> strategy<span style=color:#719e07>.</span>preferExtensionClassLoader<span style=color:#719e07>(),</span> strategy<span style=color:#719e07>.</span>overridden<span style=color:#719e07>(),</span> strategy<span style=color:#719e07>.</span>excludedPackages<span style=color:#719e07>());</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> extensionClasses<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><p>loadExtensionClasses 方法总共做了两件事情,一是对 SPI 注解进行解析,二是调用 loadDirectory 方法加载指定文件夹配置文件。SPI 注解解析过程比较简单,无需多说。下面我们来看一下 loadDirectory 做了哪些事情。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> <span style=color:#dc322f>void</span> <span style=color:#268bd2>loadDirectory</span><span style=color:#719e07>(</span>Map<span style=color:#719e07>&lt;</span>String<span style=color:#719e07>,</span> Class<span style=color:#719e07>&lt;?&gt;&gt;</span> extensionClasses<span style=color:#719e07>,</span> String dir<span style=color:#719e07>,</span> String type<span style=color:#719e07>,</span>
</span></span><span style=display:flex><span> <span style=color:#dc322f>boolean</span> extensionLoaderClassLoaderFirst<span style=color:#719e07>,</span> <span style=color:#dc322f>boolean</span> overridden<span style=color:#719e07>,</span> String<span style=color:#719e07>...</span> excludedPackages<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// fileName = 文件夹路径 + type 全限定名
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> String fileName <span style=color:#719e07>=</span> dir <span style=color:#719e07>+</span> type<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>try</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> Enumeration<span style=color:#719e07>&lt;</span>java<span style=color:#719e07>.</span>net<span style=color:#719e07>.</span>URL<span style=color:#719e07>&gt;</span> urls <span style=color:#719e07>=</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>;</span>
</span></span><span style=display:flex><span> ClassLoader classLoader <span style=color:#719e07>=</span> findClassLoader<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// try to load from ExtensionLoader&#39;s ClassLoader first
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>extensionLoaderClassLoaderFirst<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> ClassLoader extensionLoaderClassLoader <span style=color:#719e07>=</span> ExtensionLoader<span style=color:#719e07>.</span>class<span style=color:#719e07>.</span>getClassLoader<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>ClassLoader<span style=color:#719e07>.</span>getSystemClassLoader<span style=color:#719e07>()</span> <span style=color:#719e07>!=</span> extensionLoaderClassLoader<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> urls <span style=color:#719e07>=</span> extensionLoaderClassLoader<span style=color:#719e07>.</span>getResources<span style=color:#719e07>(</span>fileName<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 根据文件名加载所有的同名文件
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>urls <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>||</span> <span style=color:#719e07>!</span>urls<span style=color:#719e07>.</span>hasMoreElements<span style=color:#719e07>())</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>classLoader <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> urls <span style=color:#719e07>=</span> classLoader<span style=color:#719e07>.</span>getResources<span style=color:#719e07>(</span>fileName<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span> <span style=color:#719e07>else</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> urls <span style=color:#719e07>=</span> ClassLoader<span style=color:#719e07>.</span>getSystemResources<span style=color:#719e07>(</span>fileName<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>urls <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>while</span> <span style=color:#719e07>(</span>urls<span style=color:#719e07>.</span>hasMoreElements<span style=color:#719e07>())</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> java<span style=color:#719e07>.</span>net<span style=color:#719e07>.</span>URL resourceURL <span style=color:#719e07>=</span> urls<span style=color:#719e07>.</span>nextElement<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 加载资源
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> loadResource<span style=color:#719e07>(</span>extensionClasses<span style=color:#719e07>,</span> classLoader<span style=color:#719e07>,</span> resourceURL<span style=color:#719e07>,</span> overridden<span style=color:#719e07>,</span> excludedPackages<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span> <span style=color:#719e07>catch</span> <span style=color:#719e07>(</span>Throwable t<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> logger<span style=color:#719e07>.</span>error<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;Exception occurred when loading extension class (interface: &#34;</span> <span style=color:#719e07>+</span>
</span></span><span style=display:flex><span> type <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;, description file: &#34;</span> <span style=color:#719e07>+</span> fileName <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;).&#34;</span><span style=color:#719e07>,</span> t<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><p>loadDirectory 方法先通过 classLoader 获取所有资源链接,然后再通过 loadResource 方法加载资源。我们继续跟下去,看一下 loadResource 方法的实现。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> <span style=color:#dc322f>void</span> <span style=color:#268bd2>loadResource</span><span style=color:#719e07>(</span>Map<span style=color:#719e07>&lt;</span>String<span style=color:#719e07>,</span> Class<span style=color:#719e07>&lt;?&gt;&gt;</span> extensionClasses<span style=color:#719e07>,</span> ClassLoader classLoader<span style=color:#719e07>,</span>
</span></span><span style=display:flex><span> java<span style=color:#719e07>.</span>net<span style=color:#719e07>.</span>URL resourceURL<span style=color:#719e07>,</span> <span style=color:#dc322f>boolean</span> overridden<span style=color:#719e07>,</span> String<span style=color:#719e07>...</span> excludedPackages<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>try</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>try</span> <span style=color:#719e07>(</span>BufferedReader reader <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> BufferedReader<span style=color:#719e07>(</span><span style=color:#719e07>new</span> InputStreamReader<span style=color:#719e07>(</span>resourceURL<span style=color:#719e07>.</span>openStream<span style=color:#719e07>(),</span> StandardCharsets<span style=color:#719e07>.</span>UTF_8<span style=color:#719e07>)))</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> String line<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span> String clazz <span style=color:#719e07>=</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>;</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 按行读取配置内容
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>while</span> <span style=color:#719e07>((</span>line <span style=color:#719e07>=</span> reader<span style=color:#719e07>.</span>readLine<span style=color:#719e07>())</span> <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 定位 # 字符
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#268bd2>final</span> <span style=color:#dc322f>int</span> ci <span style=color:#719e07>=</span> line<span style=color:#719e07>.</span>indexOf<span style=color:#719e07>(</span><span style=color:#2aa198>&#39;#&#39;</span><span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>ci <span style=color:#719e07>&gt;=</span> 0<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 截取 # 之前的字符串,# 之后的内容为注释,需要忽略
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> line <span style=color:#719e07>=</span> line<span style=color:#719e07>.</span>substring<span style=color:#719e07>(</span>0<span style=color:#719e07>,</span> ci<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> line <span style=color:#719e07>=</span> line<span style=color:#719e07>.</span>trim<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>line<span style=color:#719e07>.</span>length<span style=color:#719e07>()</span> <span style=color:#719e07>&gt;</span> 0<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>try</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> String name <span style=color:#719e07>=</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>;</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 以等于号 = 为界,截取键与值
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#dc322f>int</span> i <span style=color:#719e07>=</span> line<span style=color:#719e07>.</span>indexOf<span style=color:#719e07>(</span><span style=color:#2aa198>&#39;=&#39;</span><span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>i <span style=color:#719e07>&gt;</span> 0<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> name <span style=color:#719e07>=</span> line<span style=color:#719e07>.</span>substring<span style=color:#719e07>(</span>0<span style=color:#719e07>,</span> i<span style=color:#719e07>).</span>trim<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> clazz <span style=color:#719e07>=</span> line<span style=color:#719e07>.</span>substring<span style=color:#719e07>(</span>i <span style=color:#719e07>+</span> 1<span style=color:#719e07>).</span>trim<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span> <span style=color:#719e07>else</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> clazz <span style=color:#719e07>=</span> line<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 加载类,并通过 loadClass 方法对类进行缓存
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>StringUtils<span style=color:#719e07>.</span>isNotEmpty<span style=color:#719e07>(</span>clazz<span style=color:#719e07>)</span> <span style=color:#719e07>&amp;&amp;</span> <span style=color:#719e07>!</span>isExcluded<span style=color:#719e07>(</span>clazz<span style=color:#719e07>,</span> excludedPackages<span style=color:#719e07>))</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> loadClass<span style=color:#719e07>(</span>extensionClasses<span style=color:#719e07>,</span> resourceURL<span style=color:#719e07>,</span> Class<span style=color:#719e07>.</span>forName<span style=color:#719e07>(</span>clazz<span style=color:#719e07>,</span> <span style=color:#cb4b16>true</span><span style=color:#719e07>,</span> classLoader<span style=color:#719e07>),</span> name<span style=color:#719e07>,</span> overridden<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span> <span style=color:#719e07>catch</span> <span style=color:#719e07>(</span>Throwable t<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> IllegalStateException e <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> IllegalStateException<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;Failed to load extension class (interface: &#34;</span> <span style=color:#719e07>+</span> type <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;, class line: &#34;</span> <span style=color:#719e07>+</span> line <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;) in &#34;</span> <span style=color:#719e07>+</span> resourceURL <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;, cause: &#34;</span> <span style=color:#719e07>+</span> t<span style=color:#719e07>.</span>getMessage<span style=color:#719e07>(),</span> t<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> exceptions<span style=color:#719e07>.</span>put<span style=color:#719e07>(</span>line<span style=color:#719e07>,</span> e<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span> <span style=color:#719e07>catch</span> <span style=color:#719e07>(</span>Throwable t<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> logger<span style=color:#719e07>.</span>error<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;Exception occurred when loading extension class (interface: &#34;</span> <span style=color:#719e07>+</span>
</span></span><span style=display:flex><span> type <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;, class file: &#34;</span> <span style=color:#719e07>+</span> resourceURL <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;) in &#34;</span> <span style=color:#719e07>+</span> resourceURL<span style=color:#719e07>,</span> t<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><p>loadResource 方法用于读取和解析配置文件,并通过反射加载类,最后调用 loadClass 方法进行其他操作。loadClass 方法用于主要用于操作缓存,该方法的逻辑如下:</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> <span style=color:#dc322f>void</span> <span style=color:#268bd2>loadClass</span><span style=color:#719e07>(</span>Map<span style=color:#719e07>&lt;</span>String<span style=color:#719e07>,</span> Class<span style=color:#719e07>&lt;?&gt;&gt;</span> extensionClasses<span style=color:#719e07>,</span> java<span style=color:#719e07>.</span>net<span style=color:#719e07>.</span>URL resourceURL<span style=color:#719e07>,</span> Class<span style=color:#719e07>&lt;?&gt;</span> clazz<span style=color:#719e07>,</span> String name<span style=color:#719e07>,</span>
</span></span><span style=display:flex><span> <span style=color:#dc322f>boolean</span> overridden<span style=color:#719e07>)</span> <span style=color:#268bd2>throws</span> NoSuchMethodException <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(!</span>type<span style=color:#719e07>.</span>isAssignableFrom<span style=color:#719e07>(</span>clazz<span style=color:#719e07>))</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;Error occurred when loading extension class (interface: &#34;</span> <span style=color:#719e07>+</span>
</span></span><span style=display:flex><span> type <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;, class line: &#34;</span> <span style=color:#719e07>+</span> clazz<span style=color:#719e07>.</span>getName<span style=color:#719e07>()</span> <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;), class &#34;</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>+</span> clazz<span style=color:#719e07>.</span>getName<span style=color:#719e07>()</span> <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34; is not subtype of interface.&#34;</span><span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 检测目标类上是否有 Adaptive 注解
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>clazz<span style=color:#719e07>.</span>isAnnotationPresent<span style=color:#719e07>(</span>Adaptive<span style=color:#719e07>.</span>class<span style=color:#719e07>))</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> cacheAdaptiveClass<span style=color:#719e07>(</span>clazz<span style=color:#719e07>,</span> overridden<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span> <span style=color:#719e07>else</span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>isWrapperClass<span style=color:#719e07>(</span>clazz<span style=color:#719e07>))</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 缓存包装类
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> cacheWrapperClass<span style=color:#719e07>(</span>clazz<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span> <span style=color:#719e07>else</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 进入到这里,表明只是该类只是一个普通的拓展类
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#586e75>// 检测 clazz 是否有默认的构造方法,如果没有,则抛出异常
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> clazz<span style=color:#719e07>.</span>getConstructor<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>StringUtils<span style=color:#719e07>.</span>isEmpty<span style=color:#719e07>(</span>name<span style=color:#719e07>))</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 如果 name 为空,则尝试从 Extension 注解中获取 name,或使用小写的类名作为 name
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> name <span style=color:#719e07>=</span> findAnnotationName<span style=color:#719e07>(</span>clazz<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>name<span style=color:#719e07>.</span>length<span style=color:#719e07>()</span> <span style=color:#719e07>==</span> 0<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;No such extension name for the class &#34;</span> <span style=color:#719e07>+</span> clazz<span style=color:#719e07>.</span>getName<span style=color:#719e07>()</span> <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34; in the config &#34;</span> <span style=color:#719e07>+</span> resourceURL<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> String<span style=color:#719e07>[]</span> names <span style=color:#719e07>=</span> NAME_SEPARATOR<span style=color:#719e07>.</span>split<span style=color:#719e07>(</span>name<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>ArrayUtils<span style=color:#719e07>.</span>isNotEmpty<span style=color:#719e07>(</span>names<span style=color:#719e07>))</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 如果类上有 Activate 注解,则使用 names 数组的第一个元素作为键,
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#586e75>// 存储 name 到 Activate 注解对象的映射关系
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> cacheActivateClass<span style=color:#719e07>(</span>clazz<span style=color:#719e07>,</span> names<span style=color:#719e07>[</span>0<span style=color:#719e07>]);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> <span style=color:#719e07>(</span>String n <span style=color:#719e07>:</span> names<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 存储 Class 到名称的映射关系
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> cacheName<span style=color:#719e07>(</span>clazz<span style=color:#719e07>,</span> n<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 存储 name 到 Class 的映射关系.
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#586e75>// 如果存在同一个扩展名对应多个实现类,基于 override 参数是否允许覆盖,如果不允许,则抛出异常.
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> saveInExtensionClass<span style=color:#719e07>(</span>extensionClasses<span style=color:#719e07>,</span> clazz<span style=color:#719e07>,</span> n<span style=color:#719e07>,</span> overridden<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><p>如上,loadClass 方法操作了不同的缓存,比如 cachedAdaptiveClass、cachedWrapperClasses 和 cachedNames 等等。除此之外,该方法没有其他什么逻辑了。</p><p>到此,关于缓存类加载的过程就分析完了。整个过程没什么特别复杂的地方,大家按部就班的分析即可,不懂的地方可以调试一下。接下来,我们来聊聊 Dubbo IOC 方面的内容。</p><h3 id=212-dubbo-ioc>2.1.2 Dubbo IOC</h3><p>Dubbo IOC 是通过 setter 方法注入依赖。Dubbo 首先会通过反射获取到实例的所有方法,然后再遍历方法列表,检测方法名是否具有 setter 方法特征。若有,则通过 ObjectFactory 获取依赖对象,最后通过反射调用 setter 方法将依赖设置到目标对象中。整个过程对应的代码如下:</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> T <span style=color:#268bd2>injectExtension</span><span style=color:#719e07>(</span>T instance<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>objectFactory <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> instance<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#719e07>try</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 遍历目标类的所有方法
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>for</span> <span style=color:#719e07>(</span>Method method <span style=color:#719e07>:</span> instance<span style=color:#719e07>.</span>getClass<span style=color:#719e07>().</span>getMethods<span style=color:#719e07>())</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(!</span>isSetter<span style=color:#719e07>(</span>method<span style=color:#719e07>))</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>continue</span><span style=color:#719e07>;</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>/**
</span></span></span><span style=display:flex><span><span style=color:#586e75> * 检测是否有 DisableInject 注解修饰.
</span></span></span><span style=display:flex><span><span style=color:#586e75> */</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>method<span style=color:#719e07>.</span>getAnnotation<span style=color:#719e07>(</span>DisableInject<span style=color:#719e07>.</span>class<span style=color:#719e07>)</span> <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>continue</span><span style=color:#719e07>;</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 基本类型不注入
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> Class<span style=color:#719e07>&lt;?&gt;</span> pt <span style=color:#719e07>=</span> method<span style=color:#719e07>.</span>getParameterTypes<span style=color:#719e07>()[</span>0<span style=color:#719e07>];</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>ReflectUtils<span style=color:#719e07>.</span>isPrimitives<span style=color:#719e07>(</span>pt<span style=color:#719e07>))</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>continue</span><span style=color:#719e07>;</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#719e07>try</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取属性名,比如 setName 方法对应属性名 name
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> String property <span style=color:#719e07>=</span> getSetterProperty<span style=color:#719e07>(</span>method<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 从 ObjectFactory 中获取依赖对象
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> Object object <span style=color:#719e07>=</span> objectFactory<span style=color:#719e07>.</span>getExtension<span style=color:#719e07>(</span>pt<span style=color:#719e07>,</span> property<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>object <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 注入
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> method<span style=color:#719e07>.</span>invoke<span style=color:#719e07>(</span>instance<span style=color:#719e07>,</span> object<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span> <span style=color:#719e07>catch</span> <span style=color:#719e07>(</span>Exception e<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> logger<span style=color:#719e07>.</span>error<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;Failed to inject via method &#34;</span> <span style=color:#719e07>+</span> method<span style=color:#719e07>.</span>getName<span style=color:#719e07>()</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34; of interface &#34;</span> <span style=color:#719e07>+</span> type<span style=color:#719e07>.</span>getName<span style=color:#719e07>()</span> <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;: &#34;</span> <span style=color:#719e07>+</span> e<span style=color:#719e07>.</span>getMessage<span style=color:#719e07>(),</span> e<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span> <span style=color:#719e07>catch</span> <span style=color:#719e07>(</span>Exception e<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> logger<span style=color:#719e07>.</span>error<span style=color:#719e07>(</span>e<span style=color:#719e07>.</span>getMessage<span style=color:#719e07>(),</span> e<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> instance<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><p>在上面代码中,objectFactory 变量的类型为 AdaptiveExtensionFactory,AdaptiveExtensionFactory 内部维护了一个 ExtensionFactory 列表,用于存储其他类型的 ExtensionFactory。Dubbo 目前提供了两种 ExtensionFactory,分别是 SpiExtensionFactory 和 SpringExtensionFactory。前者用于创建自适应的拓展,后者是用于从 Spring 的 IOC 容器中获取所需的拓展。这两个类的类的代码不是很复杂,这里就不一一分析了。</p><p>Dubbo IOC 目前仅支持 setter 方式注入,总的来说,逻辑比较简单易懂。</p><h3 id=22-加载自适应扩展类>2.2 加载自适应扩展类</h3><p>自适应扩展类的含义是说,基于参数,在运行时动态选择到具体的目标类,然后执行。
在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等。有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。这听起来有些矛盾。拓展未被加载,那么拓展方法就无法被调用(静态方法除外)。拓展方法未被调用,拓展就无法被加载。对于这个矛盾的问题,Dubbo 通过自适应拓展机制很好的解决了。自适应拓展机制的实现逻辑比较复杂,首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类,整个过程比较复杂。</p><p>加载自适应扩展类的入口是 ExtensionLoader 的 getAdaptiveExtension 方法。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>public</span> T <span style=color:#268bd2>getAdaptiveExtension</span><span style=color:#719e07>()</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 从缓存中获取自适应拓展
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> Object instance <span style=color:#719e07>=</span> cachedAdaptiveInstance<span style=color:#719e07>.</span>get<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>instance <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 如果存在异常,则直接抛出
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>createAdaptiveInstanceError <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;Failed to create adaptive instance: &#34;</span> <span style=color:#719e07>+</span>
</span></span><span style=display:flex><span> createAdaptiveInstanceError<span style=color:#719e07>.</span>toString<span style=color:#719e07>(),</span>
</span></span><span style=display:flex><span> createAdaptiveInstanceError<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>synchronized</span> <span style=color:#719e07>(</span>cachedAdaptiveInstance<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> instance <span style=color:#719e07>=</span> cachedAdaptiveInstance<span style=color:#719e07>.</span>get<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// double check
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>instance <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>try</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 创建自适应拓展
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#586e75>// 这里分为两种情况:一种是存在 Adaptive 类,另一个是需要生成 Adaptive 类
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> instance <span style=color:#719e07>=</span> createAdaptiveExtension<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> cachedAdaptiveInstance<span style=color:#719e07>.</span>set<span style=color:#719e07>(</span>instance<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span> <span style=color:#719e07>catch</span> <span style=color:#719e07>(</span>Throwable t<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> createAdaptiveInstanceError <span style=color:#719e07>=</span> t<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;Failed to create adaptive instance: &#34;</span> <span style=color:#719e07>+</span> t<span style=color:#719e07>.</span>toString<span style=color:#719e07>(),</span> t<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> <span style=color:#719e07>(</span>T<span style=color:#719e07>)</span> instance<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><p>getAdaptiveExtension 方法首先会检查缓存,缓存未命中,则调用 createAdaptiveExtension 方法创建自适应拓展。下面,我们看一下 createAdaptiveExtension 方法的代码。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> T <span style=color:#268bd2>createAdaptiveExtension</span><span style=color:#719e07>()</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>try</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取自适应拓展类,并通过反射实例化
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>return</span> injectExtension<span style=color:#719e07>((</span>T<span style=color:#719e07>)</span> getAdaptiveExtensionClass<span style=color:#719e07>().</span>newInstance<span style=color:#719e07>());</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span> <span style=color:#719e07>catch</span> <span style=color:#719e07>(</span>Exception e<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;Can not create adaptive extension ...&#34;</span><span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><p>createAdaptiveExtension 方法的代码比较少,但却包含了三个逻辑,分别如下:</p><ol><li>调用 getAdaptiveExtensionClass 方法获取自适应拓展 Class 对象</li><li>通过反射进行实例化</li><li>调用 injectExtension 方法向拓展实例中注入依赖</li></ol><p>前两个逻辑比较好理解,第三个逻辑用于向自适应拓展对象中注入依赖。这个逻辑看似多余,但有存在的必要,这里简单说明一下。前面说过,Dubbo 中有两种类型的自适应拓展,一种是手工编码的,一种是自动生成的。手工编码的自适应拓展中可能存在着一些依赖,而自动生成的 Adaptive 拓展则不会依赖其他类。这里调用 injectExtension 方法的目的是为手工编码的自适应拓展注入依赖,这一点需要大家注意一下。关于 injectExtension 方法,前文已经分析过了,这里不再赘述。接下来,分析 getAdaptiveExtensionClass 方法的逻辑。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> Class<span style=color:#719e07>&lt;?&gt;</span> getAdaptiveExtensionClass<span style=color:#719e07>()</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 通过 SPI 获取所有的拓展类
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> getExtensionClasses<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 检查缓存,若缓存不为空,则直接返回缓存
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>cachedAdaptiveClass <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> cachedAdaptiveClass<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 创建自适应拓展类
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>return</span> cachedAdaptiveClass <span style=color:#719e07>=</span> createAdaptiveExtensionClass<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><p>getAdaptiveExtensionClass 方法同样包含了三个逻辑,如下:</p><ol><li>调用 getExtensionClasses 获取所有的拓展类</li><li>检查缓存,若缓存不为空,则返回缓存</li><li>若缓存为空,则调用 createAdaptiveExtensionClass 创建自适应拓展类</li></ol><p>这三个逻辑看起来平淡无奇,似乎没有多讲的必要。但是这些平淡无奇的代码中隐藏了着一些细节,需要说明一下。首先从第一个逻辑说起,getExtensionClasses 这个方法用于获取某个接口的所有实现类。比如该方法可以获取 Protocol 接口的 DubboProtocol、HttpProtocol、InjvmProtocol 等实现类。在获取实现类的过程中,如果某个实现类被 Adaptive 注解修饰了,那么该类就会被赋值给 cachedAdaptiveClass 变量。此时,上面步骤中的第二步条件成立(缓存不为空),直接返回 cachedAdaptiveClass 即可。如果所有的实现类均未被 Adaptive 注解修饰,那么执行第三步逻辑,创建自适应拓展类。相关代码如下:</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> Class<span style=color:#719e07>&lt;?&gt;</span> createAdaptiveExtensionClass<span style=color:#719e07>()</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 构建自适应拓展代码
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> String code <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> AdaptiveClassCodeGenerator<span style=color:#719e07>(</span>type<span style=color:#719e07>,</span> cachedDefaultName<span style=color:#719e07>).</span>generate<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> ClassLoader classLoader <span style=color:#719e07>=</span> findClassLoader<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取编译器实现类
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> org<span style=color:#719e07>.</span>apache<span style=color:#719e07>.</span>dubbo<span style=color:#719e07>.</span>common<span style=color:#719e07>.</span>compiler<span style=color:#719e07>.</span>Compiler compiler <span style=color:#719e07>=</span> ExtensionLoader<span style=color:#719e07>.</span>getExtensionLoader<span style=color:#719e07>(</span>org<span style=color:#719e07>.</span>apache<span style=color:#719e07>.</span>dubbo<span style=color:#719e07>.</span>common<span style=color:#719e07>.</span>compiler<span style=color:#719e07>.</span>Compiler<span style=color:#719e07>.</span>class<span style=color:#719e07>).</span>getAdaptiveExtension<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 编译代码,生成 Class
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>return</span> compiler<span style=color:#719e07>.</span>compile<span style=color:#719e07>(</span>code<span style=color:#719e07>,</span> classLoader<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><p>createAdaptiveExtensionClass 方法用于生成自适应拓展类,该方法首先会生成自适应拓展类的源码,然后通过 Compiler 实例(Dubbo 默认使用 javassist 作为编译器)编译源码,得到代理类 Class 实例。接下来,我们把重点放在代理类代码生成的逻辑上,其他逻辑大家自行分析。</p><h3 id=221-自适应拓展类代码生成>2.2.1 自适应拓展类代码生成</h3><p>AdaptiveClassCodeGenerator#generate 方法生成扩展类代码</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>public</span> String <span style=color:#268bd2>generate</span><span style=color:#719e07>()</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 如果该接口中没有方法被 @Adaptive 注解修饰,直接抛出异常
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(!</span>hasAdaptiveMethod<span style=color:#719e07>())</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;No adaptive method exist on extension &#34;</span> <span style=color:#719e07>+</span> type<span style=color:#719e07>.</span>getName<span style=color:#719e07>()</span> <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;, refuse to create the adaptive class!&#34;</span><span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> StringBuilder code <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> StringBuilder<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 生成包名、import、方法等.
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> code<span style=color:#719e07>.</span>append<span style=color:#719e07>(</span>generatePackageInfo<span style=color:#719e07>());</span>
</span></span><span style=display:flex><span> code<span style=color:#719e07>.</span>append<span style=color:#719e07>(</span>generateImports<span style=color:#719e07>());</span>
</span></span><span style=display:flex><span> code<span style=color:#719e07>.</span>append<span style=color:#719e07>(</span>generateClassDeclaration<span style=color:#719e07>());</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> Method<span style=color:#719e07>[]</span> methods <span style=color:#719e07>=</span> type<span style=color:#719e07>.</span>getMethods<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> <span style=color:#719e07>(</span>Method method <span style=color:#719e07>:</span> methods<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> code<span style=color:#719e07>.</span>append<span style=color:#719e07>(</span>generateMethod<span style=color:#719e07>(</span>method<span style=color:#719e07>));</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> code<span style=color:#719e07>.</span>append<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;}&#34;</span><span style=color:#719e07>);</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>logger<span style=color:#719e07>.</span>isDebugEnabled<span style=color:#719e07>())</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> logger<span style=color:#719e07>.</span>debug<span style=color:#719e07>(</span>code<span style=color:#719e07>.</span>toString<span style=color:#719e07>());</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> code<span style=color:#719e07>.</span>toString<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><h4 id=222-生成方法>2.2.2 生成方法</h4><p>上面代码中,生成方法的逻辑是最关键的,我们详细分析下。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> String <span style=color:#268bd2>generateMethod</span><span style=color:#719e07>(</span>Method method<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> String methodReturnType <span style=color:#719e07>=</span> method<span style=color:#719e07>.</span>getReturnType<span style=color:#719e07>().</span>getCanonicalName<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> String methodName <span style=color:#719e07>=</span> method<span style=color:#719e07>.</span>getName<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 生成方法内容
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> String methodContent <span style=color:#719e07>=</span> generateMethodContent<span style=color:#719e07>(</span>method<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> String methodArgs <span style=color:#719e07>=</span> generateMethodArguments<span style=color:#719e07>(</span>method<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> String methodThrows <span style=color:#719e07>=</span> generateMethodThrows<span style=color:#719e07>(</span>method<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> String<span style=color:#719e07>.</span>format<span style=color:#719e07>(</span>CODE_METHOD_DECLARATION<span style=color:#719e07>,</span> methodReturnType<span style=color:#719e07>,</span> methodName<span style=color:#719e07>,</span> methodArgs<span style=color:#719e07>,</span> methodThrows<span style=color:#719e07>,</span> methodContent<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><p>generateMethodContent 分析</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> String <span style=color:#268bd2>generateMethodContent</span><span style=color:#719e07>(</span>Method method<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 该方法上必须有 @Adaptive 注解修饰
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> Adaptive adaptiveAnnotation <span style=color:#719e07>=</span> method<span style=color:#719e07>.</span>getAnnotation<span style=color:#719e07>(</span>Adaptive<span style=color:#719e07>.</span>class<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> StringBuilder code <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> StringBuilder<span style=color:#719e07>(</span>512<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>adaptiveAnnotation <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 没有 @Adaptive 注解修饰,生成异常信息
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>return</span> generateUnsupported<span style=color:#719e07>(</span>method<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span> <span style=color:#719e07>else</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取 URL 在参数列表上的索引
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#dc322f>int</span> urlTypeIndex <span style=color:#719e07>=</span> getUrlTypeIndex<span style=color:#719e07>(</span>method<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>urlTypeIndex <span style=color:#719e07>!=</span> <span style=color:#719e07>-</span>1<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 如果参数列表上存在 URL,生成对 URL 进行空检查
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> code<span style=color:#719e07>.</span>append<span style=color:#719e07>(</span>generateUrlNullCheck<span style=color:#719e07>(</span>urlTypeIndex<span style=color:#719e07>));</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span> <span style=color:#719e07>else</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 如果参数列表不存在 URL 类型的参数,那么就看参数列表上参数对象中是否包含 getUrl 方法
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#586e75>// 有的话,生成 URL 空检查
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> code<span style=color:#719e07>.</span>append<span style=color:#719e07>(</span>generateUrlAssignmentIndirectly<span style=color:#719e07>(</span>method<span style=color:#719e07>));</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 解析 Adaptive 注解上的 value 属性
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> String<span style=color:#719e07>[]</span> value <span style=color:#719e07>=</span> getMethodAdaptiveValue<span style=color:#719e07>(</span>adaptiveAnnotation<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 如果参数列表上有 Invocation 类型的参数,生成空检查并获取 methodName.
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#dc322f>boolean</span> hasInvocation <span style=color:#719e07>=</span> hasInvocationArgument<span style=color:#719e07>(</span>method<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> code<span style=color:#719e07>.</span>append<span style=color:#719e07>(</span>generateInvocationArgumentNullCheck<span style=color:#719e07>(</span>method<span style=color:#719e07>));</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 这段逻辑主要就是为了生成 extName(也就是扩展名)
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#586e75>// 分为多种情况:
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#586e75>// 1.defaultExtName 是否存在
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#586e75>// 2.参数中是否存在 invocation 类型参数
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#586e75>// 3.是否是为 protocol 生成代理
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#586e75>// 为什么要对 protocol 单独考虑了?因为 URL 中有获取 protocol 值的方法
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> code<span style=color:#719e07>.</span>append<span style=color:#719e07>(</span>generateExtNameAssignment<span style=color:#719e07>(</span>value<span style=color:#719e07>,</span> hasInvocation<span style=color:#719e07>));</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// check extName == null?
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> code<span style=color:#719e07>.</span>append<span style=color:#719e07>(</span>generateExtNameNullCheck<span style=color:#719e07>(</span>value<span style=color:#719e07>));</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 生成获取扩展(使用 ExtensionLoader.getExtension 方法)
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> code<span style=color:#719e07>.</span>append<span style=color:#719e07>(</span>generateExtensionAssignment<span style=color:#719e07>());</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 生成返回语句
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> code<span style=color:#719e07>.</span>append<span style=color:#719e07>(</span>generateReturnAndInvocation<span style=color:#719e07>(</span>method<span style=color:#719e07>));</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> code<span style=color:#719e07>.</span>toString<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><p>上面那段逻辑主要做了如下几件事:
1.检查方法上是否 Adaptive 注解修饰
2.为方法生成代码的时候,参数列表上要有 URL(或参数对象中有 URL)
3.使用 ExtensionLoader.getExtension 获取扩展
4.执行对应的方法</p><h3 id=223-附一个动态生成代码后的例子>2.2.3 附一个动态生成代码后的例子</h3><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#719e07>package</span> org.apache.dubbo.common.extension.adaptive<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#719e07>import</span> org.apache.dubbo.common.extension.ExtensionLoader<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#268bd2>class</span> <span style=color:#268bd2>HasAdaptiveExt$Adaptive</span> <span style=color:#268bd2>implements</span> org<span style=color:#719e07>.</span>apache<span style=color:#719e07>.</span>dubbo<span style=color:#719e07>.</span>common<span style=color:#719e07>.</span>extension<span style=color:#719e07>.</span>adaptive<span style=color:#719e07>.</span>HasAdaptiveExt <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>public</span> java<span style=color:#719e07>.</span>lang<span style=color:#719e07>.</span>String <span style=color:#268bd2>echo</span><span style=color:#719e07>(</span>org<span style=color:#719e07>.</span>apache<span style=color:#719e07>.</span>dubbo<span style=color:#719e07>.</span>common<span style=color:#719e07>.</span>URL arg0<span style=color:#719e07>,</span>
</span></span><span style=display:flex><span> java<span style=color:#719e07>.</span>lang<span style=color:#719e07>.</span>String arg1<span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// URL 空校验
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>arg0 <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalArgumentException<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;url == null&#34;</span><span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> org<span style=color:#719e07>.</span>apache<span style=color:#719e07>.</span>dubbo<span style=color:#719e07>.</span>common<span style=color:#719e07>.</span>URL url <span style=color:#719e07>=</span> arg0<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取扩展名
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> String extName <span style=color:#719e07>=</span> url<span style=color:#719e07>.</span>getParameter<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;has.adaptive.ext&#34;</span><span style=color:#719e07>,</span> <span style=color:#2aa198>&#34;adaptive&#34;</span><span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 扩展名空校验
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>if</span> <span style=color:#719e07>(</span>extName <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span><span style=color:#719e07>)</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException<span style=color:#719e07>(</span>
</span></span><span style=display:flex><span> <span style=color:#2aa198>&#34;Failed to get extension (org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt) name from url (&#34;</span> <span style=color:#719e07>+</span>
</span></span><span style=display:flex><span> url<span style=color:#719e07>.</span>toString<span style=color:#719e07>()</span> <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;) use keys([has.adaptive.ext])&#34;</span><span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取扩展
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> org<span style=color:#719e07>.</span>apache<span style=color:#719e07>.</span>dubbo<span style=color:#719e07>.</span>common<span style=color:#719e07>.</span>extension<span style=color:#719e07>.</span>adaptive<span style=color:#719e07>.</span>HasAdaptiveExt extension <span style=color:#719e07>=</span> <span style=color:#719e07>(</span>org<span style=color:#719e07>.</span>apache<span style=color:#719e07>.</span>dubbo<span style=color:#719e07>.</span>common<span style=color:#719e07>.</span>extension<span style=color:#719e07>.</span>adaptive<span style=color:#719e07>.</span>HasAdaptiveExt<span style=color:#719e07>)</span> ExtensionLoader<span style=color:#719e07>.</span>getExtensionLoader<span style=color:#719e07>(</span>org<span style=color:#719e07>.</span>apache<span style=color:#719e07>.</span>dubbo<span style=color:#719e07>.</span>common<span style=color:#719e07>.</span>extension<span style=color:#719e07>.</span>adaptive<span style=color:#719e07>.</span>HasAdaptiveExt<span style=color:#719e07>.</span>class<span style=color:#719e07>)</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>.</span>getExtension<span style=color:#719e07>(</span>extName<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 执行对应的方法
</span></span></span><span style=display:flex><span><span style=color:#586e75></span> <span style=color:#719e07>return</span> extension<span style=color:#719e07>.</span>echo<span style=color:#719e07>(</span>arg0<span style=color:#719e07>,</span> arg1<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><h2 id=3spi-扩展示例>3.SPI 扩展示例</h2><h3 id=31--加载固定扩展类>3.1 加载固定扩展类</h3><h3 id=311-编写-spi-接口及实现类>3.1.1 编写 SPI 接口及实现类</h3><p>不管是 Java SPI,还是 Dubbo 中实现的 SPI,都需要编写接口。不过 Dubbo 中的接口需要被 @SPI 注解修饰。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>@SPI</span>
</span></span><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#268bd2>interface</span> <span style=color:#268bd2>DemoSpi</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#dc322f>void</span> <span style=color:#268bd2>say</span><span style=color:#719e07>();</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#268bd2>class</span> <span style=color:#268bd2>DemoSpiImpl</span> <span style=color:#268bd2>implements</span> DemoSpi <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>public</span> <span style=color:#dc322f>void</span> <span style=color:#268bd2>say</span><span style=color:#719e07>()</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><h4 id=312-将实现类放在特定目录下>3.1.2 将实现类放在特定目录下</h4><p>从上面的代码可知,dubbo 在加载扩展类的时候,会从四个目录中读取。我们在 META-INF/dubbo 目录下新建一个以 DemoSpi 接口名为文件名的文件,内容如下:</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-text data-lang=text><span style=display:flex><span>demoSpiImpl = com.xxx.xxx.DemoSpiImpl(为 DemoSpi 接口实现类的全类名)
</span></span></code></pre></div><h4 id=313-使用>3.1.3 使用</h4><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#268bd2>class</span> <span style=color:#268bd2>DubboSPITest</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>@Test</span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>public</span> <span style=color:#dc322f>void</span> <span style=color:#268bd2>sayHello</span><span style=color:#719e07>()</span> <span style=color:#268bd2>throws</span> Exception <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> ExtensionLoader<span style=color:#719e07>&lt;</span>DemoSpi<span style=color:#719e07>&gt;</span> extensionLoader <span style=color:#719e07>=</span>
</span></span><span style=display:flex><span> ExtensionLoader<span style=color:#719e07>.</span>getExtensionLoader<span style=color:#719e07>(</span>DemoSpi<span style=color:#719e07>.</span>class<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> DemoSpi dmeoSpi <span style=color:#719e07>=</span> extensionLoader<span style=color:#719e07>.</span>getExtension<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;demoSpiImpl&#34;</span><span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> optimusPrime<span style=color:#719e07>.</span>sayHello<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><h3 id=32-加载自适应扩展类>3.2 加载自适应扩展类</h3><p>这个以 Protocol 为例进行说明</p><h3 id=321-protocol-接口抽取部分核心方法>3.2.1 Protocol 接口(抽取部分核心方法)</h3><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>@SPI</span><span style=color:#719e07>(</span><span style=color:#2aa198>&#34;dubbo&#34;</span><span style=color:#719e07>)</span>
</span></span><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#268bd2>interface</span> <span style=color:#268bd2>Protocol</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>@Adaptive</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> Exporter<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> <span style=color:#268bd2>export</span><span style=color:#719e07>(</span>Invoker<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> invoker<span style=color:#719e07>)</span> <span style=color:#268bd2>throws</span> RpcException<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>@Adaptive</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> Invoker<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> <span style=color:#268bd2>refer</span><span style=color:#719e07>(</span>Class<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> type<span style=color:#719e07>,</span> URL url<span style=color:#719e07>)</span> <span style=color:#268bd2>throws</span> RpcException<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#268bd2>class</span> <span style=color:#268bd2>DubboProtocol</span> <span style=color:#268bd2>extends</span> AbstractProtocol <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>......</span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>@Override</span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>public</span> <span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> Invoker<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> <span style=color:#268bd2>refer</span><span style=color:#719e07>(</span>Class<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> type<span style=color:#719e07>,</span> URL url<span style=color:#719e07>)</span> <span style=color:#268bd2>throws</span> RpcException <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> protocolBindingRefer<span style=color:#719e07>(</span>type<span style=color:#719e07>,</span> url<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>@Override</span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>public</span> <span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> Exporter<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> <span style=color:#268bd2>export</span><span style=color:#719e07>(</span>Invoker<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> invoker<span style=color:#719e07>)</span> <span style=color:#268bd2>throws</span> RpcException <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>......</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> exporter<span style=color:#719e07>;</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><h3 id=322-将实现类放在特定目录下>3.2.2 将实现类放在特定目录下</h3><p>在 dubbo 中,该配置路径 META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-text data-lang=text><span style=display:flex><span>dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
</span></span></code></pre></div><p>需要说明一点的是,在 dubbo 中,并不是直接使用 DubboProtocol 的,而是使用的是其包装类。</p><h3 id=323-使用>3.2.3 使用</h3><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#268bd2>class</span> <span style=color:#268bd2>DubboAdaptiveTest</span> <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>@Test</span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>public</span> <span style=color:#dc322f>void</span> <span style=color:#268bd2>sayHello</span><span style=color:#719e07>()</span> <span style=color:#268bd2>throws</span> Exception <span style=color:#719e07>{</span>
</span></span><span style=display:flex><span> URL url <span style=color:#719e07>=</span> URL<span style=color:#719e07>.</span>valueOf<span style=color:#719e07>(</span><span style=color:#2aa198>&#34;dubbo://localhost/test&#34;</span><span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> Protocol adaptiveProtocol <span style=color:#719e07>=</span> ExtensionLoader<span style=color:#719e07>.</span>getExtensionLoader<span style=color:#719e07>(</span>Protocol<span style=color:#719e07>.</span>class<span style=color:#719e07>).</span>getAdaptiveExtension<span style=color:#719e07>();</span>
</span></span><span style=display:flex><span> adaptiveProtocol<span style=color:#719e07>.</span>refer<span style=color:#719e07>(</span>type<span style=color:#719e07>,</span> url<span style=color:#719e07>);</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>}</span>
</span></span><span style=display:flex><span><span style=color:#719e07>}</span>
</span></span></code></pre></div><div id=pre-footer><h2>反馈</h2><p class=feedback--prompt>此页是否对您有帮助?</p><button class="btn btn-primary mb-4 feedback--yes"></button>
<button class="btn btn-primary mb-4 feedback--no"></button></div><script>const yes=document.querySelector(".feedback--yes"),no=document.querySelector(".feedback--no");document.querySelectorAll(".feedback--link").forEach(e=>{e.href=e.href+window.location.pathname});const sendFeedback=e=>{gtag||console.log("!gtag"),gtag("event","click",{event_category:"Helpful",event_label:window.location.pathname,value:e})},disableButtons=()=>{yes.disabled=!0,yes.classList.add("feedback--button__disabled"),no.disabled=!0,no.classList.add("feedback--button__disabled")};yes.addEventListener("click",()=>{sendFeedback(1),disableButtons(),document.querySelector(".feedback--response").classList.remove("feedback--response__hidden")}),no.addEventListener("click",()=>{sendFeedback(0),disableButtons(),document.querySelector(".feedback--response").classList.remove("feedback--response__hidden")})</script><br><div class="text-muted mt-5 pt-3 border-top">最后修改 March 1, 2023: <a href=https://github.com/apache/dubbo-website/commit/ceab0ea115ae57b25d607864d9eb2e5e9423f4cb>Move sdk docs (#2337) (ceab0ea115)</a></div></div></main><div class="d-none d-xl-block col-xl-2 td-toc d-print-none"><div class="td-page-meta ml-2 pb-1 pt-2 mb-0"><a href=https://github.com/apache/dubbo-website/edit/master/content/zh-cn/docs/references/spis/dubbo-spi.md target=_blank><i class="fa fa-edit fa-fw"></i> 编辑此页</a>
<a href="https://github.com/apache/dubbo-website/new/master/content/zh-cn/docs/references/spis/dubbo-spi.md?filename=change-me.md&amp;value=---%0Atitle%3A+%22Long+Page+Title%22%0AlinkTitle%3A+%22Short+Nav+Title%22%0Aweight%3A+100%0Adescription%3A+%3E-%0A+++++Page+description+for+heading+and+indexes.%0A---%0A%0A%23%23+Heading%0A%0AEdit+this+template+to+create+your+new+page.%0A%0A%2A+Give+it+a+good+name%2C+ending+in+%60.md%60+-+e.g.+%60getting-started.md%60%0A%2A+Edit+the+%22front+matter%22+section+at+the+top+of+the+page+%28weight+controls+how+its+ordered+amongst+other+pages+in+the+same+directory%3B+lowest+number+first%29.%0A%2A+Add+a+good+commit+message+at+the+bottom+of+the+page+%28%3C80+characters%3B+use+the+extended+description+field+for+more+detail%29.%0A%2A+Create+a+new+branch+so+you+can+preview+your+new+file+and+request+a+review+via+Pull+Request.%0A" target=_blank><i class="fa fa-edit fa-fw"></i> 创建子页面</a>
<a href="https://github.com/apache/dubbo-website/issues/new?title=%e6%89%a9%e5%b1%95%e7%82%b9%e5%bc%80%e5%8f%91%e6%8c%87%e5%8d%97" target=_blank><i class="fab fa-github fa-fw"></i> 登记问题</a>
<a href=https://github.com/apache/dubbo/issues/new target=_blank><i class="fas fa-tasks fa-fw"></i> 提交项目问题</a></div><nav id=TableOfContents><ul><li><a href=#1简介>1.简介</a><ul><li><a href=#11-加载固定的扩展类>1.1 加载固定的扩展类</a></li><li><a href=#12-加载自适应扩展类>1.2 加载自适应扩展类</a></li></ul></li><li><a href=#2-dubbo-spi-源码分析>2. Dubbo SPI 源码分析</a><ul><li><a href=#21-加载固定的扩展类>2.1 加载固定的扩展类</a></li><li><a href=#211-获取所有的拓展类>2.1.1 获取所有的拓展类</a></li><li><a href=#212-dubbo-ioc>2.1.2 Dubbo IOC</a></li><li><a href=#22-加载自适应扩展类>2.2 加载自适应扩展类</a></li><li><a href=#221-自适应拓展类代码生成>2.2.1 自适应拓展类代码生成</a></li><li><a href=#223-附一个动态生成代码后的例子>2.2.3 附一个动态生成代码后的例子</a></li></ul></li><li><a href=#3spi-扩展示例>3.SPI 扩展示例</a><ul><li><a href=#31--加载固定扩展类>3.1 加载固定扩展类</a></li><li><a href=#311-编写-spi-接口及实现类>3.1.1 编写 SPI 接口及实现类</a></li><li><a href=#32-加载自适应扩展类>3.2 加载自适应扩展类</a></li><li><a href=#321-protocol-接口抽取部分核心方法>3.2.1 Protocol 接口(抽取部分核心方法)</a></li><li><a href=#322-将实现类放在特定目录下>3.2.2 将实现类放在特定目录下</a></li><li><a href=#323-使用>3.2.3 使用</a></li></ul></li></ul></nav></div></div></div></div><footer class="bg-dark py-5 row d-print-none footer-margin-0"><div class="container-fluid mx-sm-5"><div class=row><div class="col-6 col-sm-4 text-xs-center order-sm-2"><ul class="list-inline mb-0"><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title="Dubbo mailing list archive" aria-label="Dubbo mailing list archive"><a class=text-white target=_blank rel="noopener noreferrer" href=https://lists.apache.org/list.html?dev@dubbo.apache.org><i class="fa fa-envelope"></i></a></li></ul></div><div class="col-6 col-sm-4 text-right text-xs-center order-sm-3"><ul class="list-inline mb-0"><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title=GitHub aria-label=GitHub><a class=text-white target=_blank rel="noopener noreferrer" href=https://github.com/apache/dubbo><i class="fab fa-github"></i></a></li><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title="Subscribe to mailing list" aria-label="Subscribe to mailing list"><a class=text-white target=_blank rel="noopener noreferrer" href=mailto:dev-subscribe@dubbo.apache.org><i class="fa fa-envelope"></i></a></li></ul></div><div class="col-12 col-sm-4 text-center py-2 order-sm-2"><small class=text-white>&copy; 2023 The Apache Software Foundation. Apache and the Apache feather logo are trademarks of The Apache Software Foundation. 保留所有权利</small></div></div></div></footer><div class="row pt-2 pb-2 footer-margin-0"><div class="container-fluid mx-sm-5"><div class=text-center id=my-footer><img style=float:left alt=apache_logo src=/imgs/apache_logo.png><ul><li><a href=https://www.apache.org>Foundation</a></li><li><a href=https://www.apache.org/licenses/>License</a></li><li><a href=https://www.apache.org/security/>Security</a></li><li><a href=https://www.apache.org/events/current-event>Events</a></li><li><a href=https://www.apache.org/foundation/sponsorship.html>Sponsorship</a></li><li><a href=https://privacy.apache.org/policies/privacy-policy-public.html>Privacy</a></li><li><a href=https://www.apache.org/foundation/thanks.html>Thanks</a></li></ul></div></div></div><script src=/js/popper.min.js integrity=sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49 crossorigin=anonymous></script>
<script src=/js/bootstrap.min.js integrity=sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy crossorigin=anonymous></script>
<script src=/js/main.min.a4534a01940be8c082c4c0e6777b61df9884f202b61c9519352adb6d1039d5aa.js integrity="sha256-pFNKAZQL6MCCxMDmd3th35iE8gK2HJUZNSrbbRA51ao=" crossorigin=anonymous></script>
<script src=https://cdn.jsdelivr.net/npm/@docsearch/js@3></script>
<script>docsearch({appId:"L5F4T9F0I1",apiKey:"364ae307e1da9d02b2335675e9db1eb1",indexName:"apache_dubbo",container:"#docsearch",debug:!1}),docsearch({appId:"L5F4T9F0I1",apiKey:"364ae307e1da9d02b2335675e9db1eb1",indexName:"apache_dubbo",container:"#docsearch_zh_home",debug:!1})</script></body></html>