SSISO Community

시소당

자바 클래스 로더, 200% 활용하기

클래스  로더는  자바의  강력한  기능  중  하나다.  왜  커스터마이즈드  클래스  로더를  사용해야  하고,  자체  클래스  로더를  어떻게  만들며,  클래스  로딩  메커니즘을  잘  아는  것이  어떤  도움이  되는지에  대해  알아본다.

클래스  로더는  자바의  강력한  기능이다.  그러나  개발자들은  "Hello,  world"  애플리케이션  보다  복잡한  프로그램을  작성하는데  필수적이지만  클래스  로딩  컴포넌트에  대해  잊어버리는  경우가  많다.  클래스  로더는  런타임에  클래스  파일을  찾고  로딩하는  임무를  맡는다.  자바에서는  상이한  클래스  로더를  사용하는  것이  가능하며,  커스터마이즈드  클래스  로더를  사용해도  된다.

자바  프로그램은  다수의  클래스  파일로  구성되며  각  클래스  파일은  하나의  자바  클래스와  대응된다.  이들  클래스  파일은  스태틱(static)하게  빌드된  C  프로그램처럼  메모리에  한꺼번에  로드되지  않고  온디맨드  방식으로  로드된다.  클래스  로더의  역할은  여기에서  시작된다.

왜  커스터마이즈드  클래스  로더를  사용하나

소스(.class  혹은  .jar  파일)에서  플랫폼에  독립적인  컴파일된  바이트  코드를  추출하며  JVM  메모리  공간에  로드해  이곳에서  인터프리트되고  실행되도록  한다.  기본  설정은  애플리케이션의  각  클래스가  java.lang.ClassLoader  인스턴스에  의해  로드된다.  이  클래스는  손쉽게  상속(inherit)  되므로  기능을  확장할  수  있다.

기본적인  java.lang.ClassLoader  는  로컬  파일시스템에서만  클래스를  로드할  수  있다.  자바는  로컬  하드  드라이브나  네트워크  이외의  장소에서  클래스를  가져올  수  있는  유연성을  충분히  제공하며  실제  로드가  일어나기  전에  특별한  기능을  수행하도록  할  수도  있다.  예를  들어  애플리케이션은  웹사이트나  FTP  에  존재하는  플러그-인  크래스의  새로운  버전을  주기적으로  검사하고  디지털  시그니처를  자동으로  확인한  후  신뢰성  있는  코드만  실행되도록  할  수  있다.  잘  아려진  애플리케이션  서버의  다수가  자체적인  클래스  로더를  사용한다.

소위  부트스트랩  클래스  로더로  불리는  이런  클래스  로더들이  기본적으로  사용된다.  즉  java.lang.Object  나  rt.jar  파일에  위치하고  있는  다른  런타임  코드를  메모리로  로드하는  역할을  하게  된다.  자바  랭귀지  스펙(Java  Language  Specification)은  부트스트랩  클래스  로더에  대해  세부사항을  제공하지  않기  때문에(네이티브(native)  구현을  하므로)  JVM마다  기본  클래스  로더의  동작은  다르다.  일부  웹  페이지에서  동작하는  애플릿을  봤다면  이미  커스텀  클래스  로더를  사용해  본  것이다.  브라우저에  내장된  애플릿  뷰어는  원격  서버의  웹사이트를  접근해  HTTP로  로(raw)  바이트  코드  파일을  전송하고,  JVM  내부의  클래스로  이를  전환하는  클래스  로더를  포함하기  때문이다.

자체  클래스  로더  작성

부트스트랩  클래스  로더를  제외한  클래스  로더는  부모  클래스  로더가  있으며  이는  클래스  로더를  로드한  클래스  로더이다.  가장  중요한  것은  부모  클래스  로더를  정확하게  설정하는  것이다.  그  다음  부모  클래스  로더의  getParent()  메쏘드를  사용하여  클래스  요청을  위임한다  (예.  커스텀  클래스  로더가  당신이  만든  특별한  메쏘드를  사용하여  클래스를  찾을  수  없을  경우)  컨스트럭터에서  java.lang.ClassLoader  컨스트럭터의  파라미터로  부모  클래스  로더를  설정한다.

