background image

 

 

Programowane dynamiczne

1

1. Ładowanie klas

2. Specyfikacja pliku 

class

 analiza przykładu: Hello world!

 narzędzie ASM

3. Programowanie aspektowe

 AspectJ

background image

 

 

Klasa abstrakcyjna ClassLoader

2

Do pobrania klasy JVM wykorzystuje instancję klasy 

java.lang.ClassLoader

Każda klasa zawiera referencję do do ładującego ją obiektu. Programista może 

sterować procesem ładowania klas:

ClassLoader 

loader

 = 

new

 NetworkClassLoader(

host

port

);

Object main = loader.loadClass(

"Main"

true

).newInstance();

  . . .

Przykładowa klasa:

class

 NetworkClassLoader 

extends

 ClassLoader {

String 

host

;

int

 

port

;

public

 Class findClass(String name) {

byte

[] b = loadClassData(name);

return

 defineClass(name, b, 0, b.

length

);

}

private

 

byte

[] loadClassData(String name) {

// load the class data from the connection

}

}

background image

 

 

Plik class

3

public

 

class

 Main {

/**

 

*

 

@param

 

args

 

*/

public

 

static

 

void

 main(String[] args){

System.

out

.println(

"Hello world!"

);

}

}

Nagłówek klasy:

CA FE BA BE

 – identyfikator formatu pliku class.

00 00

 

00 2E

 – numer wersji JVM: (

46

.

0

) – zgodność z Java 1.2

Ogólnie JVM 1.k (k>1) obsługuje klasy od 45.0 do 44+k.0 włącznie

00 22

 – ilość deklarowanych elementów (Constant Pool). Po tej deklaracji 

następują kolejne, 33 elementy.

background image

 

 

Plik class - Constant Pool

4

1. 

01

 

00 04

 

4D 61 69 6E

 

– 

string kodowany w UTF-8

 o długości 

4

Main

2. 

07

 

00 01

 – 

pierwszy

 z elementów to 

nazwa klasy

3. 

01

 

00 10

 java/lang/Object

4. 

07

 

00 03

 – 

trzeci

 element to 

nazwa klasy

5. 

01 

00 06

 

<init>

6. 

01

 

00 03

 ()V

7. 

01

 

00 04

 Code

8. 

0C

 

00 05 00 06

 – piąty

 element to 

nazwa metody lub atrybutu

szósty

 to 

typ 

(sygnatura)

: brak argumentów: 

()

, zwracany typ 

void

V

9. 

0A

 

00 04 00 08

 – 

