AspectJ – pierwsze starcie

Zaczynamy mini kurs programowania aspektwego z ApectJ i Eclipse. Nie chcę wnikać w spinanie projektów z mavenem (tak z lenistwa), a zatem będzie tylko w eclipse.

Instalacja softu

Z tej strony pobieramy odpowiedni dla nas pakiecik i restartujemy Eclipse. Zakładam, że wiesz jak instalować plugin eclipsowy za pomocą Update Site. Jeżeli nie to sorry Winnetou. Kurs będzie mocno Eclipse only.
Następnie tworzymy nowy projekt AspectJ. Zostaniemy zapytani czy chcemy włączyć mechanizm weaveringu i zrestartować Eclipse. Oczywiście włączamy go i restartujemy IDE.

Podstawowe pojęcia

W tradycyjnym modelu obiektowym gdy zaczynamy pisać program to okazuje się, że w pewnym momencie należy do naszego kodu dodać kilkanaście różnych ciekawych czy nie, ale wymaganych funkcjonalności. Zazwyczaj jest to logowanie, kontrola dostępu, jakieś duperele związane z profilowaniem. Robiąc to za pomocą klasycznych metod zazwyczaj łamiemy dwie zasady. Po pierwsze SRP ponieważ nasza klasa zaczyna mieć wiele odpowiedzialności. A to logowanie, a to security, a tu jeszcze admin mówi, żeby włączyć profilowanie w jednym miejscu i natłuc raportów. Poza tym robi się burdel w kodzie. Oczywiście w pewnym momencie dochodzimy do wniosku, że większość z tych bajerów nie jest nam potrzebna i łamiemy OCP. Ponieważ by je wyłączyć zaczynamy grzebać w oryginalnym kodzie. Tu z pomocą przychodzą nam aspekty. Aspekt jest to moduł aplikacji zawierający jakąś funkcjonalność, która jest wtłaczana do kodu za pomocą kompilatora aspektowego. Aspekt jest opisany za pomocą specjalnego języka, na którego podstawie kompilator odpowiednio modyfikuje kod wynikowy w procesie kompilacji. Poniżej kilka ważnych pojęć
Punkt dostępu – ang. Join Point – dowolny punkt w kodzie, w którym kompilator aspektowy może wstawić kod aspektu. To czym jest ten punkt zależy od konkretnej implementacji języka aspektowego. W przypadku AspectJ są to metody, klasy, pola.
Punkt przecięcia – ang. Pointcut – konkretny punkt dostępu do którego stosowany jest dany aspekt.
Porada – ang. advice – kod który jest wstawiany.
Wiem, że osoby, które miały do czynienia z programowaniem aspektowym uznają ten opis za uproszczony, ale KISS MY ASS (Keep Is Simple Stupid. Mayby You Are Support Soft).

Co to jest AspectJ?

Znowu upraszczając jest to rozszerzenie języka Java o kilka słów kluczowych i mechanizm weaveringu, czyli właśnie wkompilowywania kodu. Inaczej rzecz ujmując jest to biblioteka, która zawiera specjalny kompilator, będący rozszerzeniem kompilatora javy o funkcjonalność jaką jest obsługa aspektów. Zawiera specjalnego agenta JVM, który może wykonać pracę kompilatora w trakcie ładowania kodu. Jest to zacne i przydatne rozwiązanie.
AspectJ wspiera dwa rodzaje składni. Pliki .aj, które są „naturalne” w jego środowisku oraz mechanizm oparty o adnotacje i zwykłe klasy javy. Przy czym każdy prawidłowy plik java jest prawidłowym plikiem aspektowym. Koniec końców AspectJ jest tylko rozszerzeniem języka (trochę jak C++ do C), a nie czymś całkowicie nowym.

Zajawka

Dzisiejszą część poświęcimy tylko na prostą aplikację w tylu „hello world”, ale pozwoli ona załapać Ci jak działa programowanie aspektowe. Na początek mamy kod taki jak poniżej:

Listing 1. Bazowa aplikacja

package pl.koziolekweb.blog.aspectj;