public  class  MyClassLoader  extends  ClassLoader{

public  MyClassLoader(){

super(MyClassLoader.class.getClassLoader());

}

loadClass(Straing  name)  메쏘드는  우리가  만든  클래스로더의  엔트리  포인트이다.  파라미터  name은  FQCN(fully  qualified  class  name),  즉  패키지를  갖춘  클래스  네임이다.  만약  부모  클래스  로더가  정확하게  설정됐다면  MyClassLoader가  loadClass(String  name)에  의해  클래스를  로드하도록  요청받았지만  클래스를  로드할  수  없을  경우  부모를  먼저  확인하게  된다.  부모도  클래스를  찾을  수  없다면  findClass(String  name)  메쏘드가  호출된다.  findClass(String  name)의  기본  구현은  대부분의  개발자가  너무나  잘  알고  있는  ClassNotFoundException을  발생시킨다.  커스텀  클래스  로더  개발자들은  java.lang.ClassLoader를  상속받을  때  이  메쏘드를  대체해야  한다.

findClass()  메쏘드의  목적은  우리의  로더가  실패한  경우  시스템  클래스  로더를  호출하는  것과  같은  다른  코드를  복제하지  않고서도  MyClassLoader  를  위한  특수  코드를  모두  포함하는  것이다.  이  메쏘드에서  ClassLoader  는  임의의  코드에서  바이트  코드를  가져올  필요가  있다.  바이트  코드가  추출되면  메쏘드는  defineClass()  를  호출해야  한다.  로드된  클래스를  위해  ClassLoder  의  어떤  인스턴스가  메쏘드를  호출했는지는  매우  중요하다.  그러므로  두  개의  ClassLoder  인스턴스가  동일한  혹은  상이한  소스의  바이트  코드를  정의한다면  정의된  클래스는  다르게  다뤄진다.

동일한  소스에서  MyCoolClass  바이트  코드를  찾을  수  있는  두  개의  유사한  클래스  로더  MyClassLoader1과  MyClassLoder2의  예를  들어보자.  만약  애플리케이션이  MyCoolClass  인스턴스  두  개를  각  클래스  로더를  이용해  독립적으로  로드한다면(coolClass1은  MyClassLoader1을  통해  coolClass2는  MyClassLoader2를  통해서  한다고  가정하자),  MyCoolClass.class도  독립적으로  정의된다.  다음의  코드를  실행하면:

MyCoolClass  coolClass1  =  (MyCoolClass)coolClass2;

ClassCastException이  발생된다.  (클래스  로딩  메커니즘을  잘  모르는  개발자는  이  익셉션을  자주  받게  된다.)  상이한  로더에  의해  정의되므로  JVM은  두  개의  완전히  다른  클래스  타입(type)을  보게  된다.  coolClass1과  coolClass2은  동일한  클래스  타입에서  나왔고  동일한  소스에서  로드됐음에도  불구하고  그  변수들의  타입은  호환되지  않는다.

findClass()와  loadClass()를  대체할  때  마다  getSystemClassLoader()  메쏘드를  이용해  실제  ClassLoader  오브젝트의  형태로  시스템  ClassLoader에  직접  접근이  가능하다.  또한  findSystemClass(String  name)  호출을  통해  암묵적으로도  접근할  수  있다.  getParent()  메쏘드를  사용하면  부모  클래스  로더를  사용할  수  있다.  Listing  A는  실제  수행할  준비가  되어  있는  커스텀  클래스  로더의  예이다.

클래스  로딩  메커니즘을  잘  알고  있다면  애플리케이션의  ClassNotFound나  ClassCastException  에러를  디버깅할  때  결국  도움이  된다.  특히  애플리케이션이  써드  파티  애플리케이션  서버  내부에서  동작할  때  매우  중요한데  이런  애플리케이션  서버들이  독자적인  그리고  복잡한  클래스  로더를  사용하는  경우가  많기  때문이다.@

Peter  V.  Mikhalenko는  도이치  뱅크에서  비즈니스  컨설턴트로  일하고  있는  썬의  자격증을  소유한  전문가  (Sun  certified  professional)  이다.  

685 view

4.0 stars