Adnotacje w Javie

Przyznaję, że wykrakałem. Wykrakałem potencjalne opóźnienie we wpisach no i viola – w ostatni dzień urlopu żona miała mały wypadek na nartach no i w związku z tym dopiero dziś trochę czasu się znalazło by usiąść do klawiatury. No nic, zaczynamy.

Na warsztacie dziś adnotacje. Jest to rozszerzenie Javy, które zostało wprowadzone w wersji 1.5 i miało ponoć mieć fundamentalne znaczenie. Hmmmm, no dobrze. W “Thinking in Java” jej autor pisze o nich następująco: “Adnotacje (zwane też metadanymi) to sformalizowany sposób uzupełniania kodu o informacje z przeznaczeniem do wykorzystania w przyszłości”. Bardzo klarownie.

I dalej zasadniczo nie jest lepiej. Cały rozdział opisuje ich tworzenie, a także predefiniowane adnotacje istniejące w samym języku. Jednakże wciąż do końca nie wiadomo po co w ogóle one istnieją i dlaczego są takie przełomowe. Postanowiłem wyruszyć na poszukiwania i znalazłem takie oto zastosowania adnotacji:

– automatyczne generowanie deskryptorów wdrożenia i inszych tego typu rzeczy (co tyczy się głównie JavaBeans, ale też i zwykłej choćby konfiguracji i to zaraz omówię)
– dodatkowa dokumentacja kodu (poza komentarzami). Najbardziej reprezentatywnym przykładem jest JUnit, który od wersji 4.0 biblioteki używa adnotacji by uniknąć tworzenia własnej przestrzeni nazw (poprzez dodawanie suffixa -Test do nazw klas). Przykład można zobaczyć we wpisie poświęconym TDD.

Pisząc klienta do gry w Arkadię zetknąłem się z zadaniem konfiguracji aplikacji. Wypadałoby bowiem dać użytkownikowi możliwość dopasowania jej zachowania pod własne wymagania. W jednym z poprzednich wpisów podałem przykład oparcia tego tematu na plikach XML. Jednakże problem polega na tym, że zawsze taki plik mógł np. zostać uszkodzony, nie będzie można go odczytać, ktoś go brzydko zmienił (usunięto pewne fragmenty). Zawsze zatem w aplikacji musimy mieć podstawowe wartości inicjacyjne dla zmiennych, albo też po prostu sypniemy wyjątkiem w razie jakiegokolwiek problemu – wówczas aplikacja najlepiej by w ogóle się nie uruchomiła. Jednakże nie jest to rozwiązanie ani eleganckie, ani profesjonalne. U mnie póki co funkcjonuje oddzielna klasa, której jedynym zadaniem jest trzymanie defaultowych danych konfiguracyjnych. Teraz jestem mądrzejszy i pewnie przerobię to na adnotacje, ale to później…

Póki co rozpatrzmy taki oto przykład. Mamy aplikację w języku Java, którą uruchamiamy dość często z różnymi silnikami baz danych. Wynika z tego prosty wniosek, że wypadałoby gdzieś trzymać dane konfiguracji połączenia (host, driver, etc). Import danych z plików zewnętrznych lub samo wywołanie odpowiedniej metody z danymi przekazanymi w parametrze może zakończyć się niepowodzeniem (najczęściej wyrzuceniem wyjątku). Czy nie byłoby dobrze wówczas obsłużyć go poprzez próbę nawiązania ponownego połączenia z danymi domyślnymi? Jest to rozwiązanie bardzo eleganckie i bezpieczne. Jeśli bowiem inicjalizacja z danymi domyślnymi nie powiedzie się, to znaczy, że jest z naszą bazą danych naprawdę źle.

Krok 1: budujemy adnotację

package net.chlebik;
import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface dbAnnotation
{
String login();
String password();
String host();
String driver();
}

Krok 2: tworzymy klasę z metodami łączącymi (to takie rozbudowane w celach edukacyjnych)

package net.chlebik;