klasa

 (

czwarty element

zawiera metodę

 (

ósmy element

)

10. 

01

 

00 0F

 

LineNumberTable

11. 

01

 

00 12

 LocalVariableTable

background image

 

 

Plik class - Constant Pool

5

12. 

01 

00 04

 

this

13. 

01

 

00 06

 LMain;

14. 

01

 

00 04

 main

15. 

01

 

00 16

 ([Ljava/lang/String;)V

16. 

01

 

00 10

 java/lang/System

17. 

07

 

00 10

 – 

szesnasty

 element to 

nazwa klasy

18. 

01

 

00 03

 out

19. 

01

 

00 15

 Ljava/io/PrintStream;

20. 

0C

 

00 12 00 13

 – 18-ty

 element to 

nazwa metody/atrybutu

19-ty

 to 

jego typ

21. 

09

 

00 11 00 14

 – 17-ta

 

klasa zawiera

 

20-ty

 

atrybut

22. 

01

 

00 0C

 Hello world!

23. 

08

 

00 16

 – 22-gi 

element to

 

stała tekstowa

24. 

01

 

00 13

 java/io/PrintStream

25. 

07

 

00 18

 – 24-ty 

element to

 

nazwa klasy

background image

 

 

Plik class - Constant Pool

6

26. 

01 

00 07

 

println

27. 

01

 

00 15

 (Ljava/lang/String;)V

28. 

0C

 

00 1A 00 1B

 – 26-ty

 element to 

nazwa metody/atrybutu

27-ty

 to 

jej 

sygnatura

29. 

0A

 

00 19 00 1C

 – 25-ta

 

klasa zawiera

 

28-tą

 

metodę

30. 

01 

00 04

 

args

31. 

01 

00 13

 

[Ljava/lang/String;

32. 

01 

00 0A

 

SourceFile

33. 

01 

00 09

 

Main.java

background image

 

 

Plik class

7

00 21

 – modyfikatory dostępu dla klasy: 

ACC_PUBLIC

 (

00 01

|

 

ACC_SUPER

 (

00 

20

) – ze względu na kompatybilność ze starszymi JVM.

00 02

 – numer elementu określającego klasę definiowaną w tym pliku.

00 04

 – numer elementu określającego nadklasę klasy definiowanej w tym pliku. 

00 00

 – liczba interfejsów

00 00

 – liczba atrybutów

00 02

 – liczba metod

background image

 

 

Plik class – metoda <init>

8

00 01

  – 

ACC_PUBLIC

 – metoda publiczna

00 05

 – indeks elementu zawierającego nazwę metody: 

<init>

00 06

 – indeks elementu z sygnaturą metody:

()V

00 01

 – liczba dodatkowych atrybutów metody

00 07

 – indeks elementu z nazwą atrybutu: 

Code

00 00 00 2F

 – długość atrybutu

00 01

 – rozmiar stosu

00 01

- rozmiar tablicy zmiennych lokalnych

00 00 00 05

 – długość kodu

2A B7 00 09 B1

00 00

 – długość tablicy wyjątków

00 02

 – liczba dodatkowych atrybutów

background image

 

 

Plik class – metoda <init>

9

00 0A

 – 

LineNumberTable

00 00 00 06

 – długość atrybutu

00 01

 – długość tabeli numerów linii

00 00

 – indeks instrukcji w tabeli 

Code

00 06

 – odpowiadający jej numer linii w pliku źródłowym

00 0B

 – 

LocalVariableTable

00 00 00 0C

 – długość atrybutu

00 01

 – długość tabeli zmiennych lokalnych

00 00

 – początek zmiennej lokalnej w tablicy zmiennych

00 05

 – długość zmiennej

00 0C

 – indeks zmiennej: 

this

00 0D

 – typ zmiennej: 

LMain

00 00

 – indeks w lokalnej tablicy zmiennych

background image

 

 

Plik class – metoda <init>

10

Kod metody 

<init>

2A B7 00 09 B1

2A

ALOAD_0

// 

zmienna lokalna o adresie 0 jest wstawiana na stos

B7

 

00 09

:

INVOKESPECIAL

 

java/lang/Object.<init>()V;

 

//

 wywołuje metodę

 void Object.<init>();

B1

:

RETURN

//

 zwraca typ 

void

background image

 

 

Plik class – metoda main

11

00 09

  – 

ACC_PUBLIC

 (

00 01

|

 

ACC_STATIC

 (

00 08

) – publiczna metoda 

statyczna

00 0E

 – indeks elementu zawierającego nazwę metody: 

main

00 0F

 – indeks elementu z sygnaturą metody:

([Ljava/lang/String;)V

00 01

 – liczba dodatkowych atrybutów metody

00 07

 – indeks elementu z nazwą atrybutu: 

Code

00 00 00 37

 – długość atrybutu

00 02

 – rozmiar stosu

00 01

- rozmiar tablicy zmiennych lokalnych

00 00 00 09

 – długość kodu

B2 00 15 12 17 B6 00 1D B1

00 00

 – długość tablicy wyjątków

00 02

 – liczba dodatkowych atrybutów

background image

 

 

Plik class – metoda main

12

00 0A

 – 

LineNumberTable

00 00 00 0A

 – długość atrybutu

00 02

 – długość tabeli numerów linii

00 00

 – indeks instrukcji w tabeli 

Code

00 0C

 – odpowiadający jej numer linii w pliku źródłowym

00 08 00 0D

 to samo dla kolejnej linii

00 0B

 – 

LocalVariableTable

00 00 00 0C

 – długość atrybutu

00 01

 – długość tabeli zmiennych lokalnych

00 00

 – początek zmiennej lokalnej w tablicy zmiennych

00 09

 – długość zmiennej

00 1E

 – indeks zmiennej: 

Hello world!

00 1F

 – typ zmiennej: stała tekstowa

00 00

 – indeks w lokalnej tablicy zmiennych

background image

 

 

Plik class – metoda main

13

Kod metody 

main

B2 00 15 12 17 B6 00 1D B1

B2

 

00 15

:

GETSTATIC

 

java/lang/System.out : 

Ljava/io/PrintStream;

// 

inicjalizacja klasy/obiektu 

System.out

 i odłożenie

//

 go na stos

12

 

17

:

LDC

 

Hello world!

 

//

 załadowanie na stos stałej tekstowej

B6

 

00 1D

:

INVOKEVIRTUAL

java/io/PrintStream.println(Ljava/lang/String;)V

//

 wywołuje metodę 

System.out.println(String)

B1

:

RETURN

//

 zwraca typ 

void

background image

 

 

Plik class

14

00 01

 – liczba atrybutów

00 20

 – 

SourceFile

00 00 00 02

 – długość atrybutu

00 21

 – 

Main.java

KONIEC!!!

background image

 

 

Plik class – zapis assemblerowy

15

// class version 46.0 (46)
// access flags 33
public class Main {
  // compiled from: Main.java

  // access flags 1
  public <init>()V
   L0 (0)
    LINENUMBER 6 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init>()V
    RETURN
   L1 (4)
    LOCALVARIABLE this LMain; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 9
  public static main([Ljava/lang/String;)V
   L0 (0)
    LINENUMBER 12 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    LDC "Hello world!"
    INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V
   L1 (4)
    LINENUMBER 13 L1
    RETURN
   L2 (6)
    LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
    MAXSTACK = 2
    MAXLOCALS = 1
}

background image

 

 

ASM

16

ASM (http://asm.objectweb.org/index.html) jest narzędziem służącym do operacji na 

plikach binarnych Javy. Może być używany do dynamicznego generowania klas lub 

interfejsów bezpośrednio w formie binarnej lub modyfikacji istniejących klas 

w trakcie ich ładowania przez Wirtualną Maszynę Javy.

Inne narzędzia tego typu:

BCEL: http://jakarta.apache.org/bcel/

SERP: http://serp.sourceforge.net/

Javassist: http://www.csg.is.titech.ac.jp/~chiba/javassist/

background image

 

 

ASM

17

Przykład – program generujący klasę z dzisiejszego wykładu (Hello world!):

import org.objectweb.asm.ClassWriter;

import org.objectweb.asm.MethodVisitor;

import org.objectweb.asm.Opcodes;

import org.objectweb.asm.Type;

import org.objectweb.asm.commons.GeneratorAdapter;

import org.objectweb.asm.commons.Method;

import java.io.FileOutputStream;

import java.io.PrintStream;

public class Helloworld extends ClassLoader implements Opcodes {

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

ClassWriter cw = new ClassWriter(false);

cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null);

background image

 

 

ASM

18

// metoda init

MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null,

null);

mw.visitVarInsn(ALOAD, 0);

mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", ()V");

mw.visitInsn(RETURN);

mw.visitMaxs(1, 1); // glebokość stosu i liczba zmiennych lokalnych

mw.visitEnd();

// metoda main

mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", 

"([Ljava/lang/String;)V", null, null);

mw.visitFieldInsn(GETSTATIC, "java/lang/System", "out", 

"Ljava/io/PrintStream;");

mw.visitLdcInsn("Hello world!");

mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println",

"(Ljava/lang/String;)V");

        mw.visitInsn(RETURN);

background image

 

 

ASM

19

mw.visitMaxs(2, 2);

mw.visitEnd();

byte[] code = cw.toByteArray(); // pobieramy kod klasy

FileOutputStream fos = new FileOutputStream("Example.class");

fos.write(code);

fos.close();

Helloworld loader = new Helloworld();

Class exampleClass = loader.defineClass("Example",code,0,code.length);

exampleClass.getMethods()[0].invoke(null, new Object[] { null });

background image

 

 

ASM

20

// inny sposob uzyskania tego samego efektu

cw = new ClassWriter(true);

cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null);

Method m = Method.getMethod("void <init> ()");

GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null,

cw);

mg.loadThis();

mg.invokeConstructor(Type.getType(Object.class), m);

mg.returnValue();

mg.endMethod();

m = Method.getMethod("void main (String[])");

mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw);

mg.getStatic(Type.getType(System.class), "out", 

Type.getType(PrintStream.class));

background image

 

 

ASM

21

mg.push("Hello world!");

mg.invokeVirtual(Type.getType(PrintStream.class),

Method.getMethod("void println (String)"));

mg.returnValue();

mg.endMethod();

cw.visitEnd();

code = cw.toByteArray();

loader = new Helloworld();

exampleClass = loader.defineClass("Example", code, 0, code.length);

exampleClass.getMethods()[0].invoke(null, new Object[] { null });

    }

}

