JDK SPI 폭로
public interface IHello {
void sayHello();
}
두 개의 인터페이스가 있는 구현 클래스HelloImpl1, HelloImpl2
public class HelloImpl1 implements IHello {
@Override
public void sayHello() {
System.out.println(" Impl1");
}
}
public class HelloImpl2 implements IHello {
@Override
public void sayHello() {
System.out.println(" Impl2");
}
}
MATE-INF 아래의 서비스 폴더에 인터페이스의 전체 한정명으로 명명된 파일이 있는데 그 안의 내용은 클래스의 전체 한정명을 실현하는 것이다.사용 시 도움말 클래스 ServiceLoader를 사용합니다. 구체적인 사용 방법은 다음과 같습니다.
public static void main(String[] args){
ServiceLoader s = ServiceLoader.load(IHello.class);
Iterator iHelloIterator = s.iterator();
while (iHelloIterator.hasNext()) {
IHello iHello = iHelloIterator.next();
iHello.sayHello();
}
}
우리는 대체적으로 IHello 유형의 실례를 검색한 후에 반복해서 실행했다. 우리는 구체적인 어떤 IHello의 실현 클래스가 조작되는지 밝히지 않았다. 그러면 누가 지정했는가?META-INF/services 아래에 있는 파일의 내용에 따라 결정됩니다.
com.test.demo.impl.HelloImpl2
그렇다면 Impl2에서 수행합니다.
com.test.demo.impl.HelloImpl1
com.test.demo.impl.HelloImpl2
둘 다 하는 거야.코드를 변경할 필요가 없는 상황에서 실행 클래스를 동적으로 전환할 수 있습니다.ServiceLoader는 어떻게 실행됩니까?먼저 이 유형의 ServiceLoader 를 만듭니다.
public static ServiceLoader load(Class service,ClassLoader loader)
{
return new ServiceLoader<>(service, loader);
}
필요한 속성이 모두 준비되면reload를 시작합니다
private ServiceLoader(Class 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();
}
이 Lazy Iterator의 조작을 주목합니다. 검색 실현 클래스는 모두 이 클래스를 통해 진행됩니다.
public void reload() {
providers.clear();
lookupIterator = new LazyIterator(service, loader);
}
이 방법은 Lazy Iterator가 다음 서비스가 있는지 판단하는 방법입니다. 이 fullName는 접두사(META-INF/services/)에 검색 서비스의name로 구성되어 있기 때문에 저희 파일의 경로와 이름은 이미 확정되었습니다. 이렇게 쓸 수 밖에 없습니다. 그렇지 않으면 검색할 수 없습니다. PARse 방법을 다시 한 번 보겠습니다. 바로 파일의 내용을 분석하여 Iterator에 넣는 것입니다.
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;
}
다음은 서비스를 얻는 과정입니다. 이전 단계를 거치면 기본적으로 실현 클래스의 이름을 해석할 수 있기 때문에Class를 통과해야 합니다.forName은 이 클래스의 Class 대상을 반사해서 불러오고 실례화 (newInstance) 하고 마지막으로 되돌려줍니다.
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
}
이 ServiceLoader가 수행하는 프로세스가 바로 위에 있습니다.이 SPI의 대다수 개발자들은 익숙하지 않을 것이다. 왜냐하면 이것은 제조업체나 플러그인을 대상으로 하는 것이기 때문에 현재 많은 소프트웨어들이 이미 이런 것을 채택하거나 확장했다.예를 들면dubbo,jdbc 등은 알아볼 필요가 있다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.