রানটাইমে জেআর ফাইলগুলি কীভাবে গতিশীলভাবে লোড করবেন?


308

জাভাতে এটি করা এত কঠিন কেন? আপনি যদি কোনও ধরণের মডিউল সিস্টেম রাখতে চান তবে আপনাকে জেআর ফাইলগুলি গতিশীলভাবে লোড করতে সক্ষম হতে হবে। আমাকে নিজের লেখার দ্বারা এটি করার একটি উপায় আছে বলেছি ClassLoader, তবে এটি এমন কিছু কাজের জন্য অনেক কাজ যা (অন্তত আমার মনে) জেআর ফাইলের সাথে কোনও পদ্ধতি কল করার মতো তার যুক্তি হিসাবে কল্পনা করা উচিত।

সাধারণ কোডের জন্য কোনও পরামর্শ যা এটি করে?


4
আমি একই কাজটি করতে চাই তবে আরও বেশি স্যান্ডবক্সযুক্ত পরিবেশে বোঝা জারটি চালাতে চাই (সুরক্ষার কারণে স্পষ্টতই)। উদাহরণস্বরূপ, আমি সমস্ত নেটওয়ার্ক এবং ফাইল সিস্টেম অ্যাক্সেস ব্লক করতে চাই।
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
আমার আইডিইতে চলার সময় এই পদ্ধতিটি দুর্দান্ত কাজ করে, তবে আমি যখন আমার জেআর তৈরি করি তখন আমি Class.forName () কল করার সময় একটি ClassNotFoundException পাই।
ডারিকc

29
এই পদ্ধতির ব্যবহার করে আপনাকে নিশ্চিত করতে হবে যে আপনি প্রতিটি শ্রেণীর জন্য এই লোড পদ্ধতিটিকে একাধিকবার কল করবেন না। যেহেতু আপনি প্রতিটি লোড অপারেশনের জন্য একটি নতুন শ্রেণি লোডার তৈরি করছেন, ক্লাসটি ইতিমধ্যে আগে লোড হয়েছিল কিনা তা জানতে পারে না। এর খারাপ পরিণতি হতে পারে। উদাহরণস্বরূপ সিঙ্গলেটগুলি কাজ করছে না কারণ শ্রেণিটি বেশ কয়েকবার লোড হয়েছিল এবং তাই স্থির ক্ষেত্রগুলি বেশ কয়েকবার বিদ্যমান।
এডওয়ার্ড ভার্চ

8
কাজ করে। এমনকি জারের অভ্যন্তরে অন্যান্য শ্রেণীর সাথে নির্ভরতা রয়েছে। প্রথম লাইন অসম্পূর্ণ ছিল। আমি URLClassLoader child = new URLClassLoader (new URL[] {new URL("file://./my.jar")}, Main.class.getClassLoader());ধরে নিলাম যে জার ফাইলটি বলা হয় my.jarএবং একই ডিরেক্টরিতে অবস্থিত।
চোয়াল

4
ইউআরএল ইউআরএল = file.toURI ()। To URL () এ ভুলবেন না;
জনস্টোশ

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 ব্যতীত অন্য কিছু হয়ে যায় তবে এতটা ভাল কাজ করে না ...
গাস

6
জাভা 9+ সতর্ক করে যে URLClassLoader.class.getDeclaredMethod("addURL", URL.class)প্রতিবিম্বের অবৈধ ব্যবহার, এবং ভবিষ্যতে ব্যর্থ হবে।
চার্লিওয়েড

1
জাভা 9+ এর সাথে কাজ করতে এই কোডটি আপডেট করার কোনও ধারণা?
FiReTiTi

1
@ ফাইরেটিটি হ্যাঁ !!
মর্দচাই

51

আপনার ওএসজি- তে একবার নজর দেওয়া উচিত , যেমন- গ্রহন প্ল্যাটফর্মে প্রয়োগ করা । এটা ঠিক যে করে। আপনি তথাকথিত বান্ডিলগুলি ইনস্টল, আনইনস্টল, শুরু এবং বন্ধ করতে পারেন, এটি কার্যকরভাবে জেআর ফাইলগুলি। তবে এটি আরও কিছুটা করে, কারণ এটি যেমন পরিষেবাগুলি সরবরাহ করে যা রানটাইম সময়ে জেআর ফাইলগুলিতে গতিশীলভাবে আবিষ্কার করা যায়।

অথবা জাভা মডিউল সিস্টেমের স্পেসিফিকেশন দেখুন ।


41

কেমন হয় 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");

