标签搜索

目 录CONTENT

文章目录

JavaSPI之打破双亲委派

陈铭
2021-03-31 / 0 评论 / 0 点赞 / 136 阅读 / 617 字 / 正在检测是否收录...

挖源码走起!!

ServiceLoader

首先这个类就是用来实现spi的,我先放一行实现spi的代码ServiceLoader as= ServiceLoader.load(AAA.class); 这么一句话就可以把AAA这个接口和其实现类加载好,并且不会走双亲委派,而是直接用app加载器加载这个类。为什么呢,上源码

    public static  ServiceLoader load(Class service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return new ServiceLoader(Reflection.getCallerClass(), service, cl);
    }

load方法主要是返回一个ServiceLoader对象,其中获取了一个上下文加载器,这个就是app加载器。为什么这么说,Launch类里面在构造器把app加载器set为了ContextClassLoader,具体看《双亲委派最细解析,挖一下源码吧》这篇博客。那么就构建了ServiceLoader对象。我们调用一下ServiceLoader的iterator方法就会返回对应的实现类。

public Iterator iterator() {

        // create lookup iterator if needed
        if (lookupIterator1 == null) {
            lookupIterator1 = newLookupIterator();
        }

        return new Iterator() {

            // record reload count
            final int expectedReloadCount = ServiceLoader.this.reloadCount;

            // index into the cached providers list
            int index;

            /**
             * Throws ConcurrentModificationException if the list of cached
             * providers has been cleared by reload.
             */
            private void checkReloadCount() {
                if (ServiceLoader.this.reloadCount != expectedReloadCount)
                    throw new ConcurrentModificationException();
            }

            @Override
            public boolean hasNext() {
                checkReloadCount();
                if (index < instantiatedProviders.size())
                    return true;
                return lookupIterator1.hasNext();
            }

            @Override
            public S next() {
                checkReloadCount();
                S next;
                if (index < instantiatedProviders.size()) {
                    next = instantiatedProviders.get(index);
                } else {
                    next = lookupIterator1.next().get();
                    instantiatedProviders.add(next);
                }
                index++;
                return next;
            }

        };
    }

iterator方法会调用newLookupIterator方法,newLookupIterator方法返回LazyClassPathLookupIterator对象。我们看看这个对象。LazyClassPathLookupIterator是个内部类,里面就规定了AAA这个接口的实现类信息定义的路径,就是类路径下的META-INF/services/,而这个路径下必须有文件,文件名为AAA的全路径名(如com.CmJava.AAA),文件内部写实现类的全路径名(如com.CmJava.AAAImpl)。

private final class LazyClassPathLookupIterator
        implements Iterator
    {
        static final String PREFIX = "META-INF/services/";

        Set providerNames = new HashSet();  // to avoid duplicates
        Enumeration configs;
        Iterator pending;

        Provider nextProvider;
        ServiceConfigurationError nextError;

        LazyClassPathLookupIterator() { }
    }

因此iterator方法最终会返回AAA的实现类对象。而接口还是实现类都是由app加载器进行加载的,并不会受到双亲委派的影响。

小结

如果需要使用SPI,首先要在META-INF/services/定义实现类信息,文件名是接口全路径,文件内是实现类全路径。那么不管双亲委派还是全盘委托的方式都不会影响这些类的加载,都是用app加载器加载的

0

评论区