주요글: 도커 시작하기
반응형
자바2에서 제공하는 기본적인 클래스로더에 대해서 알아본다.

클래스의 이름 공간

이전 Article에서 설명하지 않은 것이 있어서 JDK에서 기본적으로 제공하는 클래스로더에 대해 알아보기 전에 알아볼 것이 있다. 바로 클래스의 이름 공간에 대한 것이다. 우선 클래스의 이름에 대해서 알아보자. 클래스의 완전한 이름은 패키지와 클래스 이름의 두 부분으로 구성된다. 예를 들어, Vector 클래스의 경우 패키지 이름 java.util과 클래스 이름 Vector가 합쳐져서 완전한 클래스 이름인 java.util.Vector로 구성된다. 이렇게 완전한 이름은 같은 이름을 가진 클래스를 구분할 때 사용한다. 예를 들면, Date 클래스의 경우 java.util과 java.sql의 두 패키지에 속해 있으며, 이 두 클래스를 동시에 사용하고자 할 경우에는 반드시 완전한 클래스 이름(즉, java.util.Date와 java.sql.Date)을 사용하여 구분해야 한다.

클래스의 이름과 관련해서 알아야 할 점이 또 하나 있다. 바로 클래스를 로딩한 클래스로더와 관련한 것이다. 하나의 JVM에서 여러개의 클래스로더를 사용하여 클래스를 로딩할 수 있다. 여기서 생각할 수 있는 점이 같은 클래스를 서로 다른 클래스로더가 로딩할 수 있다는 점이다. 이러한 예로 애플릿을 예로 들 수 있다. 현재 많이 사용하고 있는 웹 브라우저의 경우 각각의 애플릿마다 하나의 클래스로더를 사용하고 있다. 바꿔 말하면, A라는 애플릿과 관련된 클래스 집합을 A'이라고 하고, B라는 애플릿과 관련된 클래스 집합을 B'이라고 할 경우, A'과 B'을 로딩하는 클래스로더가 다르다는 것이다. 만약 A'과 B'에 모두 x.y.Z라는 클래스가 있는 데, A'에 있는 것과 B'에 있는 것이 서로 정의가 다르다고 해 보자. 이 경우, A' 애플릿을 로딩한 이후에 B' 애플릿을 로딩했다면, x.y.Z 클래스는 어떻게 처리될까? 클래스 이름 충돌 문제가 발생하지 않을까? 정답은 발생하지 않는다이다. 이유는 별도의 클래스로더가 x.y.Z를 로딩하기 때문이다. 실제로 자바에서의 클래스 이름은 클래스 이름 공간(name space)라는 개념으로 처리되면, 이 클래스 이름 공간은 다음의 형태로 구성된다.

(패키지, 이름, 클래스로더)

따라서, 제 아무리 같은 클래스라도 클래스로더가 다르면, JVM 내에서 다른 클래스로 처리된다. 필자 역시 예전에 이 부분을 미처 알지 못해서 애플릿 프로그래밍을 할 때에 실수를 한 적이 있다. 애플릿을 사용하여 인트라넷 환경의 엔터프라이즈 시스템을 개발하는 경우, 이 점에 특히 주의해야 할 것이다. 물론, 웹 브라우저의 버전이나 종류에 따라 클래스 로딩이 다르게 동작할 수 있다. 따라서, 하나의 어플리케이션에서 여러 개의 애플릿을 사용하는 경우에는 반드시 대상이 되는 웹 브라우저에서 철저하게 테스트해야 한다.

클래스로더

자바 2의 표준 런타임 라이브러리(jre/lib/rt.jar)는 기본적으로 몇 개의 클래스로더를 제공하고 있다. 이 클래스로더 중에서는 공개적으로 사용할 수 있는 것들이 있고, 공개되지 않고 런타임 라이브러리의 내부적으로만 사용되는 것들도 있다. 이번 Article에서는 이렇게 자바에서 기본적으로 제공하는 클래스로더에 대해서 알아보도록 하자.

java.net.URLClassLoader

URLClassLoader는 지정한 URL로부터 클래스를 로딩할 수 있도록 해 준다. 이 말은 올바른 URL을 사용하는 한, 파일 시스템, HTTP, FTP를 비롯한 모든 형태의 URL로부터 클래스를 로딩할 수 있다는 것을 의미한다. 여기서는 파일 시스템, HTTP, FTP를 통해서 클래스를 읽어오는 것에 대해 알아보자.

