클래스 로더의 loadClass()와 findClass()
참조: http://onjava.com/pub/a/onjava/2005/01/26/classloading.html?page=1
클래스와 데이타
모든 클래스는 first-class 자바 객체를 통해서 자신의 코드를 참조할 수 있다. 컴파일 할 때 컴파일러가 public static final class 라는 이름의 java.lang.Class 타입 필드를 추가해준다.
일단 한 번 클래스가 JVM으로 로딩되면, 같은 클래스는 다시 로딩 되지 않는다. 여기서 "같은 클래스"란 해당 클래스를 로딩한 클래스로더와 해당 클래스의 풀네임으로 구성된 식별자로 구분한다.
예를 들어, WhiteshipClassLoader가 로딩한 springsprout 패키지의 Whiteship클래스는 (WhiteshipClassLoader, springsprout, Whiteship) 식별자를 가지게되고, SpringSproutClassLoader가 로딩한 springsprout 패키지의 Whiteship클래스는 (SpringSproutClassLoader, springsprout, Whiteship) 식별자를 가지기 때문에 별개의 클래스로 인식된다는 것이다.
클래스 로더
모든 클래스는 java,lang.ClassLoader의 인스턴스가 로딩한다.
부트스트랩 클래스 로더: java.lang.Object 같은 핵심 자바 클래스를 로딩한다. 런타임 클래스들은 JRE\lib\rt.jar에 들어있다. 접근이 안 됨. sout(String.class.getClassLoader())를 호출하면 null이 나온다.
확장 클래스 로더: 핵심 자바 런타임 코드 이외의 확장 라이브러리를 java.ext.dirs 속성의 경로에 넣어둘 수 있다. ExtClassLoader는 java.ext.dirs에 위치한 모든 .jar 파일을 로딩한다.
애플리케이션 클래스 로더: AppClassLoader는 java.class.path 시스템 속성에 해당하는 경로의 모든 클래스를 로딩한다.
컨텍스트 클래스 로더: java.lang.Thread는 getContextClassLoader()라는 메서드를 가지고 있다. 이 메서드는 특정 쓰레드에서 사용중인 클래스로더를 반환해준다. 컨텍스트 클래스 로더는 쓰레드를 만들 때 사용한 클래스 로더이며, 기본적으로 parent 쓰레드의 클래스로더가 할당 된다. setContextClassLoader로 변경 가능하다.
클래스로더 동작 방식
protected synchronized Class<?> loadClass
(String name, boolean resolve)
throws ClassNotFoundException{
// First check if the class is already loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// If still not found, then invoke
// findClass to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
java.lang.ClassLoader의 loadClass() 메서드 코드이며 이 클래스를 확장하는 클래스는 findClass()를 재정의하는 것이 기본적이다. java.lang.ClassLoader의 findClass()는 ClassNotFoundException을 던진다.
findClass()를 구현할 떄는 특정 소스에서 바이트 코드를 가져오거나, 소스를 바이트 코드로 생성하거나, BCEL(Byte Code Engineering Library)를 사용할 수도 있다. 암튼.. 그렇게 해서 바이트 코드를 가져온 다음 defineClass() 메서드를 호출해서 byte 배열을 Class 인스턴스로 변환해야 한다.
defineClass() 메서드 호출이 중요한데, 이 것을 호출한 클래스 로더가 바로 해당 클래스의 defining 클래스 로더가 되는 것이며 이 defining 클래스로더가 다를 경우 다른 클래스로 인식하게 된다. Whiteship.class.getClassLoader()를 실행했을 때 반환되는 클래스 로더가 바로 defining 클래스 로더다.
findClass 구현 예제 코드
public Class findClass(String name)throws
ClassNotFoundException{
byte[] classBytes = findClassBytes(name);
if (classBytes==null){
throw new ClassNotFoundException();
}
else{
return defineClass(name, classBytes,
0, classBytes.length);
}
}