본문 바로가기

WhiteShip Java Study : 자바 처음부터 멀리까지

ServiceLoader

소프트웨어 시스템은 
에플리케이션 객체를 제작하고 의존성을 서로 연결하는 준비 과정과
런타임 로직을 분리해야 한다.

- Clean Code 시스템

소프트웨어 시스템은 컴포넌트를 사용하는 데 필요한 정보와 컴포넌트를 만드는데 필요한 정보를 분리해야 한다.

Java 에서는 일반적으로 인터페이스를 작성하고, 인터페이스를 구현하는 구현체를 작성한다.

Spring 프레임워크의 컨테이너를 사용하는 방법이 보통 쓰이지만, 더 가볍고 경량인 방법이 있다.

 

ServiceLoader는 java.util 패키지에 있다. 이 클래스는 jar 파일에 포함된 구헝 파일을 읽어서 인터페이스의 구현을 찾고, 이 구현을 선택한 오브첵트의 목록으로 사용한다. 

 

이게 무슨 말이냐.

 

공통 인터페이스를 정의하고, 그를 구현한 구현체가 여러가지라고 가정해보자.

네이버가 만든 구현체, 구글이 만든 구현체, 카카오가 만든 구현체 등등 많이 있을 것이다.

 

ServiceLoader 클래스를 이용하면, 그 구현체 jar 파일을 바꿔끼는 것만으로 내가 원하는 적절한 구현체를 사용하도록 바꿀 수 있다.

 

 

ServiceLoader 구현해보기

1. 인터페이스 만들기

프로젝트를 하나 만들자.

나는 스프링 프로젝트로 만들어서 쓸데없는 코드들과 의존성을 삭제했다.

 

그리고 인터페이스 하나를 만들어 주자.

package com.hellointerface;

public interface HelloInterface {
    String greeting();
}

 

그리고 mvn install 로 jar 를 만든다.

 

 

2. 구현체 만들기

구현 클래스는 서비스 인터페이스와 같은 패키지에 넣을 필요가 없다.

그리고 구현 클래스에는 기본 생성자가 있어야 한다.

새로 프로젝트를 하나 만든다.

그리고 위에서 만든 jar 파일을 의존성으로 받아온다.

메이븐 reloading 을 하고, jar 이 잘 들어왔는지 확인한다.

 

그리고 그 인터페이스를 구현하는 구현체를 만든다 !

 

package com.myhello;

import com.hellointerface.HelloInterface;

public class KoreanHello implements HelloInterface {

    @Override
    public String greeting() {
        return "안녕하세요!";
    }
}

이번 예제에는 필드가 없으므로 그냥 자동 생성되는 기본 생성자를 이용한다.

 

이제가 중요하다.

META-INF/services 디렉터리를 만들고, 인터페이스의 FQCN 으로 File 을 만든다.

그리고 거기에다가 구현체의 FQCN 을 적어 넣는다.

 

 

 

이후 또 이 프로젝트를 jar 로 만든다. mvn install !

 

 

3. 구현체 사용하기

구현체를 사용할 프로젝트에서 의존성을 추가한다.

mvn reload 를 하고 jar 가 잘 들어왔나 확인한다.

hello 인터페이스까지 같이 들어온 것을 볼 수 있따.

 

그럼 이제 우리 코드에서 ServiceLoader 를 이용할 수 있다!

import com.hellointerface.HelloInterface;

import java.util.ServiceLoader;

public class HelloMain {
    public static void main(String[] args) {
        ServiceLoader<HelloInterface> loader = ServiceLoader.load(HelloInterface.class);

        /**
         * 구현체를 몰라도 쓸 수 있다.
         *
         * annotation processor 도 이런 패턴으로 구현 됨.
         * springboot 의 autoConfiguration > META-INF > springFactory 보면 서비스로더 패턴은 아니지만 감이 올 수 있다.
         */
        for (HelloInterface helloService : loader) {
            System.out.println(helloService.greeting());
        }
    }
}

 

우리는 루프문을 돌면서 적절한 객체를 골라서 서비스를 수행하면 된다.

 

 

 

구현체를 만들때 우리는 META-INF/services 에 우리 구현체를 등록했다.

서비스로더는 그 파일에 등록된 파일들을 보고 구현체를 읽어오게 된다.

이런 패턴은 에너테이션 프로세서와 Springboot 의 autoConfiguration 에서도 비슷하게 사용된다.

 

 

 

'WhiteShip Java Study : 자바 처음부터 멀리까지' 카테고리의 다른 글

NIO  (0) 2021.02.19
I/O  (0) 2021.02.17
Annotation  (0) 2021.02.13
enum  (0) 2021.02.08
멀티스레드 프로그래밍  (0) 2021.02.06