[英]Loading generic service implementations via java.util.ServiceLoader
I've stumbled upon some inconvenience the other day using java.util.ServiceLoader
and some questions formed in me. 前几天我偶然发现了一些使用java.util.ServiceLoader
不便,并在我身上形成了一些问题。
Suppose I have a generic service: 假设我有一个通用服务:
public interface Service<T> { ... }
I couldn't explicitly tell ServiceLoader
to load only implementations with a specific generic type. 我无法明确告诉ServiceLoader
只加载具有特定泛型类型的实现。
ServiceLoader<Service<String>> services =
ServiceLoader.load(Service.class); // Fail.
My question is: what are reasonable ways to use ServiceLoader
to safely load implementations of a generic service? 我的问题是:使用ServiceLoader
安全加载通用服务的实现的合理方法是什么?
After asking the above question and before Paŭlo's answer I've managed to come up with a solution. 在询问了上述问题之后,在Paŭlo的回答之前,我已经设法找到了解决方案。
public interface Service<T> { ...
// true if an implementation can handle the given `t' type; false otherwise.
public boolean canHandle(Class<?> t) { ...
public final class StringService implements Service<String> { ...
@Override public boolean canHandle(Class<?> t) {
if (String.class.isAssignableFrom(type))
return true;
return false;
}
public final class DoubleService implements Service<Double> { ...
// ...
public final class Services { ...
public static <T> Service<T> getService(Class<?> t) {
for (Service<T> s : ServiceLoader.load(Service.class))
if (s.canServe(t))
return s;
throw new UnsupportedOperationException("No servings today my son!");
}
Changing boolean canServe(Class<?> t)
to boolean canServe(Object o)
and also changing <T> Service<T> getService(Class<?> t)
in the same manner can be more dynamic (I'm using the latter for myself as I had a method boolean canHandle(T t)
on my interface in the beginning.) 将boolean canServe(Class<?> t)
更改为boolean canServe(Object o)
并以相同方式更改<T> Service<T> getService(Class<?> t)
可以更加动态(我使用后者)对于我自己,因为我在开始时在我的界面上有一个方法boolean canHandle(T t)
。)
The problem here is that the service loader is using a file listing all implementations of a given class/interface, the file being named by the interfaces name. 这里的问题是服务加载器正在使用列出给定类/接口的所有实现的文件,该文件由接口名称命名。 It was not foreseen to put the type parameter into this file name, and it also is not really possible to pass generic types as Class
objects. 没有预见到将type参数放入此文件名中,并且实际上也不可能将泛型类型作为Class
对象传递。
So, you here can only get your generic services of any types, and then inspect their class object to see if it is a subtype of Service<String>
. 所以,你在这里只能得到任何类型的通用服务,然后检查它们的类对象,看它是否是Service<String>
的子类型。
Something like this: 像这样的东西:
class Test{
public Service<String> getStringService() {
// it is a bit strange that we can't explicitely construct a
// parametrized type from raw type and parameters, so here
// we use this workaround. This may need a dummy method or
// variable if this method should have another return type.
ParametrizedType stringServiceType =
(ParametrizedType)Test.class.getMethod("getStringService").getGenericReturnType();
ServiceLoader<Service<?>> loader = ServiceLoader.load(Service<?>.class);
for(Service<?> service : loader) {
if(isImplementing(service.getClass(), stringServiceType)) {
@SuppressWarnings("unchecked")
Service<String> s = (Service)service;
return s;
}
}
}
public boolean isImplementing(Class<?> candidate, ParametrizedType t) {
for(Type iFace : candidate.getGenericInterfaces()) {
if(iFace.equals(t)) {
return true;
}
if(iFace instanceof ParametrizedType &&
((ParametrizedType)iFace).getRawType().equals(t.getRawType())) {
return false;
}
}
return false;
}
}
This is not tested, and may need to be extended to also search interfaces extended by the interfaces our class implements directly, and interfaces implemented by our (generic) superclass. 这没有经过测试,可能需要扩展到我们的类直接实现的接口扩展的搜索接口,以及由我们的(通用)超类实现的接口。
And of course, this can only find classes like 当然,这只能找到类似的课程
class Example implements Service<String> { ...}
not something like 不喜欢
class Example<X> implements Service<X> { ... }
where Example<String>
might be a valid implementation of your service. 其中Example<String>
可能是您的服务的有效实现。
You could also just copy the ServiceLoader class file and remove the generic type argument from the load() method, causing it to always work. 您也可以只复制ServiceLoader类文件并从load()方法中删除泛型类型参数,使其始终有效。 You'll just need to override the warnings. 你只需要覆盖警告。
public static <S> ServiceLoader load(final Class<S> service)
{
return load(service, Thread.currentThread().getContextClassLoader());
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.