在尝试使用 OSGi 解析引用时,我遇到了奇怪的行为。解析的处理方式和字节码表示的内容之间似乎存在矛盾。
我想与 Felix 一起解析 OBR 存储库,在框架之外。
引起我兴趣的是,即使我请求 org.osgi.service.repository.Repository
类型的服务,并且我也能够获得服务该类型的引用,和我能够从该服务引用获取一个对象,和该对象的 .getClass().getInterfaces()
方法返回org.osgi.service.repository.Repository
(我认为这表明该对象可以转换为Repository
),两者都是Repository.class.isAssignableFrom
和 instanceof
对此对象返回 false
。
我查看了 OSGiRepositoryImpl
的源代码,发现它实际上是 Repository
的子类型
import org.osgi.service.repository.Repository;
class OSGiRepositoryImpl implements Repository {
// ...
}
然后我查看了类型是如何在激活器中注册的,发现它实际上(大概)正确地注册了它
import org.osgi.service.repository.Repository;
// ...
public void start(BundleContext context) {
context = context;
logger = new Logger(context);
this.m_repoAdmin = new RepositoryAdminImpl(context, logger);
context.registerService(RepositoryAdmin.class.getName(), this.m_repoAdmin, (Dictionary)null);
context.registerService(Repository.class.getName(), new OSGiRepositoryImpl(this.m_repoAdmin), (Dictionary)null);
// ...
}
所以我能看到的一切似乎都表明这应该有效。
我使用Comparable
和Integer
尝试了类似的代码,并得到了我预期的结果(可以安全地通过强制转换)。
package net.zephyrion.pokemod.launcher;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.launch.FrameworkFactory;
import org.osgi.service.repository.Repository;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
public final class Launcher {
public static void main(String[] args) throws BundleException {
Framework framework = null;
try {
Map<String, String> frameworkConfig = new HashMap<>();
frameworkConfig.put(Constants.FRAMEWORK_STORAGE_CLEAN, Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);
FrameworkFactory frameworkFactory = ServiceLoader.load(FrameworkFactory.class).iterator().next();
framework = frameworkFactory.newFramework(frameworkConfig);
framework.start();
BundleContext frameworkContext = framework.getBundleContext();
System.out.println("Starting/Installing bundles...");
Files.list(Paths.get("./", "bundles")).forEach(path -> {
try {
System.out.println("method one path:\t" + path);
String absolutePath = path.toAbsolutePath().toString();
Bundle bundle = frameworkContext.installBundle("file:///" + absolutePath);
System.out.println("Starting bundle:\t" + bundle);
bundle.start();
} catch (BundleException e) {
e.printStackTrace();
}
});
System.out.println();
printNumberExample();
printInformation(frameworkContext);
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if (framework != null) {
framework.stop();
System.exit(0);
}
}
}
private static void printInformation(BundleContext frameworkContext) {
ServiceReference<?> repositoryReferenceByName = frameworkContext.getServiceReference("org.osgi.service.repository.Repository");
System.out.println("Repository Reference by Name:\t" + repositoryReferenceByName);
Object serviceByName = frameworkContext.getService(repositoryReferenceByName);
Class<?> serviceClass = serviceByName.getClass();
System.out.println("interfaces implemented by " + serviceClass + ": {");
Arrays.stream(serviceClass.getInterfaces()).forEach(interfaceClass -> System.out.println("\t" + interfaceClass));
System.out.println("}");
System.out.println();
System.out.println("Repository Service by Name:\t\t" + serviceByName);
System.out.println("Repository Service by Name Assignable from Repository:\t\t" + Repository.class.isAssignableFrom(serviceByName.getClass()));
System.out.println("Repository Service by Name instanceof Repository:\t\t" + (serviceByName instanceof Repository));
Repository repository = Repository.class.cast(serviceByName);
System.out.println("Repository:\t" + repository);
}
private static void printNumberExample() {
Integer number = 42;
boolean comparableIsAssignableFromInteger = Comparable.class.isAssignableFrom(number.getClass());
System.out.println("Comparable is assignable from Integer:\t" + comparableIsAssignableFromInteger);
System.out.println("interfaces implemented by " + Integer.class + ": {");
Arrays.stream(Integer.class.getInterfaces()).forEach(interfaceClass -> System.out.println("\t" + interfaceClass));
System.out.println("}");
Comparable comparableNumber = (Comparable) number;
System.out.println("Comparable number:\t" + comparableNumber);
}
}
有趣的是,如果我制作一个 bundle ,在框架中安装该 bundle ,启动它,然后尝试解析框架内的存储库
,它就可以工作就好了。
package net.zephyrion.pokemod.launcher;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.repository.Repository;
import java.util.Arrays;
public class TestActivator implements BundleActivator {
@Override
public void start(BundleContext bundleContext) throws Exception {
System.out.println("=======================================");
System.out.println("Bundle context:\t" + bundleContext);
ServiceReference<Repository> repositoryReference = bundleContext.getServiceReference(Repository.class);
System.out.println("Repository reference in activator:\t" + repositoryReference);
Object serviceByName = bundleContext.getService(repositoryReference);
Class<?> serviceClass = serviceByName.getClass();
System.out.println("interfaces implemented by " + serviceClass + ": {");
Arrays.stream(serviceClass.getInterfaces()).forEach(interfaceClass -> System.out.println("\t" + interfaceClass));
System.out.println("}");
System.out.println();
Repository repository = (Repository) serviceByName;
System.out.println("Repository Service:\t" + repository);
System.out.println("Repository Service Assignable from Repository:\t\t" + Repository.class.isAssignableFrom(serviceByName.getClass()));
System.out.println("Repository Service instanceof Repository:\t\t" + (serviceByName instanceof Repository));
}
@Override
public void stop(BundleContext bundleContext) throws Exception {
}
}
下面是输出
Framework context: org.apache.felix.framework.BundleContextImpl@5ba23b66
Starting/Installing bundles...
method one path: .\bundles\org.apache.felix.bundlerepository-2.0.10.jar
Starting bundle: org.apache.felix.bundlerepository [1]
method one path: .\bundles\org.osgi.service.obr-1.0.2.jar
Starting bundle: org.osgi.service.obr [2]
method one path: .\bundles\zlauncher.jar
Starting bundle: launcher [3]
=======================================
Bundle context: org.apache.felix.framework.BundleContextImpl@482f8f11
Repository reference in activator: [org.osgi.service.repository.Repository]
interfaces implemented by class org.apache.felix.bundlerepository.impl.OSGiRepositoryImpl: {
interface org.osgi.service.repository.Repository
}
Repository Service: org.apache.felix.bundlerepository.impl.OSGiRepositoryImpl@6767c1fc
Repository Service Assignable from Repository: true
Repository Service instanceof Repository: true
Comparable is assignable from Integer: true
interfaces implemented by class java.lang.Integer: {
interface java.lang.Comparable
}
Comparable number: 42
Repository Reference by Name: [org.osgi.service.repository.Repository]
interfaces implemented by class org.apache.felix.bundlerepository.impl.OSGiRepositoryImpl: {
interface org.osgi.service.repository.Repository
}
Repository Service by Name: org.apache.felix.bundlerepository.impl.OSGiRepositoryImpl@6767c1fc
Repository Service by Name Assignable from Repository: false
Repository Service by Name instanceof Repository: false
java.lang.ClassCastException: Cannot cast org.apache.felix.bundlerepository.impl.OSGiRepositoryImpl to org.osgi.service.repository.Repository
at java.lang.Class.cast(Class.java:3369)
at net.zephyrion.pokemod.launcher.Launcher.printInformation(Launcher.java:79)
at net.zephyrion.pokemod.launcher.Launcher.main(Launcher.java:50)
Process finished with exit code 0
注意:我知道获取服务的首选方式是使用 OSGi DS。但因为我嵌入了框架,并提供了一个易于使用的启动器,所以我希望能够在框架上下文之外使用服务对象。
为什么我能够解析框架内的服务,但不能解析框架外的服务,即使服务引用返回相同的对象?为什么同一个对象可以转换到框架内的Repository
,但不能转换到框架之外?
最佳答案
在运行时,类由加载它的类加载器唯一标识。因此,同一个类文件可以由不同的类加载器加载,但不能等于
。因此,这里可能发生的情况是,您的代码从与实现类不同的类加载器加载接口(interface)类。
更新您的测试代码以显示接口(interface)类的类加载器。
您可能会发现有多个导出接口(interface)类的包。您必须确保服务使用者和服务提供者使用相同的服务接口(interface)类(从同一个类加载器加载)。
关于java - 服务注册为类型,并实现接口(interface),但不能用作类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48836415/