먼저, 파일 시스템으로부터 클래스를 로딩하는 것에 대해서 알아보자. URLClassLoader가 일반적으로 사용되는 경우는 파일 시스템으로부터 클래스를 읽어올 때이다. 예를 들어, /usr/classes 디렉토리에서 HelloWorld 클래스를 로딩해서 그 클래스의 인스턴서를 생성하고자 할 경우 다음과 같이 하면 된다.

  import java.net.URL;
  import java.net.URLClassLoader;
  
  public class FileSystemTest {
  
   public static void main(String[] args) throws Exception {
   URL[] urls = { new java.io.File("/usr/classes").toURL(); }
  
   URLClassLoader ucl = new URLClassLoader(urls);
  
   Class klass = ucl.loadClass("HelloWorld");
   Object obj = klass.newInstance();
   // obj를 사용하여 적절한 것을 한다.
   }
  }

위 코드를 보면, URLClassLoader를 생성할 때, URL의 배열을 생성자의 파라미터로 넘겨주는 것을 알 수 있다. URL 배열을 사용하는 것은 여러 개의 URL로부터 클래스를 로딩할 수 있다는 것을 의미한다. 일단 URLClassLoader를 생성하면, loadClass() 메소드를 사용하여 원하는 클래스를 로딩할 수 있고, 이어서 loadClass() 메소드의 리턴 결과인 Class 객체의 newInstance() 메소드를 사용하여 새로운 인스턴스를 생성해서 사용하면 된다. 만약 HelloWorld 클래스가 javacan.exam 패키지에 속한다고 하면, loadClass() 메소드는 다음과 같이 변경될 것이다.

  Class kalss = ucl.loadClass("javacan.exam.HelloWorld");

이 경우, URLClassLoader는 /usr/classes/javacan/exam 디렉토리에서 HelloWorld 클래스를 로딩할 것이다. URLClassLoader 클래스는 파일 시스템의 디렉토리 뿐만 아니라, URL로 Jar 파일이나 Zip 파일을 지정할 경우 자동적으로 Jar 파일과 Zip 파일로부터 클래스를 로딩한다. 이 경우, URL은 다음과 같이 변경될 것이다.

  URL[] urls = { new java.io.File("/usr/lib/madvirus.jar").toURL() };

HTTP 서버와 FTP 서버로부터 클래스를 로딩하는 것 역시 파일 시스템에서 클래스를 로딩하는 것 만큼이나 쉽다. 단지, URL을 다음과 같이 생성만 해 주면 된다.

  new URL("http", "www.hostname.com", "/lib/madvirus.jar")

이 URL을 URLClassLoader를 생성할 때 넘겨주면, URLClassLoader는 http://www.hostname.com/lib/madvirus.jar로부터 클래스를 로딩할 것이다. FTP 서버로부터 클래스를 로딩할 때는 다음과 같이 URL을 생성하면 된다.

  new URL("ftp", "user:password@www.hostname.com:", "/")

여기서 user와 password는 각각 FTP 서버에 연결할 때 사용하는 사용자 계정과 암호이다.

부트스트랩 클래스로더

부트스트랩 클래스로더는 전문적으로 말해서 클래스로더가 아니다. 왜냐면 부트스트랩 클래스로더는 JVM의 네이티브 코드 영역에 존재하며, Object와 같은 코어 자바 클래스를 VM에 로딩할 때 사용되기 때문이다. 부트스트랩 클래스로더는 sun.boot.class.path 프로퍼티에 지정되어 있는 값을 이용하여 자바 런타임 라이브러리를 찾는다. 이 값을 명시적으로 지정하지 않을 경우, [자바 2 디렉토리]/jre/lib/rt.jar 파일로부터 자바 런타임 클래스들을 로딩한다.

JDK 1.0이나 JDK 1.1.x 때부터 착실하게(?) 자바를 공부해왔던 개발자라면 누구나 [JDK디렉토리]/lib/classes.zip 파일을 CLASSPATH 환경변수에 추가해주었을 것이다. 하지만, 자바2에서 JDK 1.0이나 JDK 1.1.x 때와는 달리 CLASSPATH 환경변수나 명령행의 옵션인 -classpath에 자바 런타임 클래스들을 추가해줄 필요가 없다. 왜냐면, 부트스트랩 클래스로더가 자동적으로 읽어오기 때문이다.

sun.misc.Launcher$ExtClassLoader

