在阅读jaeger-client的源码的时候,看到了ServiceLoader的东西,个人感觉有点像已知多少种子类的策略模式。
他人的解释:ServiceLoader是SPI的是一种实现,所谓SPI,即Service Provider Interface,用于一些服务提供给第三方实现或者扩展,可以增强框架的扩展或者替换一些组件。
我理解的简单做法:通过在jar包的META-INF/services/下,添加文件,文件名为接口的全限定名,内容为希望获得的实现了此接口的类的全限定名。
如jaeger-client
项目中:
jaeger-thrift
模块中,有文件META-INF/services/io.jaegertracing.spi.SenderFactory
,此文件内容如下:
io.jaegertracing.thrift.internal.senders.ThriftSenderFactory
io.jaegertracing.thrift.internal.senders.FullLogSenderFactory
io.jaegertracing.internal.senders.NoopSenderFactory
在类io.jaegertracing.internal.senders.SenderResolver
中,
ServiceLoader<SenderFactory> senderFactoryServiceLoader = ServiceLoader.load(SenderFactory.class,
SenderFactory.class.getClassLoader());
Iterator<SenderFactory> senderFactoryIterator = senderFactoryServiceLoader.iterator();
boolean hasMultipleFactories = false;
while (senderFactoryIterator.hasNext()) {
SenderFactory senderFactory = senderFactoryIterator.next();
if (senderFactoryIterator.hasNext()) {
log.debug("There are multiple factories available via the service loader.");
hasMultipleFactories = true;
}
if (hasMultipleFactories) {
// we compare the factory name with JAEGER_SENDER_FACTORY, as a way to know which
// factory the user wants:
String requestedFactory = System.getProperty(Configuration.JAEGER_SENDER_FACTORY);
if (senderFactory.getType().equals(requestedFactory)) {
log.debug(
String.format("Found the requested (%s) sender factory: %s",
requestedFactory,
senderFactory)
);
sender = getSenderFromFactory(senderFactory, senderConfiguration);
}
} else {
sender = getSenderFromFactory(senderFactory, senderConfiguration);
}
}
ServiceLoader
的主要方法
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
// 若loader参数为null,则使用ClassLoader.getSystemClassLoader(),是AppClassLoader
public static <S> ServiceLoader<S> load(Class<S> service,
ClassLoader loader)
{
return new ServiceLoader<>(service, loader);
}
public Iterator<S> iterator() {
return new Iterator<S>() {
Iterator<Map.Entry<String,S>> knownProviders
= providers.entrySet().iterator();
public boolean hasNext() {
if (knownProviders.hasNext())
return true;
return lookupIterator.hasNext();
}
public S next() {
if (knownProviders.hasNext())
return knownProviders.next().getValue();
return lookupIterator.next();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
iterator的next会返回一个实例对象,所以被返回的对象的类要有无参构造方法。
示例:
serviceLoader/
├── ClientToTryCL.java
├── IHelloImpl.java
└── IHello.java
IHello
和IHelloImpl
public interface IHello {
String sayHello(String name);
}
public class IHelloImpl implements IHello {
public IHelloImpl() {}
public String sayHello(String name) {
// System.out.println(name + " says hello");
return name + " says hello";
}
}
ClientToTryCL
public class ClientToTryCL {
public static void main(String[] args){
ServiceLoader<IHello> serviceLoader = ServiceLoader.load(IHello.class);
Iterator<IHello> iterator = serviceLoader.iterator();
while(iterator.hasNext()){
IHello ihello = iterator.next();
System.out.println(ihello.sayHello(ihello.getClass().getName()));
}
}
}
备注:
在不同的jar中,有重复的文件是可以的,我在jaeger-thrifr
和jaeger-core
两个模块中都定义了文件,会依次找下去。
这样用maven打jar包
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>com.xuan.serviceLoader.ClientToTryCL</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
网友评论