9
এটি বগি এবং কিছু গুরুত্বপূর্ণ বাস্তবায়ন যেমন ফাইন্ড রিসোর্সেস (...) হারিয়েছে। কিছু জিনিস কেন কাজ করে না তা তদন্ত করার জন্য দুর্দান্ত রাত কাটাতে প্রস্তুত থাকুন =)
সের্গেই কার্পুশিন

আমি এখনও অবাক হয়েছি @ সের্গেই কার্পুশিনের দাবি এখনও বিদ্যমান কারণ প্রকল্পটি সময়ের সাথে সাথে দ্বিতীয় বড় সংস্করণে আপডেট করা হয়েছে। অভিজ্ঞতা শুনতে চাই।
এরদিন ইরে

2
@ এরদিনিনে, এটি খুব ভাল একটি প্রশ্ন যা আমি নিজেকেও জিজ্ঞাসা করি যেহেতু আমরা ওপেনজেডিকে পরিবর্তন করতে "বাধ্য" হয়েছিলাম। আমি এখনও জাভা প্রকল্পগুলিতে কাজ করি, এবং আমার কাছে কোনও প্রমাণ নেই যে ওপেন জেডিকে এই দিনগুলিতে আপনাকে ব্যর্থ করবে (যদিও আমি তখনই ইস্যু করেছি)। আমি অনুমান করি যে আমি অন্য কোনও কিছুতে ঝাঁপিয়ে পড়া না হওয়া পর্যন্ত আমি আমার দাবি প্রত্যাহার করেছি।
সের্গে কার্পুশিন

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
উম। প্রযুক্তিগতভাবে, মূল বিষয়বস্তু সিসি লাইসেন্সযুক্ত, তবে আপনি যদি এখানে কপিরাইটযুক্ত সামগ্রী পোস্ট করেন তবে বিষয়বস্তু কপিরাইটযুক্ত তা সরিয়ে দেয় না। আমি যদি মিকি মাউসের একটি ছবি পোস্ট করি তবে এটি সিসি-লাইসেন্সযুক্ত করে না। সুতরাং আমি আবার কপিরাইট বিবৃতি যোগ করছি।
জেসন এস

19

যদিও এখানে তালিকাভুক্ত বেশিরভাগ সমাধান হ্যাক (প্রি জেডি কে 9 পূর্ব) কনফিগার করা কঠিন (এজেন্ট) অথবা কেবলমাত্র আর কাজ করবেন না (জেডিকে 9 পোস্ট করুন) আমি এটি সত্যিই মর্মান্তিক বলে মনে করি যে কেউ পরিষ্কারভাবে নথিভুক্ত পদ্ধতির উল্লেখ করেনি ।

আপনি একটি কাস্টম সিস্টেম শ্রেণি লোডার তৈরি করতে পারেন এবং তারপরে আপনি যা খুশি তা করতে পারেন। কোনও প্রতিবিম্বের প্রয়োজন নেই এবং সমস্ত শ্রেণি একই ক্লাসলোডার ভাগ করে।

JVM শুরু করার সময় এই পতাকাটি যুক্ত করুন:

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

ক্লাস লোডারের অবশ্যই কোনও শ্রেণীবদ্ধার গ্রহণকারী একটি কনস্ট্রাক্টর থাকতে হবে, যা অবশ্যই এটির পিতামাতার হিসাবে সেট করা উচিত। কনস্ট্রাক্টরকে জেভিএম স্টার্টআপে ডাকা হবে এবং আসল সিস্টেমের ক্লাসলোডার পাস হবে, কাস্টম লোডার দ্বারা প্রধান শ্রেণিটি লোড হবে।

জারে যুক্ত করতে কেবল এটি কল করুন ClassLoader.getSystemClassLoader()এবং এটি আপনার শ্রেণিতে কাস্ট করুন।

পরীক্ষা করে দেখুন এই বাস্তবায়ন একটি সাবধানে crafted classloader জন্য। দয়া করে নোট করুন, আপনি add()পদ্ধতিটি জনসাধারণে পরিবর্তন করতে পারেন ।


আপনাকে ধন্যবাদ - এটি সত্যিই সহায়ক! জেডিকে 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

এজেন্ট চালান:

এটি চলমান জেভিএম -এ এজেন্ট যুক্ত করতে বাইট-বন্ধু-এজেন্ট লাইব্রেরি ব্যবহার করে :

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.arFileClassLoader যা আমি খুঁজে পেয়েছি তার মধ্যে সেরাটি এক্সবিয়ান প্রকল্পের অংশ ।

একটি বিশেষ ডিরেক্টরিতে সমস্ত lib ফাইল থেকে একটি শ্রেণি লোডার তৈরি করতে, আমি অতীতে একটি ছোট পদ্ধতি ব্যবহার করেছি's

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

আপনি যদি অ্যান্ড্রয়েডে কাজ করছেন তবে নিম্নলিখিত কোডটি কাজ করে:

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

