Bagaimana cara memuat file JAR secara dinamis di Runtime?

308

Mengapa begitu sulit untuk melakukan ini di Jawa? Jika Anda ingin memiliki sistem modul apa pun, Anda harus dapat memuat file JAR secara dinamis. Saya diberitahu ada cara melakukannya dengan menulis sendiri ClassLoader, tapi itu banyak pekerjaan untuk sesuatu yang seharusnya (setidaknya dalam pikiran saya) semudah memanggil metode dengan file JAR sebagai argumennya.

Ada saran untuk kode sederhana yang melakukan ini?

Allain Lalonde
sumber
4
Saya ingin melakukan hal yang sama tetapi menjalankan toples yang dimuat di lingkungan yang lebih berpasir (untuk alasan keamanan jelas). Sebagai contoh, saya ingin memblokir semua akses jaringan dan sistem file.
Jus12

Jawaban:

254

Alasan sulitnya adalah keamanan. Classloader dimaksudkan untuk tidak berubah; Anda seharusnya tidak bisa mau menambahkan kelas untuk itu saat runtime. Saya sebenarnya sangat terkejut bekerja dengan sistem classloader. Inilah cara Anda melakukannya membuat classloader anak Anda sendiri:

URLClassLoader child = new URLClassLoader(
        new URL[] {myJar.toURI().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);

Nyeri, tapi itu dia.

Jodonnell
sumber
16
Satu-satunya masalah dengan pendekatan ini adalah Anda perlu tahu kelas apa yang ada di stoples apa. Berbeda dengan hanya memuat direktori guci dan kemudian instantiating kelas. Saya salah paham?
Allain Lalonde
10
Metode ini bekerja sangat baik ketika berjalan di IDE saya, tetapi ketika saya membangun JAR saya, saya mendapatkan ClassNotFoundException ketika memanggil Class.forName ().
darrickc
29
Dengan menggunakan pendekatan ini, Anda perlu memastikan Anda tidak akan memanggil metode pemuatan ini lebih dari sekali untuk setiap kelas. Karena Anda membuat pemuat kelas baru untuk setiap operasi pemuatan, ia tidak dapat mengetahui apakah kelas tersebut sudah dimuat sebelumnya. Ini dapat memiliki konsekuensi buruk. Misalnya lajang tidak berfungsi karena kelas dimuat beberapa kali dan bidang statis ada beberapa kali.
Eduard Wirch
8
Bekerja Bahkan dengan dependensi ke kelas lain di dalam toples. Baris pertama tidak lengkap. Saya menggunakan URLClassLoader child = new URLClassLoader (new URL[] {new URL("file://./my.jar")}, Main.class.getClassLoader());asumsi bahwa file jar dipanggil my.jardan terletak di direktori yang sama.
rahang
4
Jangan lupa ke URL url = file.toURI (). ToURL ();
johnstosh
139

Solusi berikut ini adalah retas, karena menggunakan refleksi untuk mem-bypass enkapsulasi, tetapi berfungsi dengan sempurna:

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);
Allain Lalonde
sumber
40
Semua aktivitas pada respons ini membuat saya bertanya-tanya berapa banyak retas yang kami jalankan dalam produksi di berbagai sistem. Saya tidak yakin ingin tahu jawabannya
Andrei Savu
6
Tidak berfungsi dengan baik jika pemuat kelas sistem kebetulan bukan selain URLClassLoader ...
Gus
6
Java 9+ memperingatkan bahwa URLClassLoader.class.getDeclaredMethod("addURL", URL.class)penggunaan refleksi secara ilegal, dan akan gagal di masa mendatang.
Charlweed
1
Tahu cara memperbarui kode ini agar berfungsi dengan Java 9+?
FiReTiTi
1
@FiReTiTi Ya !!
Mordechai
51

Anda harus melihat OSGi , misalnya diimplementasikan di Platform Eclipse . Itu persis seperti itu. Anda dapat menginstal, mencopot, memulai dan menghentikan bundel yang disebut, yang secara efektif file JAR. Tapi itu sedikit lebih, karena ia menawarkan misalnya layanan yang dapat ditemukan secara dinamis dalam file JAR saat runtime.

