Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
Hallo,
vor ca. 6 Wochen habe ich angefangen mit dem Programmieren von Java. Nun habe ich mir überlegt sobald mein Kurs zu Java fertig ist ein etwas größeres Programm zu schreiben. Was genau weiss ich noch nicht?! Nun ist meine Frage wie es möglich ist ein Programm Modular aufzubauen. Ich möchte ein ordner nutzen dem ich sozusagen einfach neue Plugins hinzufügen tue die beim nächsten Programmstart mitgeladen werden. Leider kann ich mir nicht vorstellen wie dies unter Java läuft. Könnte mir dies jemand erklären oder hätte eventuell einen beispielcode???
ich benutze dazu ein interface, welches alle plugins implementieren: Plugable. das beinhaltet methoden wie getName() usw. hier is das itnerface: http://www.java-forum.org/de/viewtopic.php?t=4720 is natürlich an mein programm orientiert.
meine plugins sind in jar dateie im unterordner plugins. ich lade nicht alle auf einmal sondern lasse den user die auswählen und speichere den pfad ab. so lädtst du die dann:
Code:
JarFile jf = new JarFile(f);
Manifest manifest = jf.getManifest();
if(manifest != null) {
Attributes att = manifest.getMainAttributes();
String mc = att.getValue(Attributes.Name.MAIN_CLASS);
if(mc != null)
p = Plugin.loadPluginFromFile(f, mc);
else
p = Plugin.loadPluginFromFile(f);
} else {
p = Plugin.loadPluginFromFile(f);
}
// klasse Plugin
public static Plugin loadPluginFromFile(File f) throws Exception {
return loadPluginFromFile(f, f.getName().substring(0, f.getName().lastIndexOf('.')));
}
public static Plugin loadPluginFromFile(File f, String mainclass) throws Exception {
ClassLoader cl = new URLClassLoader(new URL[]{f.toURL()});
Class c = cl.loadClass(mainclass);
Plugin p = loadPlugin(c);
p.setFile(f.getAbsoluteFile());
return p;
}
achja: um sicherzustellen dass die klassen aus der jar geladen werden ist folgendes doch besser:
new URLClassLoader(new URL[]{"jar:" + f.toURL() + "!\"});
Fertig mit der Angeberei.
Ja, es ist möglich. Du musst dabei einfach immer genug abstrakt bleiben (lieber ein Interface zuviel als zuwenig).
Ich hab bei meinem Projekt bemerkt, dass es ganz nützlich ist:
- viel mit Listener zu arbeiten, weil sich dann jede Klasse wo sie will einklinken kann, ohne dass es jemanden stört
- viele Interfaces. Dann muss man später weniger Abhängigkeiten beachten (hört sich komisch an, aber dich interessieren später nur ein paar wenige Methoden eines Objektes. Was das Objekt jetzt tatsächlich ist, ist doch egal...)
- Und ein paar abstrakte Klassen, die die vielen Interfaces teilweise implementieren (zumindest die Basisfunktionen)
- viele Setter- und Getter-Methoden bereithalten, damit jede Klasse überall ein bisschen herumschrauben kann
- Eine "zentrale Datenstruktur" besitzen, von der aus jedes Objekt schnell & einfach erreichbar ist (damit wiederum jede Klasse an alle Informationen kommt).
- Niemals "static" benutzen. Sobald man das erste mal static benutzt, hat man verloren (weil das Design dann offenbar nicht gut genug war, wirkliche Objektorientiertheit zu benutzen :wink: )
- Und eine gute, ausführliche Dokumentation! (was ich auch nicht immer getan habe)
@Roar
Das dies über Interfaces geht war mir schon fast klar.
Hab jetzt mal noch eine Frage an dein Interface. Was machst du mit diesem hier: public javax.swing.JPanel getContentPane(); ???
Wenn ich das recht verstehe überschreibst du die Funktion getContentPane(), oder liege ich hier falsch???
Bei deinem gepostetem Quellcode kann ich nicht so ganz folgen.
Am Anfang holst du dir aus der Jar File anhand des Manifests die Daten welche die Startklasse ist.
Anschließend rufst du die Funktion loadPluginFromFile(); auf. Leider ist mir die Klasse Plugins nicht bekannt, da die wahrscheinlich von dir Selbst geschrieben wurde.
Habe jetzt hier noch meine probleme:
Code:
public static Plugin loadPluginFromFile(File f, String mainclass) throws Exception {
ClassLoader cl = new URLClassLoader(new URL[]{f.toURL()});
Class c = cl.loadClass(mainclass);
Plugin p = loadPlugin(c);
p.setFile(f.getAbsoluteFile());
return p;
}
Könntest du mir dies eventuell noch erklären???
@Beni
WOW, wie lange arbeitest du schon an dem Projekt???
P.S. Klasse JTable Tutorial
- Niemals "static" benutzen. Sobald man das erste mal static benutzt, hat man verloren (weil das Design dann offenbar nicht gut genug war, wirkliche Objektorientiertheit zu benutzen :wink: )
- Niemals "static" benutzen. Sobald man das erste mal static benutzt, hat man verloren (weil das Design dann offenbar nicht gut genug war, wirkliche Objektorientiertheit zu benutzen :wink: )
Warum eine Kritik an Singletons, die man sicher selber mehrfach verwendet hat? Nun, die Erklärung ist einfach: im Idealfall kommt man mit einem Singleton aus (bei kleinen Projekten). Es ist so schön einfach zu stricken, Hier jetzt aber was zum Nachdenken: Kann man über ein Singleton wirklich das genau einmalige Vorhandensein genau eines Objektes einer bestimmten Klasse in jedem Fall sicherstellen?
Nun, die Frage ist rhetorisch. Die Antwort lautet nein! Eine VM = genau eine Instanz? Mitnichten. Man braucht nur zwei Classloader und schon hat man zwei Instanzen. Da war sie dann, die schöne Eindeutigkeit ...
Weiter: zwei VMs = zwei Instanzen. Dies ist noch gefährlicher, denn man wähnt sich möglicherweise im Glauben, daß man sicher auf z.B. Dateiressourcen zugreift. Aber dies ist dann nicht der Fall! Beide VMs haben ihre eigene Instanz ...
Darüberhinaus bestehen Gefahren im Zusammenhang mit der Erweiterung von Applikationen zu Multi-User-Servlets: Man stelle sich nur einmal vor, was passiert, wenn man ein Programm entwickelt, in der User-spezifische Daten in Singletons angelegt werden. Fataler Fehler! Wenn Teile des Programms dann nämlich z.B. als Servlet umgeschrieben eingesetzt werden sollen, ist eine Umstellung sehr aufwendig. Einziger Workaround: Ja Benutzer ein eigenes Servlet, komplett mit vollem Speicherverbrauch - im Extremfall sogar ein Tomcat je Benutzer - schöne neue Ressourcenwelt ...
Sinnvoller stellt sich da die Verwendung von (Gültigkeitsbereich-spezifischen) Kontexten/Kontextdiensten dar, in denen man seine benötigten Objekte beim Start registriert, so dies nicht schon anderweitg geschehen ist. Auf diese Weise kann man über eindeutige Schlüssel den Zugriff auf die benötigten Instanzen regeln (z.B: intern über Hashtables mit Key-Value-Paaren realisiert). Dies läßt sich dann auch (natürlich nicht ganz ohne Aufwand) bis hin zu rechnerübergreifenden Diensten zwecks eindeutiger Zuordnung treiben.
- Niemals "static" benutzen. Sobald man das erste mal static benutzt, hat man verloren (weil das Design dann offenbar nicht gut genug war, wirkliche Objektorientiertheit zu benutzen :wink: )
Ich hab eigentlich nicht so viel gegen das Singelton Pattern (hm, naja, ich versuch es so selten wie möglich zu verwenden), aber oft wird "static" dann eingesetzt, wenn der Programmierer zu faul oder zu ungeschickt war ein einigermasses solides Design zu bauen.
Also im Sinne von:"oops, Klasse X sollte die Variable y abfragen, aber kommt nicht ran... ach was solls, hau ich halt ein "static" rein!".
Ich versuch meine Progis immer so zu bauen, dass man gleichzeitig mehrere unabhängige Progis mit einer VM öffnen kann:
Code:
public static void main( String[] args ){
new MainFrame().setVisible( true ); // erstes Fenster
new MainFrame().setVisible( true ); // zweites Fenster, das total unabhängig vom ersten Fenster ist.
}
... falls das nicht funktioniert, ist was mit "static" schiefgegangen - und der Übeltäter ist immer "static". [Edit: siehe Buchhalterproblem von akira's Link]
Im übrigen - persönliche Meinung wie alles hier - finde ich Programme ohne static übersichtlicher.
:arrow: es kommt auf den "massvollen" Einsatz draufan :wink:
@guenni81
Die Gesammtarbeitszeit ist ziemlich kurz (mit ein paar Tagen (allerhöchstens 1-2 Wochen) durcharbeiten hat man's).
Ich hab mir einfach erlaubt ein bisschen Automatisierung in das Progi zu bringen, Beispiel: von einer bestimmten Klasse ableiten, und schon hat man ein MenuItem/ToolbarButton mit automatischem Sprach-/LookAndFeel-/und Iconwechsel (mehrere Instanzen gleichzeitig und die Button/Menus sind verschiebbar). Hört sich grausam kompliziert an (ist es aber nicht), und man kann so innert 30 Sekunden ein neues Menu (mit grausam vielen Funktionen) herstellen :wink:
Bei 20 Menüs hat man die Zeit die für die Automatisierung draufging wieder aufgeholt...
@Beni
Ich wüsste gar nicht wie ich so was wie dein Projekt aufbauen sollte.
Das was du geschildert hast hört sich wirklich grausam kompliziert an. Aber naja, ich stehe ja noch am Anfang von Java. Vielleicht bin ich ja irgendwann mal der selben Meinung wie du
Ich hab eigentlich nicht so viel gegen das Singelton Pattern (hm, naja, ich versuch es so selten wie möglich zu verwenden), aber oft wird "static" dann eingesetzt, wenn der Programmierer zu faul oder zu ungeschickt war ein einigermasses solides Design zu bauen.
@günni: ja die klasse Plugin hab ic hselbst geschrieben. in die klasse lag ich alle plugins herein. plugin beseitzt dann die gleichen funtkionen getName() getVersion() usw. Mit der klasse Plugin wird auch in allen anderen klassen die mit plugins zu tun haben benutzt: PluginManager, PluginCache, PluginBar etc. im interface überschreibern ich die methode getContentPae() nicht. sie war vorher ja auch noch nicht da. nix da - nix überschreiben. getContentPane() liefert mir in meinem programm eben die GUI um sie in meinem programm einzubinden.
die klasse Plugin musst du natürlich selbst schrieben, oder du implementierst das ganz anders.
@Beni: grml abgeber . mein programm hat auch keine 100 klassen und reicht für meine zwecke aus
achja: mir ist aufgefallen dass du wirklich alles selbst machst in deinem programm mit den ganzen managern :###
wenn ich mal nix besseres zu tun hab werd ich mal gucken....
Also ich nutze das Singelton Pattern bisher immer für Frames, DatenBeans, DBModule und alle anderen Klassen, die ich nur einmal in meinem Programm brauche. Wenn ich eine Klasse habe, die ich zig mal brauche landet die Referenz eh im DatenBean, so dass ich darauf zugreifen kann. Und ohne mindestens ein static kommt man doch gar nicht hin oder? Wie realisiert ihr den Zugriff auf eure Frames, Daten etc.?
(Ich löse das so: eine Klasse "MainManager", die Referenzen auf alle Frames, Daten besitzt (bzw. auf weitere "Manager", die die Referenzen besitzen), und die praktisch jedem Konstruktor übergeben wird. Das ist so ein Ersatz von "static", der die Nachteile von "static" nicht hat)
Warum eine Kritik an Singletons, die man sicher selber mehrfach verwendet hat? Nun, die Erklärung ist einfach: im Idealfall kommt man mit einem Singleton aus (bei kleinen Projekten). Es ist so schön einfach zu stricken, Hier jetzt aber was zum Nachdenken: Kann man über ein Singleton wirklich das genau einmalige Vorhandensein genau eines Objektes einer bestimmten Klasse in jedem Fall sicherstellen?
Nun, die Frage ist rhetorisch. Die Antwort lautet nein! Eine VM = genau eine Instanz? Mitnichten. Man braucht nur zwei Classloader und schon hat man zwei Instanzen. Da war sie dann, die schöne Eindeutigkeit ...
Das kann eigentlich nicht funktionieren. Der zweite ClassLoader müsste einen Fehler bringen. Ansonsten könnte ich noch ganz andere Dinge verbiegen. Außerdem wüsste ich nicht, wie die VM das gebacken bekommen soll.
Wenn ich bspw. eine Klasse de.java-forum.Forum habe, die am Anfang der Anwendung mit allen Klassen der Anwendung geladen wird, und ich versuche, während das Programm läuft, aus einem ganz anderen Verzeichnis auch eine Klasse de.java-forum.Fourm mit irgendeinem ClassLoader zu laden, müsste die VM bzw. der ClassLoader einen Fehler bringen, das Laden der Klasse ignorieren oder alle bisher mit der zuerst geladenen Klasse erzeugten Objekt auf die neue Klasse umbiegen.
Weiter: zwei VMs = zwei Instanzen. Dies ist noch gefährlicher, denn man wähnt sich möglicherweise im Glauben, daß man sicher auf z.B. Dateiressourcen zugreift. Aber dies ist dann nicht der Fall! Beide VMs haben ihre eigene Instanz ...
Wer kommt auf die Idee, ein Singleton sei auch über mehrere VMs verwendbar? ???:L Jemand, der so etwas denkt, hat Java nicht verstanden und bringt bestimmt noch andere Gedanken-Konstrukte hin...
Darüberhinaus bestehen Gefahren im Zusammenhang mit der Erweiterung von Applikationen zu Multi-User-Servlets: Man stelle sich nur einmal vor, was passiert, wenn man ein Programm entwickelt, in der User-spezifische Daten in Singletons angelegt werden. Fataler Fehler! Wenn Teile des Programms dann nämlich z.B. als Servlet umgeschrieben eingesetzt werden sollen, ist eine Umstellung sehr aufwendig. Einziger Workaround: Ja Benutzer ein eigenes Servlet, komplett mit vollem Speicherverbrauch - im Extremfall sogar ein Tomcat je Benutzer - schöne neue Ressourcenwelt ...[...]
In so einem Fall würde man das entsprechend umschreiben. Außerdem ist dann ein Singleton noch die beste Ausgangsitutation. Man hätte das ganze ja auch über statische Methoden lösen können...
Der Schreiber (hab jetzt nicht geschaut, wie die oder der Gute in dem anderen Forum heisst ) hat - meiner persönlichen Meinung nach - eine etwas merkwürdige Einstellungen zu Singletons, die ich nicht gerade Objektiv nennen würde. Darüber hinaus sind seine Argumente sehr konstruiiert.
Singleton sind in manchen Fällen schon praktisch, vor allem wenn man vielleicht später eine "normale" Klasse daraus basteln will. Aber es ist halt wie bei allen Sachen: Man sollte wissen was man tut.
Also ich nutze das Singelton Pattern bisher immer für Frames, DatenBeans, DBModule und alle anderen Klassen, die ich nur einmal in meinem Programm brauche.
Man sollte aber den Gebrauch von Singletons so weit wie möglich eingeschränkt halten.. Sie sind ja nichts anderes als globale Variablen und somit von überall aus nutzbar und somit auch manipulierbar !!
Die Beispiele, die ich zitiert habe stammen aus dem Webapplication-Umfeld. Beispiel Tomcat:
Tomcat hat genau einen java-Prozess, alse eine virtuelle Maschine, trotzdem hat jede Webapplikation ihren eigenen ClassLoader. Jeder webapp kann über ihren Classloader eine Klasse z.B. com.xyz.Proxy, die sich jeweils unter WEB-INF/classes befindet laden und instanziieren, obwohl alles in einer VM abläuft.
Das kann eigentlich nicht funktionieren. Der zweite ClassLoader müsste einen Fehler bringen. Ansonsten könnte ich noch ganz andere Dinge verbiegen. Außerdem wüsste ich nicht, wie die VM das gebacken bekommen soll.
The methods and constructors of objects created by a class loader may reference other classes. To determine the class(es) referred to, the Java virtual machine invokes the loadClass method of the class loader that originally created the class.
Weiter: zwei VMs = zwei Instanzen. Dies ist noch gefährlicher, denn man wähnt sich möglicherweise im Glauben, daß man sicher auf z.B. Dateiressourcen zugreift. Aber dies ist dann nicht der Fall! Beide VMs haben ihre eigene Instanz ...
Auch hier bezog sich der Autor vermutlich auf das webapp-Umfeld, z.B. wenn ein Tomcat-Cluster aufgebaut werden soll. Auf jedem Tomcat läuft die gleiche Anwendung, die eingehenden Requests werden von einem LoadBalancer auf die verschiedenen Instanzen verteilt. Diese kann bei jedem Request eine andere sein. Um dies überhaupt erst möglich zu machen, werden die Sessions untereinander synchronisiert, Singletons jedoch nicht. Somit hätte nach der Clusterung jede Instanz einen eigenen "Singleton"
Der Schreiber (hab jetzt nicht geschaut, wie die oder der Gute in dem anderen Forum heisst icon_wink.gif ) hat - meiner persönlichen Meinung nach - eine etwas merkwürdige Einstellungen zu Singletons, die ich nicht gerade Objektiv nennen würde. Darüber hinaus sind seine Argumente sehr konstruiiert.
Nur weil Dir solche Beispiele noch nicht untergekommen sind, würde ich sie nicht als konstruiert bezeichnen.
Diese berechtigten Einwände haben auch nichts mit einer "merkwürdigen Einstellung" zu tun. Vielleicht solltest Du mal über Deine Einstellung nachdenken und nicht an der Objektivität anderer zweifeln.
Übrigens schreibt ja auch die Redaktion des Java-Magazins in ihrer Antwort:
Globale Variablen bringen eine Unzahl an Problemen und Nachteilen mit sich. Sie müssen für Zugriffe durch mehrere Threads synchronisiert werden, und sie durchbrechen die Kapselung aller Klassen, die sie verwenden. Dadurch erschweren sie es insbesondere erheblich, verschiedene Systemteile unabhängig von einander zu testen.
Außerdem gibt es subtile Varianten der „Einmaligkeit“ einer Variable: eine Instanz je Thread, je VM, je Modul, je Computer, je Cluster usw. Globale Variablen sind oft nicht ganz so global, wie sie auf den ersten Blick scheinen, und wenn man sie einmal eingeführt hat, wird man sie nur schwer wieder los.
Die Beispiele, die ich zitiert habe stammen aus dem Webapplication-Umfeld. Beispiel Tomcat:
Tomcat hat genau einen java-Prozess, alse eine virtuelle Maschine, trotzdem hat jede Webapplikation ihren eigenen ClassLoader. Jeder webapp kann über ihren Classloader eine Klasse z.B. com.xyz.Proxy, die sich jeweils unter WEB-INF/classes befindet laden und instanziieren, obwohl alles in einer VM abläuft.
Der Autor erwähnt Servlets erst im dritten Abschnitt. Das davor klang deswegen für mich eher allgemeingültig. Und im Bereich von JSP/Servlets und Application Servern gebe ich ihm auch recht geben. Wobei man hier auch andere Möglichkeiten hat und Singletons gut umgehen kann.
akira hat gesagt.:
Grizzly hat gesagt.:
Das kann eigentlich nicht funktionieren. Der zweite ClassLoader müsste einen Fehler bringen. Ansonsten könnte ich noch ganz andere Dinge verbiegen. Außerdem wüsste ich nicht, wie die VM das gebacken bekommen soll.
The methods and constructors of objects created by a class loader may reference other classes. To determine the class(es) referred to, the Java virtual machine invokes the loadClass method of the class loader that originally created the class.
Hmmm ???:L, das sagt mir leider gar nichts :bahnhof: . Könntest Du das bitte etwas mehr erläutern?
akira hat gesagt.:
Nur weil Dir solche Beispiele noch nicht untergekommen sind, würde ich sie nicht als konstruiert bezeichnen.
Diese berechtigten Einwände haben auch nichts mit einer "merkwürdigen Einstellung" zu tun. Vielleicht solltest Du mal über Deine Einstellung nachdenken und nicht an der Objektivität anderer zweifeln.
@akira
Tausendmal Entschuldigung, ich hab deinen Beitrag editiert, dabei wollte ich meinen verändern.
Ich hatte die Originalversion nicht mehr im Cache, und konnte rein gar nichts wiederherstellen.
Sorry, ich hab die traurigen Überreste gelöscht.
@günni
Sorry dass es hier so Offtopic wird, falls noch Fragen sind: mach am besten einen neuen Thread auf. (Ich verspreche auch, dass ich mich dort besser benehmen werde)
Ich hatte bisher auch noch nicht so viel mit Classloadern zu tun, ich kenne aber den Tomcat recht gut, da ich beruflich Webapplikation auf Servlet/JSP-Basis erstelle. Wie ich es selbst aus der Javadoc gelesen habe, ruft die VM beim Laden einer referenzierten Klasse einer geladenen Klasse wieder den gleichen ClassLoader auf, da jedes Objekt eine Referenz auf den ClassLoaders hält, der seine Klasse geladen hat:
Mit Hilfe des ClassLoaders ist es bei normalen Applikationen möglich, das Singelton-Pattern zu umgehen:
Ich hab das so ausprobiert:
Die folgende Klasse kommt in ein JAR-Archiv. Dass man dieses Ding nicht mehrmals initialisieren darf, ist wohl klar.
Code:
package test;
/**
* @author Benjamin Sigg
* @version 1.0
*/
public class Test {
public final static Test test = new Test();
private final long time = System.currentTimeMillis();
private Test(){
if( test == null )
System.out.println( "Initialisierung OK" );
else
System.out.println( "Das ist mehr als seltsam" );
}
public String toString(){
return "Test created at " + time;
}
}
Dann hab ich dieses kleine Progi laufenlassen:
Code:
public class Main{
public static void main( String[] args ) {
try{
load();
Thread.sleep( 250 );
load();
}
catch( Throwable t ){
t.printStackTrace();
}
}
public static void load() throws Exception{
URLClassLoader loader = new URLClassLoader( new URL[]{ new File( "d:/dateien/java/projekte04/staticTest/stat.jar").toURL() } );
Class clazz = loader.loadClass( "test.Test" );
Field field = clazz.getField( "test" );
Object value = field.get( null );
System.out.println( value.toString() );
}
}
Die Ausgabe: Initialisierung OK
Test created at 1091741796250
Initialisierung OK
Test created at 1091741796500
:arrow: static kann man innerhalb einer VM umgehen :!:
Na Gott sei Dank, ich dachte schon, ich hätte eine gespaltene Persönlichkeit (da ich beim ersten Aufruf des Thread Deinen Text unter meinem Namen sah) :lol:
Ah, jetzt, ja, eine Insel... Gutes Beispiel. Jetzt verstehe ich es erst. Danke.
Man sollte also bei Plugins (um beim Topic "modularer Aufbau" zu bleiben) keine Singleton verwenden. Damit heisst die Devise bei Singletons: Augen auf beim Eierkauf - oder einfach: Aufpassen .
Wobei ich gestehen muss, dass ich noch nie einen ClassLoader benötigt habe.