public class Main {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Service service = new Service();
		service.welcome();
		service.welcome("Koziołek");
	}

}
//....
package pl.koziolekweb.blog.aspectj;

public class Service {
	public void welcome() {
		System.out.println("Witamy w systemie");
	}

	public void welcome(String name) {
		System.out.println("Witaj " + name);
	}
}

Naszym zadaniem jest zabezpieczyć aplikację przed niepożądanymi wywołaniami. Posłuży nam do tego specjalna klasa NKWD:

Listing 2. Klasa NKWD – odpowiada za bezpieczeństwo

package pl.koziolekweb.blog.aspectj;

import java.util.Scanner;

public class NKWD {

	private boolean isAuth = false;

	public void auth() {
		if (isAuth) {
			return;
		} else {
			System.out.print("podaj hasło: ");
			Scanner scanner = new Scanner(System.in);
			String password = scanner.next();
			if ("dupa".equals(password)) {
				isAuth = true;
			}
			else{
				throw new RuntimeException("Wypad na Łubiankę");
			}
		}
	}
}

Pytanie co zrobimy w klasycznym podejściu? Pewno wystarczy utworzyć fabrykę obiektów NKWD, która będzie dbała o to by do każdego wątku był przyporządkowany odpowiedni obiekt NKWD. Będziemy go pobierać w każdej metodzie i w razie czego sprawdzać… no cóż… chłopaki od php zapewne by na tym poprzestali, ale mają łatwiej, bo jest u nich funkcja require_once i przetwarzanie skryptu zatem można takie sztuki robić. My niestety używamy języka programowania i zastosujemy programowanie aspektowe.
Stworzymy zatem aspekt, który będzie przed każdym wywołaniem metody welcome sprawdzał czy wszystko śmiga.

Listing 3. Nasz pierwszy aspekt

package pl.koziolekweb.blog.aspectj;

public aspect SecurityAspect {
	private NKWD nkwd = new NKWD();
	
	pointcut auth() : call( * Service.welcome(..));
	
	before() : auth() {
		System.out.println("NKWD działa");
		nkwd.auth();
	}
}

Kompilujemy i uruchamiamy jako aplikację AspectJ. Efekt:

Listing 4. wyniki działania naszego pierwszego aspektu

NKWD działa
podaj hasło: dupa
Witamy w systemie
NKWD działa
Witaj Koziołek

Chwila ze składnią

Za pomocą słowa pointcut definiujemy miejsce przecięcia. Format jest dość swobodny w przypadku AspectJ zazwyczaj używamy składni bardzo zbliżonej do UMLowego definiowania sygnatur metod. Znak * oznacza dowolne słowo, a .. dowolną ilość słów.
Słowo before wskazuje, że dana porada będzie uruchamiana przed wywołaniem dowolnej metody pasującej do wskazanego punktu przecięcia.

Podsumowanie

Czytam „AspectJ in Action” i mam nadzieję, że powstanie kacusiowa recenzja.

3 myśli na temat “AspectJ – pierwsze starcie

  1. Dobry tekst, więcej takich.
    Ciekawych paradygmatów do przyswojenia nigdy za wiele 🙂

  2. Ech, a mi się „aspekty” już trwale kojarzą z (e)lispowym defadvice, która to funkcja – agresywnie używana w licznych modułach – sprawia, że nie da się w ogóle niczego sensownego zakładać o semantyce wołanych funkcji, bo kto wie gdzie kto i czym je przeadviceował 😉

    Zresztą, mam wrażenie, że o dobre przykłady użycia wcale nie jest łatwo. Logowanie, transakcje, uprawnienia – wszystko zresztą trochę na siłę (bo np. uprawnienia często się jednak wiążą z jakimś użyciem informacji identyfikacyjnych w prawdziwym kodzie)

    PS Artykuł fajny, tylko przydałoby się więcej światła na różnicę między jointpoint a pointcut.

  3. @Mekk, co różnic to jeszcze chyba będzie trzeba to opisać inaczej. Nastawiam się na uproszczenie tematu tak by czytająca to osoba była wstanie od ręki wykorzystać wiedzę, a nie tonęła w teorii.

Napisz odpowiedź

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax