zoukankan      html  css  js  c++  java
  • Dubbo 源码解析(一)Dubbo SPI

    Dubbo 扩展机制 SPI

    在 Dubbo 中,SPI 贯穿在整个 Dubbo 的核心。所以有必要先对 Dubbo 中 SPI 做一个详细的介绍。

    JDK SPI

    之前写过一篇介绍 JDK SPI 的博客,可以点击查看。

    Dubbo SPI

    Dubbo 实现了自己的 SPI 机制

    • 除了可以配置在 META-INF/services 目录下,还可以配置在 META-INF/dubboMETA-INF/dubbo/internal
    • 配置文件内容采用 key=value 的形式。这样可以按需实例化加载类,而不用像 JDK 一样在启动时一次性加载实例化扩展点的所有实现,浪费性能。
    • 出现异常时可以更准确定位
    • 增加了对 Duboo 自己实现的 IOC 和 AOP 的支持

    源码解析

    这里使用的版本是 dubbo-2.6.4

    这里的分析流程是根据例子来分析内部源码的实现

    注意,在阅读下面的源码解析时,有些跟当前流程无关的代码我会标注 忽略点 - X,表示这段代码先跳过,不影响当前流程,且在后面用到的地方我会再重新解释该忽略点的意思。

    1、Dubbo SPI 的基本示例

    接口,注意加 @SPI 注解

    @SPI
    public interface Robot {
    
    	void sayHello();
    
    }
    

    实现类

    public class MIRobot implements Robot {
    
    	@Override
    	public void sayHello() {
    		System.out.println("大家好,我是小米机器人...");
    	}
    }
    

    在文件夹 resources/META-INF/services 下添加配置文件,文件名 com.lin.spi.Robot(接口的路径)

    文件内容

    miRobot = com.lin.spi.MIRobot
    

    在测试类中调用

    public class DubboSPITest {
    
    	@Test
    	public void test() {
    
    		ExtensionLoader<Robot> extensionLoader =
    				ExtensionLoader.getExtensionLoader(Robot.class);
    
    		Robot miRobot = extensionLoader.getExtension("miRobot");
    		miRobot.sayHello();
    	}
    }
    

    输出

    大家好,我是小米机器人...
    
    Process finished with exit code 0
    

    上面就是 Dubbo SPI 的基本使用,接下来开始源码分析。

    (1)入口方法 ExtensionLoader#getExtensionLoader(Class<T> type)
    @SuppressWarnings("unchecked")
    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        // ...
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        if (loader == null) {
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }
    

    getExtensionLoader(Class<T> type) 的作用只是简单的根据 type 包装一个 ExtensionLoader 实例,缓存在 EXTENSION_LOADERS 中,然后返回 ExtensionLoader 实例。

    其中 ExtensionLoader 实例化时 objectFactory 的创建我们这里先忽略

    (2)调用方法 ExtensionLoader#getExtension(String name) 获取具体的实例
    @SuppressWarnings("unchecked")
    public T getExtension(String name) {
        // ...
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<Object>());
            holder = cachedInstances.get(name);
        }
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }
    

    getExtension(String name) 的作用就是创建我们在配置文件配置的扩展点实现类(例子中的 MIRobot 实例),包装在 Holder 中然后缓存进 cachedInstances,返回实例。

    创建扩展类

    @SuppressWarnings("unchecked")
    private T createExtension(String name) {
        // 根据 type 去配置文件中加载配置的所有扩展类存进 Map<String, Class<?>> 缓存,再根据 name 得到特定的 clazz
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                                            type + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }
    

    createExtension(String name) 方法的逻辑包含如下步骤

    • 获取所有的扩展类(实例中是返回 miRobot -> "class com.lin.spi.MIRobot" )
    • 通过反射创建拓展对象
    • 向拓展对象中注入依赖(这里暂时忽略)
    • 将拓展对象包裹在相应的 Wrapper 对象中(这里暂时忽略)
    (2.1)获取所有的扩展类
    private Map<String, Class<?>> getExtensionClasses() {
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }
    
    private Map<String, Class<?>> loadExtensionClasses() {
        // 获取 @SPI 注解
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if ((value = value.trim()).length() > 0) {
                String[] names = NAME_SEPARATOR.split(value);
                if (names.length > 1) {
                    // 检测 @SPI 注解内容是否合法
                    throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                                                    + ": " + Arrays.toString(names));
                }
                if (names.length == 1) cachedDefaultName = names[0];
            }
        }
    
        Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
        // 加载指定文件夹下的配置文件  
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY); // META-INF/dubbo/internal
        loadDirectory(extensionClasses, DUBBO_DIRECTORY); // META-INF/dubbo
        loadDirectory(extensionClasses, SERVICES_DIRECTORY); // META-INF/services
        return extensionClasses;
    }
    

    loadDirectory 方法先通过 classLoader 获取所有资源链接(配置文件链接),然后再通过 loadResource 方法读取和解析配置文件,把解析出的每一个 clazz 调用 loadClass()cachedNamesextensionClassescachedActivates 等缓存起来

    2、Dubbo AOP 实现

    例子

    在上面的例子基础上,新建类 RobotWrapper

    public class RobotWrapper implements Robot {
    
    	private Robot robot;
    
    	public RobotWrapper(Robot robot) {
    		this.robot = robot;
    	}
    
    	@Override
    	public void sayHello() {
    		System.out.println("before...");
    		this.robot.sayHello();
    		System.out.println("after...");
    	}
    }
    

    在配置文件中写上

    miRobot = com.lin.spi.MIRobot
    wrapper = com.lin.spi.RobotWrapper
    

    关于 RobotWrapper,一定要有构造函数才会生效,同时在配置文件中可以不用写 wrapper=,也可以同时存在多个 xxxWrapper 实现

    接下来不用改动原来的代码,直接运行,

    输出:

    before...
    大家好,我是小米机器人...
    after...
    
    源码分析

    getExtensionClass(key) -> getExtensionClasses() -> loadExtensionClasses() -> loadDirectory() -> loadResource() -> loadClass()

    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
     	// ...
        if () {
            
        } else if (isWrapperClass(clazz)) { // 判断 clazz 是否有构造函数  忽略点-3的解释
            Set<Class<?>> wrappers = cachedWrapperClasses;
            if (wrappers == null) {
                cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                wrappers = cachedWrapperClasses;
            }
            wrappers.add(clazz); // 把 clazz 存进缓存中
        } else {
            
        }
    }
    
    private boolean isWrapperClass(Class<?> clazz) {
        try {
            clazz.getConstructor(type); // clazz 是否有 type 作为参数的构造函数
            return true;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }
    

    接下来在 clazz 实例化时,

    @SuppressWarnings("unchecked")
    private T createExtension(String name) {
        // ...
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
            // 取出所有的 wrapperClass,遍历,取出每一个,把 instance 传进 wrapperClass 的构造函数并实例化,赋值给 instance
            // 最后返回的 instance 就是被 wrapper 层层包裹的结果
            // 代入例子中,instance = RobotWrapper.class.getConstructor(Robot.class).newInstance(miRobot)
            // 类似 instance = new RobotWrapper(miRobot)
            for (Class<?> wrapperClass : wrapperClasses) {
                instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
            }
        }
        // ...
    }
    

    3、Dubbo IOC 实现

    官网解释

    Dubbo IOC 是通过 setter 方法注入依赖。Dubbo 首先会通过反射获取到实例的所有方法,然后再遍历方法列表,检测方法名是否具有 setter 方法特征。若有,则通过 ObjectFactory 获取依赖对象,最后通过反射调用 setter 方法将依赖设置到目标对象中。

    Dubbo IOC 这部分可以结合 Dubbo 的自适应扩展来讲

    4、什么是自适应扩展?

    前面我们讲解 Dubbo 可以通过 ExtensionLoader.getExtensionLoader(XXXClass).getExtension(key) 的形式来获取某个接口的实现类,但这种形式属于硬编码来指定引用哪个实现。而现在某些扩展希望可以在被调用时,根据运行时参数进行加载,这需要自适应扩展来实现。

    首先先来看和自适应扩展相关的两个参数,一个是注解 @Adaptive,一个是 com.alibaba.dubbo.common.URL

    @Adaptive 可修饰在类和方法上,

    • 当修饰在类上时,表示该类为所实现的接口的代理类实现
    • 当修饰在方法上时,Dubbo 会自动生成适配器类,会从 URL 中取值作为扩展点名去加载实现类并实例化,最后再使用这个实例调用对应的方法。

    例子:

    @SPI
    public interface Robot {
    
    	@Adaptive("robot")
    	void sayHello(URL url);
    }
    
    public class AdaptiveRobot implements Robot {
    
    	private Robot robot;
    
    	public void setRobot(Robot robot) {
    		this.robot = robot;
    	}
    
    	@Override
    	public void sayHello(URL url) {
    		System.out.println("在代理类内部通过 URL 和 SPI 机制获取和加载具体的 Robot 实现类,并调用目标方法");
    		this.robot.sayHello(url);
    	}
    }
    
    public class MIRobot implements Robot {
    
    	@Override
    	public void sayHello(URL url) {
    		System.out.println("hello");
    	}
    }
    
    import com.alibaba.dubbo.common.URL;
    
    public class DubboSPITest {
    
    	@Test
    	public void test() {
    
    		ExtensionLoader<Robot> extensionLoader =
    				ExtensionLoader.getExtensionLoader(Robot.class);
    
    		Map<String, String> map = new HashMap<>();
    		map.put("robot", "miRobot"); // key=robot 由注解 @Adaptive("robot") 决定
    		URL url = new URL("", "", 1, map);
    		Robot adaptiveRobot = extensionLoader.getExtension("adaptiveRobot");
    		adaptiveRobot.sayHello(url);
    	}
    }
    

    输出:

    在代理类内部通过 URL 和 SPI 机制获取和加载具体的 Robot 实现类,并调用目标方法
    hello
    
    源码分析

    在执行 Class<?> clazz = getExtensionClasses().get("adaptiveRobot"); 并实例化 instance 后,调用 injectExtension(instance) 方法向扩展实例中注入依赖

    @SuppressWarnings("unchecked")
    private T createExtension(String name) {
        Class<?> clazz = getExtensionClasses().get(name);
        // ...
        try {
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            // ...
            injectExtension(instance);
            // ...
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                                            type + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }
    

    接下来看 injectExtension(instance) 的具体实现

    private T injectExtension(T instance) {
        try {
            if (objectFactory != null) {
                for (Method method : instance.getClass().getMethods()) {
                    // 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public
                    if (method.getName().startsWith("set")
                        && method.getParameterTypes().length == 1
                        && Modifier.isPublic(method.getModifiers())) {
                        // 获取 setter 方法参数类型
                        Class<?> pt = method.getParameterTypes()[0];
                        try {
                            // 获取属性名,比如 setRobot 方法对应属性名 robot
                            String property = method.getName().length() > 3 ? 
                                method.getName().substring(3, 4).toLowerCase() + 
                                	method.getName().substring(4) : "";
                            // 从 ObjectFactory 中获取依赖对象  eg. XXX$Adaptive
                            Object object = objectFactory.getExtension(pt, property);
                            if (object != null) {
                                // 通过反射调用 setter 方法设置依赖
                                method.invoke(instance, object); // 往示例中的 AdaptiveRobot 实例的 setRobot() 方法中注入 Robot$Adaptive 自适应类
                            }
                        } catch (Exception e) {
                            logger.error("fail to inject via method...");
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }
    

    这部分源码解释看注释就可以,流程很清晰,主要说下里面的 Object object = objectFactory.getExtension(pt, property);objectFactory

    objectFactory 的实例化

    每一个 objectFactory 的创建都是在 ExtensionLoader 实例化的时候,

    private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }
    

    接下来看 getAdaptiveExtension()

    public T getAdaptiveExtension() {
        // 从缓存中获取自适应拓展
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            if (createAdaptiveInstanceError == null) {
                synchronized (cachedAdaptiveInstance) {
                    instance = cachedAdaptiveInstance.get();
                    if (instance == null) {
                        try {
                            // 创建自适应拓展
                            instance = createAdaptiveExtension();
                            // 设置自适应拓展到缓存中
                            cachedAdaptiveInstance.set(instance);
                        } catch (Throwable t) {
                            createAdaptiveInstanceError = t;
                            throw new IllegalStateException("fail to create adaptive instance: ...");
                        }
                    }
                }
            } else {
                throw new IllegalStateException("fail to create adaptive instance:  ...");
            }
        }
    
        return (T) instance;
    }
    
    private T createAdaptiveExtension() {
        try {
            // 获取自适应拓展类,并通过反射实例化,调用 injectExtension 方法向拓展实例中注入依赖
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can not create adaptive extension ...");
        }
    }
    
    private Class<?> getAdaptiveExtensionClass() {
        // 通过 SPI 获取所有的拓展类,例子中指所有 Robot 接口实现类
        getExtensionClasses();
        // 如果某个实现类被 Adaptive 注解修饰了,那么该类就会被赋值给 cachedAdaptiveClass 变量。
        // 那么这里直接返回,不创建自适应扩展类
        // 这也就是为什么说被 @Adaptive 注解修饰在类上和方法上不一样
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        // 创建自适应拓展类
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }
    

    现在,世界线收束,我们再从 getExtensionClasses(ExtensionFactory.class); 方法看起,一层层进到最里面的 loadClass() 方法时,这个接口 ExtensionFactory.class 的实现类有 AdaptiveExtensionFactorySpiExtensionFactorySpringExtensionFactory。其中 AdaptiveExtensionFactory 类上被注解 @Adaptive 修饰,表示该类是该接口的适配器,不提供具体业务支持,会根据在运行时的一些状态来选择具体调用 ExtensionFactory 的哪个实现。

    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
        // ...
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            if (cachedAdaptiveClass == null) {
                cachedAdaptiveClass = clazz;
            }
        } 
        // ...
    }
    

    返回 cachedAdaptiveClass = adaptiveExtensionFactory

    因此,当一开始 ExtensionLoader.getExtensionLoader(Robot.class) 初始化后,得到(忽略点 - 1 的解释)

    private ExtensionLoader(Robot.class) {
        this.type = Robot.class;
        objectFactory = adaptiveExtensionFactory;
    }
    

    搞清楚 objectFactory 等于什么之后,我们继续看 injectExtension(instance) 的内容,

    private T injectExtension(T instance) {
        // ...
        // 从 ObjectFactory 中获取依赖对象
        Object object = objectFactory.getExtension(pt, property);
        if (object != null) {
            // 通过反射调用 setter 方法设置依赖
            method.invoke(instance, object);
        }
        // ...
        return instance;
    }
    

    objectFactory.getExtension(pt, property) 也就是 adaptiveExtensionFactory.getExtension(pt, property),而 AdaptiveExtensionFactory 在实例化的时候会根据当前系统环境获取 ExtensionFactory,也就是 SpiExtensionFactorySpringExtensionFactory 存到 factories 中。

    public AdaptiveExtensionFactory() {
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
        for (String name : loader.getSupportedExtensions()) {
            list.add(loader.getExtension(name));
        }
        factories = Collections.unmodifiableList(list);
    }
    

    我们看其中一个 SpiExtensionFactory 的实现,

    public class SpiExtensionFactory implements ExtensionFactory {
        @Override
        public <T> T getExtension(Class<T> type, String name) {
            if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
                ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
                if (!loader.getSupportedExtensions().isEmpty()) {
                    return loader.getAdaptiveExtension();
                }
            }
            return null;
        }
    }
    

    loader.getAdaptiveExtension() 一步步跟进,来到 createAdaptiveExtensionClass(), 创建默认代理类

    private Class<?> createAdaptiveExtensionClass() {
        // 通过反射检测接口方法是否包含 Adaptive 注解
        // 构建自适应拓展代码
        String code = createAdaptiveExtensionClassCode();
        ClassLoader classLoader = findClassLoader();
        // 获取编译器实现类
        com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        // 编译代码,生成 Class
        return compiler.compile(code, classLoader);
    }
    

    如果你对 createAdaptiveExtensionClass() 生成的代理类感觉有点模糊的话,觉得看代码不清晰,我们可以用阿里开源的工具 arthas 反编译查看一下生成的代理类是什么样子的

    由上图可知,生成的代理类从 com.alibaba.dubbo.common.URL 中获取参数实例化并调用接口方法,返回生成的自适应类。

    在我们举的例子中,injectExtension(T instance) 的作用就是,生成自适应类 object = Robot$Adaptive,并通过 AdaptiveRobot.setRobot(Robot robot) 注入进去。

    5、@Activate 自动激活扩展点

    表示实现类是否可以被激活。通常被用在一个接口有很多现实类,但是这些实现类在特定条件才需要使用,比如有些实现只想在生产者生效,有些实现类想在消费者生效。它有两个设置过滤条件的字段,group,value 都是字符数组,用来指定这个扩展类在什么条件下激活。

    loadClass() 加载扩展类时,把所有实现类中有Activate注解的,放到 cachedActivates 中(忽略点 - 4的解释)

    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
        // ...
        if (names != null && names.length > 0) {
            Activate activate = clazz.getAnnotation(Activate.class);
            if (activate != null) {
                cachedActivates.put(names[0], activate);
            }
            // ...
        }
    }
    
    后记

    这篇文章讲解了 Dubbo SPI 的实现机制,关键是要弄懂 @SPI @Adaptive @Activate 注解的含义,知道 Dubbo 中 AOP、IOC 的各自实现,还有就是 SPI 的自适应拓展机制以及 Dubbo 中的 URL 作为配置总线,各个服务的参数配置都是通过 URL 进行传递的。

  • 相关阅读:
    Spring 事务不回滚
    Druid详细配置信息
    Servlet和JSP规范及版本对应关系
    CDN(内容分发网络)技术原理
    开发者需要了解的WebKit
    浏览器的渲染原理简介
    在浏览器中输入Google.com并且按下回车之后发生了什么?
    为什么说DOM操作很慢
    亿级Web系统搭建——单机到分布式集群
    linux下用rinetd做端口转发
  • 原文地址:https://www.cnblogs.com/linyuhong/p/15338380.html
Copyright © 2011-2022 走看看