Java 에서 커스텀 클래스로더 ( ClassLoader )를 작성하는 예
커스텀 클래스로더를 작성하는 방법을 알아보고 작성된 커스텀 클래스로더를 사용하여 클래스가 로드되는 정확한 시점을 확인해 보려고 한다. 여기에 제시된 내용은 커스텀 클래스로더가 현재 프로그램을 구성하는 클래스 중에서 특정 패키지에 포함된 클래스만 로드하고 나머지 클래스는 시스템 클래스로더에 의해 로드되도록 해본 것이다.
아래의 예제는 다음과 같이 커맨드라인 아규먼트를 사용하여 JVM 옵션을 설정하여 JVM이 클래스를 로드할 때 시스템 클래스로더가 아닌 커스텀 클래스로더를 사용하도록 지정하는 예이다.
Main 클래스를 실행할 때 지정할 JVM 옵션
java -Djava.system.class.loader=org.kdea.cloader.MyClassLoader Main
이클립스를 사용하여 JVM 옵션을 설정하는 방법은 이 페이지의 하단 그림을 참조하세요
MyClassLoader.java
package org.kdea.cloader; import java.io.*; // 클래스를 로드할 때 사용될 커스텀 클래스 로더 // 이 클래스는 JVM 에 의해서 인스턴스가 생성되고 loadClass()가 호출되도록 // 작성되었기 때문에 다음과 같이 커맨드라인에서 JVM에게 전달되는 옵션을 설정해야 한다 // >java -Djava.system.class.loader=org.kdea.cloader.MyClassLoader [실행할 클래스] public class MyClassLoader extends ClassLoader { // 인스턴스가 생성될 때 현재 클래스로더의 부모 클래스로더도 설정해 줘야 한다 public MyClassLoader(ClassLoader parent) { super(parent); } // 클래스가 포함된 패키지로부터 클래스를 로드하여 Class 인스턴스를 생성하고 리턴한다 private Class getClass(String name) throws ClassNotFoundException { String file = name.replace('.', File.separatorChar) + ".class"; byte[] buf = null; try { // 파일 시스템으로부터 .class 파일을 byte[]으로 로드한다 buf = loadBytes(file); // byte[]를 지정된 이름의 클래스에 해당하는 Class 인스턴스로 변환한다 Class cls = defineClass(name, buf, 0, buf.length); resolveClass(cls); // 링크한다. 이미 링크된 경우에는 아무 것도 하지 않는다 return cls; } catch (IOException e) { e.printStackTrace(); return null; } } // JVM은 클래스를 로드할 때 이 메소드를 호출하여 클래스를 로드한다. // 현재 프로그램(특정 패키지)에 포함된 클래스만 커스텀 클래스로더를 이용하여 로드하고 // 시스템 클래스는 시스템 클래스로더가 로드하도록 위임한다(Delegation) @Override public Class loadClass(String name) throws ClassNotFoundException { System.out.println("로드 시작("+name+")"); if (name.startsWith("org.kdea.cloader")) { System.out.println("\t\t\t--> by MyClassLoader"); return getClass(name); } return super.loadClass(name); } //파일 시스템에서 .class 파일을 읽어서 byte[] 형으로 리턴한다 private byte[] loadBytes(String name) throws IOException { InputStream stream = getClass().getClassLoader().getResourceAsStream(name); int size = stream.available(); byte buff[] = new byte[size]; DataInputStream in = new DataInputStream(stream); in.readFully(buff); in.close(); return buff; } }
Main.java
package org.kdea.cloader; public class Main { static{ System.out.println("* Main 로드 완료 *"); } public static void main(String[] args) { ClassA a = new ClassA(); a.createB(); ClassA a2 = new ClassA(); a2.createB(); } }
ClassA.java
package org.kdea.cloader; public class ClassA { static{ System.out.println("* ClassA 로드 완료 *"); } public ClassA(){ System.out.println("ClassA 인스턴스 생성"); } public void createB() { System.out.println("ClassA.createB() 실행됨"); ClassB b = new ClassB(); } }
ClassB.java
package org.kdea.cloader; public class ClassB { static{ System.out.println("* ClassB 로드 완료 *"); } public ClassB() { System.out.println("ClassB 인스턴스 생성"); } }
Main 클래스 실행을 위한 JVM 옵션
위의 프로그램을 실행할 때는 다음과 같이 JVM 옵션을 지정하고 Main 클래스를 실행해야 한다
java -Djava.system.class.loader=org.kdea.cloader.MyClassLoader Main
위의 프로그램 실행 결과화면
--> by MyClassLoader
로드 시작(java.lang.Object)
로드 시작(java.lang.String)
로드 시작(java.lang.System)
로드 시작(java.io.PrintStream)
* Main 로드 완료 *
로드 시작(org.kdea.cloader.ClassA)
--> by MyClassLoader
* ClassA 로드 완료 *
ClassA 인스턴스 생성
ClassA.createB() 실행됨
로드 시작(org.kdea.cloader.ClassB)
--> by MyClassLoader
* ClassB 로드 완료 *
ClassB 인스턴스 생성
ClassA 인스턴스 생성
ClassA.createB() 실행됨
ClassB 인스턴스 생성
이클립스에서 JVM 옵션을 설정하여 커스텀 클래스로더를 기본 클래스로더로 사용하도록 설정하는 방법
이클립스 프로젝트에서 실행할 Main 클래스를 마우스 우측으로 클릭 > Run As... > Run Configurations
위와 같이 설정하여 [Run] 버튼을 누르면 실행할 수 있고, 다음부터는 보통 실행할 때 사용하는 초록버튼이나, Run As.. > Java Application을 선택하면 JVM 옵션이 설정된 상태로 실행할 수 있다