ServiceLoader服务提供者加载设施帮助类

ServiceLoader是jdk提供的一个简单的服务提供者加载设施,一个服务(接口)的实现者在其资源目录META-INF/services 中放置提供者配置文件 来标识服务提供者。

文件名称是服务类型的完全限定名称。该文件包含一个具体提供者类的完全限定名称列表,每行一个。

通过ServiceLoader.load创建服务实现者加载器,通过iterator以延迟方式加载此加载器服务的可用提供者。

本帮助类对ServiceLoader的功能进行了增强:

1.对加载到的服务提供者进行缓存,避免重复加载。

2.提供一种按key/value的方式来配置服务提供者,按key来获取具体服务提供者的扩展。

代码如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 服务提供者加载设施帮助类,应用中每个ClassLoader都有不同的ServiceLoaderHelp,因为对同一个服务,使用不同的ClassLoader,
 * 加载的jar包可能不同,资源目录META-INF/services下放置的服务提供者配置文件也就可能不同,导致不同ClassLoader加载到的服务提供者不同。
 */
public class ServiceLoaderHelp {
    private static final Logger log = LoggerFactory.getLogger(ServiceLoaderHelp.class);

    private static final String PREFIX = "META-INF/services/";
    private static final ConcurrentHashMap<ClassLoader, ServiceLoaderHelp> MAP_Help_CACHE = new ConcurrentHashMap<ClassLoader, ServiceLoaderHelp>();

    private final ClassLoader classLoader;
    private final ConcurrentHashMap<Class<?>, Object> INSTANCE_CACHE = new ConcurrentHashMap<Class<?>, Object>();
    private final ConcurrentHashMap<Class<?>, Map<String, Class<?>>> MAP_CLASSES_CACHE = new ConcurrentHashMap<Class<?>, Map<String, Class<?>>>();
    private final ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, Object>> MAP_INSTANCES_CACHE = new ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, Object>>();

    private ServiceLoaderHelp(ClassLoader classLoader){
        this.classLoader = classLoader;
    }

    /**
     * 根据ClassLoader获取服务提供者加载设施帮助类。
     * @param classLoader
     * @return 服务提供者加载设施帮助类
     */
    public static ServiceLoaderHelp getHelpByClassLoader(ClassLoader classLoader){
        ServiceLoaderHelp help = MAP_Help_CACHE.get(classLoader);
        if (help == null){
            help = new ServiceLoaderHelp(classLoader);
            MAP_Help_CACHE.putIfAbsent(classLoader, help);
            return MAP_Help_CACHE.get(classLoader);
        } else {
            return help;
        }
    }

    /**
     * 根据服务接口Class获取服务提供者,若有多个服务提供者取ServiceLoader.load到的第一个服务提供者。
     * @param classType 服务接口Class
     * @return 服务提供者
     */
    @SuppressWarnings("unchecked")
    public <T> T getInstance(Class<T> classType) {
        T instance = (T) INSTANCE_CACHE.get(classType);
        if (instance == null) {
            try {
                instance = ServiceLoader.load(classType, classLoader).iterator().next();
                INSTANCE_CACHE.putIfAbsent(classType, instance);
                return (T) INSTANCE_CACHE.get(classType);
            } catch (Throwable e) {
                log.error("Cannot Load " + classType.getName());
                throw new RuntimeException("Cannot Load " + classType, e);
            }
        } else {
            if (classType.isAssignableFrom(instance.getClass())) {
                return instance;
            } else {
                throw new RuntimeException("instance not type " + classType);
            }
        }
    }

