रनटाइम पर JAR फ़ाइलों को गतिशील रूप से कैसे लोड करें?


308

जावा में ऐसा करना इतना कठिन क्यों है? यदि आप किसी भी तरह का मॉड्यूल सिस्टम रखना चाहते हैं, तो आपको गतिशील रूप से JAR फाइलें लोड करने में सक्षम होना चाहिए। मुझे बताया गया है कि यह अपना लिखने का एक तरीका हैClassLoader , लेकिन यह उस चीज के लिए बहुत काम है जो (मेरे दिमाग में कम से कम) एक विधि के रूप में JAR फ़ाइल के साथ अपने तर्क के रूप में कॉल करना आसान है।

सरल कोड के लिए कोई सुझाव जो ऐसा करता है?


4
मैं ऐसा ही करना चाहता हूं, लेकिन लोड किए गए जार को अधिक सैंडबॉक्स वाले वातावरण में (सुरक्षा कारणों से स्पष्ट रूप से) चलाना चाहता हूं। उदाहरण के लिए, मैं सभी नेटवर्क और फाइल सिस्टम एक्सेस को ब्लॉक करना चाहता हूं।
1812 को Jus12

जवाबों:


253

इसकी वजह कड़ी सुरक्षा है। क्लास लोडर अपरिवर्तनीय होने के लिए होते हैं; आप रन-वे पर इसे करने के लिए विली-नीली कक्षाओं को जोड़ने में सक्षम नहीं होना चाहिए। मैं वास्तव में बहुत हैरान हूं कि सिस्टम क्लास लोडर के साथ काम करता है। यहां बताया गया है कि आप इसे अपने बच्चे को क्लास लोडर कैसे बनाते हैं:

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

दर्दनाक, लेकिन वहाँ यह है।


16
इस दृष्टिकोण के साथ केवल समस्या यह है कि आपको यह जानने की आवश्यकता है कि कौन से वर्ग किस जार में हैं। के रूप में सिर्फ जार की एक निर्देशिका लोड करने और फिर तत्काल कक्षाओं का विरोध किया। मैं इसे गलत समझ रहा हूं?
एलन लालडेन्

10
मेरे IDE में चलने पर यह तरीका बहुत अच्छा है, लेकिन जब मैं अपना JAR बनाता हूं तो Class.forName () कॉल करते समय मुझे ClassNotFoundException मिलती है।
darrickc

29
इस दृष्टिकोण का उपयोग करके आपको यह सुनिश्चित करने की आवश्यकता है कि आप प्रत्येक कक्षा के लिए इस लोड विधि को एक से अधिक बार कॉल नहीं करेंगे। चूंकि आप हर लोड ऑपरेशन के लिए एक नया क्लास लोडर बना रहे हैं, इसलिए यह नहीं जान सकते कि क्या क्लास पहले से लोड थी। इसके बुरे परिणाम हो सकते हैं। उदाहरण के लिए एकल वर्ग काम नहीं कर रहा है क्योंकि कक्षा कई बार भरी हुई थी और इसलिए स्थैतिक क्षेत्र कई बार मौजूद हैं।
एडुआर्ड विर्च

8
काम करता है। यहां तक ​​कि जार के अंदर अन्य वर्गों के लिए निर्भरता के साथ। पहली लाइन अधूरी थी। मैंने URLClassLoader child = new URLClassLoader (new URL[] {new URL("file://./my.jar")}, Main.class.getClassLoader());यह मानकर उपयोग किया कि जार फ़ाइल को कहा जाता है my.jarऔर उसी निर्देशिका में स्थित है।
जबड़ा 14

4
URL url = file.toURI ()। ToURL () को न भूलें;
जॉनस्टॉश

139

निम्नलिखित समाधान हैकिश है, क्योंकि यह रिफ्लेक्शन को बाईपास इनकैप्सुलेशन का उपयोग करता है, लेकिन यह त्रुटिपूर्ण रूप से काम करता है:

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

40
इस प्रतिक्रिया पर सभी गतिविधि मुझे आश्चर्यचकित करती है कि हम विभिन्न प्रणालियों में उत्पादन में कितने हैक कर रहे हैं। मुझे यकीन नहीं है कि मैं इसका जवाब जानना चाहता हूं
आंद्रेई सावु