background image

 

 

Programowanie aspektowe

22

Każde realizowane zagadnienie pociąga za sobą w praktyce potrzebę realizacji 

zagadnień pobocznych. Najczęściej spotykane zagadnienia są związane 

z logowaniem, bezpieczeństwem, spójnością transakcyjną, autoryzacją, 

synchronizacją wielowątkową itd. Jest to zjawisko normalne, wynikające ze 

złożoności wymagań klienta. Zagadnienia te są w dużym stopniu rozłączne 

pomiędzy sobą pod względem funkcjonalnym. Aby je zrealizować programista musi 

poprzeplatać ich implementacje (tzw. warkocz), co czyni kod mniej czytelnym, 

bardziej podatnym na błędy, trudniejszym w modyfikacji.

Programowanie aspektowe (Aspect Oriented Programming) zapobiega tym 

negatywnym skutkom oddzielając fizycznie kod każdego zagadnienia poprzez 

umieszczenie go w oddzielnych aspektach wraz z określniem punktów interakcji 

pomiędzy nim a kodem programu.

Źródło: http://pl.wikipedia.org/wiki/Programowanie_aspektowe

background image

 

 

AspectJ

23

AspectJ to aspektowy język programowania opracowany w Xerox Palo Alto 

Research Center. Kluczowe cechy języka:

 AspectJ jest rozszerzeniem języka Java. Kompilator (ajc) tworzy kod źródłowy 

