Использование загрузчика классов 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 это точно так же. Каждый пакет имеет загрузчик классов, и когда вы импортируете пакет из другого пакета, загрузчик классов этого другого пакета используется для их загрузки.

Другие вопросы по тегам