갑자기 SPI가 뭐지? 싶을 수도 있다.
MySQL Connector는 JDBC의 구현체라는 것은 당연한 사실이다.
이 내용은 바로 아래에서 나올 SPI와 관련이 있다.
Service Provider Interface
자바 플랫폼에서 서비스 제공자 (구현체)를 발견하고 로드하는 메커니즘이다.
예를 들어, 자바에서 범용적으로 사용하는 로깅 라이브러리인 Log4j 를 예시로 들 수 있다.
Log4j는 2021년에 취약점이 발견되었는데, 이는 자바 생태계에 어마어마한 영향력을 끼쳤다.
(실제로 군생활 시절, Log4j 덕분에 보안 점검을 수행한 경험이 있다.)
거의 모든 자바 생태계가, log4j 를 사용하고 있었기 때문인데, 이 취약점으로 인해 기존에 작성된 코드를 다른 로깅 라이브러리로 바꾸는 것이 쉽지 않았을 것이다.
이때 등장한 것이 바로 Slf4J 라는 새로운 로깅 라이브러리인데, 결론만 말하자면 자바 진영에서 로깅 라이브러리의 표준 인터페이스라고 할 수 있다.
Log4j를 전부 다른 라이브러리로 교체하는 것은 쉬운 일이 아니므로, 표준 인터페이스를 정해놓고 구현체만 바꿔 사용하자는 논리이다.
이때 Slf4j 가 바로, 로깅 서비스를 제공해주는 표준 인터페이스 라고 할 수 있다.
스프링에서도 인터페이스를 정의해놓고, 구현체를 바꿔 사용하는 것처럼,
SPI도 비슷하다. 하나의 서비스에 대한 인터페이스를 명시해놓고, 서비스의 구현체를 바꾸어서 사용하는 것이다.
(물론, 스프링은 빈 주입, SPI는 서비스 로더라는 개념을 사용하서 동작 원리가 다르긴 하다.)

라이브러리를 사용할 때, 표준 인터페이스가 정해져 있으니, 코드를 일일이 수정하지 않아도 설정 값들만 변경해주어도 쉽게 변경할 수 있다.
SPI 라는 개념을 알고 난 후 API와 헷갈리기 시작했다.
API (Application Programming Interface) : API는 라이브러리가 사용자에게 제공하는 인터페이스로, 사용자가 라이브러리를 어떻게 사용할 수 있는지 정의하는 것
API가 위와 같다면, SPI는 약간 느낌이 다르다.
SPI (Service Provider Interface) : 라이브러리가 구현체에게 요구하는 인터페이스로, 구현체가 라이브러리와 어떻게 통합이 될 것인지를 정의하는 명세이다.
이전 예시에서는, Slf4J 와 LogBack, Log4j 를 예시로 들었지만, JDBC 와 MySQL Connector 도 예시로 들 수 있다.
자바에서 데이터베이스와 어떻게 연결할 것인지를 인터페이스로 정의해놓고, 각 데이터베이스 제조사 별로 이를 구현한 것이다.
JDBC(SPI) ↔ MySQL Connector (구현체)
SPI를 구현하기 위해서는, 4가지 종류의 파일이 필요하다.
java.util.ServiceLoader (서비스 구현체를 찾아주는 클래스)META-INF/services/ 어떤 서비스 인터페이스를 구현했는지에 대한 정보와, 구현 클래스의 위치 정보여기서, SPI를 구현한다는 것은, 라이브러리의 표준 인터페이스가 존재할 경우, 이 인터페이스를 구현한 라이브러리 구현체를 찾아서 자동으로 사용하게 해주는 기능을 구현한다는 것이다.
로깅 라이브러리에서
// 이 인터페이스는, 서비스를 구현하는 구현 라이브러리에서 implements 해야 함
public interface Logger {
/*
실제로 로깅을 수행하는 작업을 구현
*/
}
// 이 인터페이스는, 서비스를 구현하는 구현 라이브러리에서 implements 해야 함
public interface LoggingServiceProvider {
/*
Service 구현체 찾는 로직
*/
}
public class LoggerImpl implements Logger {
@Override
public String log(){
// logic..
}
}
public class LoggingServiceProviderImpl implements LogginServiceProvider {
@Override
public void findService(){
// logic..
// 내부적으로 ServiceLoader 를 사용
}
}
이제, 인터페이스를 정의하고 구현했으면, META-INF/services 안에, Service 관련 정보를 기록한 파일을 생성하면 된다.
MariaDB의 Service Impl을 보여준 것이다.

java.sql.Driver 가 파일 제목인 이유는, java.sql 패키지의 Driver 인터페이스를 의미한다.
즉, 자바에서 데이터베이스에 접근하기 위한 표준 드라이버를 Driver 인터페이스로 정의해놓았는데
이 Driver 인터페이스를 구현한 구현체가 바로 org.mariadb.jdbc.Driver 라는 것이다.

Service 인터페이스들의 구현체를 찾아서 직접 메모리에 로드하여 객체를 생성해주는 역할을 하는 클래스가 바로 이 ServiceLoader 라는 클래스이다.
내부를 다 보지는 않았지만, META-INF/services 디렉터리에서, 클래스 경로를 찾아 Java Reflection 으로 객체를 생성하는 것 같다.
ServiceLoader 가 내부적으로 어떻게, 클래스들을 로드하는지는 다음 포스팅에서 알아보도록 하자.
참고 자료