zoukankan      html  css  js  c++  java
  • motan源码分析二:使用spi机制进行类加载

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

    1.在实际的jar包的META-INFservices目录中引入相关的文件,例如下图中,我解压了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);//第一次加载
            }
            return loader;
        }
    
        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);
            }
    
            return loader;
        }

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

        private ExtensionLoader(Class<T> type) {
            this(type, Thread.currentThread().getContextClassLoader());//使用当前线程的类加载器做为加载器,type为ConfigHandler接口
        }
    
        public T getExtension(String name) {
            checkInit();//检查是否初始化
    
            if (name == null) {
                return null;
            }
    
            try {
                Spi spi = type.getAnnotation(Spi.class);
    
                if (spi.scope() == Scope.SINGLETON) {
                    return getSingletonInstance(name);//返回唯一的对象
                } else {
                    Class<T> clz = extensionClasses.get(name);
    
                    if (clz == null) {
                        return null;
                    }
    
                    return clz.newInstance();//重新创建对象
                }
            } catch (Exception e) {
                failThrows(type, "Error when getExtension " + name, e);
            }
    
            return null;
        }
    
        private synchronized void loadExtensionClasses() {
            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-INFservicescom.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 new MotanFrameworkException(
                        "ExtensionLoader loadExtensionClasses error, prefix: " + prefix + " type: " + type.getClass(), e);
            }
            for(String classN : classNames){
                System.out.println("class:"+classN);
            }
            return loadClass(classNames);
        }

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

        private void parseUrl(Class<T> type, URL url, List<String> classNames) throws ServiceConfigurationError {
            InputStream inputStream = null;
            BufferedReader reader = null;
            try {
                inputStream = url.openStream();
                reader = new BufferedReader(new InputStreamReader(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);
                }
            }
    
            return map;
    
        }

    motan类加载的知识点总结:

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

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

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

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

      

  • 相关阅读:
    在线jq库
    解决python3.6的UnicodeEncodeError: 'gbk' codec can't encode character 'xbb' in position 28613: illegal multibyte sequence
    PHP后台支付的开发:微信支付和支付宝支付
    PHP操控Excel视频教程
    微信h5静默、非静默授权获取用户openId的方法和步骤
    OAuth2.0微信网页授权登录
    微信第三方登录 -- (PC端+移动端)
    web字体规范
    移动端字体设置
    在 Web 内容中使用系统字体
  • 原文地址:https://www.cnblogs.com/mantu/p/5881975.html
Copyright © 2011-2022 走看看