首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java 9类路径和库路径扩展

Java 9类路径和库路径扩展
EN

Stack Overflow用户
提问于 2017-02-05 21:52:42
回答 1查看 1.1K关注 0票数 3

Java 9是模块化的,不能通过基于反射的Java组件进行访问。这使得以编程方式扩展类路径和java.library.path的大多数方法无效。怎样做才是正确的?以及如何使其与java.sql.DriverManager和javax.activation兼容?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-02-05 21:52:42

下面是几个小时的研究结果,研究如何以“授权”的方式以编程方式扩展类路径或java.library.path,而无需反射或尝试访问非公共的方法或字段。我还将展示如何绕过java.sql.DriverManager,因为如果JDBC驱动程序是使用与调用类的ClassLoader不同的ClassLoader创建的,那么它的"is class authorized“检查将失败。这已经在Java8和Java9上进行了测试,并且在这两种环境中都可以工作( URLClassLoader代码应该可以回溯到1.1)。

这是将在整个应用程序中使用的基本ClassLoader:

代码语言:javascript
复制
public class MiscTools
{
    private static class SpclClassLoader extends URLClassLoader
    {
        static
        {
            ClassLoader.registerAsParallelCapable();
        }

        private final Set<Path> userLibPaths = new CopyOnWriteArraySet<>();

        private SpclClassLoader()
        {
            super(new URL[0]);
        }

        @Override
        protected void addURL(URL url)
        {
            super.addURL(url);
        }

        protected void addLibPath(String newpath)
        {
            userLibPaths.add(Paths.get(newpath).toAbsolutePath());
        }

        @Override
        protected String findLibrary(String libname)
        {
            String nativeName = System.mapLibraryName(libname);
            return userLibPaths.stream().map(tpath -> tpath.resolve(nativeName)).filter(Files::exists).map(Path::toString).findFirst().orElse(super.findLibrary(libname));            }
    }
    private final static SpclClassLoader ucl = new SpclClassLoader();

    /**
     * Adds a jar file or directory to the classpath. From Utils4J.
     *
     * @param newpaths JAR filename(s) or directory(s) to add
     * @return URLClassLoader after newpaths added if newpaths != null
     */
    public static ClassLoader addToClasspath(String... newpaths)
    {
        if (newpaths != null)
            try
            {
                for (String newpath : newpaths)
                    if (newpath != null && !newpath.trim().isEmpty())
                        ucl.addURL(Paths.get(newpath.trim()).toUri().toURL());
            }
            catch (IllegalArgumentException | MalformedURLException e)
            {
                RuntimeException re = new RuntimeException(e);
                re.setStackTrace(e.getStackTrace());
                throw re;
            }
        return ucl;
    }

    /**
     * Adds to library path in ClassLoader returned by addToClassPath
     *
     * @param newpaths Path(s) to directory(s) holding OS library files
     */
    public static void addToLibraryPath(String... newpaths)
    {
        for (String newpath : Objects.requireNonNull(newpaths))
            ucl.addLibPath(newpath);
    }
}

在main()的前面,放入下面的代码来处理javax.activation之类的事情。

代码语言:javascript
复制
Thread.currentThread().setContextClassLoader(MiscTools.addToClasspath());

所有线程(包括由java.util.concurrent.Executors创建的线程)都继承上下文ClassLoader。

对于从扩展类路径加载的类,请使用以下代码:

代码语言:javascript
复制
try
{
    Class.forName(classname, true, MiscTools.addToClasspath(cptoadd);
}
catch (ClassNotFoundException IllegalArgumentException | SecurityException e)
{
    classlogger.log(Level.WARNING, "Error loading ".concat(props.getProperty("Class")), e);
}

最后,如何绕过java.sql.DriverManager,它会检查DriverManager.getDriver() ClassLoader的调用类是否与用于加载JDBC驱动程序的ClassLoader相同(不会检查调用类是否由应用程序ClassLoader加载,但驱动程序是使用SpclClassLoader加载的)。

代码语言:javascript
复制
private final static CopyOnWriteArraySet<Driver> loadedDrivers = new CopyOnWriteArraySet<>();

private static Driver isLoaded(String drivername, String... classpath) throws ClassNotFoundException
{
    Driver tdriver = loadedDrivers.stream().filter(d -> d.getClass().getName().equals(drivername)).findFirst().orElseGet(() ->
    {
        try
        {
            Driver itdriver = (Driver) Class.forName(drivername, true, addToClasspath(classpath)).newInstance();
            loadedDrivers.add(itdriver);
            return itdriver;
        }
        catch (ClassNotFoundException | IllegalAccessException | InstantiationException e)
        {
            return null;
        }
    });
    if (tdriver == null)
        throw new java.lang.ClassNotFoundException(drivername + " not found.");
    return tdriver;
}

isLoader确保我们不会在提供所请求的驱动程序时加载一堆具有所有额外开销的相同驱动程序。缺点是它需要知道JDBC类的类名(每个人都会发布它),而不是像DriverManager那样只搜索URL,但是DriverManager要求JDBC类在启动时加载,这样就不必执行Class.forName函数。

希望这将帮助其他人避免我为我编写的应用程序改进这种方法所花费的大量时间,该应用程序跨许多平台和在许多配置中使用,需要能够根据属性文件中提供的类路径加载类,并扩展library.path以使用不在默认library.path (也在属性文件中描述)中的本地库。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42052856

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档