6
अगर सिस्टम क्लास लोडर URLClassLoader के अलावा कुछ और होता है तो इतनी अच्छी तरह से काम नहीं करता ...
Gus

6
जावा 9+ चेतावनी देता है कि URLClassLoader.class.getDeclaredMethod("addURL", URL.class)प्रतिबिंब का अवैध उपयोग है, और भविष्य में विफल हो जाएगा।
Charlweed

1
किसी भी विचार जावा 9+ के साथ काम करने के लिए इस कोड को कैसे अपडेट किया जाए?
FiReTiTi


51

आपको OSGi पर एक नज़र डालनी चाहिए , जैसे कि ग्रहण प्लेटफ़ॉर्म में लागू किया गया । यह बिल्कुल वैसा ही करता है। आप तथाकथित बंडलों को स्थापित, अनइंस्टॉल, शुरू और रोक सकते हैं, जो प्रभावी रूप से JAR फाइलें हैं। लेकिन यह थोड़ा और अधिक होता है, क्योंकि यह उदाहरण के लिए सेवाएं प्रदान करता है जिन्हें रनटाइम के दौरान JAR फ़ाइलों में गतिशील रूप से खोजा जा सकता है।

या जावा मॉड्यूल सिस्टम के लिए विनिर्देश देखें ।


41

जेसीएल वर्ग लोडर ढांचे के बारे में कैसे ? मुझे मानना ​​पड़ेगा, मैंने इसका उपयोग नहीं किया है, लेकिन यह आशाजनक लगता है।

उपयोग उदाहरण:

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");

9
यह भी छोटी गाड़ी है और कुछ महत्वपूर्ण कार्यान्वयनों को गायब कर रहा है अर्थात findResources (...)। अद्भुत रातों को खर्च करने के लिए तैयार रहें, यह जांचने के लिए कि कुछ चीजें काम क्यों नहीं करती हैं) =
सर्गेई कारपुशिन

मैं अभी भी सोच रहा हूं @ सर्गेईकारपुशिन के दावे अभी भी मौजूद हैं क्योंकि परियोजना को समय के साथ दूसरे प्रमुख संस्करण में अपडेट किया गया है। अनुभव सुनना चाहेंगे।
एरडिन एर

2
@ErdinEray, यह एक बहुत अच्छा सवाल है जो मैं खुद से भी पूछता हूं क्योंकि हम OpenJDK पर स्विच करने के लिए "मजबूर" थे। मैं अभी भी जावा प्रोजेक्ट्स पर काम करता हूं, और मेरे पास कोई सबूत नहीं है कि ओपन जेडीके आपको इन दिनों विफल कर देगा (हालांकि मैंने तब वापस जारी किया था)। मुझे लगता है कि मैं अपना दावा वापस लेता हूं जब तक कि मैं किसी और चीज से नहीं टकराता।
सेर्गेई करपुशिन

20

यहां एक संस्करण है जिसे अपदस्थ नहीं किया गया है। मैंने हटाए गए कार्यक्षमता को हटाने के लिए मूल को संशोधित किया।

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

19
मैं एक पुराने धागे को टक्कर देने से नफरत करता हूं, लेकिन मैं यह बताना चाहूंगा कि स्टैकओवरफ्लो पर सभी सामग्री सीसी लाइसेंस प्राप्त है। आपका कॉपीराइट कथन प्रभावी रूप से अप्रभावी है। stackoverflow.com/faq#editing
हकल

43
उम। तकनीकी रूप से, मूल सामग्री CC लाइसेंस प्राप्त है, लेकिन यदि आप यहां कॉपीराइट सामग्री पोस्ट करते हैं, तो यह इस तथ्य को दूर नहीं करता है कि सामग्री कॉपीराइट है। अगर मैं मिकी माउस की तस्वीर पोस्ट करता हूं, तो यह इसे सीसी-लाइसेंस नहीं बनाता है। इसलिए मैं कॉपीराइट स्टेटमेंट वापस जोड़ रहा हूं।
जेसन एस

19

