{"id":279,"date":"2009-03-26T14:17:00","date_gmt":"2009-03-26T14:17:00","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=279"},"modified":"2025-06-01T13:16:23","modified_gmt":"2025-06-01T11:16:23","slug":"einfuhrung-in-den-eventbus","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2009\/03\/einfuhrung-in-den-eventbus\/","title":{"rendered":"Einf\u00fchrung in den EventBus"},"content":{"rendered":"<p><strong>Was ist EventBus?<\/strong><\/p>\n<ul>\n<li>Die Idee vom EventBus <a title=\"https:\/\/eventbus.dev.java.net\/\" href=\"https:\/\/eventbus.dev.java.net\/\">https:\/\/eventbus.dev.java.net\/<\/a> ist schnell in einem Satz beschrieben:\n<ul>\n<li>Biete einen Publish\/Subscribe-Ereignisdienst f\u00fcr Applikationen an, die innerhalb einer JVM laufen.<\/li>\n<li>Anders als also JMS, funktioniert EventBus nur in einer JVM, aber nicht \u00fcber Rechnergrenzen.<\/li>\n<\/ul>\n<\/li>\n<li>Ereignisbehandlung wird \u00fcblicherweise \u00fcber Observer\/Observable oder \u00fcber Listener realisiert.\n<ul>\n<li>Listener sind aber l\u00e4stig zu schreiben: Man ben\u00f6tigt XXXEventListener Schnittstellen und Implementierungen, addXXXListener(), removeXXXListener() und fireEventListener() Methoden und vielleicht XXXEvent-Klassen.<\/li>\n<\/ul>\n<\/li>\n<li>EventBus vereinfacht das und mit zwei Typen und drei Methoden ist ein erstes Beispiel programmiert.<\/li>\n<\/ul>\n<p><strong>Erstes Beispiel<\/strong><\/p>\n<p>&nbsp;<\/p>\n<pre class=\"prettyprint\">import org.bushe.swing.event.EventBus;\r\nimport org.bushe.swing.event.EventSubscriber;<\/pre>\n<p>class Observer<br \/>\n{<br \/>\nObserver()<br \/>\n{<br \/>\n<strong>EventBus.subscribe<\/strong>( Object.class, new <strong>EventSubscriber<\/strong>&lt;Object&gt;() {<br \/>\n@Override public void <strong>onEvent<\/strong>( Object evt ) {<br \/>\nSystem.out.println( evt );<br \/>\n}<br \/>\n} );<br \/>\n}<br \/>\n}<\/p>\n<p>public class First<br \/>\n{<br \/>\npublic static void main( String args[] )<br \/>\n{<br \/>\nnew Observer();<br \/>\n<strong>EventBus.publish<\/strong>( &#8222;Hallo Welt&#8220; );<br \/>\n}<br \/>\n}<\/p>\n<p><strong>Analyse<\/strong><\/p>\n<ul>\n<li>EventBus.publish( Object ) sendet ein Ereignis an alle Interessenten aus.<\/li>\n<li>EventBus.subscribe( Class, EventSubscriber ) meldet f\u00fcr einen speziellen Klassentyp einen Listener an.\n<ul>\n<li>Das besondere: Es ist hierarchisch. Wir senden mit EventBus.publish( &#8222;Hallo&#8220; ) einen String, aber da String eine Unterklasse von Object ist, bekommt unser &#8222;Alles&#8220;-Listener die Nachricht.<\/li>\n<li>EventBus.unsubscribe() meldet den Interessenten wieder ab.<\/li>\n<\/ul>\n<\/li>\n<li>Der EventSubscriber ist generisch deklariert, so dass es bei EventSubscriber&lt;Object&gt; somit onEvent( Object evt ) hei\u00dft.<\/li>\n<\/ul>\n<p><strong>Hierarchien<\/strong><\/p>\n<ul>\n<li>Mit der M\u00f6glichkeit Event-Hierarchien zu bilden gibt es eine gro\u00dfe Flexibilit\u00e4t.<\/li>\n<li>So horcht folgendes auf alle IOException-Events: EventBus.subscribe( IOException.class, new EventSubscriber() { &#8230; EventBus.publish( new FileNotFoundException() );<\/li>\n<li>Ist dieses Matchen nicht gew\u00fcnscht, verwendet man statt EventBus.subscribe() die Methode EventBus.subscribeExactly().<\/li>\n<\/ul>\n<p><strong>Hierarchien bei Generischen Typen (1)<\/strong><\/p>\n<ul>\n<li>Bei Generics funktioniert ein .class nicht wie erwartet.\n<ul>\n<li>Es liefert System.out.println( new Holder&lt;String&gt;().getClass() ); und System.out.println( new Holder&lt;StringBuffer&gt;().getClass() ); immer nur class javax.xml.ws.Holder.<\/li>\n<\/ul>\n<\/li>\n<li>Was passiert bei EventBus.subscribe( Holder.class, new EventSubscriber() { &#8230; und EventBus.publish( new Holder&lt;String&gt;(&#8222;String value&#8220;) ); EventBus.publish( new Holder&lt;Date&gt;( new Date() ) );<\/li>\n<li>In beiden F\u00e4llen wird der Listener benachrichtigt.<\/li>\n<\/ul>\n<p><strong>Hierarchien bei Generischen Typen (2)<\/strong><\/p>\n<ul>\n<li>Soll eine Trennung aufgrund des generischen Typs stattfinden, l\u00e4sst sich auf ein java.lang.reflect.Type anmelden und Senden. Der Quellcode ist ein wenig komplex:<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<pre class=\"prettyprint\">EventBus.subscribe( new TypeReference&lt;Holder&lt;Date&gt;&gt;(){}.getType(), new EventSubscriber&lt;Object&gt;() {\r\n    @Override public void onEvent( Object evt ) {\r\n      System.out.println( ((Holder)evt).value );\r\n    }\r\n  } );<\/pre>\n<p>EventBus.publish( new TypeReference&lt;Holder&lt;String&gt;&gt;(){}.getType(), new Holder&lt;String&gt;(&#8222;String value&#8220;) );<br \/>\nEventBus.publish( new TypeReference&lt;Holder&lt;Date&gt;&gt;(){}.getType(), new Holder&lt;Date&gt;( new Date() ) );<\/p>\n<p>Jetzt wird nur noch das Datum empfangen und auf den Bildschirm kommt:<\/p>\n<p>Thu Mar 26 14:28:15 CET 2009<\/p>\n<p><strong>Topics<\/strong><\/p>\n<ul>\n<li>Im Regelfall sind nicht alle Interessenten an allen Ereignissen interessiert.<\/li>\n<li>Man kann nun verschiedene Ereignistypen definieren, doch wenn man etwa Strings verschicken m\u00f6chte, ist es l\u00e4stig, diesen String extra in in Ereignisobjekt zu verpacken.<\/li>\n<li>Die L\u00f6sung sind Topics, also bestimmte Themen, zu denen man sich anmelden kann und zu denen man schicken kann.<\/li>\n<li>Es \u00e4ndern sich in der API zwei Dinge: Bei publish() ist der Topic anzugeben, bei subscribe() ist statt ein EventSubscriber ein EventTopicSubscriber n\u00f6tig, da der Listener neben dem Event auch den Topic \u00fcbergibt.<\/li>\n<\/ul>\n<p><strong>Beispiel mit Topics<\/strong><\/p>\n<p>&nbsp;<\/p>\n<pre class=\"prettyprint\">EventBus.subscribe( \"Error\", new EventTopicSubscriber() {\r\n@Override public void onEvent( String topic, Object evt ) { System.out.printf( \"'%s' f\u00fcr Topic '%s'%n\", evt, topic ); }\r\n} );<\/pre>\n<p>EventBus.publish( &#8222;Error&#8220;, &#8222;Hallo Welt&#8220; );<\/p>\n<p>Liefert dann<\/p>\n<p>&#8218;Hallo Welt&#8216; f\u00fcr Topic &#8218;Error&#8216;<\/p>\n<p><strong>F\u00fcr mehrere Topics anmelden<\/strong><\/p>\n<ul>\n<li>Soll ein EventTopicSubscriber f\u00fcr mehrere Topics angemeldet werden, so kann man nat\u00fcrlich schreiben: EventTopicSubscriber ets = &#8230; EventBus.subscribe( topic1, ets ); EventBus.subscribe( topic2, ets );<\/li>\n<li>Eine weitere subscribe()-Methode ist subscribe(java.util.regex.Pattern, EventTopicSubscriber).\n<ul>\n<li>Damit lassen sich leicht \u00fcber regul\u00e4re Ausdr\u00fccke Gruppen bilden.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>EventBus.subscribe( Pattern.compile( &#8222;Error|Warning&#8220; ), new EventTopicSubscriber() { &#8230;<\/p>\n<p>EventBus.publish( &#8222;Error&#8220;, &#8222;Hallo Welt&#8220; );<br \/>\nEventBus.publish( &#8222;Info&#8220;, &#8222;Hallo Welt&#8220; ); \/\/ Kommt nicht an<br \/>\nEventBus.publish( &#8222;Warning&#8220;, &#8222;Hallo Welt&#8220; );<\/p>\n<p><strong>EventBus und AWT Event Dispatching Thread (EDT)<\/strong><\/p>\n<ul>\n<li>Setzt man in die onEvent()-Methode die Anweisung System.out.println( Thread.currentThread() ); \/\/ Thread[AWT-EventQueue-0,6,main] so findet man, dass der EventBus den Programmcode im AWT Event Thread abarbeitet.<\/li>\n<li>Das ist nat\u00fcrlich gut f\u00fcr Aktionen, die an den Swing-Komponenten vorgenommen werden.\n<ul>\n<li>Zum Beispiel: Ein beliebiger Thread l\u00e4dt Daten und m\u00f6chte nach dem Laden eine Statuszeile aktualisieren. Schickt der Thread dann mit publish() ein Ereignis, wird der Programmcode vom Empf\u00e4nger im EDT ausgef\u00fchrt, sodass dort etwa ein setText() auf einem JLabel der Statuszeile erlaubt ist.<\/li>\n<\/ul>\n<\/li>\n<li>Auf der anderen Seite hat das zur Konsequenz, dass der Programmcode schnell sein muss, damit der EDT nicht zu lange blockiert wird.<\/li>\n<li>Bei nicht-AWT-Anwendungen ist die Abarbeitung im EDT unsinnig<\/li>\n<\/ul>\n<p><strong>EventBus und SwingEventService<\/strong><\/p>\n<ul>\n<li>Der EventBus setzt den Programmcode standardm\u00e4\u00dfig in den EDT, kann ihn aber auch von einem anderen Thread abarbeiten lassen.<\/li>\n<li>System.out.println( EventBus.getGlobalEventService() ); \/\/ <a href=\"mailto:org.bushe.swing.event.SwingEventService@173a10f\">org.bushe.swing.event.SwingEventService@173a10f<\/a><\/li>\n<li>Standardm\u00e4\u00dfig nutzt EventBus also intern eine SwingEventService-Objekt.<\/li>\n<li>Alternativ kann man schreiben: SwingEventService eventing = new SwingEventService(); eventing.subscribe( Object.class, new EventSubscriber() { @Override public void onEvent( Object evt ) { System.out.println( evt ); } } ); eventing.publish( &#8222;Hallo&#8220; );<\/li>\n<\/ul>\n<p><strong>SwingEventService und ThreadSafeEventService<\/strong><\/p>\n<ul>\n<li>Neben dem SwingEventService gibt es den ThreadSafeEventService f\u00fcr eine Abarbeitung, die nicht im EDT stattfindet.\n<ul>\n<li>Beide Implementieren die Schnittstelle EventService. (Genaugenommen ist SwingEventService eine Unterklasse von ThreadSafeEventService.)<\/li>\n<\/ul>\n<\/li>\n<li>Man schreibt dann: EventService eventing = new ThreadSafeEventService(); eventing.subsribe(&#8230;); eventing.publish(&#8230;);<\/li>\n<\/ul>\n<p><strong>EventServiceLocator (1)<\/strong><\/p>\n<ul>\n<li>Den EventBus kann man nun so umstellen, dass er standardm\u00e4\u00dfig den ThreadSafeEventService nutzt.\n<ul>\n<li>Dazu wird intern ein EventServiceLocator eingesetzt.<\/li>\n<\/ul>\n<p>System.out.println( EventBus.getGlobalEventService() ); \/\/ org.bushe.swing.event.SwingEventService System.out.println( EventServiceLocator.getEventBusService() ); \/\/ org.bushe.swing.event.SwingEventService System.out.println( EventServiceLocator.getSwingEventService() ); \/\/ org.bushe.swing.event.SwingEventService<\/li>\n<li>Der EventServiceLocator verwaltet also zwei unterschiedliche Event-Services.<\/li>\n<\/ul>\n<p><strong>EventServiceLocator (2)<\/strong><\/p>\n<p>try { EventServiceLocator.setEventService(EventServiceLocator.SERVICE_NAME_EVENT_BUS, new ThreadSafeEventService()); } catch ( EventServiceExistsException e ) { e.printStackTrace(); }<\/p>\n<p>System.out.println( EventBus.getGlobalEventService() ); \/\/ org.bushe.swing.event.ThreadSafeEventService System.out.println( EventServiceLocator.getEventBusService() ); \/\/ org.bushe.swing.event.ThreadSafeEventService System.out.println( EventServiceLocator.getSwingEventService() ); \/\/ org.bushe.swing.event.SwingEventService<\/p>\n<p><em>Achtung: Das muss an den Anfang bevor ein Event je den Bus sieht!<\/em><\/p>\n<p><strong>EventServiceLocator (3)<\/strong><\/p>\n<ul>\n<li>Wenn man nun Ereignisse \u00fcber einen &#8222;normalen&#8220; Thread bearbeitet haben m\u00f6chte, schreibt man wie \u00fcblich EventBus.publish()\/subscribe().<\/li>\n<li>Sollen die Eventanweisungen in den EDT, schreibt man EventServiceLocator.getSwingEventService().publish()\/subscribe().<\/li>\n<\/ul>\n<p><strong>H\u00e4ngende Referenzen bei Listenern<\/strong><\/p>\n<ul>\n<li>Ein h\u00e4ufiges Problem bei Listenern insbesondere bei Swing-Anwendungen sind angemeldete Listener, f\u00fcr die der Interessent aber schon weg ist. Ein Szenario:\n<ul>\n<li>Ein Textfeld einer Maske meldet eine Listener an, um bei Modell\u00e4nderungen die Daten darstellen zu k\u00f6nnen.<\/li>\n<li>Das Model speichert den Listener und indirekt auch eine Referenz auf das Textfeld.<\/li>\n<li>Die Maske verschwindet, somit auch der Interessent f\u00fcr Modell\u00e4nderungen.<\/li>\n<li>Da aber das Model den Listener und indirekt das Textfeld referenziert, kann der GC das Textfeld gar nicht freigeben.<\/li>\n<li>Daher m\u00fcssen Listener immer abgemeldet werden, oder&#8230;?<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><strong>EventBus und WeakReference (1)<\/strong><\/p>\n<ul>\n<li>Damit Listener bei nicht mehr aktiven Horchern automatisch verschwinden, kann man WeakReferences einsetzen.<\/li>\n<li>Eine WeakReference ist wie ein Proxy, der ein anderes Objekt ummantelt. Ist die WeakReference die einzige Referenz, die sich f\u00fcr das Objekt interessiert, so kann sie beim n\u00e4chsten GC das Objekt wegr\u00e4umen. Der Proxy wird dann ebenfalls nicht mehr benutzt.<\/li>\n<li>Standardm\u00e4\u00dfig mantelt die EventBus.subscribe(XXX, EventSubscriber)-Methoden den EventSubscriber in eine WeakReference.\n<ul>\n<li>Wenn es also keinen starken Verweis auf den EventSubscriber mehr von au\u00dfen gibt, wird der EventBus diesen Listener automatisch abmelden.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><strong>EventBus und WeakReference (2)<\/strong><\/p>\n<p>&nbsp;<\/p>\n<pre class=\"prettyprint\">public class First\r\n{\r\nFirst()\r\n{\r\n  new Observer();\r\n}<\/pre>\n<p>public static void main( String args[] )<br \/>\n{<br \/>\nnew First();<br \/>\n\/\/ System.gc();<br \/>\nEventBus.publish( &#8222;Hallo Welt&#8220; );<br \/>\n}<br \/>\n}<\/p>\n<p><strong>EventBus und WeakReference (3)<\/strong><\/p>\n<ul>\n<li>Der Konstruktor erzeugt einen Observer, der aber nicht referenziert wird. Nach dem der Konstruktor abgearbeitet wurde, ist das Exemplar Freiwild f\u00fcr den GC.<\/li>\n<li>Wenn in main() System.gc() aufgerufen wird, wird aufger\u00e4umt. Damit werden die WeakReferences entsort also auch der Listener beim EventBus abgemeldet.<\/li>\n<li>Ist das System.gc() raus, steht immer noch &#8222;Hallo&#8220;, weil das Objekt noch da ist.<\/li>\n<li>Soll EventBus <em>keinen<\/em> WeakReference-Beh\u00e4lter um den Listener bauen, so nutzt man subscribeStrongly(XXX, EventSubscriber) bzw. subscribeExactlyStrongly(XXX, EventSubscriber).<\/li>\n<\/ul>\n<p><strong>Was fehlt noch?<\/strong><\/p>\n<ul>\n<li>EventBus kann die Listener aufz\u00e4hlen<\/li>\n<li>EventBus kann Veto<\/li>\n<li>Listener k\u00f6nnen auch \u00fcber Annotationen angemeldet werden<\/li>\n<li>Timer k\u00f6nnen Events \u00fcberwachen<\/li>\n<li>Events k\u00f6nnen gechached werden, sodass sp\u00e4tere Anmelder die Events auch bekommen<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Was ist EventBus? Die Idee vom EventBus https:\/\/eventbus.dev.java.net\/ ist schnell in einem Satz beschrieben: Biete einen Publish\/Subscribe-Ereignisdienst f\u00fcr Applikationen an, die innerhalb einer JVM laufen. Anders als also JMS, funktioniert EventBus nur in einer JVM, aber nicht \u00fcber Rechnergrenzen. Ereignisbehandlung wird \u00fcblicherweise \u00fcber Observer\/Observable oder \u00fcber Listener realisiert. Listener sind aber l\u00e4stig zu schreiben: Man [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","_links_to":"","_links_to_target":""},"categories":[4],"tags":[],"class_list":["post-279","post","type-post","status-publish","format-standard","hentry","category-open-source"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/279","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/comments?post=279"}],"version-history":[{"count":1,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/279\/revisions"}],"predecessor-version":[{"id":4715,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/279\/revisions\/4715"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=279"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=279"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=279"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}