• 读Dubbo源码,学习SPI


    核心类

    ExtensionLoader

    使用方法

    1. 定义接口,使用@SPI标记
    @SPI("impl1")
    public interface SimpleExt {
        // @Adaptive example, do not specify a explicit key.
        @Adaptive
        String echo(URL url, String s);
    
        @Adaptive({"key1", "key2"})
        String yell(URL url, String s);
    
        // no @Adaptive
        String bang(URL url, int i);
    }
    
    @SPI("impl1")
    public interface UseProtocolKeyExt {
        // protocol key is the second
        @Adaptive({"key1", "protocol"})
        String echo(URL url, String s);
    
        // protocol key is the first
        @Adaptive({"protocol", "key2"})
        String yell(URL url, String s);
    }
    
    1. 扩展类
    • SimpleExt.java
    public class SimpleExtImpl1 implements SimpleExt {
        public String echo(URL url, String s) {
            return "Ext1Impl1-echo";
        }
    
        public String yell(URL url, String s) {
            return "Ext1Impl1-yell";
        }
    
        public String bang(URL url, int i) {
            return "bang1";
        }
    }
    
    public class SimpleExtImpl2 implements SimpleExt {
        public String echo(URL url, String s) {
            return "Ext1Impl2-echo";
        }
    
        public String yell(URL url, String s) {
            return "Ext1Impl2-yell";
        }
    
        public String bang(URL url, int i) {
            return "bang2";
        }
    
    }
    
    public class SimpleExtImpl3 implements SimpleExt {
        public String echo(URL url, String s) {
            return "Ext1Impl3-echo";
        }
    
        public String yell(URL url, String s) {
            return "Ext1Impl3-yell";
        }
    
        public String bang(URL url, int i) {
            return "bang3";
        }
    
    }
    

    • UseProtocolKeyExt.java
    public class UseProtocolKeyExtImpl1 implements UseProtocolKeyExt {
        public String echo(URL url, String s) {
            return "Ext3Impl1-echo";
        }
    
        public String yell(URL url, String s) {
            return "Ext3Impl1-yell";
        }
    }
    
    public class UseProtocolKeyExtImpl2 implements UseProtocolKeyExt {
        public String echo(URL url, String s) {
            return "Ext3Impl2-echo";
        }
    
        public String yell(URL url, String s) {
            return "Ext3Impl2-yell";
        }
    }
    
    public class UseProtocolKeyExtImpl3 implements UseProtocolKeyExt {
        public String echo(URL url, String s) {
            return "Ext3Impl3-echo";
        }
    
        public String yell(URL url, String s) {
            return "Ext3Impl3-yell";
        }
    }
    

    3.SPI资源路径

    • 路径-> META-INFO/dubbo/interal/{@SPI注解的全限定名}

    com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt

    • 实现类配置

      • com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt

      impl1=com.alibaba.dubbo.common.extensionloader.ext1.impl.SimpleExtImpl1#Hello World impl2=com.alibaba.dubbo.common.extensionloader.ext1.impl.SimpleExtImpl2 # Comment 2 impl3=com.alibaba.dubbo.common.extensionloader.ext1.impl.SimpleExtImpl3 # with head space

      • com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt

      impl1=com.alibaba.dubbo.common.extensionloader.ext3.impl.UseProtocolKeyExtImpl1 impl2=com.alibaba.dubbo.common.extensionloader.ext3.impl.UseProtocolKeyExtImpl2 impl3=com.alibaba.dubbo.common.extensionloader.ext3.impl.UseProtocolKeyExtImpl3

    4.测试方法

    • SimpleExt.java
        @Test
        public void test_getAdaptiveExtension_defaultAdaptiveKey() throws Exception {
            {
                // #1
                SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension();
    
                Map<String, String> map = new HashMap<String, String>();
                URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
                // #2
                String echo = ext.echo(url, "haha");
                assertEquals("Ext1Impl1-echo", echo);
            }
    
            {
                #3
                SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension();
    
                Map<String, String> map = new HashMap<String, String>();
                map.put("simple.ext", "impl2");
                URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
                
                #4
                String echo = ext.echo(url, "haha");
                assertEquals("Ext1Impl2-echo", echo);
            }
        }
    
    • #1.该方法执行后会通过ExtensionLoader.createAdaptiveExtensionClassCode生成一个代理类对象ext,见附录1
    • #2.1 ext中有一行:String extName = url.getParameter("simple.ext", "impl1"); (key,defaultValue) ,其中key:simple.ext是接口名称SimpleExt去驼峰加. 构成,原因在于echo方法没有@Adaptive注解没有传入参数;defaultValue根据接口@SPI值impl1生成。
    • #2.2 url中map为空,extName取传入的默认值impl1
    • #2.3 ext中有一行 com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt extension = (com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt.class).getExtension(extName); 根据extName扩展名去寻找真正需要的扩展实现类。此时extName是impl1,那么真正执行的echo就是impl1代表的SimpleExtImpl1实例
    • #3同#1
    • #4.1 url中map为("simple.ext", "impl2")
    • #4.2 通过String extName = url.getParameter("simple.ext", "impl1");获得extName为impl2
    • #4.3 此时extName是impl2,那么真正执行的echo就是impl2代表的SimpleExtImpl2实例
        @Test
        public void test_getAdaptiveExtension_customizeAdaptiveKey() throws Exception {
            
            SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension();
    
            Map<String, String> map = new HashMap<String, String>();
            map.put("key2", "impl2");
            URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
            #5
            String echo = ext.yell(url, "haha");
            assertEquals("Ext1Impl2-yell", echo);
            #6
            url = url.addParameter("key1", "impl3"); // note: URL is value's type
            echo = ext.yell(url, "haha");
            assertEquals("Ext1Impl3-yell", echo);
        }
    
    • #5.1 因为yell方法上@Adaptive注解有参数{"key1", "key2"},那么ext生成的代理方法中获取extName代码为:String extName = url.getParameter("key1",url.getParameter("key2", "impl1"));
    • #5.2 url中map为("key2", "impl2"),第一轮判后extName是impl2,第二轮判断后extName为impl2
    • #5.3 此时extName是impl2,那么真正执行的echo就是impl2代表的SimpleExtImpl2实例
    • #6.1 url中map为("key2", "impl2")("key1", "impl3") ,第一轮判后extName是impl2,第二轮判断后extName为impl3
    • #6.2 此时extName是impl3,那么真正执行的echo就是impl3代表的SimpleExtImpl3实例
    • 参数判断顺序与参数定义顺序相反
        @Test
        public void test_getAdaptiveExtension_protocolKey() throws Exception {
            #1
            UseProtocolKeyExt ext = ExtensionLoader.getExtensionLoader(UseProtocolKeyExt.class).getAdaptiveExtension();
        
            {
                #2
                String echo = ext.echo(URL.valueOf("1.2.3.4:20880"), "s");
                assertEquals("Ext3Impl1-echo", echo); // default value
                
                #3
                Map<String, String> map = new HashMap<String, String>();
                URL url = new URL("impl3", "1.2.3.4", 1010, "path1", map);
        
                echo = ext.echo(url, "s");
                assertEquals("Ext3Impl3-echo", echo); // use 2nd key, protocol
        
                #4
                url = url.addParameter("key1", "impl2");
                echo = ext.echo(url, "s");
                assertEquals("Ext3Impl2-echo", echo); // use 1st key, key1
            }
    
    • #1.该方法执行后会通过ExtensionLoader.createAdaptiveExtensionClassCode生成一个代理类对象ext,见附录2
    • #2.1 echo方法@Adaptive注解中有值({"key1", "protocol"}),且其中一个为protocol,在ext中extName判断方法为url.getParameter("key1", (url.getProtocol() == null ? "impl1" : url.getProtocol()));
    • #2.2 url中未指定protocol,同时map为null,判断后extName为默认值impl1
    • #2.3 此时extName是impl1,那么真正执行的echo就是impl1代表的UseProtocolKeyExtImpl1实例
    • #3.1 此时url的protocol为impl3,extName为impl3
    • #3.2 此时extName是impl3,那么真正执行的echo就是impl3代表的UseProtocolKeyExtImpl3实例
    • #4.1 此时url的proto是impl3,map("key1", "impl2"),extName是impl2
    • #4.2 此时extName是impl2,那么真正执行的echo就是impl2代表的UseProtocolKeyExtImpl2实例
            {
        
                Map<String, String> map = new HashMap<String, String>();
                URL url = new URL(null, "1.2.3.4", 1010, "path1", map);
                #5
                String yell = ext.yell(url, "s");
                assertEquals("Ext3Impl1-yell", yell); // default value
                
                #6
                url = url.addParameter("key2", "impl2"); // use 2nd key, key2
                yell = ext.yell(url, "s");
                assertEquals("Ext3Impl2-yell", yell);
                #7
                url = url.setProtocol("impl3"); // use 1st key, protocol
                yell = ext.yell(url, "d");
                assertEquals("Ext3Impl3-yell", yell);
            }
        }
    
    • #5.1 yell方法@Adaptive注解中有值({"protocol", "key1"}),且其中一个为protocol,在ext中extName判断方法为url.getProtocol() == null ? (url.getParameter( "key2", "impl1" ) ) : url.getProtocol()
    • #5.2 url中未指定protocol,同时map为null,判断后extName为默认值impl1
    • #5.3 此时extName是impl1,那么真正执行的echo就是impl1代表的UseProtocolKeyExtImpl1实例
    • #6.1 此时map("key2", "impl2"),extName为impl2
    • #6.2 此时extName是impl2,那么真正执行的echo就是impl2代表的UseProtocolKeyExtImpl2实例
    • #7.1 此时url的proto是impl3,map("key1", "impl2"),extName是impl3
    • #7.2 此时extName是impl3,那么真正执行的echo就是impl3代表的UseProtocolKeyExtImpl3实例

    附录1

    package com.alibaba.dubbo.common.extensionloader.ext1;
    
    import com.alibaba.dubbo.common.extension.ExtensionLoader;
    
    
    public class SimpleExt$Adaptive implements com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt {
        public java.lang.String echo(com.alibaba.dubbo.common.URL arg0,
            java.lang.String arg1) {
            if (arg0 == null) {
                throw new IllegalArgumentException("url == null");
            }
    
            com.alibaba.dubbo.common.URL url = arg0;
            String extName = url.getParameter("simple.ext", "impl1");
    
            if (extName == null) {
                throw new IllegalStateException(
                    "Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt) name from url(" +
                    url.toString() + ") use keys([simple.ext])");
            }
    
            com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt extension = (com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt.class)
                                                                                                                                                         .getExtension(extName);
    
            return (extension.echo(arg0, arg1));
        }
    
        public java.lang.String yell(com.alibaba.dubbo.common.URL arg0,
            java.lang.String arg1) {
            if (arg0 == null) {
                throw new IllegalArgumentException("url == null");
            }
    
            com.alibaba.dubbo.common.URL url = arg0;
            String extName = url.getParameter("key1",
                    url.getParameter("key2", "impl1"));
    
            if (extName == null) {
                throw new IllegalStateException(
                    "Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt) name from url(" +
                    url.toString() + ") use keys([key1, key2])");
            }
    
            com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt extension = (com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt.class)
                                                                                                                                                         .getExtension(extName);
    
            return (extension.yell(arg0, arg1));
        }
    
        public java.lang.String bang(com.alibaba.dubbo.common.URL arg0, int arg1) {
            throw new UnsupportedOperationException(
                "method public abstract java.lang.String com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt.bang(com.alibaba.dubbo.common.URL,int) of interface com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt is not adaptive method!");
        }
    }
    
    
    

    附录2

    package com.alibaba.dubbo.common.extensionloader.ext3;
    import com.alibaba.dubbo.common.extension.ExtensionLoader;
    public class UseProtocolKeyExt$Adaptive implements com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt {
    	public java.lang.String echo( com.alibaba.dubbo.common.URL arg0, java.lang.String arg1 )
    	{
    		if ( arg0 == null )
    			throw new IllegalArgumentException( "url == null" );
    		com.alibaba.dubbo.common.URL	url	= arg0;
    		String				extName = url.getParameter( "key1", (url.getProtocol() == null ? "impl1" : url.getProtocol() ) );
    		if ( extName == null )
    			throw new IllegalStateException( "Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt) name from url(" + url.toString() + ") use keys([key1, protocol])" );
    		com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt extension = 
    		(com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt)ExtensionLoader.getExtensionLoader( com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt.class ).getExtension( extName );
    		return(extension.echo( arg0, arg1 ) );
    	}
    
    
    	public java.lang.String yell( com.alibaba.dubbo.common.URL arg0, java.lang.String arg1 )
    	{
    		if ( arg0 == null )
    			throw new IllegalArgumentException( "url == null" );
    		com.alibaba.dubbo.common.URL	url	= arg0;
    		String				extName = url.getProtocol() == null ? (url.getParameter( "key2", "impl1" ) ) : url.getProtocol();
    		if ( extName == null )
    			throw new IllegalStateException( "Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt) name from url(" + url.toString() + ") use keys([protocol, key2])" );
    		com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt extension = (com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt)ExtensionLoader.
    		getExtensionLoader( com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt.class ).getExtension( extName );
    		return(extension.yell( arg0, arg1 ) );
    	}
    }
  • 相关阅读:
    杨辉三角
    100以内的素数
    九九
    MyDate
    计算器
    100以内素数
    杨辉三角形
    九九乘法表
    窗口关闭事件
    计算器界面
  • 原文地址:https://www.cnblogs.com/Yiran583/p/11926896.html
Copyright © 2020-2023  润新知