Java Datenaustausch über Reflection möglich?

Sp4rky

Cadet 4th Year
Registriert
März 2019
Beiträge
77
Hallo zusammen,
ich bin gerade am überlegen wie man am besten einen Datenaustausch mit zwei "Modulen" welche zur Laufzeit geladen werden umsetzt.

Ich hoffe ich erkläre das richtig was ich versuche:
Anwendung X kann über einen URLClassLoader Modul A und B laden und mit vorher gegebenen Funktionen arbeiten. (Also A und B sind bereits so gebaut das X damit etwas anfangen kann).
Jetzt würde mich aber interessieren wie ich innerhalb von B Daten von A abrufen kann (z.Bsp A hat eine Funktion <string> getID() welche von B genutzt werden soll falls A existiert)

Ich hätte die Idee, einfach den URLClassLoader an B zu übergeben um dann darüber auf A zuzugreifen, aber das scheint als müsste es dort einen besseren Weg geben, am besten ohne irgendetwas direkt zu übergeben. Was macht man da am besten?

//Edit
Man könnte auch sicherlich über .getClassLoader() an den URLClassLoader kommen mit dem man A und B geladen hat um ihn nicht mit ner Funktion übergeben zu müssen. Macht das ganze aber nicht viel schöner.
 
Zuletzt bearbeitet:
Wie @Jesterfox es richtig geschrieben hat, sollte Reflection immer eine Notlösung sein. Das übergeben der Referenzen von außen ist zudem 10.000 mal schneller.

greetz
hroessler
Ergänzung ()

@newAccount Weil Reflection (beinahe) jedewede Sicherheitmechanismen aushebelt und zudem extrem langsam ist.

greetz
hroessler
 
Jepp, Reflection ist langsam und umgeht die Trennung die man mit Objektorientierung eigentlich erreichen will. Klar gibt es ein paar Szenarien wo es ohne Reflection nicht geht, die sind aber die Ausnahme und da hat man dann auch erst gar keine andere Wahl.
 
Mit was oder wie würde denn eine 'gute' Lösung funktionieren um eine Anwendung zur Laufzeit zu erweitern (und ggf Funktionen der Erweiterungen) untereinander zu nutzen?
 
@Jesterfox Unter Java ist das sogar noch etwas kritischer. Man denke nur an die "unsauber" implementierten Generics. Da kann man per Reflection richtig böse Sachen machen :D ;)

greetz
hroessler
Ergänzung ()

@Sp4rky Warum trennst du die beiden Klassen nicht in separate Projekte und bindest diese dann "normal" ein?

greetz
hroessler
 
Zuletzt bearbeitet von einem Moderator:
Aktuell nutze ich das um ein Event auszuwerten. Ich möchte das ganze nicht direkt in die Anwendung packen, einfach weil ich die nicht ständig neu zusammen bauen möchte für jede Änderung und sich das ganze so relativ einfach erweitern lässt, ohne großen Aufwand.
 
Ich verstehe di Frage nicht. 2 Klassen in 2 Modulen A und B

Also ruft A einfach B.irgendwas auf.
Wieso reflektions die eh alt langsam und aufjedenfall nicht mehr verwendet werden sollten.

Wenn du Trennung willst Interface

Ah event ja okay ist doch super simpel

3 module 1 modul ex.api
Interface rein MeinTollesInterface

2 und 3 te Modul jeweils eine Klasse das Meintollesinterface implementiert

In der module.info

In 2 und 3 provides meintollesinterface with class A B

In modul 4 was irgendwas auswerten will

https://docs.oracle.com/javase/9/docs/api/java/util/ServiceLoader.html

Das ist genau was du suchst
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: FranzvonAssisi
blackbirdone schrieb:
Ich verstehe di Frage nicht. 2 Klassen in 2 Modulen A und B
Also ruft A einfach B.irgendwas auf.
Zum Zeitpunkt wo ich die Anwendung zusammen baue kann ich noch nicht sagen ob es ein A oder B gibt und noch weniger ob B etwas von A benötigt.
Deswegen lade ich A und B aus ner anderen jar nach falls diese Datei existiert.
Wenn man das so auch ohne reflection lösen könnte wäre das natürlich super, ich wüsste aber leider nicht wie.
 
Siehe mein Kommentar. Nachfolgend mal ein tut was vllt für jemanden dem es nicht so geläufig ist leichter verständlich ist als mein getippe auf dem Handy

