Klassen ServiceLoader Problem

devis

Mitglied
Hallo, ich komme momentan überhaupt nicht mit meinem Problem weiter.
In "Java ist auch eine Insel" steht, dass es sich mit ServiceLoader Programme realisieren lassen, die von konkreten Klassen unabhängig sind.
Das habe ich nun Versucht.
Es gibt eine Schnittstelle A. Es gibt eine konkrete Klasse die diese Schnittstelle implementiert B(A).

Nun gibt es eine dritte Klasse C, die mit Hilfe von ServieceLoader unabhängig von B sein soll.

Also Schreibe ich in C:

ServiceLoader<A> A_process = ServiceLoader.load(A.class);

Dabei erstelle ich unter "scr/META-INF/services" eine Datei mit dem Namen "packetnameInterface".A
Diese Datei hat kein Dateiende.
In die Datei schreibe ich: "paketNameKonkreteKlasse".B

Wenn ich nun versuche aus C heraus B zu starten: A.process.iterator().next();

Kommt exception: NosuchelementException .....

was habe ich falsch gemacht???
:shock:
 
Zuletzt bearbeitet:
hier ist ein konkreter Programm beispiel:
Java:
package file_manager;

import java.util.ServiceLoader;
import file_manager.abstract_iterface_class.A

public final class C {
    
      static ServiceLoader<A> gui = ServiceLoader.load(A.class);
      

        
    public static void main(String args[]) {

       gui.iterator().next();
            
    }

Java:
package file_manager.abstract_iterface_class;

/**
 *
 * @author Gast_Student
 */
public interface A{
    
    
    
}

Java:
package file_manager;

public class B impliments A{

}
 
Zuletzt bearbeitet:
Code:
Dabei erstelle ich unter "scr/META-INF/services" eine Datei mit dem Namen "packetnameInterface".A
Wie sind denn Deine Compile-Einstellungen? Landet die Datei denn so im Zielordner für den compilierten Kram?
 
Code:
Dabei erstelle ich unter "scr/META-INF/services" eine Datei mit dem Namen "packetnameInterface".A
Wie sind denn Deine Compile-Einstellungen? Landet die Datei denn so im Zielordner für den compilierten Kram?

Also ich arbeite mit netbeans.. und wenn mit "compilierter kram" der ordner build gemeint ist, dann ist da tatsächlich der Ordner META-INF zu finden.. wurde wahrscheinlci beim compilieren von scr nach build kopiert..
PS: und die Datei ist ebenfalls da drine
 
Poste die Exception doch bitte einmal. In welcher Zeile deines Codes meckert der Compiler? Wenn der Fehler sich nicht auf die Schnelle erschließen lässt, dann debugge dein Programm doch einmal in dem du Breakpoints setzt.

MfG
 
Zuletzt bearbeitet:
Poste die Exception doch bitte einmal. In welcher Zeile deines Codes meckert der Compiler? Wenn der Fehler nicht sich nicht auf die Schnelle erschließen lässt, dann debugge dein Programm doch einmal.

MfG

Wie gesagt.. in der exception steht nichts anderes als NoSuchElementException..

Der Compiler meckert bei aufruf der folgender Zeile in Main:


Java:
gui.iterator().next();

Meine vermutung ist, dass er einfach die konkrete Klasse B nicht finden kann die A implimentiert.. die frage ist warum??..???:L
 
Java:
run:
Exception in thread "main" java.util.NoSuchElementException
	at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:357)
	at java.util.ServiceLoader$1.next(ServiceLoader.java:445)
	at file_manager.C.main(C.java:59)
Java Result: 1
BUILD SUCCESSFUL (total time: 2 seconds)

Das ist die Exception
 
