ServiceLoader 구현 원리 자세히 보기

자바에서 하위 클래스에 따라 부류나 인터페이스 정보를 얻는 것은 매우 편리하지만, 하나의 인터페이스에 따라 이 인터페이스의 모든 실현 클래스를 얻는 것은 쉽지 않다.
비교적 멍청한 방법은 classpath의 모든class와jar 패키지의class를 스캔한 다음ClassLoader로 불러와서 주어진 인터페이스의 하위 클래스인지 판단하는 것이다.그러나 이런 방법을 사용하지 않을 것이 뻔하다. 대가가 너무 크다.
자바 자체도 인터페이스의 하위 클래스를 얻을 수 있는 방법을 제공했다. 그것이 바로 자바를 사용하는 것이다.util.ServiceLoader#load(java.lang.Class) 방법이지만, 이 방법을 직접 사용해도 주어진 인터페이스의 모든 하위 클래스를 얻을 수 없습니다.
인터페이스가 필요한 하위 클래스는 설정된 방식으로 하나의 인터페이스에 주동적으로 등록해야만 ServiceLoader를 사용하여 하위 클래스에 불러올 수 있으며, 하위 클래스는 ServiceLoader에 의해 실례화되는 무참 구조 방법이 있어야 한다
ServiceLoader를 사용하려면
1. 서비스 작성

package com.mogujie.uni.sl;
/**
 * Created by laibao
 */
public interface Animal {
    void eat();
}  

2. 구현 클래스 작성(주의: 구현 클래스는 반드시 인터페이스와 같은 프로젝트에 있어야 하는 것이 아니라 다른jar 패키지에 존재할 수 있음)

package com.mogujie.uni.sl;
/**
 * Created by laibao
 */
public class Pig implements Animal {
  @Override
  public void eat() {
    System.out.println("Pig eating...");
  }
}

package com.mogujie.uni.sl;
/**
 * Created by laibao
 */
public class Dog implements Animal {
  @Override
  public void eat() {
    System.out.println("Dog eating...");
  }
}

3. 구현 클래스가 있는 프로젝트의classpath 아래에 META-INF/services 디렉터리를 만듭니다. 이 디렉터리는 고정된 것입니다. 반드시 규정된 명칭에 따라 만듭니다. 이 디렉터리는 인터페이스와 구현 클래스의 매핑 관계를 설정하는 데 사용됩니다.
그리고 인터페이스의 전체 이름에 따라 이 디렉터리에 파일을 만듭니다. 예를 들어 위의 예에서 인터페이스의 전체 이름은com입니다.mogujie.uni.sl.Animal, 그러면 실현류 공정에서 META-INF/services/com을 구축해야 합니다.mogujie.uni.sl. Animal과 같은 파일을 만들고 이 파일에 이 인터페이스의 구현 클래스를 설정합니다. 만약 이 인터페이스에 여러 개의 구현 클래스가 있다면 한 줄에 한 줄을 씁니다. 예를 들어 다음과 같습니다.

com.mogujie.uni.sl.Pig
com.mogujie.uni.sl.Dog

4. 다음은 ServiceLoader의 방법으로com을 얻을 수 있습니다.mogujie.uni.sl.Animal 인터페이스의 모든 하위 클래스입니다.
테스트 클래스는 다음과 같습니다.

package com.mogujie.uni;
import com.mogujie.uni.sl.Animal;
import java.util.Iterator;
import java.util.ServiceLoader;
/**
 * Created by laibao
 */
public class TestServiceLoader {
  public static void main(String[] args) {
    ServiceLoader<Animal> serviceLoader = ServiceLoader.load(Animal.class);
    Iterator<Animal> animalIterator = serviceLoader.iterator();
    while(animalIterator.hasNext()){
      Animal animal = animalIterator.next();
      animal.eat();
    }
  }
}

출력은 다음과 같습니다.

Pig eating...
Dog eating...

ServiceLoader의 원리는 사실 매우 간단하다. 주어진 매개 변수(인터페이스)에 따라 이 인터페이스와 실행 클래스의 맵 프로필의 경로를 포지셔닝한 다음에 이 프로필을 읽으면 이 인터페이스의 하위 클래스를 얻을 수 있다
다음은 사용자 정의 서비스 로더가 시스템의 서비스 로더와 같은 기능을 가진다는 것을 스스로 실현한다

package com.mogujie.uni;

import org.apache.commons.io.IOUtils;
import java.net.URL;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;

/**
 * Created by laibao
 */
public class CustomServiceLoader {

  public static final String MAPPING_CONFIG_PREFIX = "META-INF/services";

  public static <S> List<S> loade(Class<S> service) throws Exception{
    String mappingConfigFile = MAPPING_CONFIG_PREFIX + "/" + service.getName() ;
    // jar META-INF , getResources URL 
    Enumeration<URL> configFileUrls = CustomServiceLoader.class.getClassLoader().getResources(mappingConfigFile);
    if(configFileUrls == null){
      return null ;
    }
    List<S> services = new LinkedList<S>();
    while(configFileUrls.hasMoreElements()){
      URL configFileUrl = configFileUrls.nextElement();
      String configContent = IOUtils.toString(configFileUrl.openStream());
      String[] serviceNames = configContent.split("
"); for(String serviceName : serviceNames){ Class serviceClass = CustomServiceLoader.class.getClassLoader().loadClass(serviceName); Object serviceInstance = serviceClass.newInstance(); services.add((S)serviceInstance); } } return services ; } }
테스트 클래스는 다음과 같습니다.

package com.mogujie.uni;
import com.mogujie.uni.sl.Animal;
import java.util.List;
/**
 * Created by laibao
 */
public class CustomServiceLoaderTest {
  public static void main(String[] args) throws Exception {
    List<Animal> animals = CustomServiceLoader.loade(Animal.class);
    for (Animal animal : animals){
      animal.eat();
    }
  }
}

출력:

Pig eating...
Dog eating...
자바 시스템이 정의한 ServiceLoader는 우리가 사용자 정의한 Custom ServiceLoader의loade 방법과 다르다. 그들의 반환값 유형은 다르다. ServiceLoader의loade 방법은 ServiceLoader 대상이고 ServiceLoader 대상은 Iterable 인터페이스를 실현했다. ServiceLoader의 구성원 방법인iterator()를 통해.모든 서비스 실례를 훑어볼 수 있습니다. 사용자 정의된 Custom Service Loader의load 방법은 List 대상입니다. 모든 서비스 실례를 하나의 집합에 봉인하여 되돌려줍니다.
시스템의 ServiceLoader는 Iterator 대상을 되돌려줌으로써 서비스 실례에 대한 게으름을 불러올 수 있습니다.다음 서비스 실례를 실례화하고 사용할 때만 실례화하며 독자가 원본을 읽고 연구할 수 있도록 구체적으로 실현하는 것도 그 디자인의 하이라이트 중 하나이다.
이상의 이 상술한 서비스 로더의 실현 원리는 바로 편집자가 여러분께 공유한 모든 내용입니다. 여러분께 참고가 되고 저희를 많이 사랑해 주시기 바랍니다.

좋은 웹페이지 즐겨찾기