ExtClassLoader는 익스텐션 클래스로더(extension classloader)라고도 불리며, 자바의 확장 클래스들을 로딩할 때 사용된다. ExtClassLoader는 URLClassLoader 클래스를 상속하며, java.ext.dirs 프로퍼티에서 지정한 디렉토리에 위치한 .jar 파일로부터 클래스를 읽어온다. 이 프로퍼티의 값을 명시적으로 지정하지 않으면, 기본적으로 [자바 2 디렉토리]/jre/lib/ext 디렉토리에 위치한 .jar 파일로부터 클래스를 읽어온다.

sun.misc.Launcher$AppClassLoader

AppClassLoader는 시스템 또는 어플리케이션 클래스로더라고 부르며, java.class.path 프로퍼티에 명시된 경로에서 코드를 로딩하는 클래스로더이다. ExtClassLoader과 마찬가지로 URLClassLoader를 상속하고 있다. CLASSPATH에 있는 각각의 디렉토리나 .jar 파일은 URL로 변환되어 AppClassLoader에 전달되며, AppClassLoader의 생성자에서는 이 URL들을 상위 클래스인 URLClassLoader 생성자에 전달한다.

ClassLoader.getSystemClassLoader() 메소드를 호출할 때, 이 클래스로더가 리턴된다. 개발자가 작성한 대부분의 클래스들은 이 클래스로더를 통해서 로딩된다. 또한, AppClassLoader는 ExtClassLoader를 부로 클래스로더 지정하고 있기 때문에, 어플리케이션에서 기본적으로(즉, AppClassLoader를 통해서) 익스텐션 디렉토리에 있는 Jar 파일로부터 클래스들을 읽어올 수 있다.

sun.applet.AppletClassLoader

이름에서도 알 수 있듯이, AppletClassLoader는 웹 브라우저가 웹 페이지에서 사용되는 애플릿의 바이트 코드를 다운로드 한 후, 그 애플릿을 실행하는 것을 목적으로 하는 클래스로더이다. AppletClassLoader는 URL을 사용하여 HTTP, FTP 또는 파일 시스템으로부터 클래스를 로딩하기 때문에, URLClassLoader를 상속하고 있다. 하지만, 많이 사용하고 있는 웹 브라우저인 IE나 Netscape의 경우, AppletClassLoader가 아닌 그 웹 브라우저만의 애플릿 클래스로더를 구현하고 있기 때문에, 브라우저마다 서로 다른 동작을 보일수도 있다.

java.security.SecureClassLoader

SecureClassLoader 클래스의 주요 목적은 JVM에 바이드코드를 로딩하고 사용하는 것에 대한 보안을 제어하는 것이다. 하지만, 이 클래스는 실제로 클래스 코드를 로딩할 수 있는 안전한 방법을 제공하지 않으며, 다른 클래스로더가 확장할 수 있는 베이스 클래스로서의 역할을 한다. 따라서 이 클래스는 자바 런타임 라이브러이에 있는 많은 클래스로더의 상위 클래스이며, 대표적인 것으로 URLClassLoader를 들 수 있다. 참고적으로, 이 클래스를 추상 클래스이기 때문에, 직접적으로 이 클래스의 인스턴스를 생성해서 사용할 수 없다.

java.rmi.server.RMIClassLoader

RMIClassLoader는 ClassLoader가 아니며, RMI 런타임 시스템에서 클래스의 로딩과 마샬링(marshaling)을 처리해주는 래퍼 클래스(warpper class)이다. 실제로, RMIClassLoader는 sun.rmi.serer.LoaderHandler 클래스와의 간단한 브릿지(bridge)이다. 실제 클래스의 로딩은 LoaderHandler 클래스의 이너(inner) 클래스로 존재하는 로더 클래스들을 통해서 이루어진다. 이 이너 로더 클래스는 URLClassLoader를 상속하고 있다. 실제로 엔터프라이즈 환경에서는 RMI와 관련된 부분에서만 이 클래스로더가 자동적으로 사용될 뿐, 이 클래스로더를 직접적으로 사용하는 경우는 거의 없다. 왜냐면, URLClassLoader 자체가 HTTP, FTP와 같은 URL을 통해서 클래스를 로딩할 수 있도록 해 주기 때문이다.

결론

이번장에서는 자바에서 기본적으로 클래스로더에 대해서 간단하게 알아보았다. 여러분은 이번 Article을 통해서 자바의 기본적인 클래스로더와 실제로 클래스들이 어떻게 JVM 내에서 서로 구분되는 지 알게 되었을 것이다. 다음 Article에서는 커스텀 클래스로더를 작성하는 것에 대해 알아볼 것이다.

관련링크:

+ Recent posts