Java- 올바른 동작 사용자 정의 클래스 로더

큰 젖소의 인공 지능 튜토리얼을 공유하십시오. 제로 기반! 이해하기 쉬운! 재미 있고 유머러스! 당신도 인공 지능 팀에 합류하기를 바랍니다! http://www.captainbed.net을 클릭 하십시오

Java에서 런타임에 클래스를로드하고 다시로드하는 것이 가능하지만 예상만큼 간단하지는 않습니다. 이 텍스트는 Java에서 클래스를로드하고 다시로드 할 수있는시기와 방법을 설명합니다.

ClassLoader

Java 애플리케이션의 모든 클래스는의 일부 하위 클래스를 사용하여로드됩니다  java.lang.ClassLoader. 따라서 클래스를 동적으로로드하는 작업도 java.lang.ClassLoader 하위 클래스를 사용하여 수행해야합니다  .

클래스가로드되면 참조하는 모든 클래스도로드됩니다. 이 클래스로드 패턴은 필요한 모든 클래스가로드 될 때까지 반복적으로 발생합니다. 이것은 응용 프로그램의 모든 클래스가 아닐 수 있습니다. 참조되지 않은 클래스는 참조 될 때까지로드되지 않습니다.

ClassLoader 계층

Java의 클래스 로더는 계층 구조로 구성됩니다. 새 표준 Java를 만들 때  ClassLoader 부모를 제공해야합니다  ClassLoader. A는 경우  ClassLoader 클래스를로드하도록 요청, 그것을로드 할 부모 클래스 로더를 요청합니다. 상위 클래스 로더가 클래스를 찾을 수없는 경우 하위 클래스 로더는 자체로드를 시도합니다.

클래스 로딩

지정된 클래스 로더가 클래스를로드 할 때 사용하는 단계는 다음과 같습니다.

  1. 클래스가 이미로드되었는지 확인하십시오.
  2. 로드되지 않은 경우 상위 클래스 로더에 클래스를로드하도록 요청하십시오.
  3. 상위 클래스 로더가 클래스를로드 할 수없는 경우이 클래스 로더에서로드를 시도하십시오.

클래스를 다시로드 할 수있는 클래스 로더를 구현할 때이 시퀀스에서 약간 벗어나야합니다. 다시로드 할 클래스는 상위 클래스 로더가로드하도록 요청해서는 안됩니다. 나중에 더 자세히 설명하겠습니다.

동적 클래스 로딩

클래스를 동적으로로드하는 것은 쉽습니다. 당신이해야 할 일은를 얻고  ClassLoader 그것의 loadClass() 메서드를 호출하는  것입니다. 다음은 그 예입니다.

public class MainClass {