w Javie kompilowany dalej do postaci binarnej (pliki class).

 aspekty implementowane przez AspectJ są obiektami

 obsługuje wszystkie techniki programowania aspektowego, w tym: punkty 

złączenia, punkty przekroju, rady, mechanizmy zarządzające cyklem życia aspektu

Obecnie AspectJ jest rozwijany w ramach projektu Eclipse: 

http://www.eclipse.org/aspectj/. Istnieją również narzędzia developerskie (AJDT). 

background image

 

 

Model punktów złączenia

24

Każdy język programowania określa zbiór potencjalnych punktów złączenia – model 

punktów złączenia. Przykładowe potencjalne punkty złączenia w Javie to:

 wywołanie metody,

 wykonanie metody,

 wejście do statycznego bloku kodu,

 wywołanie konstruktora.

Punkty złączenia są miejscami w które można wstawić tzw. logikę przekrojową. Po 

wstawieniu dodatkowej logiki (kodu programu) w punkt złączenia staje się on 

punktem przekroju

background image

 

 

Punkty przekroju

25

Deklaracja punktów złączenia:

public pointcut myClassTaskCalled() : 

call(public void MyClass.task());

Punkt złączenia dla wszystkich metod 

task()

 niezależnie od klasy:

public pointcut anyClassTaskCalled() : call(* *.task(..));