মনে রাখবেন যে এটি নির্দিষ্ট জেভিএমের অভ্যন্তরীণ বাস্তবায়নের জ্ঞানের উপর নির্ভর করে, সুতরাং এটি আদর্শ নয় এবং এটি সর্বজনীন সমাধান নয়। আপনি যদি জানেন যে আপনি স্ট্যান্ডার্ড ওপেনজেডিকে বা ওরাকল জেভিএম ব্যবহার করছেন তবে এটি একটি দ্রুত এবং সহজ কাজ easy এটি ভবিষ্যতে কোনও সময় ভাঙ্গতে পারে যখন নতুন জেভিএম সংস্করণ প্রকাশিত হবে, তাই আপনার এটি মনে রাখা উচিত।


জাভা 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
রিচার্ড Marak

অ্যাপ্লিকেশন সার্ভার পরিবেশে জাভা 8 ইই সঙ্গে কাজ করে।
জানুয়ারি

4

জোডোনেল প্রস্তাবিত সমাধানটি ভাল তবে কিছুটা বাড়ানো উচিত। আমি এই পোস্টটি সফলভাবে আমার অ্যাপ্লিকেশনটি বিকাশের জন্য ব্যবহার করেছি।

বর্তমান থ্রেড বরাদ্দ করুন

প্রথমত আমাদের যুক্ত করতে হবে

Thread.currentThread().setContextClassLoader(classLoader);

অথবা আপনি জারের মধ্যে সঞ্চিত সংস্থান (যেমন স্প্রিং / কনটেক্সট.এক্সএমএল) লোড করতে সক্ষম হবেন না।

অন্তর্ভুক্ত করবেন না

আপনার বয়াম পিতামাত্ত শ্রেণীর লোডারে প্রবেশ করুন বা আপনি কী বোঝাচ্ছেন তা বুঝতে সক্ষম হবেন না।

URLClassLoader ব্যবহার করে একটি জার পুনরায় লোড করতেও সমস্যা দেখুন

যাইহোক, ওএসজিআই কাঠামো সর্বোত্তম উপায় থেকে যায়।


2
আপনার উত্তরটি কিছুটা বিভ্রান্তিকর বলে মনে হচ্ছে, এবং কেবলমাত্র কোনও সাধারণ উন্নতি হলে জোডোনেলের উত্তরের মন্তব্যে এটি আরও উপযুক্ত।
জিরো

4

আল্লায়েনের হ্যাকিশ সমাধানের আর একটি সংস্করণ, এটি জেডিকে 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});

জেডিকে ১১-তে এটি কিছু অবমূল্যায়নের সতর্কতা দেয় তবে যারা জেডিকে ১১-এ আল্লায়েন দ্রবণটি ব্যবহার করেন তাদের একটি অস্থায়ী সমাধান হিসাবে কাজ করে।


আমি কি জার সরাতে পারি?
ব্যবহারকারী7294900

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জেডিকে 9+ তে সমর্থিত এবং এজেন্টটিকে লোড করার জন্য এটি কমান্ড লাইনে সুস্পষ্টভাবে ব্যাখ্যা না করেই দায়বদ্ধ:

 java -jar <your jar>

JDK 6+ এ যেভাবে কাজ করে তা -javaagentআর্গুমেন্টকে সংজ্ঞায়িত করছে :

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

রানটাইমে নতুন জার যুক্ত করা হচ্ছে

তারপরে আপনি নিম্নলিখিত কমান্ডটি ব্যবহার করে প্রয়োজনীয় হিসাবে পাত্র যুক্ত করতে পারেন:

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

ডকুমেন্টেশনে এটি ব্যবহার করে আমি কোনও সমস্যা পাইনি।


কোনও কারণে এই সমাধানটি ব্যবহার করার সময় আমি "থ্রেডে ব্যতিক্রম" মূল "java.lang.ClassNotFoundException: এজেন্ট.এজেন্ট" পাই। আমি "এজেন্ট" শ্রেণিকে আমার মূল "যুদ্ধ" অ্যাপ্লিকেশনটিতে প্যাকেজ করেছি, সুতরাং আমি নিশ্চিত যে এটি সেখানে আছে
সের্গেই লেদভানভ

3

ভবিষ্যতে যদি কেউ এটি অনুসন্ধান করে তবে এইভাবে ওপেনজেডকে ১৩.০.২ এর সাথে আমার পক্ষে কাজ করে।

আমার অনেক ক্লাস রয়েছে যা রানটাইমের সময়ে আমার গতিশীলভাবে ইনস্ট্যান্ট করতে হবে, প্রতিটি সম্ভাব্যভাবে আলাদা ক্লাসপথ রয়েছে।