  public static void main(String[] args){

    ClassLoader classLoader = MainClass.class.getClassLoader();

    try {
        Class myClass = classLoader.loadClass("com.xxx.myClass");
        System.out.println("myClass.getName() = " + myClass.getName());
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

}

동적 클래스 다시로드

동적 클래스 다시로드는 조금 더 어렵습니다. Java의 내장 클래스 로더는 항상 클래스를로드하기 전에 이미로드되었는지 확인합니다. 따라서 Java의 내장 클래스 로더를 사용하여 클래스를 다시로드 할 수 없습니다. 클래스를 다시로드하려면 고유 한 ClassLoader 하위 클래스 를 구현해야합니다  .

사용자 지정 하위 클래스가 있어도  ClassLoader 도전이 있습니다. 로드 된 모든 클래스는 링크되어야합니다. 이것은 ClassLoader.resolve() 방법을 사용하여 수행됩니다  . 이 메서드는 최종 메서드이므로 ClassLoader 하위 클래스 에서 재정의 할 수 없습니다  . 이  resolve() 메서드는 주어진 ClassLoader 인스턴스가 동일한 클래스를 두 번 연결하는 것을 허용하지 않습니다  . 따라서 클래스를 다시로드 할 때마다 ClassLoader 하위 클래스 의 새 인스턴스를 사용해야합니다  . 이것은 불가능하지는 않지만 클래스 리로딩을 위해 디자인 할 때 알아야합니다.

클래스 다시로드를위한 코드 디자인

앞서 언급했듯이 ClassLoader 이미 해당 클래스를 한 번로드 사용하여 클래스를 다시로드 할 수 없습니다  . 따라서 다른 ClassLoader 인스턴스를 사용하여 클래스를 다시로드해야  합니다. 그러나 이것은 몇 가지 새로운 도전을 제기합니다.

Java 애플리케이션에로드 된 모든 클래스는 정규화 된 이름 (패키지 이름 + 클래스 이름)과 ClassLoader 이를로드 인스턴스 로 식별됩니다  . 즉,  MyObject 클래스 로더 A에 의해로드 된 클래스는 MyObject 클래스 로더 B로로드 된 클래스와 동일한 클래스가 아닙니다  . 다음 코드를보십시오.

MyObject object = (MyObject) myClassReloadingFactory.newInstance("com.xxx.MyObject");

MyObject 클래스가 object 변수 의 유형으로 코드에서 어떻게 참조  되는지 확인하십시오  . 이로 인해이  MyObject 코드가 상주하는 클래스를로드 한 동일한 클래스 로더에 의해 클래스가로드됩니다.

는 IF  myClassReloadingFactory 객체 공장을 다시로드  MyObject 에서 클래스보다 위의 코드 상주하는 다른 클래스 로더를 사용하는 클래스, 당신은 리로디드의 인스턴스 캐스트 할 수없는  MyObject 받는 클래스  MyObject 의 유형  object 변수입니다. 두  MyObject 클래스가 서로 다른 클래스 로더를 사용하여로드되었으므로 완전히 정규화 된 클래스 이름이 동일하더라도은 서로 다른 클래스로 간주됩니다. 한 클래스의 객체를 다른 클래스의 참조로 캐스팅하려고하면  ClassCastException.

이 제한을 해결할 수 있지만 다음 두 가지 방법 중 하나로 코드를 변경해야합니다.

  1. 인터페이스를 변수 유형으로 사용하고 구현 클래스를 다시로드하십시오.
  2. 변수 유형으로 수퍼 클래스를 사용하고 서브 클래스를 다시로드하십시오.

다음은 두 가지 핵심 대응 코드 예제입니다.

MyObjectInterface object = (MyObjectInterface) myClassReloadingFactory.newInstance("com.xxx.MyObject");
MyObjectSuperclass object = (MyObjectSuperclass) myClassReloadingFactory.newInstance("com.xxx.MyObject");

구현 클래스 또는 서브 클래스가 다시로드 될 때 변수 유형 인 인터페이스 또는 수퍼 클래스가 다시로드되지 않으면이 두 메소드 중 하나가 작동합니다.

이 작업을 수행하려면 물론 부모가 인터페이스 또는 수퍼 클래스를로드 할 수 있도록 클래스 로더를 구현해야합니다. 클래스 로더가 클래스를로드하라는 요청을  MyObject 받으면 MyObjectInterface 클래스 MyObjectSuperclass 내에서 참조되기 때문에 클래스  또는 클래스  를로드하라는 메시지가 표시됩니다  MyObject . 클래스 로더는 해당 클래스의로드를 인터페이스 또는 수퍼 클래스 유형 변수를 포함하는 클래스를로드 한 동일한 클래스 로더에 위임해야합니다.

ClassLoader로드 / 다시로드 예제

위의 텍스트는 많은 이야기를 담고 있습니다. 간단한 예를 살펴 보겠습니다. 다음은 간단한 ClassLoader 하위 클래스 의 예입니다  . 다시로드 할 수 있도록 의도 된 하나의 클래스를 제외하고 클래스로드를 부모에게 위임하는 방법에 유의하십시오. 이 클래스의로드가 상위 클래스 로더에 위임되면 나중에 다시로드 할 수 없습니다. 클래스는 동일한 ClassLoader 인스턴스에 의해 한 번만로드 될 수 있습니다  .

앞서 말했듯이 이것은 ClassLoader의 행동 의 기본 사항을 보여주는 예일뿐입니다  . 자체 클래스 로더를위한 프로덕션 준비 템플릿이 아닙니다. 자신의 클래스 로더는 단일 클래스로 제한되지 않고 다시로드해야하는 클래스 모음이어야합니다. 또한 클래스 경로도 하드 코딩해서는 안됩니다.

public class MyClassLoader extends ClassLoader{

    public MyClassLoader(ClassLoader parent) {
        super(parent);
    }

    public Class loadClass(String name) throws ClassNotFoundException {
        if(!"reflection.MyObject".equals(name)){
                return super.loadClass(name);
        }
        try {
            String url = "file:C:/yourtargetpath/classes/reflection/MyObject.class";
            URL myUrl = new URL(url);
            URLConnection connection = myUrl.openConnection();
            InputStream input = connection.getInputStream();
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            int data = input.read();
            while(data != -1){
                buffer.write(data);
                data = input.read();
            }
            input.close();
            byte[] classData = buffer.toByteArray();
            return defineClass("reflection.MyObject",classData, 0, classData.length);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}

다음은  MyClassLoader.

public static void main(String[] args) throws
    ClassNotFoundException,
    IllegalAccessException,
    InstantiationException {
    ClassLoader parentClassLoader = MyClassLoader.class.getClassLoader();
    MyClassLoader classLoader = new MyClassLoader(parentClassLoader);
    Class myObjectClass = classLoader.loadClass("reflection.MyObject");
    MyObjectInterface object1 = (MyObjectInterface) myObjectClass.newInstance();
    MyObjectSuperClass object2 = (MyObjectSuperClass) myObjectClass.newInstance();

    // Create new class loader so classes can be reloaded
    classLoader = new MyClassLoader(parentClassLoader);
    myObjectClass = classLoader.loadClass("reflection.MyObject");
    object1 = (MyObjectInterface) myObjectClass.newInstance();
    object2 = (MyObjectSuperClass) myObjectClass.newInstance();
}

다음은  reflection.MyObject 클래스 로더를 사용하여로드 된 클래스입니다. 어떻게 수퍼 클래스를 확장하고 인터페이스를 구현하는지 주목하십시오. 이것은 단지 예를위한 것입니다. 자신의 코드에서는 확장 또는 구현 중 하나만 있으면됩니다.

public class MyObject extends MyObjectSuperClass implements MyObjectInterface {
    // ... body of class ...
    // override superclass methods
    // or implement interface methods
}

 

추천

출처blog.csdn.net/chimomo/article/details/111831990