2、DubboのSPIメカニズム分析1-SPIロードclass

10280 ワード

1、DubboのSPI例
@SPI
public interface Robot {
    void sayHello();
}

public class OptimusPrime implements Robot{
    @Override
    public void sayHello() {
        System.out.println("Hello, I am Optimus Prime.");
    }
}

public class Bumblebee implements Robot{
    @Override
    public void sayHello() {
        System.out.println("Hello, I am Bumblebee.");
    }
}
public class DubboSPITest {

    @Test
    public void sayHelloDubbo() throws Exception {
        ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);
        Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
        optimusPrime.sayHello();
        Robot bumblebee = extensionLoader.getExtension("bumblebee");
        bumblebee.sayHello();
    }
}
  :
Hello, I am Optimus Prime.
Hello, I am Bumblebee.

2、DubboのSPIソース分析
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);

public static  ExtensionLoader getExtensionLoader(Class type) {
    if (type == null)
        throw new IllegalArgumentException("Extension type == null");
    if (!type.isInterface()) {
        throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
    }
    if (!withExtensionAnnotation(type)) {
        throw new IllegalArgumentException("Extension type(" + type +
                ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
    }

    //              ExtensionLoader
    ExtensionLoader loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);
    if (loader == null) {
        //       ,         ,     new ExtensionLoader(type)
        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
        loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);
    }
    return loader;
}

ここでのobjectFactory作成は参考になる:DubboのSPIメカニズム解析3-DubboのIOC依存注入
 private ExtensionLoader(Class> type) {
    this.type = type;
    //    type Robot.class,          ,     SpiExtensionFactory SpringExtensionFactory,
    objectFactory = (type == ExtensionFactory.class ? null : 
                   ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
public T getExtension(String name) {
    if ("true".equals(name)) {
        return getDefaultExtension();
    }
    // Holder         ,    
    Holder holder = cachedInstances.get(name);
    if (holder == null) {
        cachedInstances.putIfAbsent(name, new Holder());
        holder = cachedInstances.get(name);
    }
    Object instance = holder.get();
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                //       ,    createExtension(name)
                instance = createExtension(name);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}

ここで依存注入は参考になる:DubboのSPIメカニズム解析3-DubboのIOC依存注入
private T createExtension(String name) {
    //               ,   “     ” “   ”      
    //       name  ,    com.alibaba.dubbo.demo.provider.spi.OptimusPrime
    //       getExtensionClasses(),          
    Class> clazz = getExtensionClasses().get(name);
    try {
        //           ,                  
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        //      cachedWrapperClasses,    
        injectExtension(instance);
        Set> wrapperClasses = cachedWrapperClasses;
        if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
           for (Class> wrapperClass : wrapperClasses) {
              instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
           }
        }
        return instance;
    } 
}
 private Map> getExtensionClasses() {
    //            
    Map> classes = cachedClasses.get();
    //     
    if (classes == null) {
        synchronized (cachedClasses) {
            classes = cachedClasses.get();
            if (classes == null) {
                //       ,       
                classes = loadExtensionClasses();
                cachedClasses.set(classes);
            }
        }
    }
    return classes;
}
private Map> loadExtensionClasses() {
    //   SPI  ,   type    getExtensionLoader      
    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     ,     ,     ,     
            if (names.length == 1) cachedDefaultName = names[0];
        }
    }

    Map> extensionClasses = new HashMap>();
    //              ,META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
    //           
    loadDirectory(extensionClasses, DUBBO_DIRECTORY);
    loadDirectory(extensionClasses, SERVICES_DIRECTORY);
    return extensionClasses;
}
private void loadDirectory(Map> extensionClasses, String dir) {
    // fileName META-INF/dubbo/com.alibaba.dubbo.demo.provider.spi.Robot
    String fileName = dir + type.getName();
    try {
        Enumeration urls;
        ClassLoader classLoader = findClassLoader();
        //              
        if (classLoader != null) {
            urls = classLoader.getResources(fileName);
        } else {
            urls = ClassLoader.getSystemResources(fileName);
        }
        if (urls != null) {
            while (urls.hasMoreElements()) {
                //   resourceURL      
                java.net.URL resourceURL = urls.nextElement();
                loadResource(extensionClasses, classLoader, resourceURL);
            }
        }
    } 
}
private void loadResource(Map> extensionClasses, ClassLoader classLoader, 
                                                                       java.net.URL resourceURL) {
    try {
        BufferedReader reader = 
                     new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
        try {
            String line;
            //         
            while ((line = reader.readLine()) != null) {
                final int ci = line.indexOf('#');
                //   #  ,#      ,  
                if (ci >= 0) line = line.substring(0, ci);
                line = line.trim();
                if (line.length() > 0) {
                    try {
                        String name = null;
                        //      
                        int i = line.indexOf('=');
                        if (i > 0) {
                            name = line.substring(0, i).trim();
                            line = line.substring(i + 1).trim();
                        }
                        if (line.length() > 0) {
                            //        
                            loadClass(extensionClasses, resourceURL, 
                                                    Class.forName(line, true, classLoader), name);
                        }
                    }
                }
            }
        }
    } 
}

ここではDubboのSPIメカニズム解析2-Adaptive詳細2を参照してください.ここではDubboのSPIメカニズム解析4-DubboがWrapperによってAOPを実現することを参照してください.
private void loadClass(Map> extensionClasses, java.net.URL resourceURL, 
                                      Class> clazz, String name) throws NoSuchMethodException {
    // clazz   type   ,     
    if (!type.isAssignableFrom(clazz)) {
    }
    //   clazz      @Adaptive  ,  1
    if (clazz.isAnnotationPresent(Adaptive.class)) {
        if (cachedAdaptiveClass == null) {
            cachedAdaptiveClass = clazz;
        } else if (!cachedAdaptiveClass.equals(clazz)) {
            //    ,        @Adaptive    
        }
    }
    //      Wrapper  ,  2
    else if (isWrapperClass(clazz)) {
        Set> wrappers = cachedWrapperClasses;
        if (wrappers == null) {
            cachedWrapperClasses = new ConcurrentHashSet>();
            wrappers = cachedWrapperClasses;
        }
        wrappers.add(clazz);
    }
    //        ,  clazz         ,Robot          
    else {
        //   clazz          ,    ,     
        clazz.getConstructor();
        if (name == null || name.length() == 0) {
            name = findAnnotationName(clazz);
            if (name.length() == 0) {
               //    
            }
        }
        String[] names = NAME_SEPARATOR.split(name);
        if (names != null && names.length > 0) {
            Activate activate = clazz.getAnnotation(Activate.class);
            if (activate != null) {
                cachedActivates.put(names[0], activate);
            }
            for (String n : names) {
                if (!cachedNames.containsKey(clazz)) {
                    cachedNames.put(clazz, n);
                }
                Class> c = extensionClasses.get(n);
                if (c == null) {
                    //      class     ,         
                    extensionClasses.put(n, clazz);
                } else if (c != clazz) {
                    //    
                }
            }
        }
    }
}