>  기사  >  Java  >  Java의 SPI 메커니즘이란 무엇입니까?

Java의 SPI 메커니즘이란 무엇입니까?

王林
王林앞으로
2023-05-17 23:40:553157검색

1: SPI 메커니즘 소개

SPI 全称是 Service Provider Interface,是一种 JDK 内置的动态加载实现扩展点的机制,通过 SPI기술 인터페이스를 직접 만들지 않고도 인터페이스의 구현 클래스를 동적으로 얻을 수 있습니다. 이는 특별한 기술이 아니라 단지 디자인 컨셉일 뿐입니다.

2: SPI 원리

Java의 SPI 메커니즘이란 무엇입니까?

Java SPI는 실제로 인터페이스 기반 프로그래밍 + 전략 모드 + 구성 파일의 조합으로 구현되는 동적 로딩 메커니즘입니다.

시스템 설계의 각 추상화에 대해 다양한 구현 솔루션이 있는 경우가 많습니다. 객체 지향 설계에서는 일반적으로 인터페이스를 기반으로 모듈을 프로그래밍하는 것이 권장되며 구현 클래스는 모듈 간에 하드 코딩되어서는 안 됩니다. 코드에서 특정 구현 클래스가 참조되는 경우 플러그 가능성 원칙을 위반하는 것입니다. 교체를 구현하려면 코드를 수정해야 합니다. 프로그램에서 모듈을 동적으로 지정하지 않고 모듈 어셈블리를 활성화하려면 서비스 검색 메커니즘이 필요합니다.

Java SPI는 특정 인터페이스와 관련된 서비스 구현을 찾는 메커니즘을 제공합니다. 모듈형 설계에서는 IOC 아이디어와 유사한 메커니즘이 널리 사용됩니다. 즉, 구성요소의 조립 제어가 프로그램 외부로 이전됩니다. 그래서 SPI의 핵심 아이디어는 디커플링입니다.

3: 사용 시나리오

호출자는 실제 필요에 따라 프레임워크의 구현 전략을 활성화, 확장 또는 대체합니다.

다음은 이 메커니즘을 사용하는 몇 가지 시나리오입니다.

  • JDBC 드라이버, 다양한 데이터베이스에 대한 드라이버 클래스 로드

  • Spring은 servlet3.0 사양의 ServletContainerInitializer 구현, 자동 유형 변환 유형 변환 SPI(Converter SPI, Formatter SPI) 등과 같은 많은 SPI를 사용합니다.

  • Dubbo는 또한 SPI를 광범위하게 사용하여 다음을 수행합니다. 확장을 구현하지만 Java에서 제공하는 기본 SPI를 캡슐화하여 사용자가 필터 인터페이스의 구현을 확장할 수 있도록 합니다.

  • Tomcat은 META-INF/services

  • 아래에 로드해야 하는 클래스를 로드합니다. SpringBoot 프로젝트에서 @SpringBootApplication 주석을 사용하면 자동 구성이 시작되고 시작 구성에서 META-INF/spring.factories

4 아래의 구성 클래스를 검색합니다. 소스 코드 데모

4.1 애플리케이션이 ServiceLoader를 호출합니다. 로드 메소드

ServiceLoader.load 메소드에서 먼저 생성 새로운 ServiceLoader를 생성하고 클래스

    private static final String PREFIX = "META-INF/services/";


  private ServiceLoader(Class<S> svc, ClassLoader cl) {
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
        reload();
    }

	/** 
     * 
     * 在调用该方法之后,迭代器方法的后续调用将延迟地从头开始查找和实例化提供程序,就像新创建的加载程序所做的		  那样
     */
   public void reload() {
        providers.clear(); //清除此加载程序的提供程序缓存,以便重新加载所有提供程序。
        lookupIterator = new LazyIterator(service, loader);
    }

	private class LazyIterator implements Iterator<S>{

        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        Iterator<String> pending = null;
        String nextName = null;


        private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                    //找到配置文件
                    String fullName = PREFIX + service.getName();
                    //加载配置文件中的内容
                    if (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    fail(service, "Error locating configuration files", x);
                }
            }
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                //解析配置文件
                pending = parse(service, configs.nextElement());
            }
            //获取配置文件中内容
            nextName = pending.next();
            return true;
        }
    }

		/** 
     	* 
     	*  通过反射 实例化配置文件中的具体实现类
    	 */
		private S nextService() {
            if (!hasNextService())
                throw new NoSuchElementException();
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
                fail(service,
                     "Provider " + cn + " not found");
            }
            if (!service.isAssignableFrom(c)) {
                fail(service,
                     "Provider " + cn  + " not a subtype");
            }
            try {
                S p = service.cast(c.newInstance());
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                fail(service,
                     "Provider " + cn + " could not be instantiated",
                     x);
            }
            throw new Error();          // This cannot happen
        }

5에서 멤버 변수를 인스턴스화합니다. 실전 전투

1단계 다음 클래스를 생성합니다

public interface IService {

    /**
     * 获取价格
     * @return
     */
    String getPrice();

    /**
     * 获取规格信息
     * @return
     */
    String getSpecifications();
}
public class GoodServiceImpl implements IService {

    @Override
    public String getPrice() {
        return "2000.00元";
    }

    @Override
    public String getSpecifications() {
        return "200g/件";
    }
}
public class MedicalServiceImpl implements IService {

    @Override
    public String getPrice() {
        return "3022.12元";
    }

    @Override
    public String getSpecifications() {
        return "30粒/盒";
    }
}

2단계, / 생성 src/main/resources/ /services 디렉토리 아래 META-INF에서 인터페이스 이름을 딴 org.example.IService.txt라는 새 파일을 추가합니다. 내용은 적용할 구현 클래스입니다. 제가 넣어야 할 데이터는 다음과 같습니다.

org.example.GoodServiceImpl
org.example.MedicalServiceImpl

3단계, ServiceLoader를 사용하여 지정된 구현을 로드합니다. 구성 파일.

public class Main {
    public static void main(String[] args) {
        final ServiceLoader<IService> serviceLoader = ServiceLoader.load(IService.class);
        serviceLoader.forEach(service -> {
            System.out.println(service.getPrice() + "=" + service.getSpecifications());
        });
    }
}

출력:

2000.00위안 = 200g/개
3022.12위안 = 30캡슐/박스

6: 장점과 단점

6.1 장점

디커플링은 타사 서비스 모듈 및 호출자 비즈니스 코드를 결합하는 대신 분리함으로써 애플리케이션은 프레임워크 확장을 활성화하거나 실제 비즈니스 조건에 따라 프레임워크 구성 요소를 교체할 수 있습니다. 인터페이스를 구현하기 위해 타사 서비스 모듈에 인터페이스 jar 패키지를 제공하는 방법과 비교할 때 SPI 방법을 사용하면 소스 프레임워크가 인터페이스 구현 클래스의 경로를 신경 쓸 필요가 없습니다

6.2 단점

  • 하지만 ServiceLoader는 지연 로딩으로 간주될 수도 있지만 기본적으로 순회를 통해서만 얻을 수 있습니다. 즉, 인터페이스의 모든 구현 클래스가 로드되고 인스턴스화됩니다. 일부 구현 클래스가 로드되고 인스턴스화되었지만 이를 사용할 필요가 없으면 리소스가 낭비됩니다. 특정 구현 클래스를 얻는 방법은 너무 제한적입니다. 반복자의 형태로만 얻을 수 있으며 특정 매개변수를 기반으로 해당 구현 클래스를 얻을 수 없습니다. ServiceLoader 클래스.

위 내용은 Java의 SPI 메커니즘이란 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제