Atau lihat spesifikasi untuk Sistem Modul Java .

Martin Klinke
sumber
41

Bagaimana dengan kerangka kerja loader kelas JCL ? Harus saya akui, saya belum menggunakannya, tapi kelihatannya menjanjikan.

Contoh penggunaan:

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");
Chris
sumber
9
Ini juga bermasalah dan kehilangan beberapa implementasi penting yaitu findResources (...). Bersiaplah untuk menghabiskan malam yang indah menyelidiki mengapa hal-hal tertentu tidak berhasil =)
Sergey Karpushin
Saya masih bertanya-tanya @ klaim SergeyKarpushin masih ada karena proyek telah diperbarui dari waktu ke waktu ke versi utama kedua. Ingin mendengar pengalaman.
Erdin Eray
2
@ErdinEray, itu adalah pertanyaan yang sangat bagus yang saya tanyakan pada diri saya juga karena kami "dipaksa" untuk beralih ke OpenJDK. Saya masih bekerja pada proyek-proyek java, dan saya tidak punya bukti bahwa Open JDK akan mengecewakan Anda hari ini (saya punya masalah saat itu). Saya kira saya menarik klaim saya sampai bertemu sesuatu yang lain.
Sergey Karpushin
20

Ini adalah versi yang tidak ditinggalkan. Saya memodifikasi yang asli untuk menghapus fungsionalitas yang sudah usang.

