motan源码分析二:使用spi机制进行类加载

x33g5p2x  于2021-12-21 转载在 其他  
字(5.3k)|赞(0)|评价(0)|浏览(232)

在motan的源码中使用了很多的spi机制进行对象的创建,下面我们来具体分析一下它的实现方法。

1.在实际的jar包的\META-INF\services目录中引入相关的文件,例如下图中,我解压了core的jar文件后,获得到的相应文件列表:

2.以第一节中的ConfigHandler为例来分析,打开上图中的com.weibo.api.motan.config.handler.ConfigHandler文件,文件内容标识着ConfigHandler接口的实现类为:com.weibo.api.motan.config.handler.SimpleConfigHandler

#
#  Copyright  2009 - 2016  Weibo, Inc.
#
#    Licensed under the Apache License, Version  2.0  (the  "License" );
#    you may not use  this  file except in compliance with the License.
#    You may obtain a copy of the License at
#
#        http: //www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an  "AS IS"  BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License  for  the specific language governing permissions and
#    limitations under the License.
#
 
com.weibo.api.motan.config.handler.SimpleConfigHandler

3.在第一节中,创建ConfigHandler对象的代码是这样的:

ConfigHandler configHandler = ExtensionLoader.getExtensionLoader(ConfigHandler.class).getExtension(MotanConstants.DEFAULT_VALUE);

4.开始进入到实际的加载代码核心部分,首先来看一下类加载器的具体实现:

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T>type) {
        checkInterfaceType(type);//基础性检查

        ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoaders.get(type);//之前是否已经加载过此加载器

        if (loader == null) {
            loader =initExtensionLoader(type);//第一次加载
        }
        returnloader;
    }

    private static <T> void checkInterfaceType(Class<T>clz) {
        if (clz == null) {
            failThrows(clz, "Error extension type is null");
        }

        if (!clz.isInterface()) {
            failThrows(clz, "Error extension type is not interface");
        }

        if (!isSpiType(clz)) {
            failThrows(clz, "Error extension type without @Spi annotation");
        }
    }
    public static synchronized <T> ExtensionLoader<T> initExtensionLoader(Class<T>type) {
        ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoaders.get(type);

        if (loader == null) {
            loader = new ExtensionLoader<T>(type);//新创建一个加载器

            extensionLoaders.putIfAbsent(type, loader);

            loader = (ExtensionLoader<T>) extensionLoaders.get(type);
        }

        returnloader;
    }

5.下面我们将进入到加载器的内部,分析具体的实现:

private ExtensionLoader(Class<T>type) {
        this(type, Thread.currentThread().getContextClassLoader());//使用当前线程的类加载器做为加载器,type为ConfigHandler接口
    }

    publicT getExtension(String name) {
        checkInit();//检查是否初始化

        if (name == null) {
            return null;
        }

        try{
            Spi spi = type.getAnnotation(Spi.class);

            if (spi.scope() ==Scope.SINGLETON) {
                returngetSingletonInstance(name);//返回唯一的对象
            } else{
                Class<T> clz =extensionClasses.get(name);

                if (clz == null) {
                    return null;
                }

                returnclz.newInstance();//重新创建对象
            }
        } catch(Exception e) {
            failThrows(type, "Error when getExtension " +name, e);
        }

        return null;
    }

    private synchronized voidloadExtensionClasses() {
        if(init) {
            return;
        }

        extensionClasses =loadExtensionClasses(PREFIX);//加载相关的类
        singletonInstances = new ConcurrentHashMap<String, T>();

        init = true;
    }
    private ConcurrentMap<String, Class<T>>loadExtensionClasses(String prefix) {
        String fullName = prefix +type.getName();//全名为:jar包名+\META-INF\services\com.weibo.api.motan.config.handler.ConfigHandler文件里的类
        List<String> classNames = new ArrayList<String>();

        try{
            Enumeration<URL>urls;
            if (classLoader == null) {
                urls =ClassLoader.getSystemResources(fullName);
            } else{
                urls =classLoader.getResources(fullName);
            }

            if (urls == null || !urls.hasMoreElements()) {
                return new ConcurrentHashMap<String, Class<T>>();
            }
            System.out.println("fullname:"+fullName);
            while(urls.hasMoreElements()) {
                URL url =urls.nextElement();
                System.out.println("url:"+url.getFile());
                parseUrl(type, url, classNames);
            }
        } catch(Exception e) {
            throw newMotanFrameworkException(
                    "ExtensionLoader loadExtensionClasses error, prefix: " + prefix + " type: " +type.getClass(), e);
        }
        for(String classN : classNames){
            System.out.println("class:"+classN);
        }
        returnloadClass(classNames);
    }

6.在parseUrl方法中进行文件的内容读取,并在loadClass中完成类的加载

private void parseUrl(Class<T> type, URL url, List<String> classNames) throwsServiceConfigurationError {
        InputStream inputStream = null;
        BufferedReader reader = null;
        try{
            inputStream =url.openStream();
            reader = new BufferedReader(newInputStreamReader(inputStream, MotanConstants.DEFAULT_CHARACTER));

            String line = null;
            int indexNumber = 0;

            while ((line = reader.readLine()) != null) {
                indexNumber++;
                parseLine(type, url, line, indexNumber, classNames);//读取到类的名称:com.weibo.api.motan.config.handler.SimpleConfigHandler
            }
        } catch(Exception x) {
            failLog(type, "Error reading spi configuration file", x);
        } finally{
            try{
                if (reader != null) {
                    reader.close();
                }
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch(IOException y) {
                failLog(type, "Error closing spi configuration file", y);
            }
        }
    }
    private ConcurrentMap<String, Class<T>> loadClass(List<String>classNames) {
        ConcurrentMap<String, Class<T>> map = new ConcurrentHashMap<String, Class<T>>();

        for(String className : classNames) {
            try{
                Class<T>clz;
                if (classLoader == null) {
                    clz = (Class<T>) Class.forName(className);//装载类:com.weibo.api.motan.config.handler.SimpleConfigHandler
                } else{
                    clz = (Class<T>) Class.forName(className, true, classLoader);
                }

                checkExtensionType(clz);

                String spiName =getSpiName(clz);

                if(map.containsKey(spiName)) {
                    failThrows(clz, ":Error spiName already exist " +spiName);
                } else{
                    map.put(spiName, clz);
                }
            } catch(Exception e) {
                failLog(type, "Error load spi class", e);
            }
        }

        returnmap;

    }

motan类加载的知识点总结:

1.使用jdk的spi规范,在\META-INF\services中添加实际的使用类描述,从而实现类与类之间的完全解耦;

2.类加载器使用的是当前线程的类加载器;

3.motan的类加载器可以支持单例和多例两种模式;

4.motan中大量使用了spi的类加载方式。

相关文章