https://www.javacodegeeks.com/2017/10/java-9-serviceloader.html viel Spaß beim lesen :) ps das gibs seit java 6 ist mit jigsaw aber massiv ausgebaut worden
 
  • Gefällt mir
Reaktionen: Sp4rky
blackbirdone schrieb:
https://www.javacodegeeks.com/2017/10/java-9-serviceloader.html viel Spaß beim lesen :) ps das gibs seit java 6 ist mit jigsaw aber massiv ausgebaut worden
Danke das scheint sich sicherlich irgendwie hin biegen zu lassen.
Ich versuch das gerade mit einem kleinen Beispiel hin zu bekommen, allerdings möchte es noch nicht so wirklich da ich nicht genügend durch blicke was passieren soll. Zum Ausführen verwende ich das Beispiel 2 von hier https://stackoverflow.com/questions/16102010/dynamically-loading-plugin-jars-using-serviceloader da das der späteren Anwendung nahe kommt.
Java:
public static void main(String[] args) throws IOException {
        File loc = new File("./plugins/");

        File[] flist = loc.listFiles(new FileFilter() {
            public boolean accept(File file) {return file.getPath().toLowerCase().endsWith(".jar");}
        });
        System.out.println(Arrays.toString(flist));
        URL[] urls = new URL[flist.length];
        for (int i = 0; i < flist.length; i++)
            urls[i] = flist[i].toURI().toURL();
        URLClassLoader ucl = new URLClassLoader(urls);
        System.out.println(">>>");
        ServiceLoader<Plugin> sl = ServiceLoader.load(Plugin.class, ucl);
        Iterator<Plugin> apit = sl.iterator();
        while (apit.hasNext()){
            System.out.println(apit.next().getID());
        }
        System.out.println(">>>");
    }
Mit Plugin.java als Interface
Java:
public interface Plugin {
    public String getID();
}
Ich bin nicht wirklich sicher: müsste ich nun eine module-info.java dafür erstellen?
Java:
module interface.mod{
    exports pfad.zum.interface?;
}
Und dann muss für die .jar ja auch noch irgendwie was genommen werden.
Kann man da einfach das Interface rein legen (oder soll man)? Braucht das auch dort wieder eine Modul Beschreibung? Wie wird das dann verbunden?
Da sehe ich leider gerade nicht durch was für welchen Teil erledigt werden muss und was wo benötigt wird.
 
Das was du da vor hast funktioniert so nicht. Also naja schon ist aber nicht schön

Also erstmal solltest du dir anschaun wie java module funktionieren. Das aus stackoveeflow ist von 2013 lange vor Modulen.

Also ohne jetzt werten zu wollen.
Ich würde dir raten erstmal raten ein Projekt zu bauen wo du mit modulen und modul Deklaration arbeitest. Die nicht dynamisch zur runtime aber dynamisch zur compilezeit sind

1 modul
Deine main
Mit paar klassen die irgendwie ein eventbus symoliere
Also abstract event
Orderevent eytends event
Paymentevent extends event

Eventbus{
Construktor bla bla für queue
loadEventHandler()
}
handleEvent(Event event)
{

}

loadEventHandler(){
Hier kommt serviceloader spass
eventHandler = Serviceloader.load(EventHandler.class)
}

1 api modul EventHandler
handleEvent(Event event)
hasSupportForEvent(Event event)

2 Modul
PaymentEventHandler implements EventHandler

Module.info
Provides eventhandler with PaymentEventHandler;

So dann hast du 3 module die nicht statisch von einander abhängen.
Aber plugin Funktionalität bieten.
Wenn du das hinbekommen hast.

Dann kannst du dir mal Modulelayer anschauen.

https://stackoverflow.com/questions...loader-runtime-module-loading-and-replacement
Der letzte Kommentar zeigt inetwa wie das funktioniert.
Dann haste ein sauberes Modulsystem mit sauberen Abhängigkeiten ServiceDefinitionen und Plugin system mit Java 9 Modulen.

Happy coding :D

Ps gradle oder maven anschaun. Das hilft mit Modulen


Sorry bin auf dem Handy, deshalb ist das bissel schwieriger.
 
Ja das ist vermutlich die beste Idee mit etwas einfacherem anzufangen :)
Trotzdem schaffe ich es, dabei irgend etwas falsch zu machen.
Mein Modul 1 bzw Main http://prntscr.com/p571dj
Die Main erzeugt einige Objekte vom Typ SomeObject und gibt sie dann nacheinander an SomeObject_handler.handleObject() weiter. SomeObject hat eine getName und getID Funktion.
Java:
package tdl.sometest;

