代码之家  ›  专栏  ›  技术社区  ›  user1611183

使用指向不同路径的类加载器的ServiceLoader

  •  1
  • user1611183  · 技术社区  · 9 年前

    几天来我一直在尝试这个,但没能成功!

    我正在尝试构建一个可插入的java应用程序,我可以从命令行运行它,并在一个单独的文件夹中提供插件(JAR)。看起来 ServiceLoader ClassLoder

    我想提供给主应用程序的一个插件是一个带有一些自定义特性的日志jar。

    下面是我正在使用的代码,但无法进入for/loop 服务加载器 无法识别/匹配任何类实现:

    final URL u = new File("C:\\data\\myLogJar-1.0-SNAPSHOT.jar").toURI().toURL();
    ClassLoader ucl = new URLClassLoader(new URL[] {u});
    
    ServiceLoader<Log> loader = ServiceLoader.load(Log.class, ucl);
    for (Iterator<Log> iterator = loader.iterator(); iterator.hasNext(); ) {
        System.out.println(iterator.next());
    }
    loader = ServiceLoader.load(Log.class,ucl);
    for (final Log log : loader) {
        log.info("Test log");                    
    }
    

    我希望你能帮忙! 谢谢

    enter image description here

        package com.company.dep.automation;
    
    import com.company.dep.automation.pluggable.Log;
    
    import java.io.File;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.net.URLClassLoader;
    
    import java.util.*;
    
    public class Main {
    
        private static ServiceLoader<Log> serviceLoader;
    
        public static void main(String[] args) {
    
            final URL u;
            ClassLoader ucl = null;
    
            try {
                u = new File("C:\\data\\myLogJar-1.0-SNAPSHOT.jar").toURI().toURL();
                 ucl = new URLClassLoader(new URL[]{u});
            } catch (MalformedURLException e1) {
                e1.printStackTrace();
            }
    
            ServiceLoader<Log> loader = ServiceLoader.load(Log.class, ucl);
            for (Iterator<Log> iterator = loader.iterator(); iterator.hasNext(); ) {
                System.out.println(iterator.next());
            }
    
            loader = ServiceLoader.load(Log.class, ucl);
            for (final Log log : loader) {
                log.info("Test log");
            }
    
        }
    
    }
    

    “日志”插件

    界面 Log

    package com.company.automation.service;
    
    public interface Log {
    
        void trace(String message);
        void debug(String message);
        void info(String message);
        void warn(String message);
        void severe(String message);
        void error(String message);
        void fatal(String message);
    
    }
    

    实施

    package com.company.automation.service.impl;
    
    import com.company.automation.service.Log;
    
    public class LogImpl implements Log {
    
        @Override
        public void trace(String message) {
            log("TRACE --> " + message);
        }
    
        @Override
        public void debug(String message) {
            log("DEBUG --> " + message);
        }
    
        @Override
        public void info(String message) {
            log("INFO --> " + message);
        }
    
        @Override
        public void warn(String message) {
            log("WARN --> " + message);
        }
    
        @Override
        public void severe(String message) {
            log("SEVERE --> " + message);
        }
    
        @Override
        public void error(String message) {
            log("ERROR --> " + message);
        }
    
        @Override
        public void fatal(String message) {
            log("FATAL --> " + message);
        }
    
        private void log(String message) {
            System.out.println(message);
        }
    
    }
    
    • 结构

    enter image description here

    =================

    调整项目结构如下,但仍不起作用:

    主应用程序: enter image description here

    扩展应用程序: enter image description here

    2 回复  |  直到 9 年前
        1
  •  2
  •   Nicolas Filotto    9 年前

    它不起作用,因为它不是同一个类 Log ,您的主要方法尝试查找 com.company.dep.automation.pluggable.Log 而jar定义了 com.company.automation.service.Log 如此 ServiceLoader.load 根本找不到任何东西。

    您应该移动界面 com.company.automation.service.Log 从扩展jar到项目 Main 类和导入 com.company.automation.service.Log 而不是 在你 主要的 在课堂上,一切都应该正常。

        2
  •  0
  •   jchampemont    9 年前

    关于应用程序的“可插拔”部分和在文件夹中加载JAR,而不是重新发明轮子,也许你可以看看OSGI。通过使用OSGI实现( Apache Felix

    如果你想要一个可插拔的插件系统,OSGI将处理很多问题。

    例如,我是这样做的:

    public ChannelListener init() {
        private ChannelListener listener = new ChannelListener();
        logger.info("Initializing Felix...");
        felix = new Felix(getConfig());
    
        try {
            felix.start();
        } catch (BundleException e) {
            logger.error("Error while initializing Felix", e);
            throw new RuntimeException(e);
        }
    
        try {
            // On the next line, you are registering a listener that will listen for all 
            //Log implementations service registration.
            felix.getBundleContext().addServiceListener(listener, "(objectClass=com.company.automation.service.Log)");
        } catch (InvalidSyntaxException e) {
            logger.error("Error while registering service listener", e);
        }
        listener.start(felix.getBundleContext());
    
        startAllBundlesInDirectory(BUNDLE_DIRECTORY, felix.getBundleContext());
        return listener;
    }
    
    private void startAllBundlesInDirectory(String directory, BundleContext context) {
        File bundleFolder = new File(directory);
        File[] bundleFilesList = bundleFolder.listFiles((dir, name) -> name.endsWith(".jar"));
    
        List<Bundle> installedBundles = new ArrayList<>();
        logger.info("Installing {} bundles in {}.", bundleFilesList.length, directory);
        for(File bundleFile : bundleFilesList) {
            logger.info("Installing {}", bundleFile.getName());
            try {
                installedBundles.add(context.installBundle("file:" + directory + bundleFile.getName()));
            } catch (BundleException e) {
                logger.error("Error while installing bundle {}{}", directory, bundleFile.getName(), e);
            }
        }
    
        for(Bundle bundle : installedBundles) {
            try {
                bundle.start();
            } catch (BundleException e) {
                logger.error("Error while starting bundle {}{}", directory, bundle.getSymbolicName(), e);
            }
        }
    }
    
    推荐文章