public class dbAnnotationDrivers {

private String driver;
private String host;
private String login;
private String password;

@dbAnnotation(driver = "com.mysql.jdbc.Driver",host = "jdbc:mysql://localhost/chlebik",login = "rootmysql",password = "haslomysql")
public void mysqlDriver( String driver, String host, String login, String password )
{
this.driver = driver;
this.host = host;
this.login = login;
this.password = password;
}

@dbAnnotation(driver = "org.postgresql.Driver",host = "jdbc:postgresql://localhost/chlebik",login = "rootpg",password = "haslopg")
public void pgDriver( String driver, String host, String login, String password )
{
this.driver = driver;
this.host = host;
this.login = login;
this.password = password;
}

}

Krok 3: parser adnotacji

package net.chlebik;
import java.lang.reflect.Method;

public class NewMain
{
public static void main( String[] args ) throws Exception
{

//dobieramy sie do klasy jako takiej
Class drivery = dbAnnotationDrivers.class;

//Mamy klase zatem dobierzmy sie do jej metod
// Przejdziemy po wszystkich metodach i wyciagniemy ich adnotacje
for( Method metoda : drivery.getMethods() ) {

if( metoda.getAnnotation(dbAnnotation.class) instanceof dbAnnotation )
{
System.out.println( "Driver dla tej metody to: " + metoda.getAnnotation( dbAnnotation.class).driver() );
System.out.println( "Host dla tej metody to: " + metoda.getAnnotation( dbAnnotation.class).host() );
System.out.println( "Login dla tej metody to: " + metoda.getAnnotation( dbAnnotation.class).login() );
System.out.println( "Hasło dla tej metody to: " + metoda.getAnnotation( dbAnnotation.class).password() );
System.out.println("");
}

}

}
}

Wynik działania programu:

Driver dla tej metody to: com.mysql.jdbc.Driver
Host dla tej metody to: jdbc:mysql://localhost/chlebik
Login dla tej metody to: rootmysql
Hasło dla tej metody to: haslomysql

Driver dla tej metody to: org.postgresql.Driver
Host dla tej metody to: jdbc:postgresql://localhost/chlebik
Login dla tej metody to: rootpg
Hasło dla tej metody to: haslopg

Ostatni krok tylko przechodzi po całej klasie dbAnnotationDrivers, wybiera jej metody, które posiadają dbAnnotation i drukuje na konsolę dane przekazane poprzez wywołanie adnotacji przed konkretną metodą. Wystarczyłoby objąć ten kod blokiem try..catch, dopisać próbę połączenia, a w blok catch wrzucić ponowną próbę połączenia z domyślnymi danymi, które wyciągnąć należy w sposób przeze mnie podany.

Jak widać takie adnotacje oszczędzają pracy – ponoć obecnie to trend by tzw. metadane umieszczać w kodzie klas. Trochę dla mnie to dziwne, gdyż potencjalna zmiana tychże danych wymaga ponownej kompilacji klasy (o opozycji XMLa pisali mądrzejsi ode mnie). Jednakże pewność, że podstawowe dane pozostaną zawsze dostępne, a także, że będą poprawne (kontrola typów!) sprawia, iż adnotacje są bardzo mocnym elementem języka, który na pewno wypada poznać.

Advertisements

3 thoughts on “Adnotacje w Javie

  1. krzyzy

    Możliwości wykorzystania adnotacji są ogromne. Mi w pracy a poźniej w opensourcowym projekcie (SimpleXtensions) przydały się do napisania mechanizmu podobnego do eclipse extensions ale niewymagającego pliku plugins.xml. Wszystkie rozszerzenia i punkty rozszerzeń znajdowane są automatycznie właśnie dzięki adnotacjom oraz skanowaniu classpath.

    Zachęcam do korzystanie z SimpleXtensions, ponieważ daje ogromne możliwości a jednocześnie jest bardzo prosty w obsłudze

  2. Grzesiek

    Adnotacje pozwalają tworzyć krótszy, bardziej elegancki, klarowny kod. Przykład to choćby specyfikacja JAX-RS do tworzenie usług sieciowych REST. Adnotacje typu @GET, @POST, @PUT, @GZIP itd. są przypisywane do konkretnych metod (parametry zresztą też), podobnie można mapować POJO na dokument XML via JAXB (@XmlElement, @XmlAttribute, @XmlAccessorType etc.).

  3. jasio

    Po przeczytaniu rozdzialu z Thinking in java i tego artykulu stanowczo stwierdzam, ze obydwa sa rownie przydatne w zrozumieniu zagadnienia, czyli praktycznie wcale…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s