हालांकि यहां सूचीबद्ध अधिकांश समाधान या तो हैक (पूर्व JDK 9) कॉन्फ़िगर करने के लिए (एजेंट) कठिन हैं या बस अब और काम नहीं करते हैं (JDK 9 पोस्ट करें) मुझे वास्तव में यह चौंकाने वाला लगता है कि किसी ने भी स्पष्ट रूप से प्रलेखित विधि का उल्लेख नहीं किया है ।

आप एक कस्टम सिस्टम क्लास लोडर बना सकते हैं और फिर आप जो चाहें कर सकते हैं। किसी भी प्रतिबिंब की आवश्यकता नहीं है और सभी वर्ग एक ही क्लास लोडर साझा करते हैं।

JVM शुरू करते समय इस ध्वज को जोड़ें:

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

क्लास-लोडर के पास एक क्लास-लोडर को स्वीकार करने वाला एक कंस्ट्रक्टर होना चाहिए, जिसे उसके माता-पिता के रूप में सेट किया जाना चाहिए। कंस्ट्रक्टर को जेवीएम स्टार्टअप पर बुलाया जाएगा और वास्तविक सिस्टम क्लास लोडर को पास किया जाएगा, मुख्य वर्ग को कस्टम लोडर द्वारा लोड किया जाएगा।

जार जोड़ने के लिए सिर्फ कॉल करें ClassLoader.getSystemClassLoader() और इसे अपनी कक्षा में डालें।

ध्यान से तैयार किए गए क्लास लोडर के लिए इस कार्यान्वयन की जांच करें । कृपया ध्यान दें, आप add()विधि को सार्वजनिक कर सकते हैं ।


धन्यवाद - यह वास्तव में मददगार है! JDK 8 या उससे पहले के वेब उपयोग के तरीकों पर अन्य सभी संदर्भ - जिसमें कई समस्याएं हैं।
विशाल बियानी

15

साथ जावा 9 , साथ जवाब URLClassLoaderअब की तरह एक त्रुटि दे:

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

ऐसा इसलिए है क्योंकि उपयोग किए जाने वाले क्लास लोडर बदल गए हैं। इसके बजाय, सिस्टम क्लास लोडर में जोड़ने के लिए, आप एक एजेंट के माध्यम से इंस्ट्रूमेंटेशन एपीआई का उपयोग कर सकते हैं ।

एक एजेंट वर्ग बनाएँ:

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

META-INF / MANIFEST.MF जोड़ें और इसे एजेंट वर्ग के साथ JAR फ़ाइल में डालें:

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

एजेंट चलाएं:

एजेंट को रनिंग JVM में जोड़ने के लिए बाइट-ब्वॉय-एजेंट लाइब्रेरी का उपयोग करता है :

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

9

सबसे अच्छा मैंने पाया जाता है org.apache.xbean.classloader.JarFileClassLoader जो का हिस्सा है XBean परियोजना।

यहाँ एक छोटी विधि है जो मैंने अतीत में इस्तेमाल की है, एक विशिष्ट निर्देशिका में सभी लिबास फ़ाइलों से एक क्लास लोडर बनाने के लिए

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

ध्यान दें कि परियोजना बहुत अच्छी तरह से बनाए रखने के लिए प्रकट नहीं होती है। भविष्य के लिए उनके रोडमैप में 2014 के कई उदाहरण हैं।
शून्य ३

6

यदि आप Android पर काम कर रहे हैं, तो निम्न कोड काम करता है:

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

6

यहाँ जावा के नए संस्करणों के साथ संगत करने के लिए एलेन की विधि के लिए एक त्वरित समाधान है:

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

ध्यान दें कि यह विशिष्ट JVM के आंतरिक कार्यान्वयन के ज्ञान पर निर्भर करता है, इसलिए यह आदर्श नहीं है और यह एक सार्वभौमिक समाधान नहीं है। लेकिन यह एक त्वरित और आसान समाधान है यदि आप जानते हैं कि आप मानक OpenJDK या Oracle JVM का उपयोग करने जा रहे हैं। नए JVM संस्करण के रिलीज़ होने पर यह भविष्य में किसी बिंदु पर टूट सकता है, इसलिए आपको इसे ध्यान में रखना होगा।