import java.util.ArrayList;
import java.util.List;

public class Main {

    public static void main(String[]args){

        List<SomeObject> moreobjects = new ArrayList<SomeObject>();
        SomeObject s1 = new SomeObject("A");
        SomeObject s2 = new SomeObject("B");
        SomeObject s3 = new SomeObject("C");
        SomeObject s4 = new SomeObject("D");
        moreobjects.add(s1);
        moreobjects.add(s2);
        moreobjects.add(s3);
        moreobjects.add(s4);

        for(SomeObject someObject : moreobjects){
            // do something
            new SomeObject_handler().handleObject(someObject);
        }
    }

}
Java:
package tdl.sometest;

import tdl.sometest.handlerinterface.ObjectHandlerModule;

import java.util.ServiceLoader;

public class SomeObject_handler{

    private static ServiceLoader<ObjectHandlerModule> serviceLoader;

    public SomeObject_handler(){
        if(serviceLoader == null){
            serviceLoader = ServiceLoader.load(ObjectHandlerModule.class);
        }
    }


    public void handleObject(SomeObject someObject) {
        /*
        Iterator<ObjectHandlerModule> iterator = serviceLoader.iterator();
        while(iterator.hasNext()){
            iterator.next();
        }
        */
        for (ObjectHandlerModule objectHandlerModule : serviceLoader) {
            objectHandlerModule.processObject(someObject);
        }
    }
}
ObjectHandlerModule soll dann als Interface für die Module eine processObject/SomeObject) funktion haben.
In die module-info.java kommt ein uses aufs interface
Code:
module ObjectHandlerIn{
    uses tdl.sometest.handlerinterface.ObjectHandlerModule;
}

Mein zweites Modul welches dort ja dann anbinden soll http://prntscr.com/p57660 besitzt eine Kopie des Interfaces und des Objekts SomeObject. Die Klasse SomeModule baut dann auf dem Interface in die processObject() Funktion ein einfaches out print getName() vom übergebenen Objekt ein. Die module-info.java beinhaltet dann
Code:
module someexthandler{
    exports tdl.sometest.handlerinterface;
    provides tdl.sometest.handlerinterface.ObjectHandlerModule with tdl.sometest.ext.SomeModule;
}

Wenn ich das ganze nun ausführe, scheint es noch nicht zu funktionieren da ich keine Ausgabe bekomme. Irgendwas muss sicherlich mit den module-infos anders sein, oder ist mein Aufbau schon nicht richtig?
 
Kannst du das mal auf github stellen?

Ansonsten der Satz macht mich stutzig


Sp4rky schrieb:
besitzt eine Kopie des Interfaces und des Objekts SomeObject. Die Klasse

Kopie? In das module musst du das interface aus deinem api package requiren

Require blabla Interface
Und das ist das was du extendest nicht eine kopie??

Deshalb github ist einfacher zu lesen :)
 
Ja Git ist ne gute Idee https://github.com/Sp4rkyy/ModTest/

//Edit
Ich glaube ich komme dem ganzen näher
export some.package gibt den Inhalt des entsprechenden Packages für andere Module frei um die Klassen dann daraus zu nutzen. Somit kann ich die komische Kopie des Objekt und der Interface Klasse entfernen.
 
Zuletzt bearbeitet:
War gestern zu müde um mir das anzugucken.

Ja so sieht das schon besser aus. Vorher war es falsch. Architekturmäßig müsste das interface natürlich in dein main modul und von dort an die implementierenden module gegeben werden. Wenn du die implementierung löscht. Geht dein main modul nichtmehr.

Für den test sollte es ja jetzt so schonmal laufen richtig?
 
Leider läuft es noch nicht, oder es sieht zumindest so aus als würde es nicht laufen da ich keine Ausgabe der Objektnamen erhalte. Scheinbar kommt das ganze nicht zum ServiceLoader, da der for loop, wie wenn leer, übersprungen wird. Fehlt in der module-info noch etwas?
 
Zuletzt bearbeitet:
Sie haben einen Pullrequest :)
 
  • Gefällt mir
Reaktionen: Sp4rky
Vielen Dank 😄
Vom kurz drüber kucken würde ich vermuten, dass Starter Events über den RandomEventGenerator erzeugt welche entweder über Named* oder Product* erstellt werden wobei die Vorlage dafür aus eventbus zu kommen scheint.
Das muss ich mir morgen mal genauer ansehen um es zu verstehen. Danke dafür :)
 
Zurück
Oben