Gut (oder auch nicht). Ich habe gerade in einem Testprojekt (allerdings "leider" Eclipse) verschiedene Fehlerfälle ausprobiert. Der von Dir beschriebene kommt nur, wenn Java die Datei nicht findet. Als Referenz meine Ordnerstruktur im build-Ordner (bei mir heißt der nach Maven-Konvention target)
  • target/classes/de/... <-- Die ganzen Klassen in ihren Packages plus evtl zugehörige Resourcen (properties-Dateien etc
  • target/classes/META-INF/services/interfacePackageUndName
Damit klappt's bei Eclipse-Run. Was ich damit eigentlich nur zeigen will, ist dass alles zusammen liegen muss, damit's im Classpath liegt. Möglicherweise kannst Du Netbeans entsprechend konfigurieren.
 
es liegt bei mir aber praktisch identisch mit deinem ausser dass es bei mir buid statt target heißt..
seltsam.. und bei dir funktioniert alles?
 
Japp, ich habe eine
Code:
java.io.FileFilter
-Implementierung geschrieben, die Datei "java.io.FileFilter" in META-INF/services/ erzeugt, dort den voll qualifizierten Klassennamen der Impl-Klasse eingetragen und dann folgenden Code zum Testen ausgeführt:
Java:
public static void main(String[] args) throws Exception {

    ServiceLoader<FileFilter> loader = ServiceLoader.load(FileFilter.class);

    System.out.println(loader);

    final Iterator<FileFilter> iter = loader.iterator();

    while (iter.hasNext()) {

      System.out.println(iter.next());
    }
Es zeigt mir genau eine Implementierungsinstanz an (genau die, die ich registriert habe). Das Programm lässt sich sowohl in Eclipse als auch von Kommandozeile mittels java-Aufruf ausführen.
 
Danke fuer die Hilfe, ich habe mien fehler gefunden.. und zwar habe ich den ordner falsch benannt wo die interface .class dateien lagen.. anstatt interface im Namen hinzuschreiben hatte ich da iterface stehen gehabt.. im Namen der Datei die im META ordner liegt hatte ich aber richtig interface stehen gehabt.. 🙂

Allerdings habe ich gemerkt, dass man mit so einer methode nicht auf Konstruktor der Konkreten Klassendatei zugreifen hat, weshalb ich einfach zu objekt erstellung der konkreten klassen zurück muss ..

Wie lässt sich sonst mit ServiceLoader auf Konstruktor der implimetierenden Datei zugreifen?
 
Allerdings habe ich gemerkt, dass man mit so einer methode nicht auf Konstruktor der Konkreten Klassendatei zugreifen hat, weshalb ich einfach zu objekt erstellung der konkreten klassen zurück muss ..
Musst Du doch nicht. Der ServiceLoader ruft doch automatisch den public no-arg Konstruktor auf und Du hast Deine Instanz. Oder willst Du einen anderen verwenden?

Wie lässt sich sonst mit ServiceLoader auf Konstruktor der implimetierenden Datei zugreifen?
Wenn Du vorher nicht weißt, welche Konstruktoren es gibt, mit Reflection. Dann brauchste aber ServiceLoader überhaupt nicht bzw. es funktioniert auch nicht, wenn es keinen public no-arg Konstruktor gibt. Dann wäre Class.forName das Mittel der Wahl um Namen von Klassen zu konfigurieren, die man gerne laden möchte.
 
Musst Du doch nicht. Der ServiceLoader ruft doch automatisch den public no-arg Konstruktor auf und Du hast Deine Instanz. Oder willst Du einen anderen verwenden?

ich habe eine grafische Benutzeroberfläche programmiert. In dieser Klassendatei werden aber die meiste Events deklariert. Das heißt, der Klassaendatei mit grafischen Benutzeroberfläche müssen anere Klassenobjekte die für verarbeitung verantwortlich sind übergeben werden.
Das heißt der Konstruktor der GUI Klasse ist prametrisiert.
Ich habe gedacht den erstml mit Servce Loader irgendwie initialisieren zu können.

Wenn Du vorher nicht weißt, welche Konstruktoren es gibt, mit Reflection. Dann brauchste aber ServiceLoader überhaupt nicht bzw. es funktioniert auch nicht, wenn es keinen public no-arg Konstruktor gibt. Dann wäre Class.forName das Mittel der Wahl um Namen von Klassen zu konfigurieren, die man gerne laden möchte.

Von reflectionen hab eich noch keine Ahnung wie das funktionieren sollte.
 
Von reflectionen hab eich noch keine Ahnung wie das funktionieren sollte.
Das macht nichts 😉, braucht man auch eher selten. Für Deinen Anwendungsfall wäre das auch nicht das Mittel der Wahl. Du hast ja eine GUI-Klasse, bei der Du die Konstruktoren kennst. Instanziere sie doch einfach direkt z.B. in der main und versorge sie mit den benötigten Services. Ggf. kann Dir ein Dependency Injection Framework einen Teil der Arbeit abnehmen. ServiceLoader ist hier wohl nicht das geeignete Mittel.
 
Das macht nichts 😉, braucht man auch eher selten. Für Deinen Anwendungsfall wäre das auch nicht das Mittel der Wahl. Du hast ja eine GUI-Klasse, bei der Du die Konstruktoren kennst. Instanziere sie doch einfach direkt z.B. in der main und versorge sie mit den benötigten Services. Ggf. kann Dir ein Dependency Injection Framework einen Teil der Arbeit abnehmen. ServiceLoader ist hier wohl nicht das geeignete Mittel.

Habe das jetzt auf folge art und weise gemacht.

Java:
 public static void main(String args[]) {
    ServiceLoader<Data_Process_Model> dat_process = ServiceLoader.load(Data_Process_Model.class);
      ServiceLoader<D_Base_Write_Read_able> d_base = ServiceLoader.load(D_Base_Write_Read_able.class);
      GUI gui = new GUI(dat_process.iterator().next(),d_base.iterator().next());
      gui.setVisible(true);
       
    }
😳
 
Sieht gut aus!

Außer den Unterschrichen in den Klassen- und Variablennamen. Das ist in Java sehr unüblich. Da benutzt man für zusammengesetzte Würder sog. CamelCase-Notation, bei Variablen mit Kleinbuchstaben beginnend, bei Klassennamen mit Großbuchstaben. Unterschriche werden in Java eigentlich nur bei static final "Konstanten" benutzt, weil hier alle Buchstaben groß sind und man deswegen CamelCase nicht anwenden kann. Zur Veranschaulichung mal folgendes Beispiel:
Java:
// camel case im Klassennamen zum Zusammensetzen von Wörtern
// geht auch mit Englisch (MySpecialGuiClass)
public class MeineSpezielleGuiKlasse {

  // static final "Konstante" hier nur Großbuchstaben
  // deswegen Wortzusammensetzung mit Unterstrich
  private static final int EMPTY_LENGTH = 0;

  // Variablennamen mit Kleinbuchstaben beginnend
  // dann camel case für Zusammensetzen von Wörtern
  private int screenWidth;

Ach ja, für Package-Namen gilt die gleiche Konvention wie für variablen (beginne mit Kleinbuchstaben, dann ggf. camel case, keine Unterstriche). Ich mach in Package Namen aber meist kein camel-case, weil auf UNIXEN Pfade case sensitiv sind und mich die Großbuchstaben nerven, wenn ich sie in der Konsole eingeben muss.
 
Zuletzt bearbeitet von einem Moderator:

Zurück
Oben