जावा 11.0.2 के साथ, मुझे मिलता है: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
रिचर्ड 11ak

अनुप्रयोग सर्वर वातावरण में Java 8 EE के साथ काम करता है।
जनवरी

4

जोडॉनेल द्वारा प्रस्तावित समाधान अच्छा है लेकिन थोड़ा बढ़ाया जाना चाहिए। मैंने अपने आवेदन को सफलता के साथ विकसित करने के लिए इस पोस्ट का उपयोग किया।

वर्तमान धागे को असाइन करें

सबसे पहले हमें जोड़ना होगा

Thread.currentThread().setContextClassLoader(classLoader);

या आप जार में संग्रहीत संसाधन (जैसे वसंत / संदर्भ.xml) को लोड करने में सक्षम नहीं होंगे।

शामिल न करें

मूल श्रेणी लोडर में आपके जार या आप समझ नहीं पाएंगे कि कौन लोड कर रहा है।

URLClassLoader का उपयोग करके जार को पुनः लोड करने की समस्या भी देखें

हालांकि, OSGi फ्रेमवर्क सबसे अच्छा तरीका है।


2
आपका उत्तर थोड़ा भ्रामक लगता है, और शायद यह एक सरल सुधार के रूप में जोंडनेल द्वारा उत्तर के लिए एक टिप्पणी के रूप में अधिक अनुकूल है।
जीरो 3

4

एलन से हैकिश समाधान का एक और संस्करण, जो 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});

JDK 11 पर यह कुछ अपक्षय चेतावनी देता है लेकिन अस्थायी समाधान के रूप में कार्य करता है जो JDK 11 पर एलन समाधान का उपयोग करते हैं।


क्या मैं जार को भी हटा सकता हूं?
user7294900

3

मेरे लिए काम करने वाले इंस्ट्रूमेंटेशन का उपयोग कर एक और काम कर समाधान। इसमें क्लास लोडर खोज को संशोधित करने, आश्रित कक्षाओं के लिए कक्षा दृश्यता में समस्याओं से बचने का लाभ है:

एक एजेंट वर्ग बनाएँ

इस उदाहरण के लिए, यह कमांड लाइन द्वारा लागू एक ही जार पर होना चाहिए:

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

MANIFEST.MF को संशोधित करें

एजेंट का संदर्भ जोड़ना:

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

मैं वास्तव में नेटबीन्स का उपयोग करता हूं, इसलिए यह पोस्ट मैनिफेस्टोफ को बदलने के तरीके पर मदद करती है

चल रहा है

Launcher-Agent-Classकेवल JDK 9+ पर समर्थित और स्पष्ट रूप से कमांड लाइन पर यह परिभाषित करने के बिना एजेंट लोड हो रहा है के लिए जिम्मेदार है:

 java -jar <your jar>

JDK 6+ पर काम करने का तरीका -javaagentतर्क को परिभाषित कर रहा है:

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

रनटाइम में नया जार जोड़ना

फिर आप निम्न कमांड का उपयोग करके जार को आवश्यकतानुसार जोड़ सकते हैं:

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

मुझे प्रलेखन पर इसका उपयोग करने में कोई समस्या नहीं आई।


इस कारण का उपयोग करते समय किसी कारण से मुझे "थ्रेड में अपवाद" मुख्य "java.lang.ClassNotFoundException: agent.Agent" मिलता है। मैंने "एजेंट" वर्ग को अपने मुख्य "युद्ध" एप्लिकेशन में पैक किया, इसलिए मुझे यकीन है कि यह वहाँ है
सर्गेई लेडवानोव

3

यदि कोई भविष्य में इसके लिए खोज करता है, तो यह तरीका मेरे लिए OpenJDK 13.0.2 के साथ काम करता है।

मेरे पास कई कक्षाएं हैं जिन्हें मुझे गतिशील रूप से रनटाइम पर प्रत्येक की आवश्यकता होती है, प्रत्येक संभावित रूप से एक अलग क्लासपैथ के साथ।

