如何在运行时动态加载Jars?
时间:2020-03-05 18:52:47 来源:igfitidea点击:
为什么用Java这么难?如果我们想要任何类型的模块系统,则需要能够动态加载jar。有人告诉我有一种方法可以通过编写自己的ClassLoader
来完成,但是对于(至少在我看来)应该像调用带有jar文件作为其参数的方法一样容易的事情来说,这是很多工作。
对执行此操作的简单代码有何建议?
解决方案
回答
以下解决方案有些骇人,因为它使用反射来绕过封装,但是它可以完美地工作:
File file = ... URL url = file.toURI().toURL(); URLClassLoader classLoader = (URLClassLoader)ClassLoader.getSystemClassLoader(); Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); method.setAccessible(true); method.invoke(classLoader, url);
回答
我们应该看一下OSGi,例如在Eclipse平台中实现。它确实做到了。我们可以安装,卸载,启动和停止所谓的捆绑软件,这些捆绑软件实际上是JAR文件。但是它提供了更多功能,例如可以在运行时在JAR文件中动态发现的服务。
或者参见Java模块系统的规范。
回答
很难的原因是安全性。类加载器是不可变的。我们不应在运行时随意向其添加类。实际上,我很惊讶能与系统类加载器一起使用。这是制作自己的子类加载器的方法:
URLClassLoader child = new URLClassLoader(myJar.toURL(), this.getClass().getClassLoader()); Class classToLoad = Class.forName("com.MyClass", true, child); Method method = classToLoad.getDeclaredMethod("myMethod"); Object instance = classToLoad.newInstance(); Object result = method.invoke(instance);
很痛苦,但确实如此。
回答
我发现的最好的是org.apache.xbean.classloader.JarFileClassLoader,它是XBean项目的一部分。
这是我过去使用的一种简短方法,可以从特定目录中的所有lib文件创建类加载器
public void initialize(String libDir) throws Exception { File dependencyDirectory = new File(libDir); File[] files = dependencyDirectory.listFiles(); ArrayList<URL> urls = new ArrayList<URL>(); for (int i = 0; i < files.length; i++) { if (files[i].getName().endsWith(".jar")) { urls.add(files[i].toURL()); //urls.add(files[i].toURI().toURL()); } } classLoader = new JarFileClassLoader("Scheduler CL" + System.currentTimeMillis(), urls.toArray(new URL[urls.size()]), GFClassLoader.class.getClassLoader()); }
然后使用类加载器,只需执行以下操作:
classLoader.loadClass(name);
回答
JCL类加载器框架如何?我不得不承认,我还没有使用它,但是它看起来很有希望。
用法示例:
JarClassLoader jcl = new JarClassLoader(); jcl.add("myjar.jar"); // Load jar file jcl.add(new URL("http://myserver.com/myjar.jar")); // Load jar from a URL jcl.add(new FileInputStream("myotherjar.jar")); // Load jar file from stream jcl.add("myclassfolder/"); // Load class folder jcl.add("myjarlib/"); // Recursively load all jar files in the folder/sub-folder(s) JclObjectFactory factory = JclObjectFactory.getInstance(); // Create object of loaded class Object obj = factory.create(jcl, "mypackage.MyClass");
回答
这是不推荐使用的版本。我修改了原始文件以删除不推荐使用的功能。
/************************************************************************************************** * Copyright (c) 2004, Federal University of So Carlos * * * * All rights reserved. * * * * Redistribution and use in source and binary forms, with or without modification, are permitted * * provided that the following conditions are met: * * * * * Redistributions of source code must retain the above copyright notice, this list of * * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list of * * * conditions and the following disclaimer in the documentation and/or other materials * * * provided with the distribution. * * * Neither the name of the Federal University of So Carlos nor the names of its * * * contributors may be used to endorse or promote products derived from this software * * * without specific prior written permission. * * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * **************************************************************************************************/ /* * Created on Oct 6, 2004 */ package tools; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; /** * Useful class for dynamically changing the classpath, adding classes during runtime. */ public class ClasspathHacker { /** * Parameters of the method to add an URL to the System classes. */ private static final Class<?>[] parameters = new Class[]{URL.class}; /** * Adds a file to the classpath. * @param s a String pointing to the file * @throws IOException */ public static void addFile(String s) throws IOException { File f = new File(s); addFile(f); } /** * Adds a file to the classpath * @param f the file to be added * @throws IOException */ public static void addFile(File f) throws IOException { addURL(f.toURI().toURL()); } /** * Adds the content pointed by the URL to the classpath. * @param u the URL pointing to the content to be added * @throws IOException */ public static void addURL(URL u) throws IOException { URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader(); Class<?> sysclass = URLClassLoader.class; try { Method method = sysclass.getDeclaredMethod("addURL",parameters); method.setAccessible(true); method.invoke(sysloader,new Object[]{ u }); } catch (Throwable t) { t.printStackTrace(); throw new IOException("Error, could not add URL to system classloader"); } } public static void main(String args[]) throws IOException, SecurityException, ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{ addFile("C:\dynamicloading.jar"); Constructor<?> cs = ClassLoader.getSystemClassLoader().loadClass("test.DymamicLoadingTest").getConstructor(String.class); DymamicLoadingTest instance = (DymamicLoadingTest)cs.newInstance(); instance.test(); } }