এই কোডটিতে, আমার কাছে ইতিমধ্যে প্যাক নামে একটি বস্তু রয়েছে, যা ক্লাসটি লোড করার চেষ্টা করছি সে সম্পর্কে কিছু মেটাডেটা ধারণ করে। GetObjectFile () পদ্ধতিটি ক্লাসের জন্য ক্লাস ফাইলের অবস্থান প্রদান করে। GetObjectRootPath () পদ্ধতিটি ক্লাস ফাইলগুলি সহ বিন / ডিরেক্টরিতে ফিরে আসে যেখানে আমি ইনস্ট্যান্ট করার চেষ্টা করছি বর্গ অন্তর্ভুক্ত। GetLibPath () পদ্ধতিটি ক্লাসটির একটি অংশ মডিউলটির জন্য ক্লাসপাথ গঠন করে জার ফাইলযুক্ত ডিরেক্টরিতে ফিরে আসে returns

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;

আমি মাভেন নির্ভরতা ব্যবহার করছিলাম: org.xeustechnologies: jcl-core: 2.8 এর আগে এটি করার জন্য, তবে জেডিকে ১.৮ পেরিয়ে যাওয়ার পরে, এটি কখনও কখনও হিমায়িত হয়ে যায় এবং রেফারেন্স :: ওয়েটফরফেরেন্সপেন্ডিংলিস্টে ("রেফারেন্সের অপেক্ষায়" আটকে থাকে না)

আমি ক্লাস লোডারগুলির একটি মানচিত্রও রাখছি যাতে আমি যে ক্লাসটি ইনস্ট্যান্ট করার চেষ্টা করছি সেগুলি যদি আমি ইতিমধ্যে ইনস্ট্যান্ট করে রেখেছি এমন ক্লাসের মতো একই মডিউলে থাকে তবে আমি তাদের সুপারিশ করব they


2

দয়া করে এই প্রকল্পটি দেখুন যা আমি শুরু করেছি: প্রক্সি-অবজেক্ট লাইব

এই lib ফাইল সিস্টেম বা অন্য কোনও স্থান থেকে জার লোড করবে। এটি কোনও শ্রেণিবদ্ধ লোডারকে জারের জন্য উত্সর্গ করবে যাতে কোনও গ্রন্থাগারের বিরোধ নেই make ব্যবহারকারীরা বোঝা জার থেকে যে কোনও অবজেক্ট তৈরি করতে এবং এতে যে কোনও পদ্ধতিতে কল করতে সক্ষম হবেন। এই লিবটি জাভা 8 সমর্থন করে এমন কোড বেস থেকে জাভা 8 তে সংযুক্ত জারগুলি লোড করার জন্য তৈরি করা হয়েছিল যা জাভা 7 সমর্থন করে।

একটি অবজেক্ট তৈরি করতে:

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

অবজেক্টবিল্ডার ফ্যাক্টরি পদ্ধতিগুলি সমর্থন করে, স্থির ফাংশনগুলি কল করে এবং ইন্টারফেস বাস্তবায়নগুলিকে কল ব্যাক করে। আমি আরও উদাহরণগুলি রিডমি পৃষ্ঠায় পোস্ট করব।


2

এটি দেরিতে প্রতিক্রিয়া হতে পারে, আমি এটি হিসাবে এটি করতে পারি (ফাস্টুটিল -৮.২.২.জারের একটি সাধারণ উদাহরণ) jhplot ব্যবহার করে M ডেটা মেল্ট থেকে ওয়েব ক্লাস ( 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

ডকুমেন্টেশন অনুসারে, এই ফাইলটি "lib / user" এর মধ্যে ডাউনলোড হবে এবং তারপরে গতিশীলভাবে লোড হবে, যাতে আপনি একই প্রোগ্রামে এই জার ফাইলটি থেকে তাত্ক্ষণিক ক্লাস ব্যবহার শুরু করতে পারেন।


1

আমার জাভা 8 এবং জাভা 9+ উভয়ের জন্য রানটাইমের সময় একটি জার ফাইল লোড করা দরকার (উপরের মন্তব্যগুলি এই সংস্করণের উভয়টির জন্যই কাজ করে না)। এটি করার পদ্ধতিটি এখানে (স্প্রিং বুট ব্যবহার করে 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.S सर्विसLoader কাজটি বেশ ভালভাবে করে। আপনি এখানে একটি উদাহরণ পেতে পারেন ।


11
সার্ভিস লোডার রানটাইম সময়ে জার ফাইলগুলি গতিশীলভাবে যুক্ত করবেন না। জার ফাইলগুলি অবশ্যই ক্লাসপথে থাকতে হবে।
এঞ্জেলসারভেরা
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.