Использование загрузчика классов Java в OSGi
У меня есть вопрос об использовании Java ClassLoader в OSGi.
Я написал два пакета OSGi, а именно серверный пакет и клиентский пакет.
В серверном комплекте я реализовал BundleActivator следующим образом:
public class Activator implements BundleActivator {
public void start(BundleContext context) {
System.out.println("[Server:Activator.java:26] " + Activator.class.getClassLoader());
context.registerService(HelloService.class, new HelloService(), null);
}
public void stop(BundleContext context) {
System.out.println("Stopping the bundle");
}
}
И в клиентском пакете я реализовал BundleActivator как:
public class Activator implements BundleActivator {
public void start(BundleContext context) {
ServiceReference<HelloService> ref = context.getServiceReference(HelloService.class);
HelloService service = context.getService(ref);
System.out.println("[Client:Activator.java:48] " + HelloService.class.getClassLoader());
System.out.println("[Client:Activator.java:49] " + Activator.class.getClassLoader());
}
public void stop(BundleContext context) {
System.out.println("Stopping the bundle");
}
}
И когда я запустил OSGi, вывод консоли:
[Сервер:Activator.java:26] org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@56b161a[osgi-сервер:1.0.0(id=54)] [Клиент:Activator.java:48] org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@56b161a[osgi-сервер:1.0.0(id=54)] [Клиент:Activator.java:49] org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@3a1b72aa[osgi-client:1.0 0,0 (ID =55)]
Как видите, загрузчик классов, который загружает HelloService, всегда имеет значение DefaultClassLoader @ 56b161a независимо от того, находится ли он на стороне сервера или на стороне клиента.
Я не могу этого понять. Насколько мне известно, когда на класс B ссылаются в классе A, загрузчик классов класса B совпадает с загрузчиком классов класса A. Но в OSGi, похоже, не так.
Вы можете просветить меня? Я что-то упускаю из-за Java ClassLoader? Или OSGi делает что-то хитрое?
МАНИФЕСТ серверного комплекта:
Manifest-Version: 1.0
Bnd-LastModified: 1452582379580
Build-Jdk: 1.7.0_45
Built-By: haoruan
Bundle-Activator: com.cisco.ruan.server.Activator
Bundle-Description: osgi-server OSGi bundle project.
Bundle-ManifestVersion: 2
Bundle-Name: osgi-server Bundle
Bundle-SymbolicName: osgi-server
Bundle-Version: 1.0
Created-By: Apache Maven Bundle Plugin
Export-Package: com.cisco.ruan.server;version="1.0";uses:="org.osgi.fram
ework"
Import-Package: org.osgi.framework;version="[1.7,2)"
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.5))"
Tool: Bnd-3.0.0.201509101326
МАНИФЕСТ клиентского пакета:
Manifest-Version: 1.0
Bnd-LastModified: 1452582396099
Build-Jdk: 1.7.0_45
Built-By: haoruan
Bundle-Activator: com.cisco.ruan.client.Activator
Bundle-Description: osgi-client OSGi bundle project.
Bundle-ManifestVersion: 2
Bundle-Name: osgi-client Bundle
Bundle-SymbolicName: osgi-client
Bundle-Version: 1.0
Created-By: Apache Maven Bundle Plugin
Export-Package: com.cisco.ruan.client;version="1.0";uses:="com.cisco.rua
n.server,org.osgi.framework"
Import-Package: com.cisco.ruan.server;version="[1.0,2)",org.osgi.framewo
rk;version="[1.7,2)",org.slf4j;version="[1.7,2)"
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.5))"
Tool: Bnd-3.0.0.201509101326
================================================== ====
Привет Нил, это эксперимент, который я только что сделал:
У меня есть ClassA и ClassB, и класс Wrapper относится к этим 2 классам.
public class Wrapper {
public Wrapper() {
showInfo();
}
public void showInfo() {
System.out.println("[Wrapper.java:5] " + ClassA.class.getClassLoader());
System.out.println("[Wrapper.java:8] " + ClassB.class.getClassLoader());
}
}
И я написал свой собственный загрузчик классов MyClassLoader:
class MyClassLoader extends ClassLoader {
private ClassLoader haocl;
private ClassLoader ruancl;
public MyClassLoader() {
this.haocl = new HaoClassLoader();
this.ruancl = new RuanClassLoader();
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.endsWith("com.cisco.ruan.classloader.ClassA")) {
return haocl.loadClass(name);
}
if (name.endsWith("com.cisco.ruan.classloader.ClassB")) {
return ruancl.loadClass(name);
}
if (name.endsWith("Wrapper")) {
InputStream is = null;
try {
is = new FileInputStream("/Users/haoruan/Desktop/Projects/cl-test/target/classes/com/cisco/ruan/classloader/Wrapper.class");
} catch (Exception e) {
e.printStackTrace();
}
byte[] bytes = null;
try {
bytes = ByteStreams.toByteArray(is);
} catch (Exception e) {
e.printStackTrace();
}
return defineClass(name, bytes, 0, bytes.length);
}
return super.loadClass(name);
}
}
Тогда я позвонил Class.forName("com.cisco.ruan.classloader.Wrapper", true, mcl).newInstance();
и консольные выводы:
[Wrapper.java:5] com.cisco.ruan.classloader.HaoClassLoader@248523a0 [Wrapper.java:8] com.cisco.ruan.classloader.RuanClassLoader@3c635421
Таким образом, можно сделать вывод, что ClassA и ClassB сначала загружаются MyClassLoader, а затем фактически загружаются HaoClassLoader и RuanClassLoader. И я думаю, что этот эксперимент может показаться очень простой реализацией механизма загрузчика классов OSGi? Правильно?
2 ответа
Это совершенно нормально в OSGi. В OSGi есть один загрузчик классов на пакет. Этот загрузчик классов обслуживает все классы, которые находятся в комплекте. Для всех классов за пределами пакета есть определения Import-Package. Во время выполнения каждый импорт пакета связывается с пакетом, который экспортирует пакет. Когда класс из такого пакета загружается, загрузка делегируется загрузчику классов других пакетов.
Давайте рассмотрим ваш сценарий.
Bundle osgi-сервер содержит класс com.cisco.ruan.server.HelloService и также экспортирует пакет com.cisco.ruan.server. Bundle osgi-клиент импортирует пакет com.cisco.ruan.server. Когда вы загружаете класс HelloService в Активатор osgi-client, загрузчик классов osgi-client запрашивается для загрузки класса. Он находит делегирование для пакета и делегирует загрузку в загрузчик классов osgi-сервера. Этот загрузчик классов затем пользователь для загрузки класса.
Это поведение по умолчанию в OSGi, и если вы обдумаете это, это имеет большой смысл.
Вы сказали: "Насколько мне известно, когда на класс B ссылаются в классе A, загрузчик классов класса B такой же, как загрузчик классов класса A. Но в OSGi, похоже, не так".
Это неверное утверждение о загрузчиках классов Java... независимо от того, используете вы OSGi или нет.
Например, каждый класс, который вы пишете, простирается от java.lang.Object
, Ваш класс загружается загрузчиком классов приложения, но java.lang.Object
загружается загрузчиком загрузчика классов. Это работает из-за делегирования: один загрузчик классов может попросить другой загрузчик классов загрузить класс от его имени.
В OSGi это точно так же. Каждый пакет имеет загрузчик классов, и когда вы импортируете пакет из другого пакета, загрузчик классов этого другого пакета используется для их загрузки.