blob: f6430c8d3aa8ae94f0c1b418090dc6130bf93a7a [file] [log] [blame]
<!doctype html><html lang=en class=no-js><head><meta name=ROBOTS content="INDEX, FOLLOW"><link rel=canonical href=https://cn.dubbo.apache.org/en/blog/2019/05/02/dubbo-extensible-mechanism-source-code-analysis-part-2/><script>var _hmt=_hmt||[];(function(){var e,t=document.createElement("script");t.src="https://hm.baidu.com/hm.js?3b78f49ba47181e4d998a66b689446e9",e=document.getElementsByTagName("script")[0],e.parentNode.insertBefore(t,e)})()</script><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta http-equiv=Content-Security-Policy content="frame-src *"><meta name=generator content="Hugo 0.122.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>Dubbo extensible mechanism source code analysis - part 2 | Apache Dubbo</title><meta property="og:title" content="Dubbo extensible mechanism source code analysis - part 2">
<meta property="og:description" content="This article introduces the principles and details of Dubbo's SPI.
"><meta property="og:type" content="article"><meta property="og:url" content="https://cn.dubbo.apache.org/en/blog/2019/05/02/dubbo-extensible-mechanism-source-code-analysis-part-2/"><meta property="article:section" content="blog"><meta property="article:published_time" content="2019-05-02T00:00:00+00:00"><meta property="article:modified_time" content="2021-01-19T10:06:15+08:00"><meta itemprop=name content="Dubbo extensible mechanism source code analysis - part 2"><meta itemprop=description content="This article introduces the principles and details of Dubbo's SPI.
"><meta itemprop=datePublished content="2019-05-02T00:00:00+00:00"><meta itemprop=dateModified content="2021-01-19T10:06:15+08:00"><meta itemprop=wordCount content="2629"><meta itemprop=keywords content><meta name=twitter:card content="summary"><meta name=twitter:title content="Dubbo extensible mechanism source code analysis - part 2"><meta name=twitter:description content="This article introduces the principles and details of Dubbo's SPI.
"><script async src="https://www.googletagmanager.com/gtag/js?id=G-NM6FFMT51J"></script><script>var doNotTrack=!1;if(!doNotTrack){window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag("js",new Date),gtag("config","G-NM6FFMT51J",{anonymize_ip:!1})}</script><link rel=preload href=/scss/main.min.f77e221bcdbe0cadb996060fe82063c747b60c229a1f8bbf0ee529adbadd84fa.css as=style><link href=/scss/main.min.f77e221bcdbe0cadb996060fe82063c747b60c229a1f8bbf0ee529adbadd84fa.css rel=stylesheet integrity><script src=/js/jquery-3.5.1.min.js integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin=anonymous></script><meta name=theme-color content="#326ce5"><link rel=stylesheet href=/css/feature-states.css><meta name=description content="This article introduces the principles and details of Dubbo's SPI.
"><meta property="og:description" content="This article introduces the principles and details of Dubbo's SPI.
"><meta name=twitter:description content="This article introduces the principles and details of Dubbo's SPI.
"><meta property="og:url" content="https://cn.dubbo.apache.org/en/blog/2019/05/02/dubbo-extensible-mechanism-source-code-analysis-part-2/"><meta property="og:title" content="Dubbo extensible mechanism source code analysis - part 2"><meta name=twitter:title content="Dubbo extensible mechanism source code analysis - part 2"><meta name=twitter:image:alt content="Apache Dubbo"><meta property="og:image" content="/imgs/blog/dubbo-extensionfactory.png"><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=/en/><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=/en/overview/><span>Overview</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=/en/docs3-v2/><span>SDK Manual</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class="nav-link active" href=/en/blog/><span class=active>Blog</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=/en/download/><span>Download</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 dropdown d-lg-block"><a class="nav-link dropdown-toggle" href=# id=navbarDropdownMenuLink role=button data-toggle=dropdown aria-haspopup=true aria-expanded=false>English</a><div class="dropdown-menu dropdown-menu-right" aria-labelledby=navbarDropdownMenuLink><a class=dropdown-item href=/zh-cn/>δΈ­ζ–‡</a></div></li><li class="nav-item dropdown d-lg-block"><div class="nav-item d-none d-lg-block"></div></li></ul></div></nav><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-enblog20190502dubbo-extensible-mechanism-source-code-analysis-part-2").addClass("active"),$("#td-section-nav #m-enblog20190502dubbo-extensible-mechanism-source-code-analysis-part-2-li span").addClass("td-sidebar-nav-active-item"),$("#td-section-nav #m-enblog20190502dubbo-extensible-mechanism-source-code-analysis-part-2").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-enblog20190502dubbo-extensible-mechanism-source-code-analysis-part-2-li").siblings("li").addClass("show"),$("#td-section-nav #m-enblog20190502dubbo-extensible-mechanism-source-code-analysis-part-2-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"><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-enblog-li><ul class=ul-1><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-enblognews-li><input type=checkbox id=m-enblognews-check>
<label for=m-enblognews-check><a href=/en/blog/news/ title="Articles About Apache Dubbo" class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-enblognews><span>Articles</span></a></label><ul class="ul-2 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20240131tracing-dubbo-with-opentelemetry-li><input type=checkbox id=m-enblog20240131tracing-dubbo-with-opentelemetry-check>
<label for=m-enblog20240131tracing-dubbo-with-opentelemetry-check><a href=/en/blog/2024/01/31/tracing-dubbo-with-opentelemetry/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20240131tracing-dubbo-with-opentelemetry><span>Tracing Dubbo With OpenTelemetry</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20231023introduction-to-apache-dubbo-plugin-for-intellij-idea-li><input type=checkbox id=m-enblog20231023introduction-to-apache-dubbo-plugin-for-intellij-idea-check>
<label for=m-enblog20231023introduction-to-apache-dubbo-plugin-for-intellij-idea-check><a href=/en/blog/2023/10/23/introduction-to-apache-dubbo-plugin-for-intellij-idea/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20231023introduction-to-apache-dubbo-plugin-for-intellij-idea><span>Introduction to Apache Dubbo plugin for IntelliJ IDEA</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20230415advanced-cloud-native-dubbo-32-officially-released-li><input type=checkbox id=m-enblog20230415advanced-cloud-native-dubbo-32-officially-released-check>
<label for=m-enblog20230415advanced-cloud-native-dubbo-32-officially-released-check><a href=/en/blog/2023/04/15/advanced-cloud-native-dubbo-3.2-officially-released/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20230415advanced-cloud-native-dubbo-32-officially-released><span>Advanced cloud native - Dubbo 3.2 officially released</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20220504how-to-proxy-dubbo-service-in-apache-shenyu-gateway-li><input type=checkbox id=m-enblog20220504how-to-proxy-dubbo-service-in-apache-shenyu-gateway-check>
<label for=m-enblog20220504how-to-proxy-dubbo-service-in-apache-shenyu-gateway-check><a href=/en/blog/2022/05/04/how-to-proxy-dubbo-service-in-apache-shenyu-gateway/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20220504how-to-proxy-dubbo-service-in-apache-shenyu-gateway><span>How to proxy Dubbo service in Apache ShenYu Gateway</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20220118makes-it-more-convenient-for-you-to-proxy-dubbo-services-in-apache-apisix-li><input type=checkbox id=m-enblog20220118makes-it-more-convenient-for-you-to-proxy-dubbo-services-in-apache-apisix-check>
<label for=m-enblog20220118makes-it-more-convenient-for-you-to-proxy-dubbo-services-in-apache-apisix-check><a href=/en/blog/2022/01/18/makes-it-more-convenient-for-you-to-proxy-dubbo-services-in-apache-apisix/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20220118makes-it-more-convenient-for-you-to-proxy-dubbo-services-in-apache-apisix><span>Makes it More Convenient for You to Proxy Dubbo Services in Apache APISIX</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20190826service-test-li><input type=checkbox id=m-enblog20190826service-test-check>
<label for=m-enblog20190826service-test-check><a href=/en/blog/2019/08/26/service-test/ title="Dubbo Admin service test" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20190826service-test><span>Service test in dubbo admin</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20190811tracing-dubbo-service-with-apache-skywalking-li><input type=checkbox id=m-enblog20190811tracing-dubbo-service-with-apache-skywalking-check>
<label for=m-enblog20190811tracing-dubbo-service-with-apache-skywalking-check><a href=/en/blog/2019/08/11/tracing-dubbo-service-with-apache-skywalking/ title="Tracing Dubbo service with Apache Skywalking" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20190811tracing-dubbo-service-with-apache-skywalking><span>Use apache skywalking in dubbo</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20190502dubbo-extensible-mechanism-source-code-analysis-part-2-li><input type=checkbox id=m-enblog20190502dubbo-extensible-mechanism-source-code-analysis-part-2-check>
<label for=m-enblog20190502dubbo-extensible-mechanism-source-code-analysis-part-2-check><a href=/en/blog/2019/05/02/dubbo-extensible-mechanism-source-code-analysis-part-2/ title="Dubbo extensible mechanism source code analysis - part 2" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20190502dubbo-extensible-mechanism-source-code-analysis-part-2><span>Dubbo extensible mechanism - part 2</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20190425dubbo-extensible-mechanism-source-code-analysis-part-1-li><input type=checkbox id=m-enblog20190425dubbo-extensible-mechanism-source-code-analysis-part-1-check>
<label for=m-enblog20190425dubbo-extensible-mechanism-source-code-analysis-part-1-check><a href=/en/blog/2019/04/25/dubbo-extensible-mechanism-source-code-analysis-part-1/ title="Dubbo extensible mechanism source code analysis - part 1" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20190425dubbo-extensible-mechanism-source-code-analysis-part-1><span>Dubbo extensible mechanism - part 1</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20190220implementation-background-and-practice-of-dubbo-client-asynchronous-interface-li><input type=checkbox id=m-enblog20190220implementation-background-and-practice-of-dubbo-client-asynchronous-interface-check>
<label for=m-enblog20190220implementation-background-and-practice-of-dubbo-client-asynchronous-interface-check><a href=/en/blog/2019/02/20/implementation-background-and-practice-of-dubbo-client-asynchronous-interface/ title="Implementation background and practice of Dubbo client asynchronous interface" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20190220implementation-background-and-practice-of-dubbo-client-asynchronous-interface><span>Dubbo Async Client</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20190220implementation-background-and-practice-of-dubbo-server-asynchronous-interface-li><input type=checkbox id=m-enblog20190220implementation-background-and-practice-of-dubbo-server-asynchronous-interface-check>
<label for=m-enblog20190220implementation-background-and-practice-of-dubbo-server-asynchronous-interface-check><a href=/en/blog/2019/02/20/implementation-background-and-practice-of-dubbo-server-asynchronous-interface/ title="Implementation background and practice of Dubbo server asynchronous interface" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20190220implementation-background-and-practice-of-dubbo-server-asynchronous-interface><span>Dubbo Async Server</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20190117how-to-use-seata-to-ensure-consistency-between-dubbo-microservices-li><input type=checkbox id=m-enblog20190117how-to-use-seata-to-ensure-consistency-between-dubbo-microservices-check>
<label for=m-enblog20190117how-to-use-seata-to-ensure-consistency-between-dubbo-microservices-check><a href=/en/blog/2019/01/17/how-to-use-seata-to-ensure-consistency-between-dubbo-microservices/ title="How to use Seata to ensure consistency between Dubbo Microservices" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20190117how-to-use-seata-to-ensure-consistency-between-dubbo-microservices><span>Use Seata in Dubbo</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20181210the-fifth-dubbo-meetup-has-been-held-in-hangzhou-li><input type=checkbox id=m-enblog20181210the-fifth-dubbo-meetup-has-been-held-in-hangzhou-check>
<label for=m-enblog20181210the-fifth-dubbo-meetup-has-been-held-in-hangzhou-check><a href=/en/blog/2018/12/10/the-fifth-dubbo-meetup-has-been-held-in-hangzhou/ title="The fifth Dubbo meetup has been held in Hangzhou" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20181210the-fifth-dubbo-meetup-has-been-held-in-hangzhou><span>The fifth Dubbo meetup</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20181107dubbo-integrates-with-nacos-to-become-a-registry-li><input type=checkbox id=m-enblog20181107dubbo-integrates-with-nacos-to-become-a-registry-check>
<label for=m-enblog20181107dubbo-integrates-with-nacos-to-become-a-registry-check><a href=/en/blog/2018/11/07/dubbo-integrates-with-nacos-to-become-a-registry/ title="Dubbo Integrates with Nacos to Become a Registry" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20181107dubbo-integrates-with-nacos-to-become-a-registry><span>Use Dubbo with Nacos</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20181005introduction-to-the-dubbo-protocol-li><input type=checkbox id=m-enblog20181005introduction-to-the-dubbo-protocol-check>
<label for=m-enblog20181005introduction-to-the-dubbo-protocol-check><a href=/en/blog/2018/10/05/introduction-to-the-dubbo-protocol/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20181005introduction-to-the-dubbo-protocol><span>Introduction to the Dubbo protocol</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180930integrate-dubbo-with-kubernetes-li><input type=checkbox id=m-enblog20180930integrate-dubbo-with-kubernetes-check>
<label for=m-enblog20180930integrate-dubbo-with-kubernetes-check><a href=/en/blog/2018/09/30/integrate-dubbo-with-kubernetes/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180930integrate-dubbo-with-kubernetes><span>Integrate Dubbo with Kubernetes</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180902how-to-prepare-an-apache-release-li><input type=checkbox id=m-enblog20180902how-to-prepare-an-apache-release-check>
<label for=m-enblog20180902how-to-prepare-an-apache-release-check><a href=/en/blog/2018/09/02/how-to-prepare-an-apache-release/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180902how-to-prepare-an-apache-release><span>How to prepare an Apache Release</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180902how-to-implement-a-fully-asynchronous-calls-chain-based-on-dubbo-li><input type=checkbox id=m-enblog20180902how-to-implement-a-fully-asynchronous-calls-chain-based-on-dubbo-check>
<label for=m-enblog20180902how-to-implement-a-fully-asynchronous-calls-chain-based-on-dubbo-check><a href=/en/blog/2018/09/02/how-to-implement-a-fully-asynchronous-calls-chain-based-on-dubbo/ title="How to implement a fully asynchronous calls chain based on Dubbo" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180902how-to-implement-a-fully-asynchronous-calls-chain-based-on-dubbo><span>New Async Call</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180826the-fourth-dubbo-meetup-has-been-held-in-chengdu-li><input type=checkbox id=m-enblog20180826the-fourth-dubbo-meetup-has-been-held-in-chengdu-check>
<label for=m-enblog20180826the-fourth-dubbo-meetup-has-been-held-in-chengdu-check><a href=/en/blog/2018/08/26/the-fourth-dubbo-meetup-has-been-held-in-chengdu/ title="The fourth Dubbo meetup has been held in Chengdu" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180826the-fourth-dubbo-meetup-has-been-held-in-chengdu><span>The fourth Dubbo meetup</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180814dubbo-basic-usage-dubbo-consumer-configuration-li><input type=checkbox id=m-enblog20180814dubbo-basic-usage-dubbo-consumer-configuration-check>
<label for=m-enblog20180814dubbo-basic-usage-dubbo-consumer-configuration-check><a href=/en/blog/2018/08/14/dubbo-basic-usage-dubbo-consumer-configuration/ title=" Dubbo Basic Usage - Dubbo Consumer Configuration" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180814dubbo-basic-usage-dubbo-consumer-configuration><span>Dubbo Consumer Configuration</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180814dubbo-several-ways-about-synchronousasynchronous-invoke-li><input type=checkbox id=m-enblog20180814dubbo-several-ways-about-synchronousasynchronous-invoke-check>
<label for=m-enblog20180814dubbo-several-ways-about-synchronousasynchronous-invoke-check><a href=/en/blog/2018/08/14/dubbo-several-ways-about-synchronous/asynchronous-invoke/ title="Dubbo: Several ways about synchronous/asynchronous invoke" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180814dubbo-several-ways-about-synchronousasynchronous-invoke><span>Dubbo Invoke</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180814dubbo-basic-usage--dubbo-provider-configuration-li><input type=checkbox id=m-enblog20180814dubbo-basic-usage--dubbo-provider-configuration-check>
<label for=m-enblog20180814dubbo-basic-usage--dubbo-provider-configuration-check><a href=/en/blog/2018/08/14/dubbo-basic-usage--dubbo-provider-configuration/ title="Dubbo Basic Usage -- Dubbo Provider Configuration" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180814dubbo-basic-usage--dubbo-provider-configuration><span>Dubbo Provider Configuration</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180814manipulating-services-dynamically-via-qos-li><input type=checkbox id=m-enblog20180814manipulating-services-dynamically-via-qos-check>
<label for=m-enblog20180814manipulating-services-dynamically-via-qos-check><a href=/en/blog/2018/08/14/manipulating-services-dynamically-via-qos/ title="Manipulating Services Dynamically via QoS" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180814manipulating-services-dynamically-via-qos><span>Dubbo QoS Introduction</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180814source-code-analysis-of-spring-boot-dubbo-app-start-and-stop-li><input type=checkbox id=m-enblog20180814source-code-analysis-of-spring-boot-dubbo-app-start-and-stop-check>
<label for=m-enblog20180814source-code-analysis-of-spring-boot-dubbo-app-start-and-stop-check><a href=/en/blog/2018/08/14/source-code-analysis-of-spring-boot-dubbo-app-start-and-stop/ title="Source code analysis of spring-boot+Dubbo App start and stop" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180814source-code-analysis-of-spring-boot-dubbo-app-start-and-stop><span>Dubbo start/stop in spring boot</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180814implementation-of-cross-language-calls-by-dubbo2js-li><input type=checkbox id=m-enblog20180814implementation-of-cross-language-calls-by-dubbo2js-check>
<label for=m-enblog20180814implementation-of-cross-language-calls-by-dubbo2js-check><a href=/en/blog/2018/08/14/implementation-of-cross-language-calls-by-dubbo2.js/ title="Implementation of cross-language calls by Dubbo2.js" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180814implementation-of-cross-language-calls-by-dubbo2js><span>dubbo2.js introduction</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180814generic-invoke-of-dubbo-li><input type=checkbox id=m-enblog20180814generic-invoke-of-dubbo-check>
<label for=m-enblog20180814generic-invoke-of-dubbo-check><a href=/en/blog/2018/08/14/generic-invoke-of-dubbo/ title="Generic invoke of Dubbo" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180814generic-invoke-of-dubbo><span>Generic invoke</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180814native-image-using-graalvm-li><input type=checkbox id=m-enblog20180814native-image-using-graalvm-check>
<label for=m-enblog20180814native-image-using-graalvm-check><a href=/en/blog/2018/08/14/native-image-using-graalvm/ title="Native Image using GraalVM" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180814native-image-using-graalvm><span>Native Image</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180810dubbos-load-balance-li><input type=checkbox id=m-enblog20180810dubbos-load-balance-check>
<label for=m-enblog20180810dubbos-load-balance-check><a href=/en/blog/2018/08/10/dubbos-load-balance/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180810dubbos-load-balance><span>Dubbo's Load Balance</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180807use-annotations-in-dubbo-li><input type=checkbox id=m-enblog20180807use-annotations-in-dubbo-check>
<label for=m-enblog20180807use-annotations-in-dubbo-check><a href=/en/blog/2018/08/07/use-annotations-in-dubbo/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180807use-annotations-in-dubbo><span>Use Annotations In Dubbo</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180807using-zookeeper-in-dubbo-li><input type=checkbox id=m-enblog20180807using-zookeeper-in-dubbo-check>
<label for=m-enblog20180807using-zookeeper-in-dubbo-check><a href=/en/blog/2018/08/07/using-zookeeper-in-dubbo/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180807using-zookeeper-in-dubbo><span>Using Zookeeper in Dubbo</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180807dubbo-101-li><input type=checkbox id=m-enblog20180807dubbo-101-check>
<label for=m-enblog20180807dubbo-101-check><a href=/en/blog/2018/08/07/dubbo-101/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180807dubbo-101><span>Your First Dubbo Demo</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180730the-third-dubbo-meetup-has-been-held-in-shenzhen-li><input type=checkbox id=m-enblog20180730the-third-dubbo-meetup-has-been-held-in-shenzhen-check>
<label for=m-enblog20180730the-third-dubbo-meetup-has-been-held-in-shenzhen-check><a href=/en/blog/2018/07/30/the-third-dubbo-meetup-has-been-held-in-shenzhen/ title="The third Dubbo meetup has been held in Shenzhen" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180730the-third-dubbo-meetup-has-been-held-in-shenzhen><span>The third Dubbo meetup</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180727sentinel-the-flow-sentinel-of-dubbo-services-li><input type=checkbox id=m-enblog20180727sentinel-the-flow-sentinel-of-dubbo-services-check>
<label for=m-enblog20180727sentinel-the-flow-sentinel-of-dubbo-services-check><a href=/en/blog/2018/07/27/sentinel-the-flow-sentinel-of-dubbo-services/ title="Sentinel: The flow sentinel of Dubbo services" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180727sentinel-the-flow-sentinel-of-dubbo-services><span>Introduce sentinel</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180712tracking-with-pinpoint-li><input type=checkbox id=m-enblog20180712tracking-with-pinpoint-check>
<label for=m-enblog20180712tracking-with-pinpoint-check><a href=/en/blog/2018/07/12/tracking-with-pinpoint/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180712tracking-with-pinpoint><span>Tracking with Pinpoint</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180701your-first-dubbo-filter-li><input type=checkbox id=m-enblog20180701your-first-dubbo-filter-check>
<label for=m-enblog20180701your-first-dubbo-filter-check><a href=/en/blog/2018/07/01/your-first-dubbo-filter/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180701your-first-dubbo-filter><span>Your First Dubbo Filter</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180623the-second-dubbo-shanghai-meetup-has-been-held-successfully-li><input type=checkbox id=m-enblog20180623the-second-dubbo-shanghai-meetup-has-been-held-successfully-check>
<label for=m-enblog20180623the-second-dubbo-shanghai-meetup-has-been-held-successfully-check><a href=/en/blog/2018/06/23/the-second-dubbo-shanghai-meetup-has-been-held-successfully/ title="The second Dubbo Shanghai meetup has been held successfully" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180623the-second-dubbo-shanghai-meetup-has-been-held-successfully><span>The second Dubbo meetup</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180512the-first-dubbo-meetup-has-been-held-in-beijing-li><input type=checkbox id=m-enblog20180512the-first-dubbo-meetup-has-been-held-in-beijing-check>
<label for=m-enblog20180512the-first-dubbo-meetup-has-been-held-in-beijing-check><a href=/en/blog/2018/05/12/the-first-dubbo-meetup-has-been-held-in-beijing/ title="The first Dubbo meetup has been held in Beijing" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180512the-first-dubbo-meetup-has-been-held-in-beijing><span>The first Dubbo meetup</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180502the-apachecon-na-schedule-has-been-announced-li><input type=checkbox id=m-enblog20180502the-apachecon-na-schedule-has-been-announced-check>
<label for=m-enblog20180502the-apachecon-na-schedule-has-been-announced-check><a href=/en/blog/2018/05/02/the-apachecon-na-schedule-has-been-announced/ title="The ApacheCon NA schedule has been announced" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180502the-apachecon-na-schedule-has-been-announced><span>ApacheCon NA</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180425the-gsocgoogle-summer-of-code-2018-li><input type=checkbox id=m-enblog20180425the-gsocgoogle-summer-of-code-2018-check>
<label for=m-enblog20180425the-gsocgoogle-summer-of-code-2018-check><a href=/en/blog/2018/04/25/the-gsocgoogle-summer-of-code-2018/ title="The GSoC(Google Summer of Code) 2018" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180425the-gsocgoogle-summer-of-code-2018><span>GSoC 2018</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20180422dubbo-roadmap-is-announced-in-qcon-beijing-2018-li><input type=checkbox id=m-enblog20180422dubbo-roadmap-is-announced-in-qcon-beijing-2018-check>
<label for=m-enblog20180422dubbo-roadmap-is-announced-in-qcon-beijing-2018-check><a href=/en/blog/2018/04/22/dubbo-roadmap-is-announced-in-qcon-beijing-2018/ title="Dubbo roadmap is announced in QCon Beijing 2018" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20180422dubbo-roadmap-is-announced-in-qcon-beijing-2018><span>QCon Beijing 2018</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-enblogproposals-li><input type=checkbox id=m-enblogproposals-check>
<label for=m-enblogproposals-check><a href=/en/blog/proposals/ title="Proposals About Apache Dubbo" class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-enblogproposals><span>Proposals</span></a></label><ul class="ul-2 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog10101application-level-service-discovery-li><input type=checkbox id=m-enblog10101application-level-service-discovery-check>
<label for=m-enblog10101application-level-service-discovery-check><a href=/en/blog/1/01/01/application-level-service-discovery/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog10101application-level-service-discovery><span>Application-Level Service Discovery</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog10101enhanced-http-standard-capabilities-of-triple-protocol-li><input type=checkbox id=m-enblog10101enhanced-http-standard-capabilities-of-triple-protocol-check>
<label for=m-enblog10101enhanced-http-standard-capabilities-of-triple-protocol-check><a href=/en/blog/1/01/01/enhanced-http-standard-capabilities-of-triple-protocol/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog10101enhanced-http-standard-capabilities-of-triple-protocol><span>Enhanced HTTP Standard Capabilities of Triple Protocol</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-enblogreleases-li><input type=checkbox id=m-enblogreleases-check>
<label for=m-enblogreleases-check><a href=/en/blog/releases/ title="New Releases" class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-enblogreleases><span>Releases</span></a></label><ul class="ul-2 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog202109202714-release-note-li><input type=checkbox id=m-enblog202109202714-release-note-check>
<label for=m-enblog202109202714-release-note-check><a href=/en/blog/2021/09/20/2.7.14-release-note/ title="2.7.14 Release Note" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog202109202714-release-note><span>2.7.14</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog202108233021-release-note-li><input type=checkbox id=m-enblog202108233021-release-note-check>
<label for=m-enblog202108233021-release-note-check><a href=/en/blog/2021/08/23/3.0.2.1-release-note/ title="3.0.2.1 Release Note" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog202108233021-release-note><span>3.0.2.1</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20210818302-release-note-li><input type=checkbox id=m-enblog20210818302-release-note-check>
<label for=m-enblog20210818302-release-note-check><a href=/en/blog/2021/08/18/3.0.2-release-note/ title="3.0.2 Release Note" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20210818302-release-note><span>3.0.2</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20210702301-release-note-li><input type=checkbox id=m-enblog20210702301-release-note-check>
<label for=m-enblog20210702301-release-note-check><a href=/en/blog/2021/07/02/3.0.1-release-note/ title="3.0.1 Release Note" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20210702301-release-note><span>3.0.1</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-enblog20200518past-releases-li><input type=checkbox id=m-enblog20200518past-releases-check>
<label for=m-enblog20200518past-releases-check><a href=/en/blog/2020/05/18/past-releases/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-enblog20200518past-releases><span>Past Releases</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://cn.dubbo.apache.org/en/blog/>Blog</a></li><li class=breadcrumb-item><a href=https://cn.dubbo.apache.org/en/blog/news/>Articles</a></li><li class="breadcrumb-item active" aria-current=page><a href=https://cn.dubbo.apache.org/en/blog/2019/05/02/dubbo-extensible-mechanism-source-code-analysis-part-2/ aria-disabled=true class="btn-link disabled">Dubbo extensible mechanism - part 2</a></li></ol></nav><section id=deprecation-warning><div class="content deprecation-warning pageinfo outdated-blog"><p>This article is more than one year old. Older articles may contain outdated content. Check that the information in the page has not become incorrect since its publication.</p></div></section><div class=td-content><h1>Dubbo extensible mechanism source code analysis - part 2</h1><div class=lead>This article introduces the principles and details of Dubbo&rsquo;s SPI.</div><div class="td-byline mb-4"><time datetime=2019-05-02 class=text-muted>Thursday, May 02, 2019</time></div><header class=article-meta></header><p>In the <a href=https://cn.dubbo.apache.org/en/blog/2019/04/25/dubbo-extensible-mechanism-source-code-analysis-part-1/ title>actual implementation of the Dubbo extensibility mechanism</a>, we learned some concepts of the Dubbo extension mechanism, explored the implementation of LoadBalance in Dubbo, and implemented a LoadBalance on our own. Do you think Dubbo&rsquo;s extension mechanism is great? Next, we will go deep into the source code of Dubbo and see what it is.</p><h2 id=extensionloader>ExtensionLoader</h2><p><code>ExtensionLoader</code> is the core class, which is responsible for the loading and lifecycle management of extension points. Let&rsquo;s start with this class. There are many methods of Extension, and the common methods include:</p><ul><li><code>public static &lt;T> ExtensionLoader&lt;T> getExtensionLoader(Class&lt;T> type)</code></li><li><code>public T getExtension(String name)</code></li><li><code>public T getAdaptiveExtension()</code></li></ul><p>The common usages are:</p><ul><li><code>LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(loadbalanceName)</code></li><li><code>RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension()</code></li></ul><p>Notice: In the source code shown below, I&rsquo;ll remove extraneous code (such as logging, exception catching, and so on) to make it easy to read and understand.</p><ol><li>getExtensionLoader
This is a static factory method that enters an extensible interface and returns an ExtensionLoader entity class for this interface. With this entity class, you can get not only a specific extension based on name, but also an adaptive extension.</li></ol><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>static</span> <span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> ExtensionLoader<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> <span style=color:#268bd2>getExtensionLoader</span>(Class<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> type) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// An extension point must be an interface</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (<span style=color:#719e07>!</span>type.isInterface()) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalArgumentException(<span style=color:#2aa198>&#34;Extension type(&#34;</span> <span style=color:#719e07>+</span> type <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;) is not interface!&#34;</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#586e75>// @SPI annotations must be provided</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (<span style=color:#719e07>!</span>withExtensionAnnotation(type)) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalArgumentException(<span style=color:#2aa198>&#34;Extension type without @SPI Annotation!&#34;</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#586e75>// Get the corresponding ExtensionLoader from the cache according to the interface</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// Each extension will only be loaded once</span>
</span></span><span style=display:flex><span> ExtensionLoader<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> loader <span style=color:#719e07>=</span> (ExtensionLoader<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span>) EXTENSION_LOADERS.get(type);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (loader <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// Initialize extension</span>
</span></span><span style=display:flex><span> EXTENSION_LOADERS.putIfAbsent(type, <span style=color:#719e07>new</span> ExtensionLoader<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span>(type));
</span></span><span style=display:flex><span> loader <span style=color:#719e07>=</span> (ExtensionLoader<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span>) EXTENSION_LOADERS.get(type);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> loader;
</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>private</span> <span style=color:#268bd2>ExtensionLoader</span>(Class<span style=color:#719e07>&lt;?&gt;</span> type) {
</span></span><span style=display:flex><span> <span style=color:#719e07>this</span>.type <span style=color:#719e07>=</span> type;
</span></span><span style=display:flex><span> objectFactory <span style=color:#719e07>=</span> (type <span style=color:#719e07>==</span> ExtensionFactory.class <span style=color:#719e07>?</span> <span style=color:#cb4b16>null</span> : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><ol start=2><li>getExtension</li></ol><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>(String name) {
</span></span><span style=display:flex><span> Holder<span style=color:#719e07>&lt;</span>Object<span style=color:#719e07>&gt;</span> holder <span style=color:#719e07>=</span> cachedInstances.get(name);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (holder <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> cachedInstances.putIfAbsent(name, <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.get(name);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> Object instance <span style=color:#719e07>=</span> holder.get();
</span></span><span style=display:flex><span> <span style=color:#586e75>// Get it from the cache. If it does not exist, create</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (instance <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#268bd2>synchronized</span> (holder) {
</span></span><span style=display:flex><span> instance <span style=color:#719e07>=</span> holder.get();
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (instance <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> instance <span style=color:#719e07>=</span> createExtension(name);
</span></span><span style=display:flex><span> holder.set(instance);
</span></span><span style=display:flex><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:#719e07>return</span> (T) instance;
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>Some judgments and caching have been made in the getExtension method, and the main logic is in the createExtension method. Let&rsquo;s move on to the createExtension method.</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>(String name) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// Get the extension class according to the name of extension point. For example, for LoadBalance, get the RandomLoadBalance class according to random</span>
</span></span><span style=display:flex><span> Class<span style=color:#719e07>&lt;?&gt;</span> clazz <span style=color:#719e07>=</span> getExtensionClasses().get(name);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> T instance <span style=color:#719e07>=</span> (T) EXTENSION_INSTANCES.get(clazz);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (instance <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// Use reflection to call newInstance to create an example of an extension class</span>
</span></span><span style=display:flex><span> EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
</span></span><span style=display:flex><span> instance <span style=color:#719e07>=</span> (T) EXTENSION_INSTANCES.get(clazz);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#586e75>// Make dependency injection for the extended class samples</span>
</span></span><span style=display:flex><span> injectExtension(instance);
</span></span><span style=display:flex><span> <span style=color:#586e75>// If there is a wrapper, add the wrapper</span>
</span></span><span style=display:flex><span> Set<span style=color:#719e07>&lt;</span>Class<span style=color:#719e07>&lt;?&gt;&gt;</span> wrapperClasses <span style=color:#719e07>=</span> cachedWrapperClasses;
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (wrapperClasses <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>&amp;&amp;</span> <span style=color:#719e07>!</span>wrapperClasses.isEmpty()) {
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> (Class<span style=color:#719e07>&lt;?&gt;</span> wrapperClass : wrapperClasses) {
</span></span><span style=display:flex><span> instance <span style=color:#719e07>=</span> injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> instance;
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>The createExtension method has done the following:</p><ol><li>First, get the corresponding extension class according to name. Read the extension point configuration file from the <code>META-INF</code> folder under ClassPath.</li><li>Use reflection to create an instance of an extended class.</li><li>make dependency injection for the attributes of the extended class instance. That is, IoC.</li><li>If there is a wrapper, add the wrapper. That is, AOP.</li></ol><p>Let&rsquo;s focus on these four processes.</p><ol><li>Get the corresponding extension class according to name. Let’s read the code first:</li></ol><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, Class<span style=color:#719e07>&lt;?&gt;&gt;</span> getExtensionClasses() {
</span></span><span style=display:flex><span> Map<span style=color:#719e07>&lt;</span>String, Class<span style=color:#719e07>&lt;?&gt;&gt;</span> classes <span style=color:#719e07>=</span> cachedClasses.get();
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (classes <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#268bd2>synchronized</span> (cachedClasses) {
</span></span><span style=display:flex><span> classes <span style=color:#719e07>=</span> cachedClasses.get();
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (classes <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> classes <span style=color:#719e07>=</span> loadExtensionClasses();
</span></span><span style=display:flex><span> cachedClasses.set(classes);
</span></span><span style=display:flex><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:#719e07>return</span> classes;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#586e75>// synchronized in getExtensionClasses</span>
</span></span><span style=display:flex><span><span style=color:#268bd2>private</span> Map<span style=color:#719e07>&lt;</span>String, Class<span style=color:#719e07>&lt;?&gt;&gt;</span> loadExtensionClasses() {
</span></span><span style=display:flex><span> <span style=color:#268bd2>final</span> SPI defaultAnnotation <span style=color:#719e07>=</span> type.getAnnotation(SPI.class);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (defaultAnnotation <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> String value <span style=color:#719e07>=</span> defaultAnnotation.value();
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (value <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>&amp;&amp;</span> (value <span style=color:#719e07>=</span> value.trim()).length() <span style=color:#719e07>&gt;</span> 0) {
</span></span><span style=display:flex><span> String<span style=color:#719e07>[]</span> names <span style=color:#719e07>=</span> NAME_SEPARATOR.split(value);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (names.length <span style=color:#719e07>&gt;</span> 1) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException(<span style=color:#2aa198>&#34;more than 1 default extension name on extension &#34;</span> <span style=color:#719e07>+</span> type.getName());
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (names.length <span style=color:#719e07>==</span> 1) cachedDefaultName <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></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> Map<span style=color:#719e07>&lt;</span>String, 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;</span>String, Class<span style=color:#719e07>&lt;?&gt;&gt;</span>();
</span></span><span style=display:flex><span> loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
</span></span><span style=display:flex><span> loadFile(extensionClasses, DUBBO_DIRECTORY);
</span></span><span style=display:flex><span> loadFile(extensionClasses, SERVICES_DIRECTORY);
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> extensionClasses;
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>This process is very simple. Get the extension class from the cache first, and if it does not exist, load it from the configuration file. The path of the configuration file has been mentioned before:</p><ul><li><code>META-INF/dubbo/internal</code></li><li><code>META-INF/dubbo</code></li><li><code>META-INF/services</code></li></ul><ol start=2><li>Use reflection to create an extended instance. This process is very simple. We can do this using <code>clazz.newInstance()</code>. The attributes of the extended instance created are all null values.</li><li>Extended instance is automatic assembly. In the actual scenario, there have dependencies between classes. Dependencies are also referenced in the extended instance, such as a simple Java class, an extension of another Dubbo, or a Spring Bean. The situation of dependencies is complex, and Dubbo&rsquo;s processing is relatively complicated. We will have a special chapter to explain it later. Now, we just need to know that Dubbo can correctly inject common dependencies in extension points, Dubbo extension dependencies or Spring dependencies, etc..</li><li>Extended instance is auto-wrapping. Auto-wrapping is about implementing Spring like AOP functionality. Dubbo uses it to implement some common functions internally, such as logging, monitoring, and so on. The contents of the extended instance auto-wrapper will also be explained separately later.</li></ol><p>After the above 4 steps, Dubbo creates and initializes an extended instance. The dependencies of this instance are injected and packaged as needed. At this point, this extended instance can be used.</p><h2 id=auto-assembly-of-dubbo-spi-advanced-usage>Auto-assembly of Dubbo SPI advanced usage</h2><p>The relevant code for auto-assembly is in the injectExtension method:</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>(T instance) {
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> (Method method : instance.getClass().getMethods()) {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (method.getName().startsWith(<span style=color:#2aa198>&#34;set&#34;</span>)
</span></span><span style=display:flex><span> <span style=color:#719e07>&amp;&amp;</span> method.getParameterTypes().length <span style=color:#719e07>==</span> 1
</span></span><span style=display:flex><span> <span style=color:#719e07>&amp;&amp;</span> Modifier.isPublic(method.getModifiers())) {
</span></span><span style=display:flex><span> Class<span style=color:#719e07>&lt;?&gt;</span> pt <span style=color:#719e07>=</span> method.getParameterTypes()<span style=color:#719e07>[</span>0<span style=color:#719e07>]</span>;
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> String property <span style=color:#719e07>=</span> method.getName().length() <span style=color:#719e07>&gt;</span> 3 <span style=color:#719e07>?</span> method.getName().substring(3, 4).toLowerCase() <span style=color:#719e07>+</span> method.getName().substring(4) : <span style=color:#2aa198>&#34;&#34;</span>;
</span></span><span style=display:flex><span> Object object <span style=color:#719e07>=</span> objectFactory.getExtension(pt, property);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (object <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> method.invoke(instance, object);
</span></span><span style=display:flex><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:#719e07>return</span> instance;
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>To accomplish the automatic assembly of dependencies of the extended instances, you first need to know what the dependencies are and what the types of dependencies are. The solution of Dubbo is to find the Java standard setter method. That is, the method name starting with set has only one parameter. If such a set method exists in an extension class, Dubbo injects it into dependencies, which is similar to the injection of Spring&rsquo;s set method. However, dependency injection in Dubbo is more complicated than that in Spring, because all the methods injected into Spring are Spring beans and managed by the Spring container. In Dubbo&rsquo;s dependency injection, you may need to inject another extension of Dubbo, or a Spring Bean, or a component of Google guice, or a component in any other framework. Dubbo needs to be able to load extensions from any scenario. In the injectExtension method, it is implemented with <code>Object object = objectFactory. getExtension (pt, property)</code>. ObjectFactory is ExtensionFactory type and initialized when creating ExtensionLoader:</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:#268bd2>ExtensionLoader</span>(Class<span style=color:#719e07>&lt;?&gt;</span> type) {
</span></span><span style=display:flex><span> <span style=color:#719e07>this</span>.type <span style=color:#719e07>=</span> type;
</span></span><span style=display:flex><span> objectFactory <span style=color:#719e07>=</span> (type <span style=color:#719e07>==</span> ExtensionFactory.class <span style=color:#719e07>?</span> <span style=color:#cb4b16>null</span> : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>ObjectFacore is also an extension, obtained through <code>ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()</code>.</p><p><img src=/imgs/blog/dubbo-extensionfactory.png alt=Dubbo-ExtensionFactory title></p><p>ExtensionFactory includes three implementations:</p><ol><li>SpiExtensionFactory: use Dubbo&rsquo;s Spi to load Extension.</li><li>SpringExtensionFactory: load Extension from the Spring container.</li><li>AdaptiveExtensionFactory: adaptive AdaptiveExtensionLoader</li></ol><p>Pay attention to the AdaptiveExtensionLoader here, the source code is as follows:</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>@Adaptive</span>
</span></span><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#268bd2>class</span> <span style=color:#268bd2>AdaptiveExtensionFactory</span> <span style=color:#268bd2>implements</span> ExtensionFactory {
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>private</span> <span style=color:#268bd2>final</span> List<span style=color:#719e07>&lt;</span>ExtensionFactory<span style=color:#719e07>&gt;</span> factories;
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>public</span> <span style=color:#268bd2>AdaptiveExtensionFactory</span>() {
</span></span><span style=display:flex><span> ExtensionLoader<span style=color:#719e07>&lt;</span>ExtensionFactory<span style=color:#719e07>&gt;</span> loader <span style=color:#719e07>=</span> ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
</span></span><span style=display:flex><span> List<span style=color:#719e07>&lt;</span>ExtensionFactory<span style=color:#719e07>&gt;</span> list <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> ArrayList<span style=color:#719e07>&lt;</span>ExtensionFactory<span style=color:#719e07>&gt;</span>();
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> (String name : loader.getSupportedExtensions()) {
</span></span><span style=display:flex><span> list.add(loader.getExtension(name));
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> factories <span style=color:#719e07>=</span> Collections.unmodifiableList(list);
</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:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> T <span style=color:#268bd2>getExtension</span>(Class<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> type, String name) {
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> (ExtensionFactory factory : factories) {
</span></span><span style=display:flex><span> T extension <span style=color:#719e07>=</span> factory.getExtension(type, name);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (extension <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> extension;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> <span style=color:#cb4b16>null</span>;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>The AdaptiveExtensionLoader class has @Adaptive annotations. As mentioned earlier, Dubbo creates an adaptive instance for each extension. If the extension class has @Adaptive annotations, it will use it as an adaptive class. If not, Dubbo will create one for us. So <code>ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension())</code> will return an AdaptiveExtensionLoader instance as an adaptive extension instance.
The AdaptiveExtensionLoader will iterate through all the ExtensionFactory implementations and try to load the extensions. If found, return. If not, continue to find it in the next ExtensionFactory. Dubbo has two ExtensionFactory built in, which are searched from Dubbo&rsquo;s own extension mechanism and Spring container. Since ExtensionFactory itself is also an extension point, we can implement our own ExtensionFactory to enable automatic assembly of Dubbo to support our custom components. For example, we used Google&rsquo;s guice as an IoC container in our project. We can implement our own GuiceExtensionFactory to enable Dubbo to load extensions from the guice container.</p><h2 id=aop-of-dubbo-spi-advanced-usage>AOP of Dubbo SPI advanced usage</h2><p>We often use AOP functionality when using Spring. Insert other logic before and after the method of the target class. For example, Spring AOP is usually used to implement logging, monitoring, and authentication, and so on.
Does Dubbo&rsquo;s extension mechanism also support similar features? The answer is yes. In Dubbo, there is a special class called the Wrapper class. It uses the wrapper class to wrap the original extension point instance through the decorator pattern, and then inserts additional logic before and after the original extension point implementation to implement AOP functionality.</p><h3 id=what-is-the-wrapper-class>What is the Wrapper class</h3><p>So what kind of class is the Wrapper class in the Dubbo extension mechanism? The Wrapper class is a class that has a replication constructor and also is a typical decorator pattern. Here&rsquo;s a Wrapper class:</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>class</span> <span style=color:#268bd2>A</span>{
</span></span><span style=display:flex><span> <span style=color:#268bd2>private</span> A a;
</span></span><span style=display:flex><span> <span style=color:#268bd2>public</span> <span style=color:#268bd2>A</span>(A a){
</span></span><span style=display:flex><span> <span style=color:#719e07>this</span>.a <span style=color:#719e07>=</span> a;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>Class A has a constructor <code>public A(A a)</code>, and the argument to the constructor is A itself. Such a class can be a Wrapper class in the Dubbo extension mechanism. Such Wrapper classes in Dubbo include ProtocolFilterWrapper, ProtocolListenerWrapper, and so on. You can check the source code to deepen your understanding.</p><h3 id=how-to-configure-the-wrapper-class>How to configure the Wrapper class</h3><p>The Wrapper class in Dubbo is also an extension point. Like other extension points, it is also configured in the <code>META-INF</code> folder. For example, the ProtocolFilterWrapper and ProtocolListenerWrapper in the previous example are configured in the path <code>dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol</code>:</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>filter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper
</span></span><span style=display:flex><span>listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
</span></span><span style=display:flex><span>mock=org.apache.dubbo.rpc.support.MockProtocol
</span></span></code></pre></div><p>When Dubbo loads the extension configuration file, there is a piece of code as follows:</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:#719e07>try</span> {
</span></span><span style=display:flex><span> clazz.getConstructor(type);
</span></span><span style=display:flex><span> Set<span style=color:#719e07>&lt;</span>Class<span style=color:#719e07>&lt;?&gt;&gt;</span> wrappers <span style=color:#719e07>=</span> cachedWrapperClasses;
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (wrappers <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> cachedWrapperClasses <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> ConcurrentHashSet<span style=color:#719e07>&lt;</span>Class<span style=color:#719e07>&lt;?&gt;&gt;</span>();
</span></span><span style=display:flex><span> wrappers <span style=color:#719e07>=</span> cachedWrapperClasses;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> wrappers.add(clazz);
</span></span><span style=display:flex><span>} <span style=color:#719e07>catch</span> (NoSuchMethodException e) {}
</span></span></code></pre></div><p>The meaning of this code is that if the extension class has a copy constructor, it will be saved for later use. The class that has the copy constructor is the Wrapper class. The parameter obtained by <code>clazz.getConstructor(type)</code> is the constructor of the extension point interface. Note that the parameter type of the constructor is an extension point interface, not an extension class.
Take Protocol as an example. The configuration file <code>dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol defines filter=org.apache.dubbo.rpc.protocol. ProtocolFilterWrapper</code>.
The code of ProtocolFilterWrapper is as follows:</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> <span style=color:#268bd2>class</span> <span style=color:#268bd2>ProtocolFilterWrapper</span> <span style=color:#268bd2>implements</span> Protocol {
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>private</span> <span style=color:#268bd2>final</span> Protocol protocol;
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// One parameter is the copy constructor of Protocol</span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>public</span> <span style=color:#268bd2>ProtocolFilterWrapper</span>(Protocol protocol) {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (protocol <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalArgumentException(<span style=color:#2aa198>&#34;protocol == null&#34;</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>this</span>.protocol <span style=color:#719e07>=</span> protocol;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>ProtocolFilterWrapper has a constructor <code>public ProtocolFilterWrapper(Protocol protocol)</code>, and the parameter is the extension point Protocol. So it is a Wrapper class in the Dubbo extension mechanism. The ExtensionLoader will cache it. When creating Extension instances later, the ExtensionLoader use these wrapper classes to wrap the original Extension point in turn.</p><h2 id=extension-point-adaptive>Extension point adaptive</h2><p>As mentioned earlier, Dubbo needs to determine which extension to use based on method parameters at runtime. So there is an extension point adaptive instance. In fact, it is an extension point proxy that delays the selection of extensions from starting Dubbo to calling RPC. Each extension point in Dubbo has an adaptive class. If it is not explicitly provided, Dubbo will automatically create one for us. By default, Javaassist is used.
Let&rsquo;s first look at the code to create an adaptive extension class:</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></span><span style=display:flex><span> Object instance <span style=color:#719e07>=</span> cachedAdaptiveInstance.get();
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (instance <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#268bd2>synchronized</span> (cachedAdaptiveInstance) {
</span></span><span style=display:flex><span> instance <span style=color:#719e07>=</span> cachedAdaptiveInstance.get();
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (instance <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> instance <span style=color:#719e07>=</span> createAdaptiveExtension();
</span></span><span style=display:flex><span> cachedAdaptiveInstance.set(instance);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><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:#719e07>return</span> (T) instance;
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>Continue to read the createAdaptiveExtension method:</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></span><span style=display:flex><span> <span style=color:#719e07>return</span> injectExtension((T) getAdaptiveExtensionClass().newInstance());
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>Continue to read the getAdaptiveExtensionClass method:</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></span><span style=display:flex><span> getExtensionClasses();
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (cachedAdaptiveClass <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> cachedAdaptiveClass;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> cachedAdaptiveClass <span style=color:#719e07>=</span> createAdaptiveExtensionClass();
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>Continue to read the createAdaptiveExtensionClass method. After a long journey, we finally come to a concrete realization. Look at this createAdaptiveExtensionClass method, which first generates the Java source code for the adaptive class, and then compile the source code into Java bytecode and load it into the JVM.</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></span><span style=display:flex><span> String code <span style=color:#719e07>=</span> createAdaptiveExtensionClassCode();
</span></span><span style=display:flex><span> ClassLoader classLoader <span style=color:#719e07>=</span> findClassLoader();
</span></span><span style=display:flex><span> org.apache.dubbo.common.compiler.Compiler compiler <span style=color:#719e07>=</span> ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> compiler.compile(code, classLoader);
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>The default implementation of Compiler&rsquo;s code is javassist.</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 style=color:#2aa198>&#34;javassist&#34;</span>)
</span></span><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#268bd2>interface</span> <span style=color:#268bd2>Compiler</span> {
</span></span><span style=display:flex><span> Class<span style=color:#719e07>&lt;?&gt;</span> compile(String code, ClassLoader classLoader);
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>The createAdaptiveExtensionClassCode () method uses a StringBuilder to build Java source code for the adaptive class. The method implementation is relatively long, and the code is not posted here. The approach to bytecode generation is also interesting, first generating Java source code, then compiling it and loading it into the jvm. In this way, the generated Java class can be better controlled. And it doesn&rsquo;t have to care about the API of the bytecode generation framework. Because the xxx.java file is universal in Java, it is also the one we are most familiar with. However, the code is not very readable and you need to build xx. Java content bit by bit.
Below are the Java code example for Protocol adaptive class created by createAdaptiveExtensionClassCode method:</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:#719e07>package</span> org.apache.dubbo.rpc;
</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></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>Protocol$Adaptive</span> <span style=color:#268bd2>implements</span> org.apache.dubbo.rpc.Protocol {
</span></span><span style=display:flex><span> <span style=color:#268bd2>public</span> <span style=color:#dc322f>void</span> <span style=color:#268bd2>destroy</span>() {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> UnsupportedOperationException(<span style=color:#2aa198>&#34;method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!&#34;</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:#dc322f>int</span> <span style=color:#268bd2>getDefaultPort</span>() {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> UnsupportedOperationException(<span style=color:#2aa198>&#34;method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!&#34;</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> org.apache.dubbo.rpc.Exporter <span style=color:#268bd2>export</span>(org.apache.dubbo.rpc.Invoker arg0) <span style=color:#268bd2>throws</span> org.apache.dubbo.rpc.RpcException {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (arg0 <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalArgumentException(<span style=color:#2aa198>&#34;org.apache.dubbo.rpc.Invoker argument == null&#34;</span>);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (arg0.getUrl() <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>)
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalArgumentException(<span style=color:#2aa198>&#34;org.apache.dubbo.rpc.Invoker argument getUrl() == null&#34;</span>);
</span></span><span style=display:flex><span> org.apache.dubbo.common.URL url <span style=color:#719e07>=</span> arg0.getUrl();
</span></span><span style=display:flex><span> String extName <span style=color:#719e07>=</span> (url.getProtocol() <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>?</span> <span style=color:#2aa198>&#34;dubbo&#34;</span> : url.getProtocol());
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (extName <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>)
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException(<span style=color:#2aa198>&#34;Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url(&#34;</span> <span style=color:#719e07>+</span> url.toString() <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;) use keys([protocol])&#34;</span>);
</span></span><span style=display:flex><span> org.apache.dubbo.rpc.Protocol extension <span style=color:#719e07>=</span> (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> extension.export(arg0);
</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> org.apache.dubbo.rpc.Invoker <span style=color:#268bd2>refer</span>(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) <span style=color:#268bd2>throws</span> org.apache.dubbo.rpc.RpcException {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (arg1 <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalArgumentException(<span style=color:#2aa198>&#34;url == null&#34;</span>);
</span></span><span style=display:flex><span> org.apache.dubbo.common.URL url <span style=color:#719e07>=</span> arg1;
</span></span><span style=display:flex><span> String extName <span style=color:#719e07>=</span> (url.getProtocol() <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>?</span> <span style=color:#2aa198>&#34;dubbo&#34;</span> : url.getProtocol());
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (extName <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>)
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException(<span style=color:#2aa198>&#34;Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url(&#34;</span> <span style=color:#719e07>+</span> url.toString() <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;) use keys([protocol])&#34;</span>);
</span></span><span style=display:flex><span> org.apache.dubbo.rpc.Protocol extension <span style=color:#719e07>=</span> (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> extension.refer(arg0, arg1);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>The general logic is the same as at the beginning. The parameters are parsed through the url, and the parsed logic is controlled by the value parameter of @adaptive, and then the extension points implementation are obtained according to the name of the extension point. And then finally make the call. If you want to know the specific construction logic of .Java code, you can see the complete implementation of <code>createAdaptiveExtensionClassCode</code>.
In the generated Protocol$Adaptive, both the getDefaultPort and destroy methods are found to throw the exception directly. Why? Take a look at the source code of 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-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>@SPI</span>(<span style=color:#2aa198>&#34;dubbo&#34;</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></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#dc322f>int</span> <span style=color:#268bd2>getDefaultPort</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> Exporter<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> <span style=color:#268bd2>export</span>(Invoker<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> invoker) <span style=color:#268bd2>throws</span> RpcException;
</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>(Class<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> type, URL url) <span style=color:#268bd2>throws</span> RpcException;
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#dc322f>void</span> <span style=color:#268bd2>destroy</span>();
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>As you can see, there are four methods in the Protocol interface, but only the methods of export and refer use the @Adaptive annotation. Dubbo automatically generates adaptive instances, and only the methods modified by @Adaptive has a specific implementation. Therefore, in the Protocol$Adaptive class, only the export and refer methods have specific implementations, and the rest of the methods throw exceptions.</p><ul class="list-unstyled d-flex justify-content-between align-items-center mb-0 pt-5"><li><a href=/en/blog/2019/04/25/dubbo-extensible-mechanism-source-code-analysis-part-1/ aria-label="Previous - Dubbo extensible mechanism source code analysis - part 1" class="btn btn-primary"><span class=mr-1>←</span>Previous</a></li><li><a href=/en/blog/2019/08/11/tracing-dubbo-service-with-apache-skywalking/ aria-label="Next - Tracing Dubbo service with Apache Skywalking" class="btn btn-primary">Next<span class=ml-1>β†’</span></a></li></ul></div><div id=pre-footer><h2>Feedback</h2><p class=feedback--prompt>Was this page helpful?</p><button class="btn btn-primary mb-4 feedback--yes">Yes</button>
<button class="btn btn-primary mb-4 feedback--no">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></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/en/blog/news/introduction-to-dubbo-spi-2.md target=_blank><i class="fa fa-edit fa-fw"></i> Edit this page</a>
<a href="https://github.com/apache/dubbo-website/new/master/content/en/blog/news/introduction-to-dubbo-spi-2.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> Create child page</a>
<a href="https://github.com/apache/dubbo-website/issues/new?title=Dubbo%20extensible%20mechanism%20source%20code%20analysis%20-%20part%202" target=_blank><i class="fab fa-github fa-fw"></i> Create an issue</a>
<a href=https://github.com/apache/dubbo/issues/new target=_blank><i class="fas fa-tasks fa-fw"></i> Create project issue</a></div><nav id=TableOfContents><ul><li><a href=#extensionloader>ExtensionLoader</a></li><li><a href=#auto-assembly-of-dubbo-spi-advanced-usage>Auto-assembly of Dubbo SPI advanced usage</a></li><li><a href=#aop-of-dubbo-spi-advanced-usage>AOP of Dubbo SPI advanced usage</a><ul><li><a href=#what-is-the-wrapper-class>What is the Wrapper class</a></li><li><a href=#how-to-configure-the-wrapper-class>How to configure the Wrapper class</a></li></ul></li><li><a href=#extension-point-adaptive>Extension point adaptive</a></li></ul></nav><div class="taxonomy taxonomy-terms-cloud taxo-tags"><h5 class=taxonomy-title>Tags</h5><ul class=taxonomy-terms><li><a class=taxonomy-term href=https://cn.dubbo.apache.org/en/tags/ecosystem/ data-taxonomy-term=ecosystem><span class=taxonomy-label>ecosystem</span><span class=taxonomy-count>1</span></a></li><li><a class=taxonomy-term href=https://cn.dubbo.apache.org/en/tags/news/ data-taxonomy-term=news><span class=taxonomy-label>news</span><span class=taxonomy-count>1</span></a></li><li><a class=taxonomy-term href=https://cn.dubbo.apache.org/en/tags/opentelemetry/ data-taxonomy-term=opentelemetry><span class=taxonomy-label>OpenTelemetry</span><span class=taxonomy-count>1</span></a></li><li><a class=taxonomy-term href=https://cn.dubbo.apache.org/en/tags/tracing/ data-taxonomy-term=tracing><span class=taxonomy-label>tracing</span><span class=taxonomy-count>1</span></a></li></ul></div></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; 2024 The Apache Software Foundation. Apache and the Apache feather logo are trademarks of The Apache Software Foundation. All Rights Reserved</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 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://dubbo.apache.org/en/overview/notices/>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.b075178d232d3b0039b3cb6af2fc2e9d90071820167a60f4eea3a79169975ee8.js integrity="sha256-sHUXjSMtOwA5s8tq8vwunZAHGCAWemD07qOnkWmXXug=" crossorigin=anonymous></script><script async src=https://widget.kapa.ai/kapa-widget.bundle.js data-website-id=d763c4f2-f871-400b-aeca-d986c4af73c2 data-project-name="Apache Dubbo" data-project-color=#E8442E data-button-text="Ask AI" data-search-mode-enabled=true data-modal-open-on-command-k=true data-modal-disclaimer="The AI supports multiple languages, but it may not be accessible in China due to recaptcha, a proxy is required." data-project-logo=https://pbs.twimg.com/profile_images/1011849068283191302/FJbH5vbF_400x400.jpg data-modal-example-questions="What is Apache Dubbo?,How to run Apache Dubbo?" data-button-position-top data-button-position-right=20px data-button-position-bottom=200px data-button-position-left></script><script>(function(e,t,n,s){e[s]=e[s]||[];var a=t.getElementsByTagName(n)[0],i=t.createElement(n);i.async=!0,i.id="beacon-aplus",i.setAttribute("exparams","userid=&aplus&sidx=aplusSidex&ckx=aplusCkx"),i.src="//g.alicdn.com/alilog/mlog/aplus_v2.js",i.crossorigin="anonymous",a.parentNode.insertBefore(i,a)})(window,document,"script","aplus_queue"),function(e){var t=e.createElement("script");t.type="text/javascript",t.async=!0,t.src="//g.alicdn.com/aes/??tracker/3.3.4/index.js,tracker-plugin-pv/3.0.5/index.js,tracker-plugin-event/3.0.0/index.js,tracker-plugin-autolog/3.0.3/index.js,tracker-plugin-survey/3.0.3/index.js,tracker-plugin-jserror/3.0.3/index.js,tracker-plugin-resourceError/3.0.3/index.js",t.onload=function(){window.AES_CONFIG=window.AES_CONFIG||{env:"prod"},window.aes=new AES({pid:"zN245h",user_type:6}),window.AESPluginAutologConfig={exposure:"auto"},window.AEMPluginInstances=[aes.use(AESPluginPV,window.AESPluginPVConfig||{enableHistory:!0}),aes.use(AESPluginEvent,window.AESPluginEventConfig||{}),aes.use(AESPluginSurvey,window.AESPluginEventConfig||{}),aes.use(AESPluginAutolog,window.AESPluginAutologConfig||{}),aes.use(AESPluginJSError,window.AESPluginJSError||{}),aes.use(AESPluginResourceError,window.AESPluginResourceError||{})]},setTimeout(function(){e.getElementsByTagName("body")[0].appendChild(t)},800)}(document)</script></body></html>