Przekazanie wartości do punktu złączenia:

public pointcut anyClassTaskCalled(int value) : 

call(* *.task(..)) && args(number);

Typy punktów złączenia:

 call (Signature)

 – wywołanie metody

 execution (Signature)

 – wykonanie metody

 handler(TypePattern)

 – przechwytywanie wyjątków

background image

 

 

Rady

26

Rada (advice) określa kod wykonywany w momencie zarejestrowania punktu 

złączenia. Od zwykłej metody różni ją dodatkowe słowo kluczowe określające 

miejsce wykonania kodu względem punktu złączenia:

before()

 – przed punktem złączenia,

around()

 – zamiast punktu złączenia, chyba że metoda 

proceed()

 mówi inaczej

after()

 – po punkcie złączenia niezależnie od wyniku jego działania

after() returning

 – po punkcie zgłoszenia jeśli nie było zgłoszenia wyjątku

after() throwing

 – po punkcie zgłoszenia jeśli został zgłoszony wyjątek.

Przykład:

before(int value) : anyClassTaskCalled(value){

System.out.println("Wartosc" + value);...

}

background image

 

 

Aspekty

27

Aspekt przypomina zwykłą klasę Javy. Instancja aspektu jest pełnoprawnym 

obiektem Javy i składnia jego obsługi jest analogiczna jak zwykłych obiektów. 

Domyślnie pojedyncza instancja aspektu jest tworzona przez aplikację w momencie 

uruchomienia i istnieje aż  do jej zakończenia (singleton). Istnieje jednak możliwość 

wybrania innego cyklu życia dla aspektu:

 issingleton()

 – singleton (domyślne)

 perthis(Pointcut)

 – nowa instancja dla każdego obiektu do którego odnosi 

się referencja 

this

 w punkcie złączenia

 pertarget(Pointcut)

 – nowa instancja dla każdego obiektu, który jest celem 

działania punktu złączenia

 percflow(Pointcut)

 – nowa instancja dla każdego wątku inicjowanego 

w ramach punktu złączenia

background image

 

 

Aspekty

28

Kompletny przykład aspektu:

public aspect MyAspect issingleton(){

   public pointcut myClassTaskCalled() : call(public void MyClass.foo());

   public pointcut anyClassBarCalled(int number) : call(* *.bar(..)) &&

args(number);

   

   before() : myClassTaskCalled(){

      System.out.println("rada before() w MyAspect");

   }

   

   before(int value) : anyClassBarCalled(value) {

      System.out.println("rada before() w MyAspect");

      System.out.println("Parametr " + value);

   }

}

background image

 

 

Przekroje statyczne

29

AspectJ może działać na statycznej strukturze aplikacji. Umożliwia to m. in.:

dodawanie nowych składowych do klasy

dodawanie nowych metod

deklarowanie nowych interfejsów

deklarowanie nowych zależności dziedziczenia

Przykład:

public aspect AddRunnable {

   declare parents : MyClass implements Runnable;

   public void MyClass.run(){

      System.out.println("Implementacja metody Runnable.run()");

   }

}

background image

 

 

Podsumowanie

30

Możliwość zmiany kodu klasy Javy w trakcie wykonywania programu wprowadza 

wiele nowych możliwości przy tworzeniu oprogramowania. Zmiany takie można 

wprowadzać bezpośrednio do kodu ładowanego przez 

ClassLoader

'a lub poprzez 

różne dostępne narzędzia (np. ASM). Wiele popularnych bibliotek korzysta z tej 

techniki aby efektywnie realizować zadania (np. Hibernate). Na koncepcji 

dynamicznej podmiany kodu opiera się jeden z paradygmatów tworzenia 

oprogramowania – programowanie aspektowe.