---
title: 'Einführung in den EventBus'
url: 'https://www.tutego.de/blog/javainsel/2009/03/einfuhrung-in-den-eventbus'
markdown: 'https://www.tutego.de/blog/javainsel/2009/03/einfuhrung-in-den-eventbus.md'
date: '2009-03-26'
description: 'Was ist EventBus? Die Idee vom EventBus https://eventbus.dev.java.net/ ist schnell in einem Satz beschrieben: Biete einen Publish/Subscribe-Ereignisdienst für Applikationen an, die innerhalb einer JVM laufen. Anders als also JMS, funktioniert EventBus nur in einer JVM, aber nicht über Rechnergre…'
taxonomy:
  category:
    - 'Open Source'
---

# Einführung in den EventBus

![Illustration einer tropischen Insel mit Palmen und einem hohen Berg](https://www.tutego.de/blog/javainsel/user/themes/tutego/images/insel-cover.jpg)# Java Blog für Programmierer

[Java Blog für Programmierer](https://www.tutego.de/blog/javainsel/)

## Einführung in den EventBus

26.03.2009 · [Christian Ullenboom](https://www.tutego.de/blog/javainsel/author/admin/)

**Was ist `EventBus`?**

- Die Idee vom `EventBus` [https://eventbus.dev.java.net/](https://eventbus.dev.java.net/ "https://eventbus.dev.java.net/") ist schnell in einem Satz beschrieben: 
    - Biete einen Publish/Subscribe-Ereignisdienst für Applikationen an, die innerhalb einer JVM laufen.
    - Anders als also JMS, funktioniert EventBus nur in einer JVM, aber nicht über Rechnergrenzen.
- Ereignisbehandlung wird üblicherweise über Observer/Observable oder über Listener realisiert. 
    - Listener sind aber lästig zu schreiben: Man benötigt XXXEventListener Schnittstellen und Implementierungen, addXXXListener(), removeXXXListener() und fireEventListener() Methoden und vielleicht XXXEvent-Klassen.
- `EventBus` vereinfacht das und mit zwei Typen und drei Methoden ist ein erstes Beispiel programmiert.

**Erstes Beispiel**

```java
import org.bushe.swing.event.EventBus;
import org.bushe.swing.event.EventSubscriber;
```

```java
class Observer
{
```

`Observer`()

```java
{
EventBus.subscribe( Object.class, new EventSubscriber<Object>() {
@Override public void onEvent( Object evt ) {
System.out.println( evt );
}
} );
}
}

public class First
{
```

public static void main( `String` args\[\] )

```java
{
new Observer();
EventBus.publish( "Hallo Welt" );
}
}
```

**Analyse**

- `EventBus.publish( Object )` sendet ein Ereignis an alle Interessenten aus.
- `EventBus.subscribe( Class, EventSubscriber )` meldet für einen speziellen Klassentyp einen Listener an. 
    - Das besondere: Es ist hierarchisch. Wir senden mit EventBus.publish( „Hallo“ ) einen String, aber da String eine Unterklasse von Object ist, bekommt unser „Alles“-Listener die Nachricht.
    - EventBus.unsubscribe() meldet den Interessenten wieder ab.
- Der `EventSubscriber` ist generisch deklariert, so dass es bei `EventSubscriber`<`Object`> somit `onEvent( Object evt )` heißt.

**Hierarchien**

- Mit der Möglichkeit Event-Hierarchien zu bilden gibt es eine große Flexibilität. ```java

    ```
- So horcht folgendes auf alle IOException-Events: EventBus.subscribe( IOException.class, new EventSubscriber() { ... EventBus.publish( new FileNotFoundException() ); ```

    ```
- Ist dieses Matchen nicht gewünscht, verwendet man statt `EventBus.subscribe()` die Methode `EventBus.subscribeExactly()`.

**Hierarchien bei Generischen Typen (1)**

- Bei Generics funktioniert ein .class nicht wie erwartet. 
    - Es liefert System.out.println( new Holder<String>().getClass() ); und System.out.println( new Holder<StringBuffer>().getClass() ); immer nur class javax.xml.ws.Holder. ```java

        ```
- Was passiert bei EventBus.subscribe( Holder.class, new EventSubscriber() { ... und EventBus.publish( new Holder("String value") ); EventBus.publish( new Holder( new Date() ) ); ```

    ```
- In beiden Fällen wird der Listener benachrichtigt.

**Hierarchien bei Generischen Typen (2)**

- Soll eine Trennung aufgrund des generischen Typs stattfinden, lässt sich auf ein `java.lang.reflect.Type` anmelden und Senden. Der Quellcode ist ein wenig komplex:

```java
EventBus.subscribe( new TypeReference(){}.getType(), new EventSubscriber() {
    @Override public void onEvent( Object evt ) {
      System.out.println( ((Holder)evt).value );
    }
  } );
```

```java
EventBus.publish( new TypeReference<Holder<String>>(){}.getType(), new Holder<String>("String value") );
EventBus.publish( new TypeReference<Holder<Date>>(){}.getType(), new Holder<Date>( new Date() ) );
```

Jetzt wird nur noch das Datum empfangen und auf den Bildschirm kommt:

Thu Mar 26 14:28:15 CET 2009

**Topics**

- Im Regelfall sind nicht alle Interessenten an allen Ereignissen interessiert.
- Man kann nun verschiedene Ereignistypen definieren, doch wenn man etwa Strings verschicken möchte, ist es lästig, diesen `String` extra in in Ereignisobjekt zu verpacken.
- Die Lösung sind Topics, also bestimmte Themen, zu denen man sich anmelden kann und zu denen man schicken kann.
- Es ändern sich in der API zwei Dinge: Bei `publish()` ist der Topic anzugeben, bei `subscribe()` ist statt ein `EventSubscriber` ein EventTopicSubscriber nötig, da der Listener neben dem Event auch den Topic übergibt.

**Beispiel mit Topics**

```java
EventBus.subscribe( "Error", new EventTopicSubscriber() {
@Override public void onEvent( String topic, Object evt ) { System.out.printf( "'%s' für Topic '%s'%n", evt, topic ); }
} );
```

```java
EventBus.publish( "Error", "Hallo Welt" );
```

Liefert dann

‚Hallo Welt‘ für Topic ‚Error‘

**Für mehrere Topics anmelden**

```java
- Soll ein EventTopicSubscriber für mehrere Topics angemeldet werden, so kann man natürlich schreiben: EventTopicSubscriber ets = ... EventBus.subscribe( topic1, ets ); EventBus.subscribe( topic2, ets );
```

- Eine weitere `subscribe()`-Methode ist `subscribe(java.util.regex.Pattern, EventTopicSubscriber)`. 
    - Damit lassen sich leicht über reguläre Ausdrücke Gruppen bilden.

```java
EventBus.subscribe( Pattern.compile( "Error|Warning" ), new EventTopicSubscriber() { ...

EventBus.publish( "Error", "Hallo Welt" );
EventBus.publish( "Info", "Hallo Welt" ); // Kommt nicht an
EventBus.publish( "Warning", "Hallo Welt" );
```

**`EventBus` und AWT Event Dispatching `Thread` (EDT)**

- 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.
- Das ist natürlich gut für Aktionen, die an den Swing-Komponenten vorgenommen werden. 
    - Zum Beispiel: Ein beliebiger Thread lädt Daten und möchte nach dem Laden eine Statuszeile aktualisieren. Schickt der Thread dann mit publish() ein Ereignis, wird der Programmcode vom Empfänger im EDT ausgeführt, sodass dort etwa ein setText() auf einem JLabel der Statuszeile erlaubt ist.
- Auf der anderen Seite hat das zur Konsequenz, dass der Programmcode schnell sein muss, damit der EDT nicht zu lange blockiert wird.
- Bei nicht-AWT-Anwendungen ist die Abarbeitung im EDT unsinnig

**`EventBus` und SwingEventService**

- Der `EventBus` setzt den Programmcode standardmäßig in den EDT, kann ihn aber auch von einem anderen `Thread` abarbeiten lassen. ```java

    ```
- System.out.println( EventBus.getGlobalEventService() ); // [org.bushe.swing.event.SwingEventService@173a10f](mailto:org.bushe.swing.event.SwingEventService@173a10f)```

    ```
- Standardmäßig nutzt `EventBus` also intern eine SwingEventService-Objekt. ```java

    ```
- 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( "Hallo" ); ```

    ```

**SwingEventService und ThreadSafeEventService**

- Neben dem SwingEventService gibt es den ThreadSafeEventService für eine Abarbeitung, die nicht im EDT stattfindet. 
    - Beide Implementieren die Schnittstelle EventService. (Genaugenommen ist SwingEventService eine Unterklasse von ThreadSafeEventService.) ```java

        ```
- Man schreibt dann: EventService eventing = new ThreadSafeEventService(); eventing.subsribe(...); eventing.publish(...); ```

    ```

**EventServiceLocator (1)**

- Den `EventBus` kann man nun so umstellen, dass er standardmäßig den ThreadSafeEventService nutzt.

    - Dazu wird intern ein EventServiceLocator eingesetzt.

    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
- Der EventServiceLocator verwaltet also zwei unterschiedliche Event-Services.

**EventServiceLocator (2)**

```java
try { EventServiceLocator.setEventService(EventServiceLocator.SERVICE_NAME_EVENT_BUS, new ThreadSafeEventService()); } catch ( EventServiceExistsException e ) { e.printStackTrace(); }

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
```

*Achtung: Das muss an den Anfang bevor ein Event je den Bus sieht!*

**EventServiceLocator (3)**

- Wenn man nun Ereignisse über einen „normalen“ `Thread` bearbeitet haben möchte, schreibt man wie üblich `EventBus.publish()`/subscribe().
- Sollen die Eventanweisungen in den EDT, schreibt man `EventServiceLocator.getSwingEventService().publish()`/subscribe().

**Hängende Referenzen bei Listenern**

- Ein häufiges Problem bei Listenern insbesondere bei Swing-Anwendungen sind angemeldete Listener, für die der Interessent aber schon weg ist. Ein Szenario: 
    - Ein Textfeld einer Maske meldet eine Listener an, um bei Modelländerungen die Daten darstellen zu können.
    - Das Model speichert den Listener und indirekt auch eine Referenz auf das Textfeld.
    - Die Maske verschwindet, somit auch der Interessent für Modelländerungen.
    - Da aber das Model den Listener und indirekt das Textfeld referenziert, kann der GC das Textfeld gar nicht freigeben.
    - Daher müssen Listener immer abgemeldet werden, oder…?

**`EventBus` und WeakReference (1)**

- Damit Listener bei nicht mehr aktiven Horchern automatisch verschwinden, kann man WeakReferences einsetzen.
- Eine WeakReference ist wie ein Proxy, der ein anderes Objekt ummantelt. Ist die WeakReference die einzige Referenz, die sich für das Objekt interessiert, so kann sie beim nächsten GC das Objekt wegräumen. Der Proxy wird dann ebenfalls nicht mehr benutzt.
- Standardmäßig mantelt die `EventBus.subscribe(XXX, EventSubscriber)`-Methoden den `EventSubscriber` in eine WeakReference. 
    - Wenn es also keinen starken Verweis auf den EventSubscriber mehr von außen gibt, wird der EventBus diesen Listener automatisch abmelden.

**`EventBus` und WeakReference (2)**

```java
public class First
{
First()
{
  new Observer();
}
```

public static void main( `String` args\[\] )

```java
{
new First();
// System.gc();
EventBus.publish( "Hallo Welt" );
}
}
```

**`EventBus` und WeakReference (3)**

- Der Konstruktor erzeugt einen `Observer`, der aber nicht referenziert wird. Nach dem der Konstruktor abgearbeitet wurde, ist das Exemplar Freiwild für den GC.
- Wenn in `main()` `System.gc()` aufgerufen wird, wird aufgeräumt. Damit werden die WeakReferences entsort also auch der Listener beim `EventBus` abgemeldet.
- Ist das `System.gc()` raus, steht immer noch „Hallo“, weil das Objekt noch da ist.
- Soll `EventBus` *keinen* WeakReference-Behälter um den Listener bauen, so nutzt man `subscribeStrongly(XXX, EventSubscriber)` bzw. `subscribeExactlyStrongly(XXX, EventSubscriber)`.

**Was fehlt noch?**

- `EventBus` kann die Listener aufzählen
- `EventBus` kann Veto
- Listener können auch über Annotationen angemeldet werden
- Timer können Events überwachen
- Events können gechached werden, sodass spätere Anmelder die Events auch bekommen

Kategorien: [Open Source](https://www.tutego.de/blog/javainsel/category/open-source/)

## 4 Kommentare

**someDude** · [24.09.2013 02:33](#comment-4889)

Erstes Beispiel = bad smell "Subscriptions are weak by default. \\\[...\\\] This anonymous inner class subscriber will likely get garbage collected almost immediately." http://eventbus.org/pivot.html#WarningSubscriptionsAreWeak

**bennidi** · [11.07.2013 15:54](#comment-4590)

Update: Mit Mbassador existiert das Problem der Speicherlecks nicht mehr, da per Default WeakReferences benutzt werden. Somit kann man alles in den Bus reinstopfen und muss sich nicht um Deregistrierung kümmern. Das ist sehr praktisch wenn die Objekte von anderen Frameworks wie Swing, Spring, Guice etc. gemanaged werden

**bennidi** · [11.07.2013 15:51](#comment-4589)

Danke für den Artikel und die Erklärungen. Der vorgestellte EventBus hat aber mehrere Schwächen. Zunächst sind viele Methoden statisch und es ist deshalb nicht möglich mehrere Instanzen zu verwenden. Außerdem muss man sich an die onEvent Konvention halten oder den AnnotationProcessor verwenden. Im Performance-Vergleich liegt EventBus außerdem an letzter Stelle, wie man hier sehen kann: codeblock.engio.net/?p=37‎ (Benchmark-Code auf github). Bessere Frameworks sind MBassador und/oder Google Guavas EventBus. Note: Ich bin Autor von MBassador.

**Gedärme: Event Bus für Google Guice** · [02.06.2010 18:27](#comment-3370)

\\\[...\\\] ist, steht das für einen Event Bus noch aus. Über eine Lösung habe ich schon geschrieben: http://www.tutego.de/blog/javainsel/2009/03/einfuhrung-in-den-eventbus/. Ein Event Bus sollte natürlich mit einem DI-Container harmonieren. Nutzer von Google Guice \\\[...\\\]

Die bisherigen Kommentare wurden aus WordPress übernommen. Neue Kommentare sind derzeit geschlossen.

[← Alle Beiträge](https://www.tutego.de/blog/javainsel/)

---

## Navigation

- Previous: [gxp: Google XML Pages](https://www.tutego.de/blog/javainsel/2009/03/gxp-google-xml-pages.md)
- Next: [Na endlich: JDK 7 feature list, build and integration schedule](https://www.tutego.de/blog/javainsel/2009/03/na-endlich-jdk-7-feature-list-build-and-integration-schedule.md)
