[java] 런타임에 JAR 파일을 동적으로로드하는 방법은 무엇입니까?

Java에서 왜 이렇게하기가 어렵습니까? 모든 종류의 모듈 시스템을 원한다면 JAR 파일을 동적으로로드 할 수 있어야합니다. 나는 당신 자신의 ClassLoader것을 작성하여 그것을 수행하는 방법이 있다고 들었지만, 그것은 적어도 내 생각으로는 JAR 파일을 가진 메소드를 인수로 호출하는 것만 큼 쉬워야 할 일이 많이 있습니다.

이를 수행하는 간단한 코드에 대한 제안 사항이 있습니까?



답변

어려운 이유는 보안입니다. 클래스 로더는 변경 불가능합니다. 런타임에 클래스를 고의로 추가 할 수 없어야합니다. 실제로 시스템 클래스 로더와 함께 작동하는 것에 매우 놀랐습니다. 자식 클래스 로더를 만드는 방법은 다음과 같습니다.

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

고통 스럽지만 거기에 있습니다.


답변

다음 솔루션은 리플렉션을 사용하여 캡슐화를 우회하기 때문에 해킹 적이지만 완벽하게 작동합니다.

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


답변

예를 들어 Eclipse 플랫폼 에서 구현 된 OSGi를 살펴보아야 합니다. 정확히 그렇습니다. 효과적으로 JAR 파일 인 소위 번들을 설치, 설치 제거, 시작 및 중지 할 수 있습니다. 그러나 런타임시 JAR 파일에서 동적으로 발견 될 수있는 서비스와 같은 서비스를 제공하므로 약간 더 많은 작업을 수행합니다.

또는 Java 모듈 시스템 의 사양을 참조하십시오 .


답변

방법에 대한 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();
    }
}


답변

여기에 나열된 대부분의 솔루션은 구성하기 어려운 에이전트 (JDK 9 이전)이거나 더 이상 작동하지 않습니다 (JDK 9 이후) . 명확하게 문서화 된 방법을 언급하지 않은 사람은 정말 충격적입니다 .

커스텀 시스템 클래스 로더를 만들면 원하는대로 자유롭게 할 수 있습니다. 반영이 필요하지 않으며 모든 클래스가 동일한 클래스 로더를 공유합니다.

JVM을 시작할 때이 플래그를 추가하십시오.

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

클래스 로더에는 클래스 로더를 허용하는 생성자가 있어야하며, 클래스 로더를 상위로 설정해야합니다. JVM 시작시 생성자가 호출되고 실제 시스템 클래스 로더가 전달되고 기본 클래스는 사용자 정의 로더에 의해로드됩니다.

항아리를 추가하려면 전화 ClassLoader.getSystemClassLoader()하여 수업에 캐스트하십시오.

신중하게 제작 된 클래스 로더에 대해서는 이 구현 을 확인하십시오 . add()방법을 공개로 변경할 수 있습니다 .


답변

함께 자바 9 과 답변 URLClassLoader현재 오류가 같이 제공 :

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

사용 된 클래스 로더가 변경 되었기 때문입니다. 대신 시스템 클래스 로더에 추가하기 위해 에이전트를 통해 인스 트루먼 테이션 API를 사용할 수 있습니다 .

에이전트 클래스를 작성하십시오.

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