【问题标题】:Child first class loader and Service Provider Interface (SPI)子类加载器和服务提供者接口 (SPI)
【发布时间】:2020-04-08 14:30:56
【问题描述】:

我找到了一个custom class loader,它按照儿童优先原则加载类。它工作正常,但我遇到了以下问题。 当我尝试加载使用 SPI 的类时,出现异常:

Exception in thread "main" java.util.ServiceConfigurationError: test.spi.SayMyNameProvider: test.spi.ImplProvider not a subtype

我使用以下模块创建了simple SPI project:spi-api、spi-impl 和 spi-app。

当我使用 URLClassLoader 时它可以工作,但是每当我使用 ChildFirstClassLoader 时,都会出现上述异常:

public class TestMain {
    public static void main(String[] args) throws MalformedURLException {

        //!!! comment ChildFirstClassLoader and uncomment URLClassLoader to get the correct behavior 
        ChildFirstClassLoader classLoader = getCustomClassLoader();
        //URLClassLoader classLoader = getUrlClassLoader();

        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(classLoader);

        try {
            List<SayMyNameProvider> providers = Speaker.providers();
            for (SayMyNameProvider provider : providers) {
                SayMyNameManager sayMyNameManager = provider.create();
                sayMyNameManager.sayIt("main");
            }
            System.out.println("done");

        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
    }

    private static ChildFirstClassLoader getCustomClassLoader() throws MalformedURLException {
        URL[] urls = getUrls();

        return new ChildFirstClassLoader(urls);
    }

    private static URLClassLoader getUrlClassLoader() throws MalformedURLException {
        URL[] urls = getUrls();

        return new URLClassLoader(urls);
    }

    private static URL[] getUrls() throws MalformedURLException {
        File spiImpl = Paths.get("spi-impl", "target", "spi-impl-1.0.0-SNAPSHOT.jar").toFile();
        File spiApi = Paths.get("spi-api", "target", "spi-api-1.0.0-SNAPSHOT.jar").toFile();

        URL[] urls = new URL[2];
        urls[0] = spiImpl.toURI().toURL();
        urls[1] = spiApi.toURI().toURL();
        return urls;
    }
}

也许有人以前已经遇到过这个问题并且知道如何解决它。如果有任何帮助或建议,我将不胜感激。

【问题讨论】:

    标签: java classloader serviceloader


    【解决方案1】:

    所以,3 天后,我终于得到了我的问题的答案。它说我很愚蠢:) 因为在最后的article 中,作者提供了正确行为的示例,它适用于我的情况。 但是,它在我的其他带有 slf4j 和 logback 依赖项的测试中不起作用。但令我惊讶的是,没有系统类加载器的代码可以工作。 简而言之,我尝试使用不同版本的 slf4j 和 logback。

    Pom.xml:

    <dependencies>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
    </dependencies>
    

    TestMain.class

    public class TestMain {
        private static Logger log = LoggerFactory.getLogger(TestMain.class);
    
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, MalformedURLException {
            log.info("Hello");
    
            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
    
            ChildFirstClassLoader2 classLoader = new ChildFirstClassLoader2(getUrls());
    
            Thread.currentThread().setContextClassLoader(classLoader);
    
            try {
                Class<?> testClass = classLoader.loadClass("petrovskyi.TestClass");
                Object o = testClass.getDeclaredConstructor().newInstance();
                Method test = testClass.getMethod("test");
                test.setAccessible(true);
                test.invoke(o);
    
            } finally {
                Thread.currentThread().setContextClassLoader(contextClassLoader);
            }
        }
    
        private static URL[] getUrls() throws MalformedURLException {
            File libDir = Paths.get("src","main", "resources", "testClasses").toFile();
    
            URL[] urls;
    
            List<URL> urlsList = new ArrayList<>();
    
            URL classUrl = libDir.toURI().toURL();
            urlsList.add(classUrl);
    
            try (Stream<Path> walk = Files.walk(libDir.toPath())) {
                List<File> result = walk.map(Path::toFile)
                        .filter(x -> x.getName().endsWith(".jar"))
                        .collect(Collectors.toList());
    
                for (File jarFile : result) {
                    urlsList.add(jarFile.toURI().toURL());
                }
            } catch (IOException e) {
                throw new RuntimeException("Error while walking through " + libDir + " to find jar files", e);
            }
    
            urls = urlsList.toArray(new URL[0]);
            return urls;
        }
    }
    

    上面我尝试从某个目录中获取所有 jars。罐子是下一个:

    • logback-classic-1.3.0-alpha5.jar
    • logback-core-1.3.0-alpha5.jar
    • slf4j-api-2.0.0-alpha1.jar

    TestClass.class

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.slf4j.event.Level;
    import org.slf4j.spi.DefaultLoggingEventBuilder;
    import org.slf4j.spi.LoggingEventBuilder;
    
     public class TestClass {
            private static Logger log = LoggerFactory.getLogger(TestClass.class);
    
            public TestClass() {
            }
    
            public void test() {
                LoggingEventBuilder loggingEventBuilder = new DefaultLoggingEventBuilder(log, Level.ERROR);
                loggingEventBuilder.log(" =========== Hello, World! ===========");
                log.info("Test from test class");
            }
        }
    

    上面的类使用了 pom.xml 中提到的 slf4j 版本中不存在的 LoggingEventBuilder

    ChildFirstClassLoader2.class

    public class ChildFirstClassLoader2 extends URLClassLoader {
    
        public ChildFirstClassLoader2(URL[] urls) {
            super(URLs);
        }
    
        @Override
        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
            Class<?> loadedClass = findLoadedClass(name);
            if (loadedClass == null) {
    
                try {
                    if (loadedClass == null) {
                        loadedClass = findClass(name);
                    }
    
                } catch (ClassNotFoundException e) {
                    loadedClass = super.loadClass(name, resolve);
                }
            }
    
            if (resolve) {
                resolveClass(loadedClass);
            }
            return loadedClass;
        }
    
    
        @Override
        public Enumeration<URL> getResources(String name) throws IOException {
            List<URL> allRes = new LinkedList<>();
    
            Enumeration<URL> thisRes = findResources(name);
            if (thisRes != null) {
                while (thisRes.hasMoreElements()) {
                    allRes.add(thisRes.nextElement());
                }
            }
    
            Enumeration<URL> parentRes = super.findResources(name);
            if (parentRes != null) {
                while (parentRes.hasMoreElements()) {
                    allRes.add(parentRes.nextElement());
                }
            }
    
            return new Enumeration<URL>() {
                Iterator<URL> it = allRes.iterator();
    
                @Override
                public boolean hasMoreElements() {
                    return it.hasNext();
                }
    
                @Override
                public URL nextElement() {
                    return it.next();
                }
            };
        }
    
        @Override
        public URL getResource(String name) {
            URL res = null;
    
            if (res == null) {
                res = findResource(name);
            }
            if (res == null) {
                res = super.getResource(name);
            }
            return res;
        }
    }
    

    输出:

    2020-04-10 12:01:39,928 [main] INFO TestMain - Hello
    2020-04-10 12:01:40,095 [main] ERROR TestClass -  =========== Hello, World! ===========
    2020-04-10 12:01:40,097 [main] INFO TestClass - Test from test class
    

    【讨论】:

      猜你喜欢
      • 2023-03-28
      • 2012-07-07
      • 1970-01-01
      • 1970-01-01
      • 2016-08-30
      • 1970-01-01
      • 2017-01-11
      • 2019-05-29
      • 2017-07-23
      相关资源
      最近更新 更多