/**************************************************************************************************
 * 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();
    }
}
Jonathan Nadeau
sumber
19
Saya benci untuk membenturkan utas lama, tetapi saya ingin menunjukkan bahwa semua konten di stackoverflow adalah lisensi CC. Pernyataan hak cipta Anda secara efektif tidak efektif. stackoverflow.com/faq#editing
Huckle
43
Um Secara teknis, konten asli adalah lisensi CC, tetapi jika Anda memposting konten yang dilindungi hak cipta di sini, itu tidak menghapus fakta bahwa konten tersebut memiliki hak cipta. Jika saya memposting gambar Mickey Mouse, itu tidak membuatnya berlisensi CC. Jadi saya menambahkan pernyataan hak cipta kembali.
Jason S
19

Walaupun sebagian besar solusi yang tercantum di sini adalah peretasan (pra JDK 9) yang sulit dikonfigurasi (agen) atau tidak berfungsi lagi (posting JDK 9) Saya merasa sangat mengejutkan bahwa tidak ada yang menyebutkan metode yang terdokumentasi dengan jelas .

Anda dapat membuat pemuat kelas sistem khusus dan kemudian Anda bebas melakukan apa pun yang Anda inginkan. Tidak diperlukan refleksi dan semua kelas berbagi classloader yang sama.

Saat memulai JVM tambahkan flag ini:

java -Djava.system.class.loader=com.example.MyCustomClassLoader

Classloader harus memiliki konstruktor yang menerima classloader, yang harus ditetapkan sebagai induknya. Konstruktor akan dipanggil pada startup JVM dan classloader sistem nyata akan dilewati, kelas utama akan dimuat oleh loader kustom.

Untuk menambahkan toples cukup panggil ClassLoader.getSystemClassLoader()dan cor ke kelas Anda.

Lihatlah implementasi ini untuk classloader yang dibuat dengan cermat. Harap dicatat, Anda dapat mengubah add()metode ini ke publik.

Mordechai
sumber
Terima kasih - ini sangat membantu! Semua referensi lain tentang metode penggunaan web untuk JDK 8 atau sebelumnya - yang memiliki banyak masalah.
Vishal Biyani
15

Dengan Java 9 , jawaban dengan URLClassLoadersekarang memberikan kesalahan seperti:

java.lang.ClassCastException: java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.base/java.net.URLClassLoader

Ini karena pemuat kelas yang digunakan telah berubah. Sebagai gantinya, untuk menambahkan ke pemuat kelas sistem, Anda dapat menggunakan API Instrumentasi melalui agen.

Buat kelas agen:

package ClassPathAgent;

import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.util.jar.JarFile;

public class ClassPathAgent {
    public static void agentmain(String args, Instrumentation instrumentation) throws IOException {
        instrumentation.appendToSystemClassLoaderSearch(new JarFile(args));
    }
}

Tambahkan META-INF / MANIFEST.MF dan letakkan di file JAR dengan kelas agen:

Manifest-Version: 1.0
Agent-Class: ClassPathAgent.ClassPathAgent

Jalankan agen:

Ini menggunakan perpustakaan byte-buddy-agent untuk menambahkan agen ke JVM yang berjalan:

import java.io.File;

import net.bytebuddy.agent.ByteBuddyAgent;

public class ClassPathUtil {
    private static File AGENT_JAR = new File("/path/to/agent.jar");

    public static void addJarToClassPath(File jarFile) {
        ByteBuddyAgent.attach(AGENT_JAR, String.valueOf(ProcessHandle.current().pid()), jarFile.getPath());
    }
}
fgb
sumber
9

Yang terbaik yang saya temukan adalah org.apache.xbean.classloader.JarFileClassLoader yang merupakan bagian dari proyek XBean .

Inilah metode singkat yang saya gunakan di masa lalu, untuk membuat pemuat kelas dari semua file lib di direktori tertentu

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());
}

Kemudian untuk menggunakan classloader, cukup lakukan:

classLoader.loadClass(name);
Zeusoflightning125
sumber
Perhatikan bahwa proyek tampaknya tidak terawat dengan baik. Peta jalan mereka untuk masa depan mengandung beberapa rilis untuk tahun 2014, misalnya.
Zero3
6

Jika Anda bekerja di Android, kode berikut berfungsi:

String jarFile = "path/to/jarfile.jar";
DexClassLoader classLoader = new DexClassLoader(jarFile, "/data/data/" + context.getPackageName() + "/", null, getClass().getClassLoader());
Class<?> myClass = classLoader.loadClass("MyClass");
Caner
sumber
6

Berikut ini adalah solusi cepat untuk metode Allain agar kompatibel dengan versi Java yang lebih baru:

ClassLoader classLoader = ClassLoader.getSystemClassLoader();
try {
    Method method = classLoader.getClass().getDeclaredMethod("addURL", URL.class);
    method.setAccessible(true);
    method.invoke(classLoader, new File(jarPath).toURI().toURL());
} catch (NoSuchMethodException e) {
    Method method = classLoader.getClass()
            .getDeclaredMethod("appendToClassPathForInstrumentation", String.class);
    method.setAccessible(true);
    method.invoke(classLoader, jarPath);
}

Perhatikan bahwa ini bergantung pada pengetahuan tentang implementasi internal JVM tertentu, jadi itu tidak ideal dan itu bukan solusi universal. Tapi ini solusi cepat dan mudah jika Anda tahu bahwa Anda akan menggunakan OpenJDK standar atau Oracle JVM. Mungkin juga rusak di beberapa titik di masa depan ketika versi JVM baru dirilis, jadi Anda perlu mengingatnya.

Anton Tananaev
sumber
Dengan Java 11.0.2, saya mendapatkan:Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make void jdk.internal.loader.ClassLoaders$AppClassLoader.appendToClassPathForInstrumentation(java.lang.String) accessible: module java.base does not "opens jdk.internal.loader" to unnamed module @18ef96
Richard Żak
Bekerja dengan Java 8 EE di lingkungan server aplikasi.
Jan
4

Solusi yang diusulkan oleh jodonnell bagus tetapi harus sedikit ditingkatkan. Saya menggunakan pos ini untuk mengembangkan aplikasi saya dengan sukses.

Tetapkan utas saat ini

Pertama-tama kita harus menambahkan

Thread.currentThread().setContextClassLoader(classLoader);

atau Anda tidak akan dapat memuat sumber daya (seperti pegas / context.xml) yang disimpan dalam toples.

Tidak termasuk

guci Anda ke pemuat kelas induk atau Anda tidak akan dapat memahami siapa yang memuat apa.

lihat juga Masalah memuat ulang toples menggunakan URLClassLoader

Namun, kerangka kerja OSGi tetap merupakan cara terbaik.

venergiac
sumber
2
Jawaban Anda tampaknya sedikit membingungkan, dan mungkin lebih cocok sebagai komentar untuk jawaban oleh jodonnell jika itu hanya perbaikan sederhana.
Zero3
4

Versi lain dari solusi peretasan dari Allain, yang juga berfungsi pada JDK 11:

File file = ...
URL url = file.toURI().toURL();
URLClassLoader sysLoader = new URLClassLoader(new URL[0]);

Method sysMethod = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
sysMethod.setAccessible(true);
sysMethod.invoke(sysLoader, new Object[]{url});

Pada JDK 11 memberikan peringatan penghentian tetapi berfungsi sebagai solusi sementara mereka yang menggunakan solusi Allain pada JDK 11.

czdepski
sumber
Bisakah saya juga mengeluarkan stoples?
user7294900
3

Solusi kerja lain menggunakan Instrumentasi yang bekerja untuk saya. Ini memiliki keuntungan dari memodifikasi pencarian loader kelas, menghindari masalah pada visibilitas kelas untuk kelas dependen:

Buat Kelas Agen

Untuk contoh ini, itu harus berada di jar yang sama dipanggil oleh baris perintah:

package agent;

import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.util.jar.JarFile;

public class Agent {
   public static Instrumentation instrumentation;

   public static void premain(String args, Instrumentation instrumentation) {
      Agent.instrumentation = instrumentation;
   }

   public static void agentmain(String args, Instrumentation instrumentation) {
      Agent.instrumentation = instrumentation;
   }

   public static void appendJarFile(JarFile file) throws IOException {
      if (instrumentation != null) {
         instrumentation.appendToSystemClassLoaderSearch(file);
      }
   }
}

Ubah MANIFEST.MF

Menambahkan referensi ke agen:

Launcher-Agent-Class: agent.Agent
Agent-Class: agent.Agent
Premain-Class: agent.Agent

Saya benar-benar menggunakan Netbeans, jadi posting ini membantu tentang cara mengubah manifest.mf

Lari

Ini Launcher-Agent-Classhanya didukung pada JDK 9+ dan bertanggung jawab untuk memuat agen tanpa secara eksplisit mendefinisikannya pada baris perintah:

 java -jar <your jar>

Cara yang bekerja pada JDK 6+ adalah mendefinisikan -javaagentargumen:

java -javaagent:<your jar> -jar <your jar>

Menambahkan Jar baru di Runtime

Anda kemudian dapat menambahkan toples seperlunya menggunakan perintah berikut:

Agent.appendJarFile(new JarFile(<your file>));

Saya tidak menemukan masalah menggunakan ini pada dokumentasi.

czdepski
sumber
Untuk beberapa alasan ketika menggunakan solusi ini saya mendapatkan "Pengecualian di utas" utama "java.lang.ClassNotFoundException: agent.Agent". Saya mengemas kelas "Agen" ke dalam aplikasi "perang" utama saya, jadi saya yakin itu ada di sana
Sergei Ledvanov
3

Jika ada yang mencari ini di masa depan, cara ini berfungsi untuk saya dengan OpenJDK 13.0.2.

Saya memiliki banyak kelas yang perlu saya instantiate secara dinamis saat runtime, masing-masing berpotensi dengan classpath yang berbeda.

Dalam kode ini, saya sudah memiliki objek yang disebut paket, yang menampung beberapa metadata tentang kelas yang saya coba muat. Metode getObjectFile () mengembalikan lokasi file kelas untuk kelas. Metode getObjectRootPath () mengembalikan path ke direktori bin / yang berisi file kelas yang menyertakan kelas yang saya coba instantiate. Metode getLibPath () mengembalikan path ke direktori yang berisi file jar yang merupakan classpath untuk modul yang menjadi bagian dari kelas.

File object = new File(pack.getObjectFile()).getAbsoluteFile();
Object packObject;
try {
    URLClassLoader classloader;

    List<URL> classpath = new ArrayList<>();
    classpath.add(new File(pack.getObjectRootPath()).toURI().toURL());
    for (File jar : FileUtils.listFiles(new File(pack.getLibPath()), new String[] {"jar"}, true)) {
        classpath.add(jar.toURI().toURL());
    }
    classloader = new URLClassLoader(classpath.toArray(new URL[] {}));

    Class<?> clazz = classloader.loadClass(object.getName());
    packObject = clazz.getDeclaredConstructor().newInstance();

} catch (Exception e) {
    e.printStackTrace();
    throw e;
}
return packObject;

Saya menggunakan dependensi Maven: org.xeustechnologies: jcl-core: 2.8 untuk melakukan ini sebelumnya, tetapi setelah melewati JDK 1.8, kadang-kadang membeku dan tidak pernah kembali terjebak "menunggu referensi" di Reference :: waitForReferencePendingList ().

Saya juga menyimpan peta pemuat kelas agar dapat digunakan kembali jika kelas yang saya coba instantiate ada di modul yang sama dengan kelas yang sudah saya instantiated, yang akan saya rekomendasikan.

ZGorlock
sumber
2

silakan lihat proyek yang saya mulai ini: proxy-object lib

Lib ini akan memuat jar dari sistem file atau lokasi lainnya. Ini akan mendedikasikan loader kelas untuk tabung untuk memastikan tidak ada konflik perpustakaan. Pengguna akan dapat membuat objek apa pun dari toples yang dimuat dan memanggil metode apa pun di atasnya. Lib ini dirancang untuk memuat guci yang dikompilasi di Java 8 dari basis kode yang mendukung Java 7.

Untuk membuat objek:

    File libDir = new File("path/to/jar");

    ProxyCallerInterface caller = ObjectBuilder.builder()
            .setClassName("net.proxy.lib.test.LibClass")
            .setArtifact(DirArtifact.builder()
                    .withClazz(ObjectBuilderTest.class)
                    .withVersionInfo(newVersionInfo(libDir))
                    .build())
            .build();
    String version = caller.call("getLibVersion").asString();

ObjectBuilder mendukung metode pabrik, memanggil fungsi statis, dan memanggil kembali implementasi antarmuka. saya akan memposting lebih banyak contoh di halaman readme.

Aleksey
sumber
2

Ini bisa menjadi respons yang terlambat, saya bisa melakukannya karena ini (contoh sederhana untuk fastutil-8.2.2.jar) menggunakan kelas jhplot.Web dari DataMelt ( http://jwork.org/dmelt )

import jhplot.Web;
Web.load("http://central.maven.org/maven2/it/unimi/dsi/fastutil/8.2.2/fastutil-8.2.2.jar"); // now you can start using this library

Menurut dokumentasi, file ini akan diunduh di dalam "lib / pengguna" dan kemudian dimuat secara dinamis, sehingga Anda dapat segera mulai menggunakan kelas dari file jar ini di program yang sama.

steve212
sumber
1

Saya perlu memuat file jar saat runtime untuk java 8 dan java 9+ (komentar di atas tidak bekerja untuk kedua versi ini). Berikut adalah metode untuk melakukannya (menggunakan Spring Boot 1.5.2 jika itu berhubungan).

public static synchronized void loadLibrary(java.io.File jar) {
    try {            
        java.net.URL url = jar.toURI().toURL();
        java.lang.reflect.Method method = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{java.net.URL.class});
        method.setAccessible(true); /*promote the method to public access*/
        method.invoke(Thread.currentThread().getContextClassLoader(), new Object[]{url});
    } catch (Exception ex) {
        throw new RuntimeException("Cannot load library from jar file '" + jar.getAbsolutePath() + "'. Reason: " + ex.getMessage());
    }
}
Bik Rikimaru
sumber
-2

Saya pribadi menemukan java.util.ServiceLoader melakukan pekerjaan dengan cukup baik. Anda bisa mendapatkan contoh di sini .

tanyehzheng
sumber
11
ServiceLoader tidak menambahkan file jar secara dinamis saat runtime. file jar harus ada di classpath sebelumnya.
angelcervera