    /**
     * 根据服务接口Class获取服务提供者列表。
     * @param classType 服务接口Class
     * @return 服务提供者列表
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> getInstances(Class<T> classType) {
        List<T> list = (List<T>) INSTANCE_CACHE.get(classType);
        if (list == null) {
            try {
                list = new ArrayList<T>();
                for (T instance : ServiceLoader.load(classType, classLoader)) {
                    list.add(instance);
                }
                INSTANCE_CACHE.putIfAbsent(classType, list);
                return (List<T>) INSTANCE_CACHE.get(classType);
            } catch (RuntimeException e) {
                log.error("Cannot Load List By" + classType.getName());
                throw new RuntimeException("Cannot Load List By" + classType, e);
            }
        } else {
            if (List.class.isAssignableFrom(list.getClass())) {
                return list;
            } else {
                throw new RuntimeException("instance not List, type " + classType);
            }
        }
    }

    /**
     * 根据服务接口Class和服务提供者key获取服务提供者。
     * 此种服务提供者加载方式,在资源目录META-INF/services下放置的服务提供者配置文件的内容不是ServiceLoader
     * 规范中的每行一个“具体服务提供者类的完全限定名称”,
     * 而是每行一个“key=具体服务提供者类的完全限定名称”。
     * 此方式提供了根据配置选取服务提供者的便利。
     *
     * @param classType 服务接口Class
     * @param key 服务提供者key
     * @return 服务提供者
     */
    @SuppressWarnings("unchecked")
    public <T> T getInstance(Class<T> classType, String key) {
        ConcurrentHashMap<String, Object> objMap = MAP_INSTANCES_CACHE.get(classType);
        if (objMap == null) {
            objMap = new ConcurrentHashMap<String, Object>();
            MAP_INSTANCES_CACHE.putIfAbsent(classType, objMap);
        }
        objMap = MAP_INSTANCES_CACHE.get(classType);
        T obj = (T) objMap.get(key);
        if (obj != null) {
            return obj;
        }
        Map<String, Class<?>> classMap = MAP_CLASSES_CACHE.get(classType);
        if (classMap == null) {
            loadFile(classType);
        }
        classMap = MAP_CLASSES_CACHE.get(classType);
        if (classMap == null) {
            throw new IllegalStateException("Failed to load extension class(interface: " + classType + ", key: " + key
                    + ")");
        }
        Class<?> clazz = classMap.get(key);
        if (clazz != null) {
            try {
                Object newObj = clazz.newInstance();
                Object provious = objMap.putIfAbsent(key, newObj);
                return null == provious ? (T) newObj : (T) provious;
            } catch (Exception e) {
                throw new IllegalStateException("Failed to load extension class(interface: " + classType + ", key: "
                        + key + ")", e);
            }
        }
        throw new IllegalStateException("Failed to load extension class(interface: " + classType + ", key: " + key
                + ")");
    }

    private void loadFile(Class<?> type) {
        String fileName = PREFIX + type.getName();
        Map<String, Class<?>> map = new HashMap<String, Class<?>>();
        try {
            Enumeration<URL> urls;
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
            if (urls != null) {
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    try {
                        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
                        try {
                            String line = null;
                            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) {
                                            Class<?> clazz = Class.forName(line, false, classLoader);
                                            if (!type.isAssignableFrom(clazz)) {
                                                throw new IllegalStateException(
                                                        "Error when load extension class(interface: " + type
                                                                + ", class line: " + clazz.getName() + "), class "
                                                                + clazz.getName() + "is not subtype of interface.");
                                            }
                                            map.put(name, clazz);
                                        }
                                    } catch (Throwable t) {
                                    }
                                }
                            } // end of while read lines
                        } finally {
                            reader.close();
                        }
                    } catch (Throwable t) {
                        log.error("", "Exception when load extension class(interface: " + type + ", class file: "
                                + url + ") in " + url, t);
                    }
                } // end of while urls
            }
        } catch (Throwable t) {
            log.error("", "Exception when load extension class(interface: " + type + ", description file: "
                    + fileName + ").", t);
        }
        MAP_CLASSES_CACHE.put(type, map);
    }
}

 

猜你喜欢

转载自wydyyhwzx.iteye.com/blog/2301009
今日推荐