इस कोड में, मेरे पास पहले से ही एक ऑब्जेक्ट है, जिसे पैक कहा जाता है, जो उस वर्ग के बारे में कुछ मेटाडेटा रखता है जिसे मैं लोड करने की कोशिश कर रहा हूं। GetObjectFile () विधि क्लास के लिए क्लास फ़ाइल का स्थान लौटाती है। GetObjectRootPath () विधि बिन / निर्देशिका के लिए पथ को वापस लेती है जिसमें वर्ग फाइलें शामिल होती हैं जिसमें वह वर्ग शामिल होता है जिसे मैं तुरंत करने की कोशिश कर रहा हूं। GetLibPath () विधि एक निर्देशिका के लिए रास्ता देती है जिसमें जार फाइलें होती हैं जिसमें क्लास उस मॉड्यूल का क्लासपैथ होता है जिसका एक हिस्सा होता है।

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;

मैं Maven निर्भरता का उपयोग कर रहा था: org.xeustechnologies: jcl-core: 2.8 इससे पहले ऐसा करने के लिए, लेकिन पिछले JDK 1.8 को स्थानांतरित करने के बाद, यह कभी-कभी जम जाता है और कभी भी संदर्भ में "प्रतीक्षा के संदर्भ में" अटक नहीं रहा है - WaitForReferencePendingList ()।

मैं क्लास लोडर का एक नक्शा भी रख रहा हूं ताकि उनका पुन: उपयोग किया जा सके अगर मैं जिस क्लास को इंस्टेंट करने की कोशिश कर रहा हूं, वह उसी क्लास के मॉड्यूल में है जिसे मैंने पहले ही इंस्टेंट कर दिया है, जिसकी मैं सिफारिश करूंगा।


2

कृपया इस परियोजना पर एक नज़र डालें जो मैंने शुरू की थी: प्रॉक्सी-ऑब्जेक्ट लिब

यह परिवाद जार को फ़ाइल सिस्टम या किसी अन्य स्थान से लोड करेगा। यह सुनिश्चित करने के लिए जार के लिए एक वर्ग लोडर समर्पित करेगा कि कोई पुस्तकालय संघर्ष न हो। उपयोगकर्ता लोड किए गए जार से किसी भी वस्तु को बनाने और उस पर किसी भी विधि को कॉल करने में सक्षम होंगे। इस परिवाद को जावा 7 को सपोर्ट करने वाले कोड बेस से जावा 8 में संकलित जार को लोड करने के लिए डिज़ाइन किया गया था।

एक वस्तु बनाने के लिए:

    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 कारखाने के तरीकों का समर्थन करता है, स्थिर कार्यों को बुला रहा है, और इंटरफ़ेस कार्यान्वयन को वापस बुलाता है। मैं रीडमी पृष्ठ पर अधिक उदाहरण पोस्ट कर रहा हूं।


2

यह एक देर से प्रतिक्रिया हो सकती है, मैं इसे इस तरह से कर सकता हूं (फास्टटिल-8.2.2.jar के लिए एक सरल उदाहरण) का उपयोग करके jhplot.Web वर्ग से 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

प्रलेखन के अनुसार, यह फ़ाइल "लीब / यूज़र" के अंदर डाउनलोड की जाएगी और फिर डायनामिकली लोड की जाएगी, इसलिए आप उसी प्रोग्राम में तुरंत इस जार फ़ाइल से कक्षाओं का उपयोग करना शुरू कर सकते हैं।


1

मुझे java 8 और java 9+ के लिए रनटाइम पर एक jar फ़ाइल लोड करने की आवश्यकता थी (ऊपर की टिप्पणियाँ इन दोनों संस्करणों के लिए काम नहीं करती हैं)। यहां इसे करने की विधि है (यदि यह संबंधित हो तो स्प्रिंग बूट 1.5.2 का उपयोग करके)।

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

-2

मुझे व्यक्तिगत रूप से लगता है कि java.util.ServiceLoader काम को बहुत अच्छी तरह से करता है। आप यहां एक उदाहरण प्राप्त कर सकते हैं


11
ServiceLoader जार फ़ाइलों को गतिशील रूप से रनटाइम पर नहीं जोड़ते हैं। जार फ़ाइलें पहले classpath में होनी चाहिए।
फरिश्ता angel
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.