본문 바로가기

Java

[Java] Class.forName(String className) 그리고 Service Provider Interface


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에 대해 듣고 써봤습니다..