JAVA의 jdbc를 공부 할 때 많이 보는 Class.forName("oracle.jdbc.OracleDrvier"); 이런 실행 문장이 있습니다.

단지, 사용 할 DBMS에 맞는 드라이버의 full name을 인수로 주었을 뿐인데
그리고 보통 저 메서드를 실행하고 리턴값을 받지도 않죠.. 그런데 저 문장을 실행하고 나면 바로 DriverManager라는
클래스에서 Connection을 얻어 쓸 수 있습니다.

어떻게 저렇게 작동하는 것일까요?

각 DBMS 마다 드라이버와 Connection의 구현이 다를 것 입니다.
하지만 Java를 사용하는 입장에서는 각 DBMS 마다 다른 API를 사용하여 DB에 연결하는 것을 원하지 않죠.
그렇게 된다면 DBMS가 바뀌는 날에는 끔찍한 참사가 일어나겠죠.. --;

소스를 다 수정 해줘야 하니..

하지만 JAVA를 설계하고 만드는 사람들이 모든 DBMS에 대해서 위 API를 구현 할 수는 없을 것 입니다.

Java에서 제공하여 주는 것은 Connection , Driver 인터페이스와 Driver를 등록하고 그 드라이버의
Connection을 리턴해주는 DriverManager 클래스입니다.

그리고 Java에서는 Driver와 Connection 인터페이스를 각 벤더에서 구현하도록 하고
DriverManager라는 클래스를 통해 사용자가 이들을 사용 하도록 하였습니다. 보통, ojdbc14.jar 뭐 이런 파일을
클래스 패스에 추가하여 사용하지요? 이게 Driver와 Connection 인터페이스를 각 DBMS에 맞게 구현한 라이브러리 입니다.

일반적으로 jdbc의 사용은 아래와 같습니다.
아무것도 하지 않고 Class.forName만 사용하여 드라이버의 이름을 호출하기만 했는데
알아서 DriverManager에 Driver가 등록되고 거기서 우리는 Connection을 얻어 쓸 수 있습니다.
그리고, 드라이버 클래스가 없으면 ClassNotFoundException이 떨어집니다.

어떻게 저렇게 동작 할까요?


우선 기본적은 SPI는 이렇습니다.
이해를 쉽게 하기 위해서 인터페이스와 클래스 이름을 jdbc와 똑같이 해보겠습니다.
Java에서는 위와 같이 두개의 인터페이스를 제공합니다.

그리고 DriverManager 클래스를 통해 사용자들은 DriverManager로 부터 Connection을 얻을 수 있습니다.
사용하는 입장에선 이제 DriverManager.getConnection() 혹은 DriverManager.getConnection(String name) 메서드를 통해
원하는 Connection을 얻을 수 있고 이것으로부터 Statement를 받아서 SQL을 보내고 결과를 받을 수 있습니다.
그럼 이제 Class.forName() 메서드가 이 SPI 프레임워크에서 어떻게 사용되는지 보겠습니다.

Driver 인터페이스를 보면 아래와 같은 명세가 있습니다.
 * <P>When a Driver class is loaded, it should create an instance of
 * itself and register it with the DriverManager. This means that a
 * user can load and register a driver by calling
 * <pre>
 *   <code>Class.forName("foo.bah.Driver")</code>
 * </pre>
Driver 클래스가 클래스로더에 의해서 로드가 되면 DriverManager 클래스에 등록이 되어야 하고 그것은 결국
Class.forName(String name) 메서드에 의해서 작동한다고 되어있습니다.
앞 포스트에서 이야기 했지만 이것은 static 필드를 통해서 가능합니다.
Class.forName(String name) 클래스에 의해 클래스가 로드 될 때 static 필드의 내용이 실행되는 것을 이용해 자기 자신을
DriverManager 클래스에 등록하는 것 입니다.

실제로 OracleDriver 클래스를 보면 아래와 같은 부분이 있습니다.


그럼 한번 Driver 인터페이스를 상속한 Driver 구현 클래스를 작성해 보겠습니다.
제가 MyDB라는 DB를 만들었고 거기에 맞는 Driver를 JAVA 개발자들에 제공하기 위해
MyDriver를 만드는 것이지요.


이제 jdbc 사용하는 것 처럼 사용해 보겠습니다.
결과는..?

Driver 등록
MyDriver's Connection return

다른 DBMS가 나와도 그 벤더사는
Driver와 Connection 인터페이스를 구현하여 제공하면 Java를 사용하는 개발자는
다른 DBMS 드라이버와 동일한 API를 사용 할 수 있게 됩니다.

사실 위에서 얘기한 static 필드의 사용이 SPI의 핵심은 아닙니다.
static 필드는 단지 Class.forName 메서드를 사용해 Service Provider를 이용 할 수 있는 방법을 제공하기 위해
사용 된 것이지요..

SPI는 Driver, Connection 인터페이스와 실제 그 인터페이스를 구현하는 구현체 클래스가
완전히 분리 되어 제공된다는 것이 포인트입니다.
인터페이스를 사용해 틀을 만들어 놓고, 그 틀에 맞춰서 각각의 서비스 제공자들이 자신의 서비스에 맞춰서
구현 클래스를 제공하도록 하는 것입니다.

새벽에 나와서 에러가 없으니 딱히 할 건 없고..
멍 하다가..
풍주형님의 static에 대해서 생각해보라는 댓글과 어제 메신저로 얘기해주신 spi에 대해 듣고 써봤습니다..

Posted by 용식
앞선 포스트에서
Class.class에 대해 이야기 하였는데
그 내용안에 Class.forName(String className) 메서드가 나옵니다.
이 메서드에 대해서 좀 더 알아보겠습니다. 풍대리님이 static 키워드를 생각해보라고 하신 것이
이 메서드를 말씀하신 거였더라구요..

우선 Class.forName(String className)는
이렇게 구현이 되어 있고 forName0 메서드는 native 메서드입니다.
Class.forName 메서드를 실행하면 해당 클래스 이름을 갖는 클래스가 클래스 로더에 로드 되면서
Class.class의 인스턴스를 리턴합니다. 앞에서도 이야기 하였지만 Class.forName("java.lang.String")을 실행하면
String.class의 명세를 갖는 Class.class의 인스턴스가 리턴되는 것 입니다.

그럼.. Class.forName(String className)을 실행하면 그 클래스에게는 어떤일이 벌어지는가..


위 테스트 클래스를 실행 하면 어떤 결과가 나올까요?
"나 로드 되었네." 라고 출력이 됩니다.
ClassLoader에 의해서 클래스가 적재 되면서 static으로 선언 되어 있는 영역이 실행이 되는 것 입니다.

그럼 아래와 같은 Test는 결과가 어떻게 될까요?

단 한번만 "나 로드 되었네"가 출력됩니다.
한번 클래스로더에 적재 된 이후에는 이미 적재된 클래스를 계속 사용합니다. 일반적으로 클래스로더가 작동하는
방식과 동일합니다.

다음에는 위 내용을 응용한 Service Provider Interface에 대해서 살짝 얘기해 보겠습니다.



Posted by 용식