{"id":10006820,"date":"2022-11-07T10:40:49","date_gmt":"2022-11-07T02:40:49","guid":{"rendered":"https:\/\/ityet.com\/?p=10006820"},"modified":"2022-11-07T10:40:49","modified_gmt":"2022-11-07T02:40:49","slug":"%e6%ba%90%e7%a0%81%e7%ba%a7%e6%b7%b1%e5%ba%a6%e7%90%86%e8%a7%a3-java-spi","status":"publish","type":"post","link":"https:\/\/itmax.cn\/?p=10006820","title":{"rendered":"\u6e90\u7801\u7ea7\u6df1\u5ea6\u7406\u89e3 Java SPI"},"content":{"rendered":"<blockquote><p>\u4f5c\u8005\uff1avivo \u4e92\u8054\u7f51\u670d\u52a1\u5668\u56e2\u961f- Zhang Peng<\/p><\/blockquote>\n<p>SPI \u662f\u4e00\u79cd\u7528\u4e8e\u52a8\u6001\u52a0\u8f7d\u670d\u52a1\u7684\u673a\u5236\u3002\u5b83\u7684\u6838\u5fc3\u601d\u60f3\u5c31\u662f\u89e3\u8026\uff0c\u5c5e\u4e8e\u5178\u578b\u7684\u5fae\u5185\u6838\u67b6\u6784\u6a21\u5f0f\u3002SPI \u5728 Java \u4e16\u754c\u5e94\u7528\u975e\u5e38\u5e7f\u6cdb\uff0c\u5982\uff1aDubbo\u3001Spring Boot \u7b49\u6846\u67b6\u3002\u672c\u6587\u4ece\u6e90\u7801\u5165\u624b\u5206\u6790\uff0c\u6df1\u5165\u63a2\u8ba8 Java SPI \u7684\u7279\u6027\u3001\u539f\u7406\uff0c\u4ee5\u53ca\u5728\u4e00\u4e9b\u6bd4\u8f83\u7ecf\u5178\u9886\u57df\u7684\u5e94\u7528\u3002<\/p>\n<h2>\u4e00\u3001SPI \u7b80\u4ecb<\/h2>\n<p>SPI \u5168\u79f0 Service Provider Interface\uff0c\u662f Java \u63d0\u4f9b\u7684\uff0c\u65e8\u5728\u7531\u7b2c\u4e09\u65b9\u5b9e\u73b0\u6216\u6269\u5c55\u7684 API\uff0c\u5b83\u662f\u4e00\u79cd\u7528\u4e8e\u52a8\u6001\u52a0\u8f7d\u670d\u52a1\u7684\u673a\u5236\u3002Java \u4e2d SPI \u673a\u5236\u4e3b\u8981\u601d\u60f3\u662f\u5c06\u88c5\u914d\u7684\u63a7\u5236\u6743\u79fb\u5230\u7a0b\u5e8f\u4e4b\u5916\uff0c\u5728\u6a21\u5757\u5316\u8bbe\u8ba1\u4e2d\u8fd9\u4e2a\u673a\u5236\u5c24\u5176\u91cd\u8981\uff0c\u5176\u6838\u5fc3\u601d\u60f3\u5c31\u662f \u89e3\u8026\u3002<\/p>\n<p>Java SPI \u6709\u56db\u4e2a\u8981\u7d20\uff1a<\/p>\n<ul>\n<li><strong>SPI \u63a5\u53e3\uff1a<\/strong>\u4e3a\u670d\u52a1\u63d0\u4f9b\u8005\u5b9e\u73b0\u7c7b\u7ea6\u5b9a\u7684\u7684\u63a5\u53e3\u6216\u62bd\u8c61\u7c7b\u3002<\/li>\n<li><strong>SPI \u5b9e\u73b0\u7c7b\uff1a<\/strong>\u5b9e\u9645\u63d0\u4f9b\u670d\u52a1\u7684\u5b9e\u73b0\u7c7b\u3002<\/li>\n<li><strong>SPI \u914d\u7f6e\uff1a<\/strong>Java SPI \u673a\u5236\u7ea6\u5b9a\u7684\u914d\u7f6e\u6587\u4ef6\uff0c\u63d0\u4f9b\u67e5\u627e\u670d\u52a1\u5b9e\u73b0\u7c7b\u7684\u903b\u8f91\u3002\u914d\u7f6e\u6587\u4ef6\u5fc5\u987b\u7f6e\u4e8e META-INF\/services \u76ee\u5f55\u4e2d\uff0c\u5e76\u4e14\uff0c\u6587\u4ef6\u540d\u5e94\u4e0e\u670d\u52a1\u63d0\u4f9b\u8005\u63a5\u53e3\u7684\u5b8c\u5168\u9650\u5b9a\u540d\u4fdd\u6301\u4e00\u81f4\u3002\u6587\u4ef6\u4e2d\u7684\u6bcf\u4e00\u884c\u90fd\u6709\u4e00\u4e2a\u5b9e\u73b0\u670d\u52a1\u7c7b\u7684\u8be6\u7ec6\u4fe1\u606f\uff0c\u540c\u6837\u662f\u670d\u52a1\u63d0\u4f9b\u8005\u7c7b\u7684\u5b8c\u5168\u9650\u5b9a\u540d\u79f0\u3002<\/li>\n<li><strong>ServiceLoader\uff1a<\/strong>Java SPI \u7684\u6838\u5fc3\u7c7b\uff0c\u7528\u4e8e\u52a0\u8f7d SPI \u5b9e\u73b0\u7c7b\u3002ServiceLoader \u4e2d\u6709\u5404\u79cd\u5b9e\u7528\u65b9\u6cd5\u6765\u83b7\u53d6\u7279\u5b9a\u5b9e\u73b0\u3001\u8fed\u4ee3\u5b83\u4eec\u6216\u91cd\u65b0\u52a0\u8f7d\u670d\u52a1\u3002<\/li>\n<\/ul>\n<h2>\u4e8c\u3001SPI \u793a\u4f8b<\/h2>\n<p>\u6b63\u6240\u8c13\uff0c\u5b9e\u8df5\u51fa\u771f\u77e5\uff0c\u6211\u4eec\u4e0d\u59a8\u901a\u8fc7\u4e00\u4e2a\u5177\u4f53\u7684\u793a\u4f8b\u6765\u770b\u4e00\u4e0b\uff0c\u5982\u4f55\u4f7f\u7528 Java SPI\u3002<\/p>\n<h4>2.1 SPI \u63a5\u53e3<\/h4>\n<p>\u9996\u5148\uff0c\u9700\u8981\u5b9a\u4e49\u4e00\u4e2a SPI \u63a5\u53e3\uff0c\u548c\u666e\u901a\u63a5\u53e3\u5e76\u6ca1\u6709\u4ec0\u4e48\u5dee\u522b\u3002<\/p>\n<pre><code class=\"java\">package io.github.dunwu.javacore.spi;\n\npublic interface DataStorage {\n    String search(String key);\n}<\/code><\/pre>\n<h4>2.2 SPI \u5b9e\u73b0\u7c7b<\/h4>\n<p>\u5047\u8bbe\uff0c\u6211\u4eec\u9700\u8981\u5728\u7a0b\u5e8f\u4e2d\u4f7f\u7528\u4e24\u79cd\u4e0d\u540c\u7684\u6570\u636e\u5b58\u50a8\u2014\u2014MySQL \u548c Redis\u3002\u56e0\u6b64\uff0c\u6211\u4eec\u9700\u8981\u4e24\u4e2a\u4e0d\u540c\u7684\u5b9e\u73b0\u7c7b\u53bb\u5206\u522b\u5b8c\u6210\u76f8\u5e94\u5de5\u4f5c\u3002<\/p>\n<p>MySQL\u67e5\u8be2 MOCK \u7c7b<\/p>\n<pre><code class=\"java\">package io.github.dunwu.javacore.spi;\n\npublic class MysqlStorage implements DataStorage {\n    @Override\n    public String search(String key) {\n        return &quot;\u3010Mysql\u3011\u641c\u7d22&quot; + key + &quot;\uff0c\u7ed3\u679c\uff1aNo&quot;;\n    }\n}<\/code><\/pre>\n<p>Redis \u67e5\u8be2 MOCK \u7c7b<\/p>\n<pre><code class=\"java\">package io.github.dunwu.javacore.spi;\n\npublic class RedisStorage implements DataStorage {\n    @Override\n    public String search(String key) {\n        return &quot;\u3010Redis\u3011\u641c\u7d22&quot; + key + &quot;\uff0c\u7ed3\u679c\uff1aYes&quot;;\n    }\n}<\/code><\/pre>\n<p>service \u4f20\u5165\u7684\u662f\u671f\u671b\u52a0\u8f7d\u7684 SPI \u63a5\u53e3\u7c7b\u578b \u5230\u76ee\u524d\u4e3a\u6b62\uff0c\u5b9a\u4e49\u63a5\u53e3\uff0c\u5e76\u5b9e\u73b0\u63a5\u53e3\u548c\u666e\u901a\u7684 Java \u63a5\u53e3\u5b9e\u73b0\u6ca1\u6709\u4efb\u4f55\u4e0d\u540c\u3002<\/p>\n<h4>2.3 SPI \u914d\u7f6e<\/h4>\n<p>\u5982\u679c\u60f3\u901a\u8fc7 Java SPI \u673a\u5236\u6765\u53d1\u73b0\u670d\u52a1\uff0c\u5c31\u9700\u8981\u5728 SPI \u914d\u7f6e\u4e2d\u7ea6\u5b9a\u597d\u53d1\u73b0\u670d\u52a1\u7684\u903b\u8f91\u3002\u914d\u7f6e\u6587\u4ef6\u5fc5\u987b\u7f6e\u4e8e META-INF\/services \u76ee\u5f55\u4e2d\uff0c\u5e76\u4e14\uff0c\u6587\u4ef6\u540d\u5e94\u4e0e\u670d\u52a1\u63d0\u4f9b\u8005\u63a5\u53e3\u7684\u5b8c\u5168\u9650\u5b9a\u540d\u4fdd\u6301\u4e00\u81f4\u3002\u6587\u4ef6\u4e2d\u7684\u6bcf\u4e00\u884c\u90fd\u6709\u4e00\u4e2a\u5b9e\u73b0\u670d\u52a1\u7c7b\u7684\u8be6\u7ec6\u4fe1\u606f\uff0c\u540c\u6837\u662f\u670d\u52a1\u63d0\u4f9b\u8005\u7c7b\u7684\u5b8c\u5168\u9650\u5b9a\u540d\u79f0\u3002\u4ee5\u672c\u793a\u4f8b\u4ee3\u7801\u4e3a\u4f8b\uff0c\u5176\u6587\u4ef6\u540d\u5e94\u8be5\u4e3aio.github.dunwu.javacore.spi.DataStorage\uff0c<\/p>\n<p>\u6587\u4ef6\u4e2d\u7684\u5185\u5bb9\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"java\">io.github.dunwu.javacore.spi.MysqlStorage\nio.github.dunwu.javacore.spi.RedisStorage<\/code><\/pre>\n<h4>2.4 ServiceLoader<\/h4>\n<p>\u5b8c\u6210\u4e86\u4e0a\u9762\u7684\u6b65\u9aa4\uff0c\u5c31\u53ef\u4ee5\u901a\u8fc7 ServiceLoader \u6765\u52a0\u8f7d\u670d\u52a1\u3002\u793a\u4f8b\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"java\">import java.util.ServiceLoader;\n\npublic class SpiDemo {\n\n    public static void main(String[] args) {\n        ServiceLoader&lt;DataStorage&gt; serviceLoader = ServiceLoader.load(DataStorage.class);\n        System.out.println(&quot;============ Java SPI \u6d4b\u8bd5============&quot;);\n        serviceLoader.forEach(loader -&gt; System.out.println(loader.search(&quot;Yes Or No&quot;)));\n    }\n\n}<\/code><\/pre>\n<p>\u8f93\u51fa\uff1a<\/p>\n<pre><code class=\"java\">============ Java SPI \u6d4b\u8bd5============\n\u3010Mysql\u3011\u641c\u7d22Yes Or No\uff0c\u7ed3\u679c\uff1aNo\n\u3010Redis\u3011\u641c\u7d22Yes Or No\uff0c\u7ed3\u679c\uff1aYes<\/code><\/pre>\n<h2>\u4e09\u3001SPI \u539f\u7406<\/h2>\n<p>\u4e0a\u6587\u4e2d\uff0c\u6211\u4eec\u5df2\u7ecf\u4e86\u89e3 Java SPI \u7684\u8981\u7d20\u4ee5\u53ca\u4f7f\u7528 Java SPI \u7684\u65b9\u6cd5\u3002\u4f60\u6709\u6ca1\u6709\u60f3\u8fc7\uff0cJava SPI \u548c\u666e\u901a Java \u63a5\u53e3\u6709\u4f55\u4e0d\u540c\uff0cJava SPI \u662f\u5982\u4f55\u5de5\u4f5c\u7684\u3002\u5b9e\u9645\u4e0a\uff0cJava SPI \u673a\u5236\u4f9d\u8d56\u4e8e ServiceLoader \u7c7b\u53bb\u89e3\u6790\u3001\u52a0\u8f7d\u670d\u52a1\u3002\u56e0\u6b64\uff0c\u638c\u63e1\u4e86 ServiceLoader \u7684\u5de5\u4f5c\u6d41\u7a0b\uff0c\u5c31\u638c\u63e1\u4e86 SPI \u7684\u539f\u7406\u3002ServiceLoader \u7684\u4ee3\u7801\u672c\u8eab\u5f88\u7cbe\u7ec3\uff0c\u63a5\u4e0b\u6765\uff0c\u8ba9\u6211\u4eec\u901a\u8fc7\u8d70\u8bfb\u6e90\u7801\u7684\u65b9\u5f0f\uff0c\u9010\u4e00\u7406\u89e3 ServiceLoader \u7684\u5de5\u4f5c\u6d41\u7a0b\u3002<\/p>\n<h4>3.1 ServiceLoader \u7684\u6210\u5458\u53d8\u91cf<\/h4>\n<p>\u5148\u770b\u4e00\u4e0b ServiceLoader \u7c7b\u7684\u6210\u5458\u53d8\u91cf\uff0c\u5927\u81f4\u6709\u4e2a\u5370\u8c61\uff0c\u540e\u9762\u7684\u6e90\u7801\u4e2d\u90fd\u4f1a\u4f7f\u7528\u5230\u3002<\/p>\n<pre><code class=\"java\">public final class ServiceLoader&lt;S&gt; implements Iterable&lt;S&gt; {\n\n    \/\/ SPI \u914d\u7f6e\u6587\u4ef6\u76ee\u5f55\n    private static final String PREFIX = &quot;META-INF\/services\/&quot;;\n\n    \/\/ \u5c06\u8981\u88ab\u52a0\u8f7d\u7684 SPI \u670d\u52a1\n    private final Class&lt;S&gt; service;\n\n    \/\/ \u7528\u4e8e\u52a0\u8f7d SPI \u670d\u52a1\u7684\u7c7b\u52a0\u8f7d\u5668\n    private final ClassLoader loader;\n\n    \/\/ ServiceLoader \u521b\u5efa\u65f6\u7684\u8bbf\u95ee\u63a7\u5236\u4e0a\u4e0b\u6587\n    private final AccessControlContext acc;\n\n    \/\/ SPI \u670d\u52a1\u7f13\u5b58\uff0c\u6309\u5b9e\u4f8b\u5316\u7684\u987a\u5e8f\u6392\u5217\n    private LinkedHashMap&lt;String,S&gt; providers = new LinkedHashMap&lt;&gt;();\n\n    \/\/ \u61d2\u67e5\u8be2\u8fed\u4ee3\u5668\n    private LazyIterator lookupIterator;\n\n    \/\/ ...\n}<\/code><\/pre>\n<h4>3.2 ServiceLoader \u7684\u5de5\u4f5c\u6d41\u7a0b<\/h4>\n<p><strong>\uff081\uff09ServiceLoader.load \u9759\u6001\u65b9\u6cd5<\/strong><\/p>\n<p>\u5e94\u7528\u7a0b\u5e8f\u52a0\u8f7d Java SPI \u670d\u52a1\uff0c\u90fd\u662f\u5148\u8c03\u7528 ServiceLoader.load \u9759\u6001\u65b9\u6cd5\u3002<\/p>\n<p>ServiceLoader.load \u9759\u6001\u65b9\u6cd5\u7684\u4f5c\u7528\u662f\uff1a<\/p>\n<p>\u2460 \u6307\u5b9a\u7c7b\u52a0\u8f7d ClassLoader \u548c\u8bbf\u95ee\u63a7\u5236\u4e0a\u4e0b\u6587\uff1b<\/p>\n<p>\u2461 \u7136\u540e\uff0c\u91cd\u65b0\u52a0\u8f7d SPI \u670d\u52a1<\/p>\n<ul>\n<li>\u6e05\u7a7a\u7f13\u5b58\u4e2d\u6240\u6709\u5df2\u5b9e\u4f8b\u5316\u7684 SPI \u670d\u52a1<\/li>\n<li>\u6839\u636e ClassLoader \u548c SPI \u7c7b\u578b\uff0c\u521b\u5efa\u61d2\u52a0\u8f7d\u8fed\u4ee3\u5668<\/li>\n<\/ul>\n<p>\u8fd9\u91cc\uff0c\u6458\u5f55 ServiceLoader.load \u76f8\u5173\u6e90\u7801\uff0c\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"java\">\/\/ service \u4f20\u5165\u7684\u662f\u671f\u671b\u52a0\u8f7d\u7684 SPI \u63a5\u53e3\u7c7b\u578b\n\/\/ loader \u662f\u7528\u4e8e\u52a0\u8f7d SPI \u670d\u52a1\u7684\u7c7b\u52a0\u8f7d\u5668\npublic static &lt;S&gt; ServiceLoader&lt;S&gt; load(Class&lt;S&gt; service, ClassLoader loader) {\n  return new ServiceLoader&lt;&gt;(service, loader);\n}\n\npublic void reload() {\n    \/\/ \u6e05\u7a7a\u7f13\u5b58\u4e2d\u6240\u6709\u5df2\u5b9e\u4f8b\u5316\u7684 SPI \u670d\u52a1\n  providers.clear();\n    \/\/ \u6839\u636e ClassLoader \u548c SPI \u7c7b\u578b\uff0c\u521b\u5efa\u61d2\u52a0\u8f7d\u8fed\u4ee3\u5668\n  lookupIterator = new LazyIterator(service, loader);\n}\n\n\/\/ \u79c1\u6709\u6784\u9020\u65b9\u6cd5\n\/\/ \u91cd\u65b0\u52a0\u8f7d SPI \u670d\u52a1\nprivate ServiceLoader(Class&lt;S&gt; svc, ClassLoader cl) {\n  service = Objects.requireNonNull(svc, &quot;Service interface cannot be null&quot;);\n    \/\/ \u6307\u5b9a\u7c7b\u52a0\u8f7d ClassLoader \u548c\u8bbf\u95ee\u63a7\u5236\u4e0a\u4e0b\u6587\n  loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;\n  acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;\n    \/\/ \u7136\u540e\uff0c\u91cd\u65b0\u52a0\u8f7d SPI \u670d\u52a1\n  reload();\n}<\/code><\/pre>\n<p><strong>\uff082\uff09\u5e94\u7528\u7a0b\u5e8f\u901a\u8fc7 ServiceLoader \u7684 iterator \u65b9\u6cd5\u904d\u5386 SPI \u5b9e\u4f8b<\/strong><\/p>\n<p>ServiceLoader \u7684\u7c7b\u5b9a\u4e49\uff0c\u660e\u786e\u4e86 ServiceLoader \u7c7b\u5b9e\u73b0\u4e86\u00a0Iterable&lt;T&gt;\u00a0\u63a5\u53e3\uff0c\u6240\u4ee5\uff0c\u5b83\u662f\u53ef\u4ee5\u8fed\u4ee3\u904d\u5386\u7684\u3002\u5b9e\u9645\u4e0a\uff0cServiceLoader \u7c7b\u7ef4\u62a4\u4e86\u4e00\u4e2a\u7f13\u5b58 providers\uff08\u00a0LinkedHashMap\u00a0\u5bf9\u8c61\uff09\uff0c\u7f13\u5b58 providers \u4e2d\u4fdd\u5b58\u4e86\u5df2\u7ecf\u88ab\u6210\u529f\u52a0\u8f7d\u7684 SPI \u5b9e\u4f8b\uff0c\u8fd9\u4e2a Map \u7684 key \u662f SPI \u63a5\u53e3\u5b9e\u73b0\u7c7b\u7684\u5168\u9650\u5b9a\u540d\uff0cvalue \u662f\u8be5\u5b9e\u73b0\u7c7b\u7684\u4e00\u4e2a\u5b9e\u4f8b\u5bf9\u8c61\u3002<\/p>\n<p>\u5f53\u5e94\u7528\u7a0b\u5e8f\u8c03\u7528 ServiceLoader \u7684 iterator \u65b9\u6cd5\u65f6\uff0cServiceLoader \u4f1a\u5148\u5224\u65ad\u7f13\u5b58 providers \u4e2d\u662f\u5426\u6709\u6570\u636e\uff1a\u5982\u679c\u6709\uff0c\u5219\u76f4\u63a5\u8fd4\u56de\u7f13\u5b58 providers \u7684\u8fed\u4ee3\u5668\uff1b\u5982\u679c\u6ca1\u6709\uff0c\u5219\u8fd4\u56de\u61d2\u52a0\u8f7d\u8fed\u4ee3\u5668\u7684\u8fed\u4ee3\u5668\u3002<\/p>\n<pre><code class=\"java\">public Iterator&lt;S&gt; iterator() {\n  return new Iterator&lt;S&gt;() {\n\n        \/\/ \u7f13\u5b58 SPI providers\n    Iterator&lt;Map.Entry&lt;String,S&gt;&gt; knownProviders\n      = providers.entrySet().iterator();\n\n        \/\/ lookupIterator \u662f LazyIterator \u5b9e\u4f8b\uff0c\u7528\u4e8e\u61d2\u52a0\u8f7d SPI \u5b9e\u4f8b\n    public boolean hasNext() {\n      if (knownProviders.hasNext())\n        return true;\n      return lookupIterator.hasNext();\n    }\n\n    public S next() {\n      if (knownProviders.hasNext())\n        return knownProviders.next().getValue();\n      return lookupIterator.next();\n    }\n\n    public void remove() {\n      throw new UnsupportedOperationException();\n    }\n\n  };\n}<\/code><\/pre>\n<p><strong>\uff083\uff09\u61d2\u52a0\u8f7d\u8fed\u4ee3\u5668\u7684\u5de5\u4f5c\u6d41\u7a0b<\/strong><\/p>\n<p>\u4e0a\u9762\u7684\u6e90\u7801\u4e2d\u63d0\u5230\u4e86\uff0clookupIterator \u662f LazyIterator \u5b9e\u4f8b\uff0c\u800c LazyIterator \u7528\u4e8e\u61d2\u52a0\u8f7d SPI \u5b9e\u4f8b\u3002\u90a3\u4e48\uff0c LazyIterator \u662f\u5982\u4f55\u5de5\u4f5c\u7684\u5462\uff1f<\/p>\n<p>\u8fd9\u91cc\uff0c\u6458\u53d6 LazyIterator \u5173\u952e\u4ee3\u7801<\/p>\n<blockquote><p><strong>hasNextService \u65b9\u6cd5\uff1a<\/strong><\/p><\/blockquote>\n<ul>\n<li>\u62fc\u63a5\u00a0META-INF\/services\/\u00a0+ SPI \u63a5\u53e3\u5168\u9650\u5b9a\u540d<\/li>\n<li>\u901a\u8fc7\u7c7b\u52a0\u8f7d\u5668\uff0c\u5c1d\u8bd5\u52a0\u8f7d\u8d44\u6e90\u6587\u4ef6<\/li>\n<li>\u89e3\u6790\u8d44\u6e90\u6587\u4ef6\u4e2d\u7684\u5185\u5bb9\uff0c\u83b7\u53d6 SPI \u63a5\u53e3\u7684\u5b9e\u73b0\u7c7b\u7684\u5168\u9650\u5b9a\u540d\u00a0nextName<\/li>\n<\/ul>\n<blockquote><p><strong>nextService \u65b9\u6cd5\uff1a<\/strong><\/p><\/blockquote>\n<ul>\n<li>hasNextService()\u00a0\u65b9\u6cd5\u89e3\u6790\u51fa\u4e86 SPI \u5b9e\u73b0\u7c7b\u7684\u7684\u5168\u9650\u5b9a\u540d nextName\uff0c\u901a\u8fc7\u53cd\u5c04\uff0c\u83b7\u53d6 SPI \u5b9e\u73b0\u7c7b\u7684\u7c7b\u5b9a\u4e49 Class\u3002<\/li>\n<li>\u7136\u540e\uff0c\u5c1d\u8bd5\u901a\u8fc7 Class \u7684 newInstance \u65b9\u6cd5\u5b9e\u4f8b\u5316\u4e00\u4e2a SPI \u670d\u52a1\u5bf9\u8c61\u3002\u5982\u679c\u6210\u529f\uff0c\u5219\u5c06\u8fd9\u4e2a\u5bf9\u8c61\u52a0\u5165\u5230\u7f13\u5b58 providers \u4e2d\u5e76\u8fd4\u56de\u8be5\u5bf9\u8c61\u3002<\/li>\n<\/ul>\n<pre><code class=\"java\">private boolean hasNextService() {\n  if (nextName != null) {\n    return true;\n  }\n  if (configs == null) {\n    try {\n            \/\/ 1.\u62fc\u63a5 META-INF\/services\/ + SPI \u63a5\u53e3\u5168\u9650\u5b9a\u540d\n            \/\/ 2.\u901a\u8fc7\u7c7b\u52a0\u8f7d\u5668\uff0c\u5c1d\u8bd5\u52a0\u8f7d\u8d44\u6e90\u6587\u4ef6\n            \/\/ 3.\u89e3\u6790\u8d44\u6e90\u6587\u4ef6\u4e2d\u7684\u5185\u5bb9\n      String fullName = PREFIX + service.getName();\n      if (loader == null)\n        configs = ClassLoader.getSystemResources(fullName);\n      else\n        configs = loader.getResources(fullName);\n    } catch (IOException x) {\n      fail(service, &quot;Error locating configuration files&quot;, x);\n    }\n  }\n  while ((pending == null) || !pending.hasNext()) {\n    if (!configs.hasMoreElements()) {\n      return false;\n    }\n    pending = parse(service, configs.nextElement());\n  }\n  nextName = pending.next();\n  return true;\n}\n\nprivate S nextService() {\n  if (!hasNextService())\n    throw new NoSuchElementException();\n  String cn = nextName;\n  nextName = null;\n  Class&lt;?&gt; c = null;\n  try {\n    c = Class.forName(cn, false, loader);\n  } catch (ClassNotFoundException x) {\n    fail(service,\n       &quot;Provider &quot; + cn + &quot; not found&quot;);\n  }\n  if (!service.isAssignableFrom(c)) {\n    fail(service,\n       &quot;Provider &quot; + cn  + &quot; not a s&quot;);\n  }\n  try {\n    S p = service.cast(c.newInstance());\n    providers.put(cn, p);\n    return p;\n  } catch (Throwable x) {\n    fail(service,\n       &quot;Provider &quot; + cn + &quot; could not be instantiated&quot;,\n       x);\n  }\n  throw new Error();          \/\/ This cannot happen\n}<\/code><\/pre>\n<h4>3.3 SPI \u548c\u7c7b\u52a0\u8f7d\u5668<\/h4>\n<p>\u901a\u8fc7\u4e0a\u9762\u4e24\u4e2a\u7ae0\u8282\u4e2d\uff0c\u8d70\u8bfb ServiceLoader \u4ee3\u7801\uff0c\u6211\u4eec\u5df2\u7ecf\u5927\u81f4\u4e86\u89e3 Java SPI \u7684\u5de5\u4f5c\u539f\u7406\uff0c\u5373\u901a\u8fc7 ClassLoader \u52a0\u8f7d SPI \u914d\u7f6e\u6587\u4ef6\uff0c\u89e3\u6790 SPI \u670d\u52a1\uff0c\u7136\u540e\u901a\u8fc7\u53cd\u5c04\uff0c\u5b9e\u4f8b\u5316 SPI \u670d\u52a1\u5b9e\u4f8b\u3002\u6211\u4eec\u4e0d\u59a8\u601d\u8003\u4e00\u4e0b\uff0c\u4e3a\u4ec0\u4e48\u52a0\u8f7d SPI \u670d\u52a1\u65f6\uff0c\u9700\u8981\u6307\u5b9a\u7c7b\u52a0\u8f7d\u5668 ClassLoader \u5462\uff1f<\/p>\n<p>\u5b66\u4e60\u8fc7 JVM \u7684\u8bfb\u8005\uff0c\u60f3\u5fc5\u90fd\u4e86\u89e3\u8fc7\u7c7b\u52a0\u8f7d\u5668\u7684<strong>\u53cc\u4eb2\u59d4\u6d3e\u6a21\u578b<\/strong>\uff08Parents Delegation Model\uff09\u3002\u53cc\u4eb2\u59d4\u6d3e\u6a21\u578b\u8981\u6c42\u9664\u4e86\u9876\u5c42\u7684 BootstrapClassLoader \u5916\uff0c\u5176\u4f59\u7684\u7c7b\u52a0\u8f7d\u5668\u90fd\u5e94\u6709\u81ea\u5df1\u7684\u7236\u7c7b\u52a0\u8f7d\u5668\u3002\u8fd9\u91cc\u7c7b\u52a0\u8f7d\u5668\u4e4b\u95f4\u7684\u7236\u5b50\u5173\u7cfb\u4e00\u822c\u901a\u8fc7\u7ec4\u5408\uff08Composition\uff09\u5173\u7cfb\u6765\u5b9e\u73b0\uff0c\u800c\u4e0d\u662f\u901a\u8fc7\u7ee7\u627f\uff08Inheritance\uff09\u7684\u5173\u7cfb\u5b9e\u73b0\u3002<\/p>\n<p>\u53cc\u4eb2\u59d4\u6d3e\u673a\u5236\u7ea6\u5b9a\u4e86\uff1a<strong>\u4e00\u4e2a\u7c7b\u52a0\u8f7d\u5668\u9996\u5148\u5c06\u7c7b\u52a0\u8f7d\u8bf7\u6c42\u4f20\u9001\u5230\u7236\u7c7b\u52a0\u8f7d\u5668\uff0c\u53ea\u6709\u5f53\u7236\u7c7b\u52a0\u8f7d\u5668\u65e0\u6cd5\u5b8c\u6210\u7c7b\u52a0\u8f7d\u8bf7\u6c42\u65f6\u624d\u5c1d\u8bd5\u52a0\u8f7d\u3002<\/strong><\/p>\n<p><strong>\u53cc\u4eb2\u59d4\u6d3e\u7684\u597d\u5904\uff1a<\/strong>\u4f7f\u5f97 Java \u7c7b\u4f34\u968f\u7740\u5b83\u7684\u7c7b\u52a0\u8f7d\u5668\uff0c\u5929\u7136\u5177\u5907\u4e00\u79cd\u5e26\u6709\u4f18\u5148\u7ea7\u7684\u5c42\u6b21\u5173\u7cfb\uff0c\u4ece\u800c\u4f7f\u5f97\u7c7b\u52a0\u8f7d\u5f97\u5230\u7edf\u4e00\uff0c\u4e0d\u4f1a\u51fa\u73b0\u91cd\u590d\u52a0\u8f7d\u7684\u95ee\u9898\uff1a<\/p>\n<ol>\n<li>\u7cfb\u7edf\u7c7b\u9632\u6b62\u5185\u5b58\u4e2d\u51fa\u73b0\u591a\u4efd\u540c\u6837\u7684\u5b57\u8282\u7801<\/li>\n<li>\u4fdd\u8bc1 Java \u7a0b\u5e8f\u5b89\u5168\u7a33\u5b9a\u8fd0\u884c<\/li>\n<\/ol>\n<p>\u4f8b\u5982\uff1ajava.lang.Object \u5b58\u653e\u5728 rt.jar \u4e2d\uff0c\u5982\u679c\u7f16\u5199\u53e6\u5916\u4e00\u4e2a java.lang.Object \u7684\u7c7b\u5e76\u653e\u5230 classpath \u4e2d\uff0c\u7a0b\u5e8f\u53ef\u4ee5\u7f16\u8bd1\u901a\u8fc7\u3002\u56e0\u4e3a\u53cc\u4eb2\u59d4\u6d3e\u6a21\u578b\u7684\u5b58\u5728\uff0c\u6240\u4ee5\u5728 rt.jar \u4e2d\u7684 Object \u6bd4\u5728 classpath \u4e2d\u7684 Object \u4f18\u5148\u7ea7\u66f4\u9ad8\uff0c\u56e0\u4e3a rt.jar \u4e2d\u7684 Object \u4f7f\u7528\u7684\u662f\u542f\u52a8\u7c7b\u52a0\u8f7d\u5668\uff0c\u800c classpath \u4e2d\u7684 Object \u4f7f\u7528\u7684\u662f\u5e94\u7528\u7a0b\u5e8f\u7c7b\u52a0\u8f7d\u5668\u3002\u6b63\u56e0\u4e3a rt.jar \u4e2d\u7684 Object \u4f18\u5148\u7ea7\u66f4\u9ad8\uff0c\u56e0\u4e3a\u7a0b\u5e8f\u4e2d\u6240\u6709\u7684 Object \u90fd\u662f\u8fd9\u4e2a Object\u3002<\/p>\n<p><strong>\u53cc\u4eb2\u59d4\u6d3e\u7684\u9650\u5236\uff1a<\/strong>\u5b50\u7c7b\u52a0\u8f7d\u5668\u53ef\u4ee5\u4f7f\u7528\u7236\u7c7b\u52a0\u8f7d\u5668\u5df2\u7ecf\u52a0\u8f7d\u7684\u7c7b\uff0c\u800c\u7236\u7c7b\u52a0\u8f7d\u5668\u65e0\u6cd5\u4f7f\u7528\u5b50\u7c7b\u52a0\u8f7d\u5668\u5df2\u7ecf\u52a0\u8f7d\u7684\u3002\u2014\u2014\u8fd9\u5c31\u5bfc\u81f4\u4e86\u53cc\u4eb2\u59d4\u6d3e\u6a21\u578b\u5e76\u4e0d\u80fd\u89e3\u51b3\u6240\u6709\u7684\u7c7b\u52a0\u8f7d\u5668\u95ee\u9898\u3002Java SPI \u5c31\u9762\u4e34\u7740\u8fd9\u6837\u7684\u95ee\u9898\uff1a<\/p>\n<ul>\n<li>SPI \u7684\u63a5\u53e3\u662f Java \u6838\u5fc3\u5e93\u7684\u4e00\u90e8\u5206\uff0c\u662f\u7531 BootstrapClassLoader \u52a0\u8f7d\u7684\uff1b<\/li>\n<li>\u800c SPI \u5b9e\u73b0\u7684 Java \u7c7b\u4e00\u822c\u662f\u7531 AppClassLoader \u6765\u52a0\u8f7d\u7684\u3002BootstrapClassLoader \u662f\u65e0\u6cd5\u627e\u5230 SPI \u7684\u5b9e\u73b0\u7c7b\u7684\uff0c\u56e0\u4e3a\u5b83\u53ea\u52a0\u8f7d Java \u7684\u6838\u5fc3\u5e93\u3002\u5b83\u4e5f\u4e0d\u80fd\u4ee3\u7406\u7ed9 AppClassLoader\uff0c\u56e0\u4e3a\u5b83\u662f\u6700\u9876\u5c42\u7684\u7c7b\u52a0\u8f7d\u5668\u3002\u8fd9\u4e5f\u89e3\u91ca\u4e86\u672c\u8282\u5f00\u59cb\u7684\u95ee\u9898\u2014\u2014\u4e3a\u4ec0\u4e48\u52a0\u8f7d SPI \u670d\u52a1\u65f6\uff0c\u9700\u8981\u6307\u5b9a\u7c7b\u52a0\u8f7d\u5668 ClassLoader \u5462\uff1f\u56e0\u4e3a\u5982\u679c\u4e0d\u6307\u5b9a ClassLoader\uff0c\u5219\u65e0\u6cd5\u83b7\u53d6 SPI \u670d\u52a1\u3002<\/li>\n<\/ul>\n<p>\u5982\u679c\u4e0d\u505a\u4efb\u4f55\u7684\u8bbe\u7f6e\uff0cJava \u5e94\u7528\u7684\u7ebf\u7a0b\u7684\u4e0a\u4e0b\u6587\u7c7b\u52a0\u8f7d\u5668\u9ed8\u8ba4\u5c31\u662f AppClassLoader\u3002\u5728\u6838\u5fc3\u7c7b\u5e93\u4f7f\u7528 SPI \u63a5\u53e3\u65f6\uff0c\u4f20\u9012\u7684\u7c7b\u52a0\u8f7d\u5668\u4f7f\u7528\u7ebf\u7a0b\u4e0a\u4e0b\u6587\u7c7b\u52a0\u8f7d\u5668\uff0c\u5c31\u53ef\u4ee5\u6210\u529f\u7684\u52a0\u8f7d\u5230 SPI \u5b9e\u73b0\u7684\u7c7b\u3002\u7ebf\u7a0b\u4e0a\u4e0b\u6587\u7c7b\u52a0\u8f7d\u5668\u5728\u5f88\u591a SPI \u7684\u5b9e\u73b0\u4e2d\u90fd\u4f1a\u7528\u5230\u3002<\/p>\n<p>\u901a\u5e38\u53ef\u4ee5\u901a\u8fc7Thread.currentThread().getClassLoader()\u548c Thread.currentThread().getContextClassLoader() \u83b7\u53d6\u7ebf\u7a0b\u4e0a\u4e0b\u6587\u7c7b\u52a0\u8f7d\u5668\u3002<\/p>\n<h4>3.4 Java SPI \u7684\u4e0d\u8db3<\/h4>\n<p>Java SPI \u5b58\u5728\u4e00\u4e9b\u4e0d\u8db3\uff1a<\/p>\n<ul>\n<li>\u4e0d\u80fd\u6309\u9700\u52a0\u8f7d\uff0c\u9700\u8981\u904d\u5386\u6240\u6709\u7684\u5b9e\u73b0\uff0c\u5e76\u5b9e\u4f8b\u5316\uff0c\u7136\u540e\u5728\u5faa\u73af\u4e2d\u624d\u80fd\u627e\u5230\u6211\u4eec\u9700\u8981\u7684\u5b9e\u73b0\u3002\u5982\u679c\u4e0d\u60f3\u7528\u67d0\u4e9b\u5b9e\u73b0\u7c7b\uff0c\u6216\u8005\u67d0\u4e9b\u7c7b\u5b9e\u4f8b\u5316\u5f88\u8017\u65f6\uff0c\u5b83\u4e5f\u88ab\u8f7d\u5165\u5e76\u5b9e\u4f8b\u5316\u4e86\uff0c\u8fd9\u5c31\u9020\u6210\u4e86\u6d6a\u8d39\u3002<\/li>\n<li>\u83b7\u53d6\u67d0\u4e2a\u5b9e\u73b0\u7c7b\u7684\u65b9\u5f0f\u4e0d\u591f\u7075\u6d3b\uff0c\u53ea\u80fd\u901a\u8fc7 Iterator \u5f62\u5f0f\u83b7\u53d6\uff0c\u4e0d\u80fd\u6839\u636e\u67d0\u4e2a\u53c2\u6570\u6765\u83b7\u53d6\u5bf9\u5e94\u7684\u5b9e\u73b0\u7c7b\u3002<\/li>\n<li>\u591a\u4e2a\u5e76\u53d1\u591a\u7ebf\u7a0b\u4f7f\u7528 ServiceLoader \u7c7b\u7684\u5b9e\u4f8b\u662f\u4e0d\u5b89\u5168\u7684\u3002<\/li>\n<\/ul>\n<h2>\u56db\u3001SPI \u5e94\u7528\u573a\u666f<\/h2>\n<p>SPI \u5728 Java \u5f00\u53d1\u4e2d\u5e94\u7528\u5341\u5206\u5e7f\u6cdb\u3002\u9996\u5148\uff0c\u5728 Java \u7684 java.util.spi package \u4e2d\u5c31\u7ea6\u5b9a\u4e86\u5f88\u591a SPI \u63a5\u53e3\u3002\u4e0b\u9762\uff0c\u5217\u4e3e\u4e00\u4e9b SPI \u63a5\u53e3\uff1a<\/p>\n<ul>\n<li><a href=\"https:\/\/link.segmentfault.com\/?enc=6YTN9X3YFY3R1lXKh98aQg%3D%3D.71t%2Fj%2BHWoSXQNladNNjJ8BTeXMEmaDDw2p%2BWGRiOi0bktOo6Eaq%2B5LWIol5pawJsz7Ma67A6j28lOuM9d5ssy8aRrcOHbZMwBWrjt6FOASn8BOVU%2FC4e0BMUggTDM5AGvbebBDifKPaW1FOMR25ZJA%3D%3D\" rel=\"nofollow\">TimeZoneNameProvider<\/a>: \u4e3a TimeZone \u7c7b\u63d0\u4f9b\u672c\u5730\u5316\u7684\u65f6\u533a\u540d\u79f0\u3002<\/li>\n<li><a href=\"https:\/\/link.segmentfault.com\/?enc=qSCPZAw2Ggm%2B81gdPK5B0Q%3D%3D.WafbknbWyH6uhOcR5tVuODIx0gt9ynE6bjLb7ZFBraWn4nFneeaOzdwm4yw88UthnrE%2FcR4lPWZAPftrkdJMkaBZhTa%2FI%2FDmoFjJH0FtkY16%2BwmFNF%2FYye81THJfDQvf44FyWdTIgHSObVPLx5etYw%3D%3D\" rel=\"nofollow\">DateFormatProvider<\/a>: \u4e3a\u6307\u5b9a\u7684\u8bed\u8a00\u73af\u5883\u63d0\u4f9b\u65e5\u671f\u548c\u65f6\u95f4\u683c\u5f0f\u3002<\/li>\n<li><a href=\"https:\/\/link.segmentfault.com\/?enc=mb0lXBvjE0P8S84G%2FsTCCw%3D%3D.%2Fd5v06ouMakxxRmnVUOahr4oWBk8wXKOj2FluTOB0rdrmq5gQMlgI4D5G9irg48Mr9%2FRql%2B90AB2Gn1HjrwKxxhOR6mCxaFkEZiNo2wf%2BNL%2B5tv1ImXnmrs3e5DRrB9VLNyaPmlrz045LojAMKNapg%3D%3D\" rel=\"nofollow\">NumberFormatProvider<\/a>: \u4e3a NumberFormat \u7c7b\u63d0\u4f9b\u8d27\u5e01\u3001\u6574\u6570\u548c\u767e\u5206\u6bd4\u503c\u3002<\/li>\n<li><a href=\"https:\/\/link.segmentfault.com\/?enc=PSllrtJUy56fAMENf%2B%2FqhA%3D%3D.3BABJ18eMR4Uqd%2FO2m5xSJGZJzj6Te3IZ8I5%2FMQla1aTB%2BUyjjCdLK87jkXwsU2xFgTTqukUMvVkwiU0L9aXIcR2aUSjTFCmJ9QwhKkcfyk%2Fej%2Fa%2FZoBdNBqwEqaJzlZ\" rel=\"nofollow\">Driver<\/a>: \u4ece 4.0 \u7248\u5f00\u59cb\uff0cJDBC API \u652f\u6301 SPI \u6a21\u5f0f\u3002\u65e7\u7248\u672c\u4f7f\u7528 Class.forName() \u65b9\u6cd5\u52a0\u8f7d\u9a71\u52a8\u7a0b\u5e8f\u3002<\/li>\n<li><a href=\"https:\/\/link.segmentfault.com\/?enc=a7%2Bn5XeCiUBP8q5P6rXipQ%3D%3D.2cUmZxiD30nquWQqX%2BUWG62JSUCC7A7MVzH9fg9CRzNqWdJaxVEj%2F9xvv%2BSIDagspXaB8evL3UDV1HZ0oSc%2F8EJr3EWrXOP%2B2Ob3aEwS8gQ%2BedYmaOSVQSVpOQfcPHp1\" rel=\"nofollow\">PersistenceProvider<\/a>: \u63d0\u4f9b JPA API \u7684\u5b9e\u73b0\u3002<\/li>\n<li>\u7b49\u7b49<\/li>\n<\/ul>\n<p>\u9664\u6b64\u4ee5\u5916\uff0cSPI \u8fd8\u6709\u5f88\u591a\u5e94\u7528\uff0c\u4e0b\u9762\u5217\u4e3e\u51e0\u4e2a\u7ecf\u5178\u6848\u4f8b\u3002<\/p>\n<h4>4.1 SPI \u5e94\u7528\u6848\u4f8b\u4e4b JDBC DriverManager<\/h4>\n<p>\u4f5c\u4e3a Java \u5de5\u7a0b\u5e08\uff0c\u5c24\u5176\u662f CRUD \u5de5\u7a0b\u5e08\uff0c\u76f8\u5fc5\u90fd\u975e\u5e38\u719f\u6089 JDBC\u3002\u4f17\u6240\u5468\u77e5\uff0c\u5173\u7cfb\u578b\u6570\u636e\u5e93\u6709\u5f88\u591a\u79cd\uff0c\u5982\uff1aMySQL\u3001Oracle\u3001PostgreSQL \u7b49\u7b49\u3002JDBC \u5982\u4f55\u8bc6\u522b\u5404\u79cd\u6570\u636e\u5e93\u7684\u9a71\u52a8\u5462\uff1f<\/p>\n<h5>4.1.1 \u521b\u5efa\u6570\u636e\u5e93\u8fde\u63a5<\/h5>\n<p>\u6211\u4eec\u5148\u56de\u987e\u4e00\u4e0b\uff0cJDBC \u5982\u4f55\u521b\u5efa\u6570\u636e\u5e93\u8fde\u63a5\u7684\u5462\uff1f<\/p>\n<p>\u5728 JDBC4.0 \u4e4b\u524d\uff0c\u8fde\u63a5\u6570\u636e\u5e93\u7684\u65f6\u5019\uff0c\u901a\u5e38\u4f1a\u7528 Class.forName(XXX) \u65b9\u6cd5\u6765\u52a0\u8f7d\u6570\u636e\u5e93\u76f8\u5e94\u7684\u9a71\u52a8\uff0c\u7136\u540e\u518d\u83b7\u53d6\u6570\u636e\u5e93\u8fde\u63a5\uff0c\u7ee7\u800c\u8fdb\u884c CRUD \u7b49\u64cd\u4f5c\u3002<\/p>\n<pre><code class=\"java\">Class.forName(&quot;com.mysql.jdbc.Driver&quot;)<\/code><\/pre>\n<p>\u800c JDBC4.0 \u4e4b\u540e\uff0c\u4e0d\u518d\u9700\u8981\u7528Class.forName(XXX) \u65b9\u6cd5\u6765\u52a0\u8f7d\u6570\u636e\u5e93\u9a71\u52a8\uff0c\u76f4\u63a5\u83b7\u53d6\u8fde\u63a5\u5c31\u53ef\u4ee5\u4e86\u3002\u663e\u7136\uff0c\u8fd9\u79cd\u65b9\u5f0f\u5f88\u65b9\u4fbf\uff0c\u4f46\u662f\u5982\u4f55\u505a\u5230\u7684\u5462\uff1f<\/p>\n<p><strong>\uff081\uff09JDBC \u63a5\u53e3\uff1a<\/strong>\u9996\u5148\uff0cJava \u4e2d\u5185\u7f6e\u4e86\u63a5\u53e3 java.sql.Driver\u3002<\/p>\n<p><strong>\uff082\uff09JDBC \u63a5\u53e3\u5b9e\u73b0\uff1a<\/strong>\u5404\u4e2a\u6570\u636e\u5e93\u7684\u9a71\u52a8\u81ea\u884c\u5b9e\u73b0 java.sql.Driver \u63a5\u53e3\uff0c\u7528\u4e8e\u7ba1\u7406\u6570\u636e\u5e93\u8fde\u63a5\u3002<\/p>\n<ul>\n<li>MySQL\uff1a\u5728 MySQL\u7684 Java \u9a71\u52a8\u5305 mysql-connector-java-XXX.jar \u4e2d\uff0c\u53ef\u4ee5\u627e\u5230 META-INF\/services \u76ee\u5f55\uff0c\u8be5\u76ee\u5f55\u4e0b\u4f1a\u6709\u4e00\u4e2a\u540d\u5b57\u4e3ajava.sql.Driver \u7684\u6587\u4ef6\uff0c\u6587\u4ef6\u5185\u5bb9\u662fcom.mysql.cj.jdbc.Driver\u3002<\/li>\n<\/ul>\n<p>com.mysql.cj.jdbc.Driver \u6b63\u662f MySQL \u7248\u7684 java.sql.Driver \u5b9e\u73b0\u3002\u5982\u4e0b\u56fe\u6240\u793a\uff1a<\/p>\n<p><img decoding=\"async\" referrerpolicy=\"no-referrer\" src=\"https:\/\/segmentfault.com\/img\/remote\/1460000042736848\" alt=\"\u56fe\u7247\" title=\"\u56fe\u7247\" loading=\"lazy\"><\/p>\n<ul>\n<li>PostgreSQL \u5b9e\u73b0\uff1a\u5728 PostgreSQL \u7684 Java \u9a71\u52a8\u5305 postgresql-42.0.0.jar \u4e2d\uff0c\u4e5f\u53ef\u4ee5\u627e\u5230\u540c\u6837\u7684\u914d\u7f6e\u6587\u4ef6\uff0c\u6587\u4ef6\u5185\u5bb9\u662f org.postgresql.Driver\uff0corg.postgresql.Driver \u6b63\u662f PostgreSQL \u7248\u7684 java.sql.Driver \u5b9e\u73b0\u3002<\/li>\n<\/ul>\n<p><strong>\uff083\uff09\u521b\u5efa\u6570\u636e\u5e93\u8fde\u63a5<\/strong><\/p>\n<p>\u4ee5 MySQL \u4e3a\u4f8b\uff0c\u521b\u5efa\u6570\u636e\u5e93\u8fde\u63a5\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"java\">final String DB_URL = String.format(&quot;jdbc:mysql:\/\/%s:%s\/%s&quot;, DB_HOST, DB_PORT, DB_SCHEMA);\nconnection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);<\/code><\/pre>\n<h5>4.1.2 DriverManager<\/h5>\n<p>\u4ece\u524d\u6587\uff0c\u6211\u4eec\u5df2\u7ecf\u77e5\u9053 DriverManager \u662f\u521b\u5efa\u6570\u636e\u5e93\u8fde\u63a5\u7684\u5173\u952e\u3002\u5b83\u7a76\u7adf\u662f\u5982\u4f55\u5de5\u4f5c\u7684\u5462\uff1f<\/p>\n<p>\u53ef\u4ee5\u770b\u5230\u662f\u52a0\u8f7d\u5b9e\u4f8b\u5316\u9a71\u52a8\u7684\uff0c\u63a5\u7740\u770b loadInitialDrivers \u65b9\u6cd5\uff1a<\/p>\n<pre><code class=\"java\">private static void loadInitialDrivers() {\n  String drivers;\n  try {\n    drivers = AccessController.doPrivileged(new PrivilegedAction&lt;String&gt;() {\n      public String run() {\n        return System.getProperty(&quot;jdbc.drivers&quot;);\n      }\n    });\n  } catch (Exception ex) {\n    drivers = null;\n  }\n  \/\/ \u901a\u8fc7 classloader \u83b7\u53d6\u6240\u6709\u5b9e\u73b0 java.sql.Driver \u7684\u9a71\u52a8\u7c7b\n  AccessController.doPrivileged(new PrivilegedAction&lt;Void&gt;() {\n    public Void run() {\n            \/\/ \u5229\u7528 SPI\uff0c\u8bb0\u8f7d\u6240\u6709 Driver \u670d\u52a1\n      ServiceLoader&lt;Driver&gt; loadedDrivers = ServiceLoader.load(Driver.class);\n            \/\/ \u83b7\u53d6\u8fed\u4ee3\u5668\n      Iterator&lt;Driver&gt; driversIterator = loadedDrivers.iterator();\n      try{\n                \/\/ \u904d\u5386\u8fed\u4ee3\u5668\n        while(driversIterator.hasNext()) {\n          driversIterator.next();\n        }\n      } catch(Throwable t) {\n      \/\/ Do nothing\n      }\n      return null;\n    }\n  });\n\n    \/\/ \u6253\u5370\u6570\u636e\u5e93\u9a71\u52a8\u4fe1\u606f\n  println(&quot;DriverManager.initialize: jdbc.drivers = &quot; + drivers);\n\n  if (drivers == null || drivers.equals(&quot;&quot;)) {\n    return;\n  }\n  String[] driversList = drivers.split(&quot;:&quot;);\n  println(&quot;number of Drivers:&quot; + driversList.length);\n  for (String aDriver : driversList) {\n    try {\n      println(&quot;DriverManager.Initialize: loading &quot; + aDriver);\n            \/\/ \u5c1d\u8bd5\u5b9e\u4f8b\u5316\u9a71\u52a8\n      Class.forName(aDriver, true,\n          ClassLoader.getSystemClassLoader());\n    } catch (Exception ex) {\n      println(&quot;DriverManager.Initialize: load failed: &quot; + ex);\n    }\n  }\n}<\/code><\/pre>\n<p>\u4e0a\u9762\u7684\u4ee3\u7801\u4e3b\u8981\u6b65\u9aa4\u662f\uff1a<\/p>\n<ol>\n<li>\u4ece\u7cfb\u7edf\u53d8\u91cf\u4e2d\u83b7\u53d6\u9a71\u52a8\u7684\u5b9e\u73b0\u7c7b\u3002<\/li>\n<li>\u5229\u7528 SPI \u6765\u83b7\u53d6\u6240\u6709\u9a71\u52a8\u7684\u5b9e\u73b0\u7c7b\u3002<\/li>\n<li>\u904d\u5386\u6240\u6709\u9a71\u52a8\uff0c\u5c1d\u8bd5\u5b9e\u4f8b\u5316\u5404\u4e2a\u5b9e\u73b0\u7c7b\u3002<\/li>\n<li>\u6839\u636e\u7b2c 1 \u6b65\u83b7\u53d6\u5230\u7684\u9a71\u52a8\u5217\u8868\u6765\u5b9e\u4f8b\u5316\u5177\u4f53\u7684\u5b9e\u73b0\u7c7b\u3002<\/li>\n<\/ol>\n<p>\u9700\u8981\u5173\u6ce8\u7684\u662f\u4e0b\u9762\u8fd9\u884c\u4ee3\u7801\uff1a<\/p>\n<pre><code class=\"java\">ServiceLoader&lt;Driver&gt; loadedDrivers = ServiceLoader.load(Driver.class);<\/code><\/pre>\n<p>\u8fd9\u91cc\u5b9e\u9645\u83b7\u53d6\u7684\u662fjava.util.ServiceLoader.LazyIterator \u8fed\u4ee3\u5668\u3002\u8c03\u7528\u5176 hasNext \u65b9\u6cd5\u65f6\uff0c\u4f1a\u641c\u7d22 classpath \u4e0b\u4ee5\u53ca jar \u5305\u4e2d\u7684 META-INF\/services \u76ee\u5f55\uff0c\u67e5\u627e java.sql.Driver \u6587\u4ef6\uff0c\u5e76\u627e\u5230\u6587\u4ef6\u4e2d\u7684\u9a71\u52a8\u5b9e\u73b0\u7c7b\u7684\u5168\u9650\u5b9a\u540d\u3002\u8c03\u7528\u5176 next \u65b9\u6cd5\u65f6\uff0c\u4f1a\u6839\u636e\u9a71\u52a8\u7c7b\u7684\u5168\u9650\u5b9a\u540d\u53bb\u5c1d\u8bd5\u5b9e\u4f8b\u5316\u4e00\u4e2a\u9a71\u52a8\u7c7b\u7684\u5bf9\u8c61\u3002<\/p>\n<h4>4.2 SPI \u5e94\u7528\u6848\u4f8b\u4e4b Common-Loggin<\/h4>\n<p>common-logging\uff08\u4e5f\u79f0 Jakarta Commons Logging\uff0c\u7f29\u5199 JCL\uff09\u662f\u5e38\u7528\u7684\u65e5\u5fd7\u95e8\u9762\u5de5\u5177\u5305\u3002common-logging \u7684\u6838\u5fc3\u7c7b\u662f\u5165\u53e3\u662f LogFactory\uff0cLogFatory \u662f\u4e00\u4e2a\u62bd\u8c61\u7c7b\uff0c\u5b83\u8d1f\u8d23\u52a0\u8f7d\u5177\u4f53\u7684\u65e5\u5fd7\u5b9e\u73b0\u3002<\/p>\n<p>\u5176\u5165\u53e3\u65b9\u6cd5\u662f LogFactory.getLog \u65b9\u6cd5\uff0c\u6e90\u7801\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"java\">public static Log getLog(Class clazz) throws LogConfigurationException {\n  return getFactory().getInstance(clazz);\n}\n\npublic static Log getLog(String name) throws LogConfigurationException {\n  return getFactory().getInstance(name);\n}<\/code><\/pre>\n<p>\u4ece\u4ee5\u4e0a\u6e90\u7801\u53ef\u77e5\uff0cgetLog \u91c7\u7528\u4e86\u5de5\u5382\u8bbe\u8ba1\u6a21\u5f0f\uff0c\u662f\u5148\u8c03\u7528 getFactory \u65b9\u6cd5\u83b7\u53d6\u5177\u4f53\u65e5\u5fd7\u5e93\u7684\u5de5\u5382\u7c7b\uff0c\u7136\u540e\u6839\u636e\u7c7b\u540d\u79f0\u6216\u7c7b\u578b\u521b\u5efa\u65e5\u5fd7\u5b9e\u4f8b\u3002<\/p>\n<p>LogFatory.getFactory \u65b9\u6cd5\u8d1f\u8d23\u9009\u51fa\u5339\u914d\u7684\u65e5\u5fd7\u5de5\u5382\uff0c\u5176\u6e90\u7801\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"java\">\npublic static LogFactory getFactory() throws LogConfigurationException {\n  \/\/ \u7701\u7565...\n\n  \/\/ \u52a0\u8f7d commons-logging.properties \u914d\u7f6e\u6587\u4ef6\n  Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);\n\n  \/\/ \u7701\u7565...\n\n    \/\/ \u51b3\u5b9a\u521b\u5efa\u54ea\u4e2a LogFactory \u5b9e\u4f8b\n  \/\/ \uff081\uff09\u5c1d\u8bd5\u8bfb\u53d6\u5168\u5c40\u5c5e\u6027 org.apache.commons.logging.LogFactory\n  if (isDiagnosticsEnabled()) {\n    logDiagnostic(&quot;[LOOKUP] Looking for system property [&quot; + FACTORY_PROPERTY +\n            &quot;] to define the LogFactory subclass to use...&quot;);\n  }\n\n  try {\n        \/\/ \u5982\u679c\u6307\u5b9a\u4e86 org.apache.commons.logging.LogFactory \u5c5e\u6027\uff0c\u5c1d\u8bd5\u5b9e\u4f8b\u5316\u5177\u4f53\u5b9e\u73b0\u7c7b\n    String factoryClass = getSystemProperty(FACTORY_PROPERTY, null);\n    if (factoryClass != null) {\n      if (isDiagnosticsEnabled()) {\n        logDiagnostic(&quot;[LOOKUP] Creating an instance of LogFactory class '&quot; + factoryClass +\n                &quot;' as specified by system property &quot; + FACTORY_PROPERTY);\n      }\n      factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);\n    } else {\n      if (isDiagnosticsEnabled()) {\n        logDiagnostic(&quot;[LOOKUP] No system property [&quot; + FACTORY_PROPERTY + &quot;] defined.&quot;);\n      }\n    }\n  } catch (SecurityException e) {\n      \/\/ \u5f02\u5e38\u5904\u7406\n  } catch (RuntimeException e) {\n      \/\/ \u5f02\u5e38\u5904\u7406\n  }\n\n    \/\/ \uff082\uff09\u5229\u7528 Java SPI \u673a\u5236\uff0c\u5c1d\u8bd5\u5728 classpatch \u7684 META-INF\/services \u76ee\u5f55\u4e0b\u5bfb\u627e org.apache.commons.logging.LogFactory \u5b9e\u73b0\u7c7b\n  if (factory == null) {\n    if (isDiagnosticsEnabled()) {\n      logDiagnostic(&quot;[LOOKUP] Looking for a resource file of name [&quot; + SERVICE_ID +\n              &quot;] to define the LogFactory subclass to use...&quot;);\n    }\n    try {\n      final InputStream is = getResourceAsStream(contextClassLoader, SERVICE_ID);\n\n      if( is != null ) {\n        \/\/ This code is needed by EBCDIC and other strange systems.\n        \/\/ It's a fix for bugs reported in xerces\n        BufferedReader rd;\n        try {\n          rd = new BufferedReader(new InputStreamReader(is, &quot;UTF-8&quot;));\n        } catch (java.io.UnsupportedEncodingException e) {\n          rd = new BufferedReader(new InputStreamReader(is));\n        }\n\n        String factoryClassName = rd.readLine();\n        rd.close();\n\n        if (factoryClassName != null &amp;&amp; ! &quot;&quot;.equals(factoryClassName)) {\n          if (isDiagnosticsEnabled()) {\n            logDiagnostic(&quot;[LOOKUP]  Creating an instance of LogFactory class &quot; +\n                    factoryClassName +\n                    &quot; as specified by file '&quot; + SERVICE_ID +\n                    &quot;' which was present in the path of the context classloader.&quot;);\n          }\n          factory = newFactory(factoryClassName, baseClassLoader, contextClassLoader );\n        }\n      } else {\n        \/\/ is == null\n        if (isDiagnosticsEnabled()) {\n          logDiagnostic(&quot;[LOOKUP] No resource file with name '&quot; + SERVICE_ID + &quot;' found.&quot;);\n        }\n      }\n    } catch (Exception ex) {\n      \/\/ note: if the specified LogFactory class wasn't compatible with LogFactory\n      \/\/ for some reason, a ClassCastException will be caught here, and attempts will\n      \/\/ continue to find a compatible class.\n      if (isDiagnosticsEnabled()) {\n        logDiagnostic(\n          &quot;[LOOKUP] A security exception occurred while trying to create an&quot; +\n          &quot; instance of the custom factory class&quot; +\n          &quot;: [&quot; + trim(ex.getMessage()) +\n          &quot;]. Trying alternative implementations...&quot;);\n      }\n      \/\/ ignore\n    }\n  }\n\n  \/\/ \uff083\uff09\u5c1d\u8bd5\u4ece classpath \u76ee\u5f55\u4e0b\u7684 commons-logging.properties \u6587\u4ef6\u4e2d\u67e5\u627e org.apache.commons.logging.LogFactory \u5c5e\u6027\n\n  if (factory == null) {\n    if (props != null) {\n      if (isDiagnosticsEnabled()) {\n        logDiagnostic(\n          &quot;[LOOKUP] Looking in properties file for entry with key '&quot; + FACTORY_PROPERTY +\n          &quot;' to define the LogFactory subclass to use...&quot;);\n      }\n      String factoryClass = props.getProperty(FACTORY_PROPERTY);\n      if (factoryClass != null) {\n        if (isDiagnosticsEnabled()) {\n          logDiagnostic(\n            &quot;[LOOKUP] Properties file specifies LogFactory subclass '&quot; + factoryClass + &quot;'&quot;);\n        }\n        factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);\n\n        \/\/ TODO: think about whether we need to handle exceptions from newFactory\n      } else {\n        if (isDiagnosticsEnabled()) {\n          logDiagnostic(&quot;[LOOKUP] Properties file has no entry specifying LogFactory subclass.&quot;);\n        }\n      }\n    } else {\n      if (isDiagnosticsEnabled()) {\n        logDiagnostic(&quot;[LOOKUP] No properties file available to determine&quot; + &quot; LogFactory subclass from..&quot;);\n      }\n    }\n  }\n\n  \/\/ \uff084\uff09\u4ee5\u4e0a\u60c5\u51b5\u90fd\u4e0d\u6ee1\u8db3\uff0c\u5b9e\u4f8b\u5316\u9ed8\u8ba4\u5b9e\u73b0\u7c7b org.apache.commons.logging.impl.LogFactoryImpl\n\n  if (factory == null) {\n    if (isDiagnosticsEnabled()) {\n      logDiagnostic(\n        &quot;[LOOKUP] Loading the default LogFactory implementation '&quot; + FACTORY_DEFAULT +\n        &quot;' via the same classloader that loaded this LogFactory&quot; +\n        &quot; class (ie not looking in the context classloader).&quot;);\n    }\n\n    factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);\n  }\n\n  if (factory != null) {\n    \/**\n     * Always cache using context class loader.\n     *\/\n    cacheFactory(contextClassLoader, factory);\n\n    if (props != null) {\n      Enumeration names = props.propertyNames();\n      while (names.hasMoreElements()) {\n        String name = (String) names.nextElement();\n        String value = props.getProperty(name);\n        factory.setAttribute(name, value);\n      }\n    }\n  }\n\n  return factory;\n}<\/code><\/pre>\n<p>\u4ece getFactory \u65b9\u6cd5\u7684\u6e90\u7801\u53ef\u4ee5\u770b\u51fa\uff0c\u5176\u6838\u5fc3\u903b\u8f91\u5206\u4e3a 4 \u6b65\uff1a<\/p>\n<ul>\n<li>\u9996\u5148\uff0c\u5c1d\u8bd5\u67e5\u627e\u5168\u5c40\u5c5e\u6027org.apache.commons.logging.LogFactory\uff0c\u5982\u679c\u6307\u5b9a\u4e86\u5177\u4f53\u7c7b\uff0c\u5c1d\u8bd5\u521b\u5efa\u5b9e\u4f8b\u3002<\/li>\n<li>\u5229\u7528 Java SPI \u673a\u5236\uff0c\u5c1d\u8bd5\u5728 classpatch \u7684 META-INF\/services \u76ee\u5f55\u4e0b\u5bfb\u627eorg.apache.commons.logging.LogFactory \u7684\u5b9e\u73b0\u7c7b\u3002<\/li>\n<li>\u5c1d\u8bd5\u4ece classpath \u76ee\u5f55\u4e0b\u7684 commons-logging.properties \u6587\u4ef6\u4e2d\u67e5\u627eorg.apache.commons.logging.LogFactory \u5c5e\u6027\uff0c\u5982\u679c\u6307\u5b9a\u4e86\u5177\u4f53\u7c7b\uff0c\u5c1d\u8bd5\u521b\u5efa\u5b9e\u4f8b\u3002<\/li>\n<li>\u4ee5\u4e0a\u60c5\u51b5\u5982\u679c\u90fd\u4e0d\u6ee1\u8db3\uff0c\u5219\u5b9e\u4f8b\u5316\u9ed8\u8ba4\u5b9e\u73b0\u7c7b\uff0c\u5373org.apache.commons.logging.impl.LogFactoryImpl\u3002<\/li>\n<\/ul>\n<h4>4.3 SPI \u5e94\u7528\u6848\u4f8b\u4e4b Spring Boot<\/h4>\n<p>Spring Boot \u662f\u57fa\u4e8e Spring \u6784\u5efa\u7684\u6846\u67b6\uff0c\u5176\u8bbe\u8ba1\u76ee\u7684\u5728\u4e8e\u7b80\u5316 Spring \u5e94\u7528\u7684\u914d\u7f6e\u3001\u8fd0\u884c\u3002\u5728 Spring Boot \u4e2d\uff0c\u5927\u91cf\u8fd0\u7528\u4e86\u81ea\u52a8\u88c5\u914d\u6765\u5c3d\u53ef\u80fd\u51cf\u5c11\u914d\u7f6e\u3002<\/p>\n<p>\u4e0b\u9762\u662f\u4e00\u4e2a Spring Boot \u5165\u53e3\u793a\u4f8b\uff0c\u53ef\u4ee5\u770b\u5230\uff0c\u4ee3\u7801\u975e\u5e38\u7b80\u6d01\u3002<\/p>\n<pre><code class=\"java\">import org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@SpringBootApplication\n@RestController\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n    @GetMapping(&quot;\/hello&quot;)\n    public String hello(@RequestParam(value = &quot;name&quot;, defaultValue = &quot;World&quot;) String name) {\n        return String.format(&quot;Hello %s!&quot;, name);\n    }\n}<\/code><\/pre>\n<p>\u90a3\u4e48\uff0cSpring Boot \u662f\u5982\u4f55\u505a\u5230\u5be5\u5be5\u51e0\u884c\u4ee3\u7801\uff0c\u5c31\u53ef\u4ee5\u8fd0\u884c\u4e00\u4e2a Spring Boot \u5e94\u7528\u7684\u5462\u3002\u6211\u4eec\u4e0d\u59a8\u5e26\u7740\u7591\u95ee\uff0c\u4ece\u6e90\u7801\u5165\u624b\uff0c\u4e00\u6b65\u6b65\u63a2\u7a76\u5176\u539f\u7406\u3002<\/p>\n<h5>4.3.1 @SpringBootApplication \u6ce8\u89e3<\/h5>\n<p>\u9996\u5148\uff0cSpring Boot \u5e94\u7528\u7684\u542f\u52a8\u7c7b\u4e0a\u90fd\u4f1a\u6807\u8bb0\u4e00\u4e2a<\/p>\n<p>@SpringBootApplication \u6ce8\u89e3\u3002<\/p>\n<p>@SpringBootApplication \u6ce8\u89e3\u5b9a\u4e49\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"java\">@Target({ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@SpringBootConfiguration\n@EnableAutoConfiguration\n@ComponentScan(\n    excludeFilters = {@Filter(\n    type = FilterType.CUSTOM,\n    classes = {TypeExcludeFilter.class}\n), @Filter(\n    type = FilterType.CUSTOM,\n    classes = {AutoConfigurationExcludeFilter.class}\n)}\n)\npublic @interface SpringBootApplication {\n    \/\/ \u7565\n}<\/code><\/pre>\n<p>\u9664\u4e86\u00a0@Target\u3001\u00a0@Retention\u3001@Documented\u3001@Inherited \u8fd9\u51e0\u4e2a\u5143\u6ce8\u89e3\uff0c\u00a0<\/p>\n<p>@SpringBootApplication \u6ce8\u89e3\u7684\u5b9a\u4e49\u4e2d\u8fd8\u6807\u8bb0\u4e86\u00a0@SpringBootConfiguration\u3001@EnableAutoConfiguration\u3001@ComponentScan \u4e09\u4e2a\u6ce8\u89e3\u3002<\/p>\n<h5>4.3.2 @SpringBootConfiguration \u6ce8\u89e3<\/h5>\n<p>\u4ece@SpringBootConfiguration \u6ce8\u89e3\u7684\u5b9a\u4e49\u6765\u770b\uff0c@SpringBootConfiguration \u6ce8\u89e3\u672c\u8d28\u4e0a\u5c31\u662f\u4e00\u4e2a @Configuration \u6ce8\u89e3\uff0c\u8fd9\u610f\u5473\u7740\u88ab@SpringBootConfiguration \u6ce8\u89e3\u4fee\u9970\u7684\u7c7b\u4f1a\u88ab Spring Boot \u8bc6\u522b\u4e3a\u4e00\u4e2a\u914d\u7f6e\u7c7b\u3002<\/p>\n<pre><code class=\"java\">@Target({ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Configuration\npublic @interface SpringBootConfiguration {\n    @AliasFor(\n        annotation = Configuration.class\n    )\n    boolean proxyBeanMethods() default true;\n}<\/code><\/pre>\n<h5>4.3.3 @EnableAutoConfiguration \u6ce8\u89e3<\/h5>\n<p>@EnableAutoConfiguration \u6ce8\u89e3\u5b9a\u4e49\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"java\">@Target({ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@AutoConfigurationPackage\n@Import({AutoConfigurationImportSelector.class})\npublic @interface EnableAutoConfiguration {\n    String ENABLED_OVERRIDE_PROPERTY = &quot;spring.boot.enableautoconfiguration&quot;;\n\n    Class&lt;?&gt;[] exclude() default {};\n\n    String[] excludeName() default {};\n}<\/code><\/pre>\n<p>@EnableAutoConfiguration \u6ce8\u89e3\u5305\u542b\u4e86 @AutoConfigurationPackage\u4e0e @Import({AutoConfigurationImportSelector.class}) \u4e24\u4e2a\u6ce8\u89e3\u3002<\/p>\n<h5>4.3.4 @AutoConfigurationPackage \u6ce8\u89e3<\/h5>\n<p>@AutoConfigurationPackage \u4f1a\u5c06\u88ab\u4fee\u9970\u7684\u7c7b\u4f5c\u4e3a\u4e3b\u914d\u7f6e\u7c7b\uff0c\u8be5\u7c7b\u6240\u5728\u7684 package \u4f1a\u88ab\u89c6\u4e3a\u6839\u8def\u5f84\uff0cSpring Boot \u9ed8\u8ba4\u4f1a\u81ea\u52a8\u626b\u63cf\u6839\u8def\u5f84\u4e0b\u7684\u6240\u6709 Spring Bean\uff08\u88ab @Component \u4ee5\u53ca\u7ee7\u627f @Component \u7684\u5404\u4e2a\u6ce8\u89e3\u6240\u4fee\u9970\u7684\u7c7b\uff09\u3002\u2014\u2014\u8fd9\u5c31\u662f\u4e3a\u4ec0\u4e48 Spring Boot \u7684\u542f\u52a8\u7c7b\u4e00\u822c\u8981\u7f6e\u4e8e\u6839\u8def\u5f84\u7684\u539f\u56e0\u3002\u8fd9\u4e2a\u529f\u80fd\u7b49\u540c\u4e8e\u5728 Spring xml \u914d\u7f6e\u4e2d\u901a\u8fc7 context:component-scan \u6765\u6307\u5b9a\u626b\u63cf\u8def\u5f84\u3002@Import \u6ce8\u89e3\u7684\u4f5c\u7528\u662f\u5411 Spring \u5bb9\u5668\u4e2d\u76f4\u63a5\u6ce8\u5165\u6307\u5b9a\u7ec4\u4ef6\u3002@AutoConfigurationPackage \u6ce8\u89e3\u4e2d\u6ce8\u660e\u4e86@Import({Registrar.class})\u3002Registrar \u7c7b\u7528\u4e8e\u4fdd\u5b58 Spring Boot \u7684\u5165\u53e3\u7c7b\u3001\u6839\u8def\u5f84\u7b49\u4fe1\u606f\u3002<\/p>\n<h5>4.3.5 SpringFactoriesLoader.loadFactoryNames \u65b9\u6cd5<\/h5>\n<p>@Import(AutoConfigurationImportSelector.class) \u8868\u793a\u76f4\u63a5\u6ce8\u5165AutoConfigurationImportSelector\u3002<\/p>\n<p>AutoConfigurationImportSelector \u6709\u4e00\u4e2a\u6838\u5fc3\u65b9\u6cd5getCandidateConfigurations \u7528\u4e8e\u83b7\u53d6\u5019\u9009\u914d\u7f6e\u3002\u8be5\u65b9\u6cd5\u8c03\u7528\u4e86SpringFactoriesLoader.loadFactoryNames \u65b9\u6cd5\uff0c\u8fd9\u4e2a\u65b9\u6cd5\u5373\u4e3a Spring Boot SPI \u7684\u5173\u952e\uff0c\u5b83\u8d1f\u8d23\u52a0\u8f7d\u6240\u6709 META-INF\/spring.factories \u6587\u4ef6\uff0c\u52a0\u8f7d\u7684\u8fc7\u7a0b\u7531 SpringFactoriesLoader \u8d1f\u8d23\u3002<\/p>\n<p>Spring Boot \u7684 META-INF\/spring.factories \u6587\u4ef6\u672c\u8d28\u4e0a\u5c31\u662f\u4e00\u4e2a properties \u6587\u4ef6\uff0c\u6570\u636e\u5185\u5bb9\u5c31\u662f\u4e00\u4e2a\u4e2a\u952e\u503c\u5bf9\u3002<\/p>\n<p>SpringFactoriesLoader.loadFactoryNames \u65b9\u6cd5\u7684\u5173\u952e\u6e90\u7801\uff1a<\/p>\n<pre><code class=\"java\">\/\/ spring.factories \u6587\u4ef6\u7684\u683c\u5f0f\u4e3a\uff1akey=value1,value2,value3\n\/\/ \u904d\u5386\u6240\u6709 META-INF\/spring.factories \u6587\u4ef6\n\/\/ \u89e3\u6790\u6587\u4ef6\uff0c\u83b7\u5f97 key=factoryClass \u7684\u7c7b\u540d\u79f0\npublic static List&lt;String&gt; loadFactoryNames(Class&lt;?&gt; factoryType, @Nullable ClassLoader classLoader) {\n  String factoryTypeName = factoryType.getName();\n  return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());\n}\n\nprivate static Map&lt;String, List&lt;String&gt;&gt; loadSpringFactories(@Nullable ClassLoader classLoader) {\n  \/\/ \u5c1d\u8bd5\u83b7\u53d6\u7f13\u5b58\uff0c\u5982\u679c\u7f13\u5b58\u4e2d\u6709\u6570\u636e\uff0c\u76f4\u63a5\u8fd4\u56de\n  MultiValueMap&lt;String, String&gt; result = cache.get(classLoader);\n  if (result != null) {\n    return result;\n  }\n\n  try {\n    \/\/ \u83b7\u53d6\u8d44\u6e90\u6587\u4ef6\u8def\u5f84\n    Enumeration&lt;URL&gt; urls = (classLoader != null ?\n        classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :\n        ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));\n    result = new LinkedMultiValueMap&lt;&gt;();\n    \/\/ \u904d\u5386\u6240\u6709\u8def\u5f84\n    while (urls.hasMoreElements()) {\n      URL url = urls.nextElement();\n      UrlResource resource = new UrlResource(url);\n      \/\/ \u89e3\u6790\u6587\u4ef6\uff0c\u5f97\u5230\u5bf9\u5e94\u7684\u4e00\u7ec4 Properties\n      Properties properties = PropertiesLoaderUtils.loadProperties(resource);\n      \/\/ \u904d\u5386\u89e3\u6790\u51fa\u7684 properties\uff0c\u7ec4\u88c5\u6570\u636e\n      for (Map.Entry&lt;?, ?&gt; entry : properties.entrySet()) {\n        String factoryTypeName = ((String) entry.getKey()).trim();\n        for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {\n          result.add(factoryTypeName, factoryImplementationName.trim());\n        }\n      }\n    }\n    cache.put(classLoader, result);\n    return result;\n  }\n  catch (IOException ex) {\n    throw new IllegalArgumentException(&quot;Unable to load factories from location [&quot; +\n        FACTORIES_RESOURCE_LOCATION + &quot;]&quot;, ex);\n  }\n}<\/code><\/pre>\n<p>\u5f52\u7eb3\u4e0a\u9762\u7684\u65b9\u6cd5\uff0c\u4e3b\u8981\u4f5c\u4e86\u8fd9\u4e9b\u4e8b\uff1a<\/p>\n<p>\u52a0\u8f7d\u6240\u6709 META-INF\/spring.factories \u6587\u4ef6\uff0c\u52a0\u8f7d\u8fc7\u7a0b\u6709 SpringFactoriesLoader \u8d1f\u8d23\u3002<\/p>\n<ul>\n<li>\u5728 CLASSPATH \u4e2d\u641c\u5bfb\u6240\u6709 META-INF\/spring.factories \u914d\u7f6e\u6587\u4ef6\u3002<\/li>\n<li>\u7136\u540e\uff0c\u89e3\u6790 spring.factories \u6587\u4ef6\uff0c\u83b7\u53d6\u6307\u5b9a\u81ea\u52a8\u88c5\u914d\u7c7b\u7684\u5168\u9650\u5b9a\u540d\u3002<\/li>\n<\/ul>\n<h5>4.3.6 Spring Boot \u7684 AutoConfiguration \u7c7b<\/h5>\n<p>Spring Boot \u6709\u5404\u79cd starter \u5305\uff0c\u53ef\u4ee5\u6839\u636e\u5b9e\u9645\u9879\u76ee\u9700\u8981\uff0c\u6309\u9700\u53d6\u6750\u3002\u5728\u9879\u76ee\u5f00\u53d1\u4e2d\uff0c\u53ea\u8981\u5c06 starter \u5305\u5f15\u5165\uff0c\u6211\u4eec\u5c31\u53ef\u4ee5\u7528\u5f88\u5c11\u7684\u914d\u7f6e\uff0c\u751a\u81f3\u4ec0\u4e48\u90fd\u4e0d\u914d\u7f6e\uff0c\u5373\u53ef\u83b7\u53d6\u76f8\u5173\u7684\u80fd\u529b\u3002\u901a\u8fc7\u524d\u9762\u7684 Spring Boot SPI \u6d41\u7a0b\uff0c\u53ea\u5b8c\u6210\u4e86\u81ea\u52a8\u88c5\u914d\u5de5\u4f5c\u7684\u4e00\u534a\uff0c\u5269\u4e0b\u7684\u5de5\u4f5c\u5982\u4f55\u5904\u7406\u5462 \uff1f<\/p>\n<p>\u4ee5 spring-boot-starter-web \u7684 jar \u5305\u4e3a\u4f8b\uff0c\u67e5\u770b\u5176 maven pom\uff0c\u53ef\u4ee5\u770b\u5230\uff0c\u5b83\u4f9d\u8d56\u4e8e spring-boot-starter\uff0c\u6240\u6709 Spring Boot \u5b98\u65b9 starter \u5305\u90fd\u4f1a\u4f9d\u8d56\u4e8e\u8fd9\u4e2a jar \u5305\u3002\u800c spring-boot-starter \u53c8\u4f9d\u8d56\u4e8e spring-boot-autoconfigure\uff0cSpring Boot \u7684\u81ea\u52a8\u88c5\u914d\u79d8\u5bc6\uff0c\u5c31\u5728\u4e8e\u8fd9\u4e2a jar \u5305\u3002<\/p>\n<p>\u4ece spring-boot-autoconfigure \u5305\u7684\u7ed3\u6784\u6765\u770b\uff0c\u5b83\u6709\u4e00\u4e2a META-INF\/spring.factories \uff0c\u663e\u7136\u5229\u7528\u4e86 Spring Boot SPI\uff0c\u6765\u81ea\u52a8\u88c5\u914d\u5176\u4e2d\u7684\u914d\u7f6e\u7c7b\u3002<\/p>\n<p><img decoding=\"async\" referrerpolicy=\"no-referrer\" src=\"https:\/\/segmentfault.com\/img\/remote\/1460000042736849\" alt=\"\u56fe\u7247\" title=\"\u56fe\u7247\" loading=\"lazy\"><\/p>\n<p>\u4e0b\u56fe\u662f spring-boot-autoconfigure \u7684 META-INF\/spring.factories \u6587\u4ef6\u7684\u90e8\u5206\u5185\u5bb9\uff0c\u53ef\u4ee5\u770b\u5230\u5176\u4e2d\u6ce8\u518c\u4e86\u4e00\u957f\u4e32\u4f1a\u88ab\u81ea\u52a8\u52a0\u8f7d\u7684 AutoConfiguration \u7c7b\u3002<\/p>\n<p><img decoding=\"async\" referrerpolicy=\"no-referrer\" src=\"https:\/\/segmentfault.com\/img\/remote\/1460000042736850\" alt=\"\u56fe\u7247\" title=\"\u56fe\u7247\" loading=\"lazy\"><\/p>\n<p>\u4ee5 RedisAutoConfiguration \u4e3a\u4f8b\uff0c\u8fd9\u4e2a\u914d\u7f6e\u7c7b\u4e2d\uff0c\u4f1a\u6839\u636e @ConditionalXXX \u4e2d\u7684\u6761\u4ef6\u53bb\u51b3\u5b9a\u662f\u5426\u5b9e\u4f8b\u5316\u5bf9\u5e94\u7684 Bean\uff0c\u5b9e\u4f8b\u5316 Bean \u6240\u4f9d\u8d56\u7684\u91cd\u8981\u53c2\u6570\u5219\u901a\u8fc7 RedisProperties \u4f20\u5165\u3002<\/p>\n<p><img decoding=\"async\" referrerpolicy=\"no-referrer\" src=\"https:\/\/segmentfault.com\/img\/remote\/1460000042736851\" alt=\"\u56fe\u7247\" title=\"\u56fe\u7247\" loading=\"lazy\"><\/p>\n<p>RedisProperties \u4e2d\u7ef4\u62a4\u4e86 Redis \u8fde\u63a5\u6240\u9700\u8981\u7684\u5173\u952e\u5c5e\u6027\uff0c\u53ea\u8981\u5728 yml \u6216 properties \u914d\u7f6e\u6587\u4ef6\u4e2d\uff0c\u6307\u5b9a spring.redis \u5f00\u5934\u7684\u5c5e\u6027\uff0c\u90fd\u4f1a\u88ab\u81ea\u52a8\u88c5\u8f7d\u5230 RedisProperties \u5b9e\u4f8b\u4e2d\u3002<\/p>\n<p><img decoding=\"async\" referrerpolicy=\"no-referrer\" src=\"https:\/\/segmentfault.com\/img\/remote\/1460000042736852\" alt=\"\u56fe\u7247\" title=\"\u56fe\u7247\" loading=\"lazy\"><\/p>\n<p>\u901a\u8fc7\u4ee5\u4e0a\u5206\u6790\uff0c\u5df2\u7ecf\u4e00\u6b65\u6b65\u89e3\u8bfb\u51fa Spring Boot \u81ea\u52a8\u88c5\u8f7d\u7684\u539f\u7406\u3002<\/p>\n<h2>\u4e94\u3001SPI \u5e94\u7528\u6848\u4f8b\u4e4b Dubbo<\/h2>\n<p>Dubbo \u5e76\u672a\u4f7f\u7528 Java SPI\uff0c\u800c\u662f\u81ea\u5df1\u5c01\u88c5\u4e86\u4e00\u5957\u65b0\u7684 SPI \u673a\u5236\u3002Dubbo SPI \u6240\u9700\u7684\u914d\u7f6e\u6587\u4ef6\u9700\u653e\u7f6e\u5728 META-INF\/dubbo \u8def\u5f84\u4e0b\uff0c\u914d\u7f6e\u5185\u5bb9\u5f62\u5f0f\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"java\">optimusPrime = org.apache.spi.OptimusPrime\nbumblebee = org.apache.spi.Bumblebee<\/code><\/pre>\n<p>\u4e0e Java SPI \u5b9e\u73b0\u7c7b\u914d\u7f6e\u4e0d\u540c\uff0cDubbo SPI \u662f\u901a\u8fc7\u952e\u503c\u5bf9\u7684\u65b9\u5f0f\u8fdb\u884c\u914d\u7f6e\uff0c\u8fd9\u6837\u53ef\u4ee5\u6309\u9700\u52a0\u8f7d\u6307\u5b9a\u7684\u5b9e\u73b0\u7c7b\u3002Dubbo SPI \u9664\u4e86\u652f\u6301\u6309\u9700\u52a0\u8f7d\u63a5\u53e3\u5b9e\u73b0\u7c7b\uff0c\u8fd8\u589e\u52a0\u4e86 IOC \u548c AOP \u7b49\u7279\u6027\u3002<\/p>\n<h4>5.1 ExtensionLoader \u5165\u53e3<\/h4>\n<p>Dubbo SPI \u7684\u76f8\u5173\u903b\u8f91\u88ab\u5c01\u88c5\u5728\u4e86 ExtensionLoader \u7c7b\u4e2d\uff0c\u901a\u8fc7 ExtensionLoader\uff0c\u53ef\u4ee5\u52a0\u8f7d\u6307\u5b9a\u7684\u5b9e\u73b0\u7c7b\u3002<\/p>\n<p>ExtensionLoader \u7684 getExtension \u65b9\u6cd5\u662f\u5176\u5165\u53e3\u65b9\u6cd5\uff0c\u5176\u6e90\u7801\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"java\">public T getExtension(String name) {\n    if (name == null || name.length() == 0)\n        throw new IllegalArgumentException(&quot;Extension name == null&quot;);\n    if (&quot;true&quot;.equals(name)) {\n        \/\/ \u83b7\u53d6\u9ed8\u8ba4\u7684\u62d3\u5c55\u5b9e\u73b0\u7c7b\n        return getDefaultExtension();\n    }\n    \/\/ Holder\uff0c\u987e\u540d\u601d\u4e49\uff0c\u7528\u4e8e\u6301\u6709\u76ee\u6807\u5bf9\u8c61\n    Holder&lt;Object&gt; holder = cachedInstances.get(name);\n    if (holder == null) {\n        cachedInstances.putIfAbsent(name, new Holder&lt;Object&gt;());\n        holder = cachedInstances.get(name);\n    }\n    Object instance = holder.get();\n    \/\/ \u53cc\u91cd\u68c0\u67e5\n    if (instance == null) {\n        synchronized (holder) {\n            instance = holder.get();\n            if (instance == null) {\n                \/\/ \u521b\u5efa\u62d3\u5c55\u5b9e\u4f8b\n                instance = createExtension(name);\n                \/\/ \u8bbe\u7f6e\u5b9e\u4f8b\u5230 holder \u4e2d\n                holder.set(instance);\n            }\n        }\n    }\n    return (T) instance;\n}<\/code><\/pre>\n<p>\u53ef\u4ee5\u770b\u51fa\uff0c\u8fd9\u4e2a\u65b9\u6cd5\u7684\u4f5c\u7528\u5c31\u662f\uff1a\u9996\u5148\u68c0\u67e5\u7f13\u5b58\uff0c\u7f13\u5b58\u672a\u547d\u4e2d\u5219\u8c03\u7528 createExtension \u65b9\u6cd5\u521b\u5efa\u62d3\u5c55\u5bf9\u8c61\u3002\u90a3\u4e48\uff0ccreateExtension \u662f\u5982\u4f55\u521b\u5efa\u62d3\u5c55\u5bf9\u8c61\u7684\u5462\uff0c\u5176\u6e90\u7801\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"java\">private T createExtension(String name) {\n    \/\/ \u4ece\u914d\u7f6e\u6587\u4ef6\u4e2d\u52a0\u8f7d\u6240\u6709\u7684\u62d3\u5c55\u7c7b\uff0c\u53ef\u5f97\u5230\u201c\u914d\u7f6e\u9879\u540d\u79f0\u201d\u5230\u201c\u914d\u7f6e\u7c7b\u201d\u7684\u6620\u5c04\u5173\u7cfb\u8868\n    Class&lt;?&gt; clazz = getExtensionClasses().get(name);\n    if (clazz == null) {\n        throw findException(name);\n    }\n    try {\n        T instance = (T) EXTENSION_INSTANCES.get(clazz);\n        if (instance == null) {\n            \/\/ \u901a\u8fc7\u53cd\u5c04\u521b\u5efa\u5b9e\u4f8b\n            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());\n            instance = (T) EXTENSION_INSTANCES.get(clazz);\n        }\n        \/\/ \u5411\u5b9e\u4f8b\u4e2d\u6ce8\u5165\u4f9d\u8d56\n        injectExtension(instance);\n        Set&lt;Class&lt;?&gt;&gt; wrapperClasses = cachedWrapperClasses;\n        if (wrapperClasses != null &amp;&amp; !wrapperClasses.isEmpty()) {\n            \/\/ \u5faa\u73af\u521b\u5efa Wrapper \u5b9e\u4f8b\n            for (Class&lt;?&gt; wrapperClass : wrapperClasses) {\n                \/\/ \u5c06\u5f53\u524d instance \u4f5c\u4e3a\u53c2\u6570\u4f20\u7ed9 Wrapper \u7684\u6784\u9020\u65b9\u6cd5\uff0c\u5e76\u901a\u8fc7\u53cd\u5c04\u521b\u5efa Wrapper \u5b9e\u4f8b\u3002\n                \/\/ \u7136\u540e\u5411 Wrapper \u5b9e\u4f8b\u4e2d\u6ce8\u5165\u4f9d\u8d56\uff0c\u6700\u540e\u5c06 Wrapper \u5b9e\u4f8b\u518d\u6b21\u8d4b\u503c\u7ed9 instance \u53d8\u91cf\n                instance = injectExtension(\n                    (T) wrapperClass.getConstructor(type).newInstance(instance));\n            }\n        }\n        return instance;\n    } catch (Throwable t) {\n        throw new IllegalStateException(&quot;...&quot;);\n    }\n}<\/code><\/pre>\n<p>createExtension \u65b9\u6cd5\u7684\u7684\u5de5\u4f5c\u6b65\u9aa4\u53ef\u4ee5\u5f52\u7eb3\u4e3a\uff1a<\/p>\n<ol>\n<li>\u901a\u8fc7 getExtensionClasses \u83b7\u53d6\u6240\u6709\u7684\u62d3\u5c55\u7c7b<\/li>\n<li>\u901a\u8fc7\u53cd\u5c04\u521b\u5efa\u62d3\u5c55\u5bf9\u8c61<\/li>\n<li>\u5411\u62d3\u5c55\u5bf9\u8c61\u4e2d\u6ce8\u5165\u4f9d\u8d56<\/li>\n<li>\u5c06\u62d3\u5c55\u5bf9\u8c61\u5305\u88f9\u5728\u76f8\u5e94\u7684 Wrapper \u5bf9\u8c61\u4e2d<\/li>\n<\/ol>\n<p>\u4ee5\u4e0a\u6b65\u9aa4\u4e2d\uff0c\u7b2c\u4e00\u4e2a\u6b65\u9aa4\u662f\u52a0\u8f7d\u62d3\u5c55\u7c7b\u7684\u5173\u952e\uff0c\u7b2c\u4e09\u548c\u7b2c\u56db\u4e2a\u6b65\u9aa4\u662f Dubbo IOC \u4e0e AOP \u7684\u5177\u4f53\u5b9e\u73b0\u3002<\/p>\n<h4>5.2 \u83b7\u53d6\u6240\u6709\u7684\u62d3\u5c55\u7c7b<\/h4>\n<p>Dubbo \u5728\u901a\u8fc7\u540d\u79f0\u83b7\u53d6\u62d3\u5c55\u7c7b\u4e4b\u524d\uff0c\u9996\u5148\u9700\u8981\u6839\u636e\u914d\u7f6e\u6587\u4ef6\u89e3\u6790\u51fa\u62d3\u5c55\u9879\u540d\u79f0\u5230\u62d3\u5c55\u7c7b\u7684\u6620\u5c04\u5173\u7cfb\u8868\uff08Map&lt;\u540d\u79f0, \u62d3\u5c55\u7c7b&gt;\uff09\uff0c\u4e4b\u540e\u518d\u6839\u636e\u62d3\u5c55\u9879\u540d\u79f0\u4ece\u6620\u5c04\u5173\u7cfb\u8868\u4e2d\u53d6\u51fa\u76f8\u5e94\u7684\u62d3\u5c55\u7c7b\u5373\u53ef\u3002\u76f8\u5173\u8fc7\u7a0b\u7684\u4ee3\u7801\u5206\u6790\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"java\">private Map&lt;String, Class&lt;?&gt;&gt; getExtensionClasses() {\n    \/\/ \u4ece\u7f13\u5b58\u4e2d\u83b7\u53d6\u5df2\u52a0\u8f7d\u7684\u62d3\u5c55\u7c7b\n    Map&lt;String, Class&lt;?&gt;&gt; classes = cachedClasses.get();\n    \/\/ \u53cc\u91cd\u68c0\u67e5\n    if (classes == null) {\n        synchronized (cachedClasses) {\n            classes = cachedClasses.get();\n            if (classes == null) {\n                \/\/ \u52a0\u8f7d\u62d3\u5c55\u7c7b\n                classes = loadExtensionClasses();\n                cachedClasses.set(classes);\n            }\n        }\n    }\n    return classes;\n}<\/code><\/pre>\n<p>\u8fd9\u91cc\u4e5f\u662f\u5148\u68c0\u67e5\u7f13\u5b58\uff0c\u82e5\u7f13\u5b58\u672a\u547d\u4e2d\uff0c\u5219\u901a\u8fc7 synchronized \u52a0\u9501\u3002\u52a0\u9501\u540e\u518d\u6b21\u68c0\u67e5\u7f13\u5b58\uff0c\u5e76\u5224\u7a7a\u3002\u6b64\u65f6\u5982\u679c classes \u4ecd\u4e3a null\uff0c\u5219\u901a\u8fc7 loadExtensionClasses \u52a0\u8f7d\u62d3\u5c55\u7c7b\u3002\u4e0b\u9762\u5206\u6790 loadExtensionClasses \u65b9\u6cd5\u7684\u903b\u8f91\u3002<\/p>\n<pre><code class=\"java\">private Map&lt;String, Class&lt;?&gt;&gt; loadExtensionClasses() {\n    \/\/ \u83b7\u53d6 SPI \u6ce8\u89e3\uff0c\u8fd9\u91cc\u7684 type \u53d8\u91cf\u662f\u5728\u8c03\u7528 getExtensionLoader \u65b9\u6cd5\u65f6\u4f20\u5165\u7684\n    final SPI defaultAnnotation = type.getAnnotation(SPI.class);\n    if (defaultAnnotation != null) {\n        String value = defaultAnnotation.value();\n        if ((value = value.trim()).length() &gt; 0) {\n            \/\/ \u5bf9 SPI \u6ce8\u89e3\u5185\u5bb9\u8fdb\u884c\u5207\u5206\n            String[] names = NAME_SEPARATOR.split(value);\n            \/\/ \u68c0\u6d4b SPI \u6ce8\u89e3\u5185\u5bb9\u662f\u5426\u5408\u6cd5\uff0c\u4e0d\u5408\u6cd5\u5219\u629b\u51fa\u5f02\u5e38\n            if (names.length &gt; 1) {\n                throw new IllegalStateException(&quot;more than 1 default extension name on extension...&quot;);\n            }\n\n            \/\/ \u8bbe\u7f6e\u9ed8\u8ba4\u540d\u79f0\uff0c\u53c2\u8003 getDefaultExtension \u65b9\u6cd5\n            if (names.length == 1) {\n                cachedDefaultName = names[0];\n            }\n        }\n    }\n\n    Map&lt;String, Class&lt;?&gt;&gt; extensionClasses = new HashMap&lt;String, Class&lt;?&gt;&gt;();\n    \/\/ \u52a0\u8f7d\u6307\u5b9a\u6587\u4ef6\u5939\u4e0b\u7684\u914d\u7f6e\u6587\u4ef6\n    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);\n    loadDirectory(extensionClasses, DUBBO_DIRECTORY);\n    loadDirectory(extensionClasses, SERVICES_DIRECTORY);\n    return extensionClasses;\n}<\/code><\/pre>\n<p>loadExtensionClasses \u65b9\u6cd5\u603b\u5171\u505a\u4e86\u4e24\u4ef6\u4e8b\u60c5\uff0c\u4e00\u662f\u5bf9 SPI \u6ce8\u89e3\u8fdb\u884c\u89e3\u6790\uff0c\u4e8c\u662f\u8c03\u7528 loadDirectory \u65b9\u6cd5\u52a0\u8f7d\u6307\u5b9a\u6587\u4ef6\u5939\u914d\u7f6e\u6587\u4ef6\u3002SPI \u6ce8\u89e3\u89e3\u6790\u8fc7\u7a0b\u6bd4\u8f83\u7b80\u5355\uff0c\u65e0\u9700\u591a\u8bf4\u3002\u4e0b\u9762\u6211\u4eec\u6765\u770b\u4e00\u4e0b loadDirectory \u505a\u4e86\u54ea\u4e9b\u4e8b\u60c5\u3002<\/p>\n<pre><code class=\"java\">private void loadDirectory(Map&lt;String, Class&lt;?&gt;&gt; extensionClasses, String dir) {\n    \/\/ fileName = \u6587\u4ef6\u5939\u8def\u5f84 + type \u5168\u9650\u5b9a\u540d\n    String fileName = dir + type.getName();\n    try {\n        Enumeration&lt;java.net.URL&gt; urls;\n        ClassLoader classLoader = findClassLoader();\n        \/\/ \u6839\u636e\u6587\u4ef6\u540d\u52a0\u8f7d\u6240\u6709\u7684\u540c\u540d\u6587\u4ef6\n        if (classLoader != null) {\n            urls = classLoader.getResources(fileName);\n        } else {\n            urls = ClassLoader.getSystemResources(fileName);\n        }\n        if (urls != null) {\n            while (urls.hasMoreElements()) {\n                java.net.URL resourceURL = urls.nextElement();\n                \/\/ \u52a0\u8f7d\u8d44\u6e90\n                loadResource(extensionClasses, classLoader, resourceURL);\n            }\n        }\n    } catch (Throwable t) {\n        logger.error(&quot;...&quot;);\n    }\n}<\/code><\/pre>\n<p>loadDirectory \u65b9\u6cd5\u5148\u901a\u8fc7 classLoader \u83b7\u53d6\u6240\u6709\u8d44\u6e90\u94fe\u63a5\uff0c\u7136\u540e\u518d\u901a\u8fc7 loadResource \u65b9\u6cd5\u52a0\u8f7d\u8d44\u6e90\u3002\u6211\u4eec\u7ee7\u7eed\u8ddf\u4e0b\u53bb\uff0c\u770b\u4e00\u4e0b loadResource \u65b9\u6cd5\u7684\u5b9e\u73b0\u3002<\/p>\n<pre><code class=\"java\">private void loadResource(Map&lt;String, Class&lt;?&gt;&gt; extensionClasses,\n  ClassLoader classLoader, java.net.URL resourceURL) {\n    try {\n        BufferedReader reader = new BufferedReader(\n            new InputStreamReader(resourceURL.openStream(), &quot;utf-8&quot;));\n        try {\n            String line;\n            \/\/ \u6309\u884c\u8bfb\u53d6\u914d\u7f6e\u5185\u5bb9\n            while ((line = reader.readLine()) != null) {\n                \/\/ \u5b9a\u4f4d # \u5b57\u7b26\n                final int ci = line.indexOf('#');\n                if (ci &gt;= 0) {\n                    \/\/ \u622a\u53d6 # \u4e4b\u524d\u7684\u5b57\u7b26\u4e32\uff0c# \u4e4b\u540e\u7684\u5185\u5bb9\u4e3a\u6ce8\u91ca\uff0c\u9700\u8981\u5ffd\u7565\n                    line = line.substring(0, ci);\n                }\n                line = line.trim();\n                if (line.length() &gt; 0) {\n                    try {\n                        String name = null;\n                        int i = line.indexOf('=');\n                        if (i &gt; 0) {\n                            \/\/ \u4ee5\u7b49\u4e8e\u53f7 = \u4e3a\u754c\uff0c\u622a\u53d6\u952e\u4e0e\u503c\n                            name = line.substring(0, i).trim();\n                            line = line.substring(i + 1).trim();\n                        }\n                        if (line.length() &gt; 0) {\n                            \/\/ \u52a0\u8f7d\u7c7b\uff0c\u5e76\u901a\u8fc7 loadClass \u65b9\u6cd5\u5bf9\u7c7b\u8fdb\u884c\u7f13\u5b58\n                            loadClass(extensionClasses, resourceURL,\n                                      Class.forName(line, true, classLoader), name);\n                        }\n                    } catch (Throwable t) {\n                        IllegalStateException e = new IllegalStateException(&quot;Failed to load extension class...&quot;);\n                    }\n                }\n            }\n        } finally {\n            reader.close();\n        }\n    } catch (Throwable t) {\n        logger.error(&quot;Exception when load extension class...&quot;);\n    }\n}<\/code><\/pre>\n<p>loadResource \u65b9\u6cd5\u7528\u4e8e\u8bfb\u53d6\u548c\u89e3\u6790\u914d\u7f6e\u6587\u4ef6\uff0c\u5e76\u901a\u8fc7\u53cd\u5c04\u52a0\u8f7d\u7c7b\uff0c\u6700\u540e\u8c03\u7528 loadClass \u65b9\u6cd5\u8fdb\u884c\u5176\u4ed6\u64cd\u4f5c\u3002loadClass \u65b9\u6cd5\u7528\u4e8e\u4e3b\u8981\u7528\u4e8e\u64cd\u4f5c\u7f13\u5b58\uff0c\u8be5\u65b9\u6cd5\u7684\u903b\u8f91\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"java\">private void loadClass(Map&lt;String, Class&lt;?&gt;&gt; extensionClasses, java.net.URL resourceURL,\n    Class&lt;?&gt; clazz, String name) throws NoSuchMethodException {\n\n    if (!type.isAssignableFrom(clazz)) {\n        throw new IllegalStateException(&quot;...&quot;);\n    }\n\n    \/\/ \u68c0\u6d4b\u76ee\u6807\u7c7b\u4e0a\u662f\u5426\u6709 Adaptive \u6ce8\u89e3\n    if (clazz.isAnnotationPresent(Adaptive.class)) {\n        if (cachedAdaptiveClass == null) {\n            \/\/ \u8bbe\u7f6e cachedAdaptiveClass\u7f13\u5b58\n            cachedAdaptiveClass = clazz;\n        } else if (!cachedAdaptiveClass.equals(clazz)) {\n            throw new IllegalStateException(&quot;...&quot;);\n        }\n\n    \/\/ \u68c0\u6d4b clazz \u662f\u5426\u662f Wrapper \u7c7b\u578b\n    } else if (isWrapperClass(clazz)) {\n        Set&lt;Class&lt;?&gt;&gt; wrappers = cachedWrapperClasses;\n        if (wrappers == null) {\n            cachedWrapperClasses = new ConcurrentHashSet&lt;Class&lt;?&gt;&gt;();\n            wrappers = cachedWrapperClasses;\n        }\n        \/\/ \u5b58\u50a8 clazz \u5230 cachedWrapperClasses \u7f13\u5b58\u4e2d\n        wrappers.add(clazz);\n\n    \/\/ \u7a0b\u5e8f\u8fdb\u5165\u6b64\u5206\u652f\uff0c\u8868\u660e clazz \u662f\u4e00\u4e2a\u666e\u901a\u7684\u62d3\u5c55\u7c7b\n    } else {\n        \/\/ \u68c0\u6d4b clazz \u662f\u5426\u6709\u9ed8\u8ba4\u7684\u6784\u9020\u65b9\u6cd5\uff0c\u5982\u679c\u6ca1\u6709\uff0c\u5219\u629b\u51fa\u5f02\u5e38\n        clazz.getConstructor();\n        if (name == null || name.length() == 0) {\n            \/\/ \u5982\u679c name \u4e3a\u7a7a\uff0c\u5219\u5c1d\u8bd5\u4ece Extension \u6ce8\u89e3\u4e2d\u83b7\u53d6 name\uff0c\u6216\u4f7f\u7528\u5c0f\u5199\u7684\u7c7b\u540d\u4f5c\u4e3a name\n            name = findAnnotationName(clazz);\n            if (name.length() == 0) {\n                throw new IllegalStateException(&quot;...&quot;);\n            }\n        }\n        \/\/ \u5207\u5206 name\n        String[] names = NAME_SEPARATOR.split(name);\n        if (names != null &amp;&amp; names.length &gt; 0) {\n            Activate activate = clazz.getAnnotation(Activate.class);\n            if (activate != null) {\n                \/\/ \u5982\u679c\u7c7b\u4e0a\u6709 Activate \u6ce8\u89e3\uff0c\u5219\u4f7f\u7528 names \u6570\u7ec4\u7684\u7b2c\u4e00\u4e2a\u5143\u7d20\u4f5c\u4e3a\u952e\uff0c\n                \/\/ \u5b58\u50a8 name \u5230 Activate \u6ce8\u89e3\u5bf9\u8c61\u7684\u6620\u5c04\u5173\u7cfb\n                cachedActivates.put(names[0], activate);\n            }\n            for (String n : names) {\n                if (!cachedNames.containsKey(clazz)) {\n                    \/\/ \u5b58\u50a8 Class \u5230\u540d\u79f0\u7684\u6620\u5c04\u5173\u7cfb\n                    cachedNames.put(clazz, n);\n                }\n                Class&lt;?&gt; c = extensionClasses.get(n);\n                if (c == null) {\n                    \/\/ \u5b58\u50a8\u540d\u79f0\u5230 Class \u7684\u6620\u5c04\u5173\u7cfb\n                    extensionClasses.put(n, clazz);\n                } else if (c != clazz) {\n                    throw new IllegalStateException(&quot;...&quot;);\n                }\n            }\n        }\n    }\n}<\/code><\/pre>\n<p>\u5982\u4e0a\uff0cloadClass \u65b9\u6cd5\u64cd\u4f5c\u4e86\u4e0d\u540c\u7684\u7f13\u5b58\uff0c\u6bd4\u5982 cachedAdaptiveClass\u3001cachedWrapperClasses \u548c cachedNames \u7b49\u7b49\u3002\u9664\u6b64\u4e4b\u5916\uff0c\u8be5\u65b9\u6cd5\u6ca1\u6709\u5176\u4ed6\u4ec0\u4e48\u903b\u8f91\u4e86\u3002<\/p>\n<p>\u53c2\u8003\u8d44\u6599<\/p>\n<ul>\n<li><a href=\"https:\/\/link.segmentfault.com\/?enc=t1F6SfcZf3IUHFiefiWq6A%3D%3D.ovwaYIrME9aKjcBw3vVKyLxcb6dv81%2FfkUv1X7bgWUl0djbgfKcjtY05ujlNy6Ec\" rel=\"nofollow\">Java SPI \u601d\u60f3\u68b3\u7406<\/a><\/li>\n<li><a href=\"https:\/\/link.segmentfault.com\/?enc=uWb8bT5M45aYSvdTTBjpMA%3D%3D.iIUB1OeZaQz57Cphzf7kk6BkwtpQjgzPSzNZTWHQEtJ701ypJDm2O4hFaLCq9Sl%2Bobv6ezM%2Fn%2B4%2B4Fq8vXp6KJ7bBee2Yyv7v%2FTSBwUwhItYDqmzY%2FdFJLNe5g6PipRn\" rel=\"nofollow\">Dubbo SPI<\/a><\/li>\n<li><a href=\"https:\/\/link.segmentfault.com\/?enc=kTPVMDuYtFftx6IKbPOEGw%3D%3D.17F8ZFfAxqftgRNP4x0OCSm3SHCKlUz36XI19bKhDj6X1G2cQfd2WTm1oditNH7p\" rel=\"nofollow\">springboot \u4e2d SPI \u673a\u5236<\/a><\/li>\n<li><a href=\"https:\/\/link.segmentfault.com\/?enc=jKcknErYB774ok0Azx4WgA%3D%3D.wDe3ST4qx0yNanuJyDsrrSZ29sf%2FbTDqIgRlwyNtUNwOWa4dHGE6pLwlaZ%2FF%2F9MKC8qiHNCLYXlZUmjdBDnnuw%3D%3D\" rel=\"nofollow\">SpringBoot \u7684\u81ea\u52a8\u88c5\u914d\u539f\u7406\u3001\u81ea\u5b9a\u4e49 starter \u4e0e spi \u673a\u5236\uff0c\u4e00\u7f51\u6253\u5c3d<\/a><\/li>\n<\/ul>\n<p><a href=\"https:\/\/segmentfault.com\/a\/1190000042736846\">https:\/\/segmentfault.com\/a\/1190000042736846<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u4f5c\u8005\uff1avivo \u4e92\u8054\u7f51\u670d\u52a1\u5668\u56e2\u961f- Zhang Peng SPI \u662f\u4e00\u79cd\u7528\u4e8e\u52a8\u6001\u52a0\u8f7d\u670d\u52a1\u7684\u673a\u5236\u3002\u5b83\u7684\u6838\u5fc3\u601d\u60f3\u5c31 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[4],"tags":[],"class_list":["post-10006820","post","type-post","status-publish","format-standard","hentry","category-share"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts\/10006820","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=10006820"}],"version-history":[{"count":0,"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts\/10006820\/revisions"}],"wp:attachment":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=10006820"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=10006820"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=10006820"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}