Professional Documents
Culture Documents
ii
Spis treci
1. Wprowadzenie 1.1. Wane informacje dla czytelnikw . . . . . . . . . . . . . . . . 1.2. O autorze . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3. Dla kogo przeznaczona jest ta ksika? . . . . . . . . . . . . . 1.4. Cel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5. Zakres materiau . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.1. Omawiane zagadnienia . . . . . . . . . . . . . . . . . . 1.5.2. O czym nie jest ta ksika? . . . . . . . . . . . . . . . 1.6. Rekomendowane lektury . . . . . . . . . . . . . . . . . . . . . 1.6.1. Ksiki . . . . . . . . . . . . . . . . . . . . . . . . . . 1.6.2. Strony WWW . . . . . . . . . . . . . . . . . . . . . . 1.7. Narzdzia przydatne podczas lektury . . . . . . . . . . . . . . 1.8. Przyjte konwencje . . . . . . . . . . . . . . . . . . . . . . . . 1.9. Podzikowania . . . . . . . . . . . . . . . . . . . . . . . . . . 2. Wprowadzenie do Spring Framework 2.1. Kolejny framework?! . . . . . . . . . . . . . . . . . . . . . . . 2.1.1. W mnogoci sia? . . . . . . . . . . . . . . . . . . . . . 2.1.2. Wady tradycyjnych platform . . . . . . . . . . . . . . iii
1 1 1 2 3 3 3 4 4 4 5 5 5 5 7 8 8 8
Spis treci 2.1.3. Rozsdny kompromis Spring Framework . . . . . . . 2.2. Dlaczego powsta Spring Framework? . . . . . . . . . . . . .
iv 10 11 12 13 15 16 17 23 25 27 27 30 30 31 34 35 41 42 43 45 45 47 49 51 54
2.3. Architektura . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4. Zalety . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3. Bean i kontener IoC 3.1. Bean i fabryka beanw . . . . . . . . . . . . . . . . . . . . . . 3.1.1. Deniowanie prostych beanw . . . . . . . . . . . . . . 3.1.2. Cykl ycia . . . . . . . . . . . . . . . . . . . . . . . . . 3.1.3. Niestandardowe edytory waciwoci . . . . . . . . . . 3.1.4. Bean bez domylnego konstruktora . . . . . . . . . . . 3.1.5. Tworzenie beanw przy pomocy fabryki . . . . . . . . 3.1.6. Relacje dziedziczenia . . . . . . . . . . . . . . . . . . . 3.2. Kontener IoC . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2.1. Deniowanie zalenoci midzy beanami . . . . . . . . 3.2.2. Sprawdzanie poprawnoci konguracji beanw . . . .
3.2.3. Automatyczne dopasowywanie zalenoci . . . . . . . 4. Programowanie aspektowe w Spring Framework 4.1. Wstp do aspektw . . . . . . . . . . . . . . . . . . . . . . . . 4.1.1. Obiekty Proxy . . . . . . . . . . . . . . . . . . . . . . 4.2. Aspekty pod lup . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.1. Koncepcja AOP . . . . . . . . . . . . . . . . . . . . . 4.2.2. Sposoby implementacji AOP . . . . . . . . . . . . . . 4.2.3. Zastosowania AOP . . . . . . . . . . . . . . . . . . . . 4.2.4. Frameworki AOP . . . . . . . . . . . . . . . . . . . . . 4.3. AOP w Spring Framework . . . . . . . . . . . . . . . . . . . .
Spis treci
Spis treci
vi
ROZDZIA 1
Wprowadzenie
wprowadzenie
1.2. O autorze
Piotr Maj absolwent Akademii Ekonomicznej we Wrocawiu (obrona w 2002 r.), programista z zamiowania, w Javie programuje od 4 lat. Wsptwrca i redaktor portalu dla mionikw Javy http://jdn.pl/. 1
Waciciel rmy jcake software http://jcake.com/ zajmujcej si tworzeniem aplikacji internetowych, programw w Javie (server side oraz desktop Eclipse RCP), a take szkoleniami dot. technologii Eclipse RCP. Wygoszone prelekcje: 1. Przenone aplikacje w Javie Eclipse RCP 22.09.2005, wykad na Politechnice witokrzyskiej w ramach spotka Kieleckiej Grupy Uytkownikw Linuksa. 2. Przenone aplikacje w Javie Eclipse RCP 26.10.2005, wykad na konferencji Java Techconf organizowanej przez Naukowe Koo Informatyki przy AE w Krakowie oraz serwis jdn.pl. 3. Przenone aplikacje w Javie Eclipse RCP 24.02.2006, wykad dla deweloperw w rmie Rector http://www.rector.com.pl/.
1.4. Cel
1.4. Cel
Gwnym celem i motywacj do napisania tej ksiki jest ch przedstawienia alternatywnego sposobu tworzenia aplikacji internetowych opartego na nieco innych zaoeniach ni najmodniejsze obecnie podejcie wykorzystujce komponenty EJB i serwery aplikacji zgodne ze standardem J2EE. To ostatnie czsto niepotrzebnie wprowadza duy stopie skomplikowania do programw, sprawia, e s one mao czytelne, charakteryzuje je niska wydajno, a co waniejsze, czsto s modelowym przykadem przerostu formy nad treci.
konguracj kontekstu i kontener IoC, wsparcie dla programowania aspektowego w Spring Framework, zarzdzanie transakcjami, omwienie warstwy dostpu do baz danych i utrwalania obiektw, zdalne wywoywanie metod w Spring Framework, prezentacja implementacji wzorca MVC.
Wszystkie zagadnienia zostay omwione w takim stopniu, aby po przeczytaniu tej ksiki czytelnik mg w peni wykorzysta potencja Spring Framework we wasnym projekcie.
Spring Framework (http://www.springframework.org/), Ant (http://ant.apache.org/), Tomcat (http://jakarta.apache.org/tomcat/), hsqldb (http://hsqldb.sourceforge.net/), Hibernate (http://www.hibernate.org/).
Dodakowo potrzeba oczywicie rodowiska J2SDK 5.0, ktre mona pobra ze strony rmy Sun (http://java.sun.com/j2se/1.5.0/download.jsp), oraz dowolny edytor kodu lub rodowisko IDE (np. Eclipse).
1.9. Podzikowania
Dzikuj Piotrowi Kobdzie za, jak zawsze, cenne uwagi jakimi si ze mn podzieli po przeczytaniu szkicu rozdziau powiconego teorii AOP.
1.9. Podzikowania
Dzikuj Michaowi Ciechelskiemu i Tomaszowi Bartkowiczowi za wnikliwe przeczytanie niniejeszego opracowania i znalezienie mnstwa literwek i innych potkni jzykowych.
ROZDZIA 2
wymaga powicenia dodatkowego czasu na stworzenie nowych bd poznanie i integracj gotowych rozwiza wykonujcych brakujce platformie funkcje, nie oferuje wsparcia dla transparentnego spenienia zalenoci midzykomponentowych.
Framework, ktry oferuje jedynie podstawowe narzdzia w zakresie obsugi da HTTP i kontroli przepywu danych z modelu do widoku to zdecydowanie za mao. Kada aplikacja korzysta z pewnych zasobw zewntrznych (np. baz danych, serwera SMTP). Jeeli framework nie oferuje gotowych mechanizmw usprawniajcych dostp do zasobw bdziemy musieli straci czas albo na implementacj wasnych pomysw, albo na integracj naszego projektu z innymi, komplementarnymi. Aplikacje skadaj si przewanie z wielu komponentw, z ktrych kady odpowiedzialny jest za pewien wybrany aspekt systemu. Jest wiele przypadkw, w ktrych funkcjonalno komponentw w jakim stopniu si pokrywa. Za prosty przykad niech nam posuy komponent odpowiedzialny za tworzenie konta uytkownia, ktry moe chcie wysa e-mail z potwiedzeniem faktu rejestracji. Aby nie tworzy dwukrotnie kodu wysyajcego e-mail mona utworzy komponent, ktrego domen bdzie wysyanie listw elektronicznych i przekaza referencj tego komponentu do kadego innego, ktry takiej funkcjonalnoci potrzebuje. Istnieje wiele sposobw na zrealizowanie tego zadania niestety proste platformy MVC nie wspieraj adnego z nich, kolejny raz odcigajc programist od rzeczywistej pracy nad systemem. . . Zaawansowane platformy z kolei pozbawione s przewanie wyej wymienionych wad. Niestety, to co ma stanowi o ich sile, a wic kompleksowe podejcie do zagadnie infrastruktury czsto staje si ich najwikszym balastem. Narzdzia te s przewanie: skomplikowane, zmuszajce do stosowania pewnych, przyjtych przez ich twrcw, schematw,
10
czsto nie przystajce do specycznych wymaga naszego projektu. Stopie zoonoci platformy nie pozostaje bez wpywu na wydajno zespou projektowego, poniewa potrzeba przeznaczy sporo czasu na jej dokadne poznanie i zrozumienie. Jest to konieczne, jeeli chcemy w peni wykorzysta moliwoci oferowane przez framework. Decydujc si na taki framework tracimy kontrol nad sposobem implementacji pewnych aspektw, przez co nie tylko platforma, ale i tworzony w oparciu o ni system moe by bardziej skomplikowany ni by tego wymagaa sytuacja. Kompleksowe rozwizania przewanie narzucaj pewne schematy postpowania, ktre niekoniecznie musz pokrywa si z naszym rozumieniem dobrych praktyk programistycznych. Niestety, korzystajc z gotowca niewiele moemy na to poradzi musimy zda si na efekt przemyle twrcw platformy, rezygnujc z wasnych. To nie musi by samo w sobie wad, jednak praktyka pokazuje, e wikszo programistw i tak tworzy wasne alternatywne rozwizania eliminujce niedostatki platformy, bd lekko zmieniajc jej semantyk. Celem wielu autorw rozbudowanych platform do tworzenia aplikacji jest stworzenie systemu kompleksowego, ktry bdzie mona przystosowa do dowolnego typu aplikacji, wreszcie ktry bdzie w stanie zadowoli wikszo uytkownikw. Prowadzi to do pewnych uproszcze i koncentrowania si na wikszoci typowych przypadkw. Niestety, nie zawsze jestemy w tej komfortowej sytuacji, e piszemy typowy system, czsto specyczne wymagania klienta wykraczaj daleko poza te ramy, co wymusza albo przerabianie gotowego rozwizania albo implementacj wasnego od podstaw.
11
tworzenie aplikacji, z ktrych to mechanizmw moglibymy skorzysta zalenie od potrzeb. Spring Framework stara si realizowa to wanie podejcie dostarczy budulca bez wskazywania jedynie susznej drogi jego zagospodarowania. Cho, jak si pniej przekonamy, Spring Framework doskonale to zadanie realizuje dystansujc pod wieloma wzgldami inne platformy, wcale nie to byo gwnym celem i motywacj autorw podczas jego tworzenia.
1. Stworzenie zintegrowanej platformy oferujcej solidn infrastruktur dla podstawowych aspektw kadej aplikacji. Istnieje wiele ciekawych i godnych uwagi projektw, ktre obejmuj swym zakresem funkcjonalnym pojedynczy aspekt zoonej aplikacji (np. PicoContainer jako kontener IoC, Hibernate jako narzdzie do mapowania O/R). Niestety ich integracja jest czasochonna i odciga programistw od zajmowania si rzeczywistymi problemami logiki biznesowej aplikacji. Spring Framework oferuje gotowe do uycia komponenty stanowice dobrze przetestowan, stabiln platform do tworzenia aplikacji. 2. Minimalizacja stopnia zoonoci platformy. Wikszo typowych, prostych problemw aplikacji internetowych powinno da si rozwiza przy uyciu prostych rodkw. Zdecydowanie naley unika zalenoci od skomplikowanych mechanizmw, jak np. JTA, jeeli tylko nie ma ku temu wyranych powodw. 3. Nieinwazyjno. Nasza aplikacja nie powinna, a jeeli ju to tylko w minimalnym stopniu zalee od infrastruktury platformy. Duy stopie
2.3. Architektura
12
niezalenoci mona osign przez zastosowanie kontenera IoC oraz programowania aspektowego. 4. Prostota testowania. Pisanie automatycznych testw kodu aplikacji powinno by proste, a poszczeglne komponenty systemu atwo zastpowalne na czas testw obiektami zastpczymi (ang. mock objects). 5. atwo rozbudowy. Platforma powinna promowa programowanie zorientowane na interfejsy, a nie na konkretne klasy, co umoliwia proste dostosowywanie jej do wasnych potrzeb. Co wane, Spring Framework nie jest projektem laboratoryjnym, napisanym w oderwaniu od rzeczywistoci. Powstawa w procesie ewolucyjnym, a jego przydatno zostaa pozytywnie zwerykowana przez autorw w kilku duych komercyjnych projektach, nad ktrymi mieli okazj pracowa.
2.3. Architektura
Spring Framework charakteryzuje si wielowarstow budow. Poszczeglne warstwy s waciwie osobnymi, niezwizanymi ze sob podprojektami. Istnieje jednake jeden element, ktry umoliwia szybkie i proste zintegrowanie poszczeglnych czci, a jest nim kontener IoC. Taki podzia czyni ze Spring Framework bardzo elastyczn konstrukcj, ktr mona dowolnie kongurowa w zalenoci od potrzeb. Warty podkrelenia jest rwnie fakt, e architektura ta nie ogranicza w aden sposb Spring Framework do jednego konkretnego zastosowania. Wrcz przeciwnie framework ten doskonale si sprawdza zarwno w aplikacjach internetowych, jak i okienkowych programach uytkowych. Na poszczeglne warstwy Spring Framework skadaj si nastpujce elementy [?]: kontener IoC (ang. Inversion of Control ), zarzdzanie kontekstem aplikacji,
2.4. Zalety wsparcie dla AOP, zarzdzanie transakcjami, abstrakcja DAO, wsparcie dla JDBC, integracja z narzdziami do mapowania O/R, implementacja wzorca MVC, wsparcie dla web-services.
13
2.4. Zalety
Czym takim wyrnia si Spring Framework? Co powinno nas przekona akurat do tego jednego, spord kilkunastu dostpnych darmowych frameworkw? Moja osobista lista zalet wyglda nastpujco: Spring Framework to rozwizanie kompleksowe framework ten oferuje wsparcie dla wszystkich elementw potrzebnych do stworzenia typowej aplikacji, Spring Framework implementuje sprawdzone wzroce projektowe i zachca do ich stosowania. Dziki temu kod staje si przejrzysty i atwy do zrozumienia dla kadego, kto te wzorce zna, projekt jest doskonale udokumentowany dziki czemu mona bardzo atwo zrozumie zasad jego dziaania i pozna rwnie zaawansowane funkcje, wok projektu dziaa silna i prna spoeczno, zawsze gotowa do pomocy (listy mailingowe, forum dyskusyjne).
2.4. Zalety
14
ROZDZIA 3
16
17
Kolejnym krokiem jest poinformowanie Spring Framework o istnieniu naszej klasy. Framework obsuguje standardowo dwa formaty plikw konguracyjnych. Pierwszy z nich to XML, drugi to standardowy plik *.properties. Ten ostatni jest niemniej jednak bardzo mao popularny, dlatego te w dalszej czci ksiki wszystkie przykady bd zapisane wycznie w formacie XML. Dla naszego prostego przykadu plik konguracyjny moe wyglda nastpujco:
Listing 3.2. Minimalny plik konguracyjny w wersji XML
1 2 3 4 5 6 <b e a n s> <bean i d= h e l l o w o r l d c l a s s= p r z y k l a d y . r o z d z i a l 3 . H e l l o W o r l d /> </ b e a n s> <?xml versi on= 1 . 0 e n c o d i n g=UTF8 ?> < !D C Y E b e a n s PUBLIC //SPRING//DTD BEAN//EN O T P h t t p : //www. s p r i n g f r a m e w o r k . o r g / dtd / s p r i n g b e a n s . dtd >
Na najprostsz denicj beanu skadaj si: identykator oraz pena kwalikowana nazwa klasy. Identykator umoliwia jednoznaczne odwoanie si do konkretnego beanu. Nie pozostaje ju zatem nic innego, jak tylko zobaczy nasz bean w akcji. Poniszy kod tworzy dwie identyczne fabryki beanw: pierwsz na podstawie wpisw zawartych w pliku helloworld.xml, a drug na podstawie pliku
18
helloworld.properties. Nastpnie z fabryk pobierany jest nasz przykadowy bean, na ktrym mona ju bez przeszkd wywoywa metody biznesowe.
Listing 3.4. Przykad wykorzystania beanu HelloWorld
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 } } bean . h e l l o ( ) ; bean2 . h e l l o ( ) ; H e l l o W o r l d bean = ( HelloWorld ) xmlFactory . getBean ( h e l l o w o r l d ) ; H e l l o W o r l d bean2 = ( H e l l o W o r l d ) p r o p F a c t o r y . g e t B e a n ( h e l l o w o r l d ) ; // T w o r z e n i e fabryki na p o d s t a w i e pliku . pr o pe r tie s . DefaultListableBeanFactory p r o p F a c t o r y = new D e f a u l t L i s t a b l e B e a n F a c t o r y ( ) ; reader = // T w o r z e n i e fabryki na p o d s t a w i e p l i k u XML. XmlBeanFactory x m l F a c t o r y = new XmlBeanFactory ( x m l C o n f i g F i l e ) ; Resource xmlConfigFile = new C l a s s P a t h R e s o u r c e ( / p r z y k l a d y / r o z d z i a l 3 / h e l l o w o r l d . xml ) ; Resource propertiesConfigFile = new C l a s s P a t h R e s o u r c e ( / p r z y k l a d y / r o z d z i a l 3 / h e l l o w o r l d . p r o p e r t i e s ) ; public s t a t i c void main ( S t r i n g [ ] args ) { public c l a s s HelloWorldExample { import o r g . s p r i n g f r a m e w o r k . b e a n s . f a c t o r y . s u p p o r t . D e f a u l t L i s t a b l e B e a n F a c t o r y ; import o r g . s p r i n g f r a m e w o r k . b e a n s . f a c t o r y . s u p p o r t . P r o p e r t i e s B e a n D e f i n i t i o n R e a d e r ; import o r g . s p r i n g f r a m e w o r k . b e a n s . f a c t o r y . xml . XmlBeanFactory ; import o r g . s p r i n g f r a m e w o r k . c o r e . i o . C l a s s P a t h R e s o u r c e ; import o r g . s p r i n g f r a m e w o r k . c o r e . i o . R e s o u r c e ; package p r z y k l a d y . r o z d z i a l 3 ;
PropertiesBeanDefinitionReader
Aby powyszy kod poprawnie si skompilowa i uruchomi naley doda do cieki klas archiwum spring.jar oraz pakiety zalene. Istnieje jeszcze trzeci sposb na utworzenie fabryki beanw, a mianowicie zapisanie jej konguracji bezporednio w kodzie Java.
Nieco wicej szczegw na temat beanw Spring Framework pozwala cile okreli natur i moment utworzenia konkretnego beanu. Ponisza denicja jest semantycznie zgodna z t z listingu 3.2. Odkrywa jednak kilka nowych szczegw.
Listing 3.5. Bardziej szczegowy plik konguracyjny
1 <bean
19
2 3 4 5 6
i d= h e l l o w o r l d c l a s s= p r z y k l a d y . r o z d z i a l 3 . H e l l o W o r l d name= w i t a j s w i e c i e s i n g l e t o n= t r u e l a z y i n i t = f a l s e />
Pojawiy si trzy nowe atrybuty: name, singleton oraz lazy-init. name podobnie jak id przypisuje beanowi unikatow nazw; przy pomocy tego atrybutu mona rwnie zdeniowa jeden lub wicej aliasw dla tego samego beana (aliasy oddziela naley przecinkiem lub rednikiem; spacje s ignorowane), singleton okrela natur beanu; jeeli warto tego atrybutu zostaa ustawiona na true (co jest wartoci domyln) kade pobranie danego beanu z fabryki zwrci zawsze referencj do jego pojedynczej instancji; warto false spowoduje tworzenie za kadym razem nowej instancji beana, lazy-init pozwala okreli, w ktrym momencie fabryka powinna utworzy pojedyncz instancj danego beanu; warto false (domylna) oznacza, e fabryka powinna utworzy instancj beanu od razu przy starcie; warto true powoduje odroczenie chwili utworzenia instancji obiektu tak dugo, jak to tylko moliwe, czyli do czasu pierwszego pobrania beanu z fabryki. Szczegln rozwag naley zachowa przy stosowaniu atrybutu singleton. Domylnie Spring Framework tworzy pojedyncze, wspdzielone instancje kadego beanu i w przewaajcej liczbie przypadkw jest to dziaanie podane. Taki bean podlega standardowemu cyklowi ycia i jest zarzdzany przez kontener. Inaczej wyglda sytuacja beanw zdeniowanych z opcj singleton=false. Rola Spring Framework sprowadza si wwczas do tworzenia obiektw i zwracania referencji do nich. Po spenieniu tego zadania kontener zapomina o ich istnieniu. Warto atrybutu lazy-init moe mie duy wpyw na czas uruchamiania fabryki. Ma to szczeglne znaczenie, gdy zdeniowane beany korzystaj z zasobw zewntrznych (np. nawizuj poczenia z baz danych) lub te
20
wykonuj przy starcie inne czasochonne czynnoci (np. obliczeniowe). Opcj leniwej inicjalizacji mona aktywowa globalnie dla caej fabryki poprzez dodanie atrybutu default-lazy-init="true" do elementu <beans> w pliku konguracyjnym. Wad takiego podejcia jest to, e Spring Framework nie zasygnalizuje tu po starcie fabryki ewentualnych bdw w konguracji naszych beanw. Jest to wic dobra opcja dla rodowiska produkcyjnego na etapie tworzenia systemu lepiej wyczy leniw inicjalizacj beanw, co pozwoli na szybsze wyapywanie ewentualnych bdw w konguracji. Uywajc atrybutu lazy-init moemy rwnie zapobiec tworzeniu przez fabryk instancji beanw, ktre nie powinny by tworzone, bo np. su tylko jako nadrzdne denicje dla innych beanw. Wicej na ten temat w dalszej czci tego rozdziau.
Edycja waciwoci beanw Kolejnym wanym zagadnieniem jest ustalenie stanu pocztkowego beanu. Beany powinno si pisa tak, aby bez wikszych, a najlepiej bez adnych zmian mona je byo ponownie wykorzyta, np. w innych projektach, dostosowujc tylko ustawienia konguracji. Konguracja nie powinna wic by zaszyta w kodzie klasy, lecz przekazana z zewntrz. Spring Framework oferuje bardzo prosty, a jednoczenie bardzo elastyczny mechanizm edycji waciwoci beanw bazujcy na specykacji JavaBeans. Wystarczy doda do beana publiczn metod ustawiajc pole, czyli tzw. setter. Listing 3.6 przedstawia prost klas z dwoma publicznymi metodami zgodnymi ze specykacj JavaBean.
Listing 3.6. Klasa bez konstruktora domylnego
1 2 3 4 5 6 7 8 9 10 11 12 13 public void setName ( S t r i n g name ) { t h i s . name = name ; } public void s e t V a l u e ( I n t e g e r this . value = value ; value ) { private Integer value ; p r i v a t e S t r i n g name ; public c l a s s SimpleSetter { package p r z y k l a d y . r o z d z i a l 3 ;
21
14 15 }
Pola value oraz name mona ustawi w pliku konguracyjnym fabryki w nastpujcy sposb:
Listing 3.7. Konguracja waciwoci beanw
1 2 3 4 <bean i d=mybean c l a s s= p r z y k l a d y . r o z d z i a l 3 . S i m p l e S e t t e r > <p r o p e r t y name= v a l u e > <v a l u e>5</ v a l u e> </ p r o p e r t y> <p r o p e r t y name=name> <v a l u e>H e l l o World !</ v a l u e> </ p r o p e r t y> </ bean>
Spring Framework automatycznie dokona konwersji podstawowych typw znajdujcych si w pakiecie .lang. Bardziej skomplikowane konwersje (w praktyce dowolne) s rwnie moliwe, ale wymagaj napisania rozszerzenia do kontenera. Zagadnieniu temu przyjrzymy si dokadniej w rozdziale 3.1.3. Uwaga! Zapis <value></value> nie oznacza ustawienia wartoci pola na null. Do tego celu suy specjalny element: <null/>.
Mapowanie kolekcji
umoliwia deniowanie w pliku konguracyjnym kolekcji obiektw, a w szczeglnoci: list, zbiorw, map oraz szczeglnego przypadku mapy obiektu java.util.Properties. Reprezentatywny plik konguracyjny mgby wyglda nastpujco:
Listing 3.8. Deniowanie kolekcji
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <bean i d=mybean c l a s s= C o l l e c t i o n E x a m p l e > <p r o p e r t y name= a l l o w e d > < l i s t> <v a l u e> 1 2 7 . 0 . 0 . 1</ v a l u e> <v a l u e>1 9 2 . 1 6 8 . 1 7 . 1 0 0</ v a l u e> </ l i s t> </ p r o p e r t y> <p r o p e r t y name= r o l e s > <map> <e n t r y key= bob > <v a l u e>admin</ v a l u e> </ e n t r y> <e n t r y key= s c o t t > <v a l u e>manager , s u p e r u s e r</ v a l u e> </ e n t r y> </map> </ p r o p e r t y> <p r o p e r t y name= c o u n t r i e s > <s e t> <v a l u e>p o l a n d</ v a l u e> <v a l u e>germany</ v a l u e> </ s e t> </ p r o p e r t y> <p r o p e r t y name= c o n f i g > <p r o p s> <prop key= s e n d . e m a i l > <v a l u e>t r u e</ v a l u e> prop> </ <prop key= app . name> <v a l u e>M y A p p l i c a t i o n</ v a l u e> prop> </
22
24 25 26
W prawie wszystkich przypadkach kolekcje mona dowolnie zagnieda (np. mapa moe zawiera listy czy zbiory jako wartoci, etc.). Wyjtkiem jest tylko element <props> odpowiadajcy obiektowi java.util.Properties, ktry przyjmuje jako wartoci tylko acuchy tekstowe. Dla podanego przykadu klasa beanu musi zawiera nastpujce sygnatury metod 1 :
Listing 3.9. Metody zgodne ze specykacj JavaBean
1 2 3 4 void s e t A l l o w e d ( j a v a . u t i l . L i s t void s e t C o u n t r i e s ( j a v a . u t i l . S e t allowed ) ; countries ); configuration ); void s e t R o l e s ( j a v a . u t i l . Map r o l e s ) ; void s e t C o n f i g ( j a v a . u t i l . P r o p e r t i e s
Mapowanie dowolnych beanw jako waciwoci innych beanw Mapowanie typw prostych i kolekcji to za mao, by osign wszystkie cele. Moe si zdarzy, e zapragniemy utworzy i przekaza do beanu dowolny obiekt. Na przykad obiekt moe zawiera metod setConfiguration(Configuration config), gdzie Configuration, to specjalna klasa, ktra oprcz przechowywania stanu konguracji robi jeszcze kilka innych poytecznych rzeczy. Twrcy Spring Framework przewidzieli tak ewentualno i w prawie kadym miejscu pliku konguracyjnego (wyjtek stanowi wspomniany ju element <props>), w ktrym wskazuje si warto mona wstawi denicj nowego beana. Ilustruje to listing 3.10.
Listing 3.10. Zagniedony bean
1 2 3 4 5 6 7 8 9 10 11 <bean i d=mybean c l a s s= A p p l i c a t i o n > <p r o p e r t y name= c o n f i g u r a t i o n > <bean c l a s s= C o n f i g u r a t i o n > <p r o p e r t y name= p r o p e r t i e s > <p r o p s> <prop key= v a l u e 1 > <v a l u e>t r u e</ v a l u e> prop> </ <prop key= v a l u e 2 > <v a l u e> f a l s e</ v a l u e> prop> </ </ p r o p s> </ p r o p e r t y> </ bean> </ p r o p e r t y> 1
23
12
</ bean>
Ostatni wan kwesti dotyczc konguracji beanw jest przekazywanie w metodzie ustawiajcej referencji do ju utworzonego beanu. Zagadnienie zostanie poruszone dalszej czci rozdziau, przy okazji omawiania kontenera IoC (rozdz. 3.2).
24
implementujc specjalne interfejsy dostarczone przez Spring Framework, wskazujc w pliku konguracyjnym nazwy metod, ktre maj posuy do inicjalizacji i zamknicia beanu.
Interfejsy wpywajce na cykl ycia Spring Framework dostarcza dwa interfejsy, ktre bean moe zaimplementowa, aby wpyn na swj cykl ycia. S to:
Pierwszy z nich, InitializingBean (listing 3.11), deniuje jedn metod, ktra zostatnie wykonana w momencie pierwszego pobrania beanu z kontenera. Metoda ta moe i powinna rzuci wyjtek, jeeli okae si, e bean nie jest gotowy do wiadczenia usug.
Listing 3.11. Interfejs InitializingBean
1 void a f t e r P r o p e r t i e s S e t ( ) throws j a v a . l a n g . E x c e p t i o n ;
Interfejs DisposableBean (listing 3.12) rwnie skada si z pojedynczej metody, ktr bean musi zaimplementowa. Metoda destroy() zostanie wykonana w momencie zamykania fabryki i powinna skutkowa zwolnieniem wszystkich zasobw uywanych przez bean. Moe ona rzuci wyjtek, jeeli zamknicie beanu nie jest moliwe. Wyjtek taki zostanie zapisany w dzienniku systemowym, po czym zostanie zignorowany, tj. nie spowoduje nagego zatrzymania/zniszczenia caej fabryki.
Listing 3.12. Interfejs DisposableBean
1 void d e s t r o y ( ) throws j a v a . l a n g . E x c e p t i o n ;
25
Uywanie interfejsw do zasygnalizowania chci ingerencji w cykl ycia beanu ma jedn zasadnicz wad. Kod beanu staje si cakowicie zaleny od Spring Framework. Nie jest moliwe wykorzystanie tak napisanego beanu w innym rodowisku bez dokonania stosownych zmian w kodzie. Dla wszystkich osb, ktre wolayby zachowa niezaleno beanw od Spring Framework autorzy tej platformy przewidzieli alternatywny sposb. Mona umieci nazwy metod w pliku konguracyjnym, zostan one wywoane przez Spring Framework za pomoc mechanizmu odbicia (ang. reection).
Listing 3.13. Deklaratywne deniowanie cyklu ycia
1 2 3 4 5 <bean i d= b e a n i d c l a s s= SampleBean i n i t method= i n i t i a l i z e d e s t r o y method= d e s t r o y />
Metody okrelone przez argumenty init-method oraz destroy-method nie powinny zwraca adnej wartoci, ale mog deklarowa rzucanie dowolnego wyjtku, czyli podlegaj dokadnie tym samym reguom, co metody z interfejsw InitializingBean oraz DisposableBean.
26
Zamy, e chcemy doda do fabryki moliwo automatycznej konwersji acuchw w formacie YYYY-MM-DD na daty. Konguracja beana wygldaaby nastpujco:
Listing 3.14. Przykad konguracji beanu z polem typu java.util.Date
1 2 3 4 5 <bean i d=mybean c l a s s= p r z y k l a d y . r o z d z i a l 3 . CustomEditorSample > <p r o p e r t y name= d a t e > <v a l u e>20041012</ v a l u e> </ p r o p e r t y> </ bean>
czyli dokadnie tak samo jak kada inna. Bean z kolei posiadaby nastpujc metod ustawiajc pole date:
Listing 3.15. Metoda ustawiajca pole date
1 void s e t D a t e ( j a v a . u t i l . Date d a t e ) ;
Spring Framework nie potra automatycznie przekonwertowa wartoci typu java.lang.String do typu java.util.Date. Naley wic rozszerzy moliwoci fabryki o tak funkcj. Robi si to poprzez zarejestrowanie wasnego edytora tu po utworzeniu fabryki, a przed pobraniem z niej beanw. Poniszy przykad rejestruje jeden edytor, ktry pozwala edytowa pola typu java.util.Date o ustalonym formacie.
Listing 3.16. Rejestrowanie wasnych edytorw waciwoci
1 2 3 4 5 6 7 Resource config = new C l a s s P a t h R e s o u r c e ( p r z y k l a d y / r o z d z i a l 3 / c u s t o m e d i t o r . xml ) ; XmlBeanFactory b f = new XmlBeanFactory ( c o n f i g ) ; b f . r e g i s t e r C u s t o m E d i t o r ( j a v a . u t i l . Date . c l a s s , new CustomDateEditor ( new SimpleDateFormat ( yyyy dd ) , true ) ) ; MM CustomEditorSample bean = ( CustomEditorSample ) b f . g e t B e a n ( mybean ) ;
Metoda registerCustomEditor() wymaga podania dwch argumentw: typu, ktry chcemy edytowa musi to by ten sam typ, ktry przyjmuje metoda ustawiajca pole, edytor, implementujcy interfejs java.beans.PropertyEditor. Mechanizm rejestrowania edytorw jest wic bardzo elastyczny i umoliwia dokonywanie konwersji dowolnych acuchw tekstowych na obiekty i odwrotnie.
27
Element <constructor-arg> moe zawiera dwa opcjonalne atrybuty: type czyli pen nazw typu parametru, index czyli nieujemn liczb okrelajc, ktrego parametru denicja dotyczy. Mona pomin powysze atrybuty, jeeli da si jednoznacznie rozpozna argumenty, czyli w przypadku, gdy s one rnych typw. Element constructor-arg moe zawiera dowolny element spord value, list, map, set, properties, bean, null, a take referencje do innych beanw.
28
instancji beanu nie da si z pewnych powodw utworzy bezporednio (np. klasa, ktra ma by beanem nie jest zgodna ze standardem JavaBeans). Spring Framework oferuje stosowanie fabryk beanw na dwa sposoby: poprzez implementacj interfejsu FactoryBean, poprzez uycie atrybutw factory-bean oraz factory-method w pliku konguracyjnym.
Interfejs org.springframework.beans.factory.FactoryBean Bean, ktry implementuje interfejs FactoryBean jest traktowany przez Spring Framework jako szczeglny rodzaj beanu. W tym przypadku kontener tworzy instancj beanu, ale nie zwraca referencji do niej lecz zleca jej zadanie utworzenia obiektu docelowego. Interfejs ten deniuje trzy metody:
Listing 3.19. Interfejs org.springframework.beans.factory.FactoryBean
1 2 3 O b j e c t g e t O b j e c t ( ) throws E x c e p t i o n ; Class getObjectType ( ) ; boolean i s S i n g l e t o n ( ) ;
Metoda getObject() zwraca docelowy obiekt utworzony przez fabryk. Metoda getObjectType() zwraca typ docelowego obiektu, ale moe te zwrci warto null, jeeli fabryka nie jest w stanie zidentykowa typu przed utworzeniem obiektu docelowego. Ostatnia metoda, isSingleton() wskazuje, czy bean ma by jedn wspdzielon instancj, czy te fabryka za kadym razem powinna utworzy nowy obiekt.
Atrybuty factory-bean i factory-method Alternatyw dla interfejsu FactoryBean stanowi dopisanie do denicji beanu dodatkowych informacji wskazujcych na fabryk. Poniszy przykad zawiera dwie denicje beanw, ktre s tworzone przez fabryki.
Listing 3.20. Fabryki beanw
1 2 3 4 < ! f a b r y k a 1 > <bean i d= bean1 c l a s s= Bean
29
5 6 7 8 9 10 11 12
f a c t o r y method= c r e a t e O b j e c t > < ! f a b r y k a 2 > <bean i d= f a c t o r y B e a n c l a s s= F a c t o r y B e a n /> <bean i d= bean2 f a c t o r y bean= f a c t o r y B e a n f a c t o r y method= c r e a t e O b j e c t />
Klasa Bean w fabryce 1 musi posiada statyczn metod createObject() zwracajc docelowy obiekt. Typ obiektu moe by dowolny, w szczeglnoci nie musi to by typ wskazany przez atrybut class. Jeeli jest to ten sam typ to metod tworzc mona traktowa jako alternatyw dla konstruktora. Metoda wskazana w atrybucie factory-method moe przyjmowa dowoln ilo argumentw. Konguruje si je za pomoc poznanego ju elementu constructor-arg, dokadnie w ten sam sposb, jak klasy bez konstruktora domylnego, z tym wyjtkiem, e nie dziaa w tym przypadku automatyczne dopasowywanie beanw (zagadnienie to omwione zostanie szerzej przy okazji prezentacji kontenera IoC). Fabryka 2 dziaa podobnie, jednak tworzenie instancji beanu nie nastpuje w tej samej klasie w wyniku wywoania statycznej metody, lecz jest delegowane do innego beanu. Fabryka jest wic w tym przypadku zwykym beanem, ktry posiada, ju niestatyczn, metod createObject.
Interfejs FactoryBean czy atrybuty? Poniewa Spring Framework oferuje dwa alternatywne mechanizmy stosowania fabryk zasadnym wydaje si pytanie: ktry stosowa i jakich sytuacjach? Jeeli moemy sobie pozwoli na zaleno od API Spring Framework w naszym projekcie to najwygodniej jest uy interfejsu FactoryBean. Wszystkie rozszerzenia Spring Framework stosuj wanie to podejcie. W innym przypadku lepiej zda si na atrybuty factory-method i factory-bean.
30
Z lektury poprzedniego rozdziau wiemy ju sporo o beanach, ich powstawaniu, naturze i cyklu ycia. Kolejnym fundamentalnym elementem platformy Spring Framework jest kontener IoC umoliwiajcy deniowanie wzajemnych relacji midzy beanami. O relacji czy zalenoci midzy obiekami (beanami) mona mwi wtedy, gdy jeden bean wymaga do prawidowego funkcjonowania innego obiektu. IoC, czyli Inversion of Control to wzorzec projektowy umoliwiajcy realizacj zalenoci midzy obiektami niejako bez wiedzy o tym fakcie samych zainteresowanych. IoC realizuje si najczciej poprzez wstrzykiwanie zalenoci (ang. dependency injection) umieszczajc uprzednio obiekty w specjalnym kontenerze. Kontener sam potra poprawnie dopasowa wszystkie zalenoci i skongurowa obiekty przed udostpnieniem ich uytkownikowi. Wicej na temat wzorca IoC mona poczyta w moim artykule Wprowadzenie do lightweight containers opublikowanym w portalu JDN (http://jdn.pl/node/1). Spring Framework zawiera bardzo wygodn w uyciu implementacj kontenera IoC. Springowy kontener IoC jest sercem caej plaformy, wszystkie pozostae elementy z niego korzystaj. Co wane kontener IoC w Spring Framework jest rwnie dostpny w postaci samodzielnej biblioteki, ktr mona zaintegrowa z wasnym projektem bez koniecznoci korzystania z caego frameworka. W takim przypadku Spring Beans (w postaci archiwum jar o rozmiarze nieco wikszym ni 200Kb) stanowi doskona alternatyw dla innych dostpnych na rynku kontenerw IoC (Picocontainer, HiveMind). Kontener IoC w Spring Framework jest nazywany rwnie fabryk beanw. Te dwie nazwy stosowane bd w niniejszej ksice wymiennie.
31
32
19 20 }
Widzimy wic, e klasa NameWriter korzysta z klasy NameProvider w celu pobrania imienia. Jak zapisa t zaleno w Spring Framework?
Wstrzykiwanie zalenoci za pomoc setterw Pierwszym omwionym sposobem wstrzykiwania zalenoci jest wykorzystanie setterw, czyli metod ustawiajcych dane pole. W tym przypadku konguracja beanw powinna wyglda nastpujco (dla czytelnoci pominito deklaracj DTD):
Listing 3.23. Wstrzykiwanie zalenoci przy pomocy setterw
1 2 3 4 5 6 <b e a n s> <bean i d= n a m e P r o v i d e r c l a s s= p r z y k l a d y . r o z d z i a l 3 . NameProvider /> <bean i d= nameWriter c l a s s= p r z y k l a d y . r o z d z i a l 3 . NameWriter > <p r o p e r t y name= n a m e P r o v i d e r > r e f < </ bean> </ b e a n s> bean= n a m e P r o v i d e r /> </ p r o p e r t y>
W linijce 4 uywajc elementu <ref bean="nameProvider"/> informujemy kontener, e w trakcie tworzenia beana o nazwie nameWriter powinien przekaza do settera o nazwie nameProvider (czyli konretnie do metody setNameProvider()) beana o nazwie nameProvider. Jeli bean nameProvider nie zosta wczeniej utworzony kontener utworzy go i zainicjuje automatycznie. Mona sprawdzi dziaanie wstrzykiwania zalenoci przez settery uruchamiajc przykad przyklady.rozdzial3.SetterInjectionExample.
Wstrzykiwanie zalenoci za pomoc konstruktorw Przeledmy teraz ten sam przykad, ale zamiast setterw uyjmy argumentu konstruktora do przekazania referencji do obiektu zalenego.
Listing 3.24. Wstrzykiwanie zalenoci przy pomocy konstruktorw
1 2 3 4 5 <b e a n s> <bean i d= n a m e P r o v i d e r c l a s s= p r z y k l a d y . r o z d z i a l 3 . NameProvider /> <bean i d= nameWriter c l a s s= p r z y k l a d y . r o z d z i a l 3 . NameWriter > <c o n s t r u c t o r a r g> r e f < </ bean> bean= n a m e P r o v i d e r /> </ c o n s t r u c t o r a r g>
33
</ b e a n s>
Element <constructor-arg> suy wanie do ustawiania argumentw konstruktora. Jeli konstruktor miaby wicej ni jeden argument to oczywicie naley dla kadego z nich stworzy odpowiedni wpis <constructor-arg>. Wstrzykiwanie zalenoci za pomoc konstruktorw demonstruje klasa przyklady.rozdzial3.ContructorInjectionExample. Efekt dziaania wstrzykiwania zalenoci przez konstruktor z pozoru niczym nie rni si od poprzedniego przykadu, w ktrym uyto do tego celu metod ustawiajcy pola. I w jednym i w drugim przykadzie zosta osignity ten sam cel obiekt NameWriter otrzyma referencj do obiektu NameProvider. Niemniej jednak warto wspomnie o pewnej istotnej rnicy wynikajcej z zastosowania innego sposobu wstrzykiwania zalenoci. Ot uywajc wstrzykiwania zalenoci za pomoc konstruktorw moemy mie pewno, e bean, ktrego chcemy uy zosta poprawnie zainicjowany. Jeli np. nie byoby konstruktora domylnego w klasie a programista zapomniaby doda odpowiedniego elementu <constructor-arg> do pliku konguracyjnego to obiekt w ogle nie zostaby utworzony. Wirtualna maszyna zgosiaby stosowny wyjtek i kontener IoC w ogle by nie wystartowa. W przypadku wstrzykiwania zalenoci za pomoc setterw obiekt zostaby utworzony, kontener IoC funkcjonowaby z pozoru poprawnie, a o bdnej konguracji zostalibymy poinformowani dopiero podczas prby wywoania metody biznesowej le skongurowanego beanu. O tym jak radzi sobie ze sprawdzaniem i zapewnieniem poprawnej konguracji beanw w dalszej czci tego rozdziau.
Co potra, a czego nie kontener IoC? W powyszym prostym przykadzie sytuacja bya wrcz komfortowa. Dwa beany prosta zaleno. Taki scenariusz jednak daleko odbiega od typowego zastosowania. Czsto zalenoci s duo bardziej zawie, kaskadowe, jeden bean czsto wymaga do poprawnego funkcjonowania wiele innych beanw.
34
Spring Framework umoliwia zdeniowanie prawie dowolnych zalenoci midzy obiektami. Jedynym wyjtkiem s zalenoci cykliczne, gdy bean A zaley od beana B i jednoczenie bean B zaley od beana A. Takiej sytuacji kontener IoC w Spring Framework nie potra obsuy, ale potra j wykry i zasygnalizowa bd w konguracji.
35
atrybut init-method moemy sprawdzi, czy wszystkie pola zostay poprawnie ustawione i czy bean jest gotowy do wiadczenia usug. Jeli tak nie jest to mamy szans zgosi stosowny wyjtek. Bardzo wczenie (na etapie tworzenia kontenera) jestemy wic w stanie wykry braki w konguracji.
Automatyczne dopasowywanie beanw na podstawie nazw Pierwszy sposb automatycznego dopasowywania beanw opiera si na bardzo prostym zaoeniu. Jeeli jeden bean posiada waciwo xxx w myl specykacji Java Beans (czyli posiada publiczn metod void setXxx()), to kontener IoC poszuka beana o nazwie xxx i przekae referencj do niego. To najprostszy i chyba najbardziej intuicyjny sposb automatycznego deniowania zalenoci. W pliku konguracyjnym wyglda to nastpujco:
Listing 3.25. Dopasowywanie zalenoci wg nazwy
36
1 2 3 4
<b e a n s> <bean i d= n a m e P r o v i d e r c l a s s= p r z y k l a d y . r o z d z i a l 3 . NameProvider /> <bean i d= nameWriter c l a s s= p r z y k l a d y . r o z d z i a l 3 . NameWriter a u t o w i r e=byName /> </ b e a n s>
Klasa NameWriter posiada publiczn metod setNameProvider(), zastosowalimy dopasowanie wg nazwy (atrybut autowire="byName"), dlatego te kontener poszuka wrd beanw jednego o nazwie nameProvider, zainicjuje go i przekae referencj do niego beanowi NameWriter.
Automatyczne dopasowywanie beanw na podstawie typu Innym typem automatycznego dopasowywania beanw jest dopasowywanie ich na podstawie typu. Listing 3.26 przedstawia przykadow konguracj:
Listing 3.26. Dopasowywanie zalenoci wg typu
1 2 3 4 <b e a n s> <bean i d= dowolnaNazwa c l a s s= p r z y k l a d y . r o z d z i a l 3 . NameProvider /> <bean i d= nameWriter c l a s s= p r z y k l a d y . r o z d z i a l 3 . NameWriter a u t o w i r e= byType /> </ b e a n s>
Atrybut autowire="byType" informuje fabryk beanw, e powinna ona poszuka w klasie NameWriter wszytkich publicznych metod ustawiajcych, sprawdzi typy argumentw, jakie te metody przyjmuj, a nastpnie wyszuka pasujce do tych typw beany zadeklarowane w kontenerze. Dopasowywanie na podstawie typu ma jedn zasadnicz wad: moe by stosowane tylko w przypadku, gdy mamy pewno, e istnieje tylko jeden bean szukanego typu. Jeli jest ich wicej kontener zgosi wyjtek i zakoczy prac.
Automatyczne dopasowywanie beanw na podstawie konstruktorw Trzecim typem automatycznego dopasowywania beanw jest wyszukiwanie ich na podstawie argumentw konstruktora. Zasada dziaania jest tu analogiczna do dopasowywania wg typu, z tym jednak wyjtkiem, e pod uwag brane s argumenty konstruktorw zamiast setterw. Listing 3.27 zawiera przykadow konguracj:
37
Peen automat Spring Framework oferuje rwnie jeden specjalny tryb automatycznego wyszukiwania zalenoci. Jeli warto atrybutu autowire ustawimy na autodetect to kontener sprbuje dobra optymaln metod szukania zalenoci. Algorytm przedstawia si nastpujco: jeli klasa nie posiada domylnego konstruktora to zalenoci dopasowywane s na podstawie dostpnego konstruktora, jeli klasa posiada kilka konstruktorw to wybierany jest ten z najwiksz iloci atrybutw, jeli natomiast klasa posiada domylny konstruktor to stosowana jest metoda automatycznego dopasowywania zalenoci na podstawie typu (autowire="byType").
Domylna polityka autodopasowania Z przytoczonych przykadw wynika, e atrybut autowire naley doda do kadego beana, ktrego autodopasowanie ma dotyczy. Domyln polityk Spring Framework jest cakowity brak autodopasowania (atrybut autowire przyjmuje warto no). Jeli chcielibymy, aby wszystkie beany w kontenerze podlegay np. regule autodopasowywania wg nazwy powinnimy przy kadej deklaracji beanu umieci atrybut autowire i nada mu podan warto. Rczne przerabianie caego pliku konguracyjnego mogoby by do monotonne i czasochonne. Autorzy Spring Framework przewidzieli wic moliwo ustawienia domylnej polityki autodopasowania dla caego kontenera. Wystarczy ustawi atrybut default-autowire gwnego elementu
38
beans nadajc mu jedn warto z no, byName, byType, constructor lub autodetect. Np.:
Listing 3.28. Domylna polityka autodopasowania
1 2 3 4 <b e a n s d e f a u l ta u t o w i r e=byName> <bean i d= n a m e P r o v i d e r c l a s s= p r z y k l a d y . r o z d z i a l 3 . NameProvider /> <bean i d= nameWriter c l a s s= p r z y k l a d y . r o z d z i a l 3 . NameWriter /> </ b e a n s>
Stosowa autodopasowywanie czy nie? Autorzy Spring Framework nie polecaj stosowania mechanizmu automatycznego dopasowywania zalenoci. Mimo kilku niewtpliwych zalet (znaczne zmniejszenie wielkoci pliku XML, czy brak koniecznoci wprowadzania zmian w pliku XML w przypadku zmiany sygnatury konstruktora, czy dodania nowych setterw) stosowanie autodopasowania moe wprowadzi sporo zamieszania. Przede wszystkim nie jest oczywiste dla czytajcego tak skonstruowany plik konguracyjny. Aby zrozumie zalenoci midzy beanami trzeba zajrze do kodu. Oczywist prawd jest, e prosty, zrozumiay, intuicyjny i samodokumentujcy si kod to klucz do sukcesu projektu w duszej perspektywie. Automatyczne dopasowywanie wprowadza do projektu swego rodzaju magi - co si dzieje, ale nie wida dlaczego. Oczywicie istniej sytuacje, gdzie stosowanie autodopasowywania jest wrcz wskazane. S to przede wszytkim mae projekty (prototypy), ktre pisze si szybko i wprowadza czste zmiany w kodzie. Autodopasowywanie uwalnia wtedy programist od koniecznoci czstej rcznej modykacji pliku XML. Z dowiadczenia mog powiedzie, e cakiem niele sprawdza si dopasowywanie wg nazwy. Przede wszystkim dlatego, e wymusza pewn konwencj nazewnicz (nazwa beana musi by tosama z setterem), co zapewnia, e kod staje si bardzo intuicyjny - wiemy, czego si spodziewa i gdzie. Jeli dodatkowo zastosuje si samoopisujce si nazwy setterw (np.
39
nameProvider zamiast nProv) to czytelny pozostanie zarwno kod, jak i plik konguracyjny. Problematyczne natomiast moe okaza si dowizywanie wg typu, poniewa na jakim etapie prac nad projektem moe si zdarzy, e pojawi si dwa lub wicej beany tego samego typu. To z kolei doprowadzioby pewnie do sytuacji, w ktrej autodopasowanie stosowane by byo dla wikszoci beanw (bo tak np. ustawiona byaby domyla polityka autowire), a dla kilku naleaoby zdeniowa zalenoci explicite. Czytelno takiego pliku drastycznie si pogarsza. Jak w kadym przypadku zoty rodek naley znale samemu. Pocztkujcym uytkownikom Spring Framework zalecam nie korzystanie z mechanizmu autodopasowania.
40
ROZDZIA 4
Co jaki czas pojawiaj si w informatyce przeomowe koncepcje, ktre rzucaj zupenie nowe wiato na dotychczas stosowane rozwizania. Jednym z wikszych skokw jakociowych ostatnich lat jest, zdaniem autora i nie tylko, programowanie aspektowe (ang. AOP - Aspect Oriented Programming). Pozwala ono spojrze na program w kategoriach powtarzajcych si zada (czyli wanie aspektw), ktre mona wydzieli do postaci niezalenych od siebie moduw i poczy wzajemnie tam, gdzie ich funkcje si krzyuj. Kod rdowy staje si przez to duo prostszy, bardziej elegancki, a jednoczenie zachowuje w peni swoj funkcjonalno.
W niniejszym rozdziale omwione zostan koncepcja programowania aspektowego oraz mechanizmy realizujce AOP w Spring Framework.
Osoby, ktre teoretyczne podstawy AOP maj ju opanowane, mog pomin lektur nastpnego podrozdziau i przej bezporednio do podrozdziau 4.3. 41
42
Metoda service() jest uywana w rnych miejscach projektu. Aby wykona nasze zadanie moemy doda do kodu aplikacji odpowiednie linijki:
Listing 4.2. Rozwizanie oczywiste
1 2 3 System . o u t . p r i n t l n ( Wchodze do metody s e r v i c e ( ) : service (); System . o u t . p r i n t l n ( Wychodze z metody s e r v i c e ( ) : + System . c u r r e n t T i m e M i l l i s ( ) ) ; + System . c u r r e n t T i m e M i l l i s ( ) ) ;
To dziaa, ma jednak jedn zasadnicz wad. Moe si zdarzy, e w bardzo krtkim czasie staniemy si posiadaczami kodu, w ktrym ilo linijek diagnostycznych bdzie wiksza ni kodu waciwego. Powinnimy wic poszuka lepszego rozwizania, ktre nie przyczyniaoby si do zaciemnienia kodu gwnego. Najprostszym rozwizaniem, jakie kademu zapewne przychodzi na myl jest umieszczenie linijki wypisujcej komunikat bezporednio w ciele metody service(). Jest to ju zdecydowanie lepsze rozwizanie. Kod wypisujcy komunikaty znajduje si w jednym miejscu i wszystko wydaje si by w porzdku. . . do czasu, gdy rozbudujemy nasz usug o kolejn metod biz-
43
nesow np. service2(), ktrej wywoania rwnie chcielibymy ujrze w dzienniku systemowym. Dodanie linijek System.out. . . do kadej nowej metody biznesowej nie rozwizuje globalnie problemu, przenosi go jedynie w inne miejsce. Trzeba poszuka wic jeszcze lepszego, denitywnego rozwizania.
44
30 31 32 33 34 35 36 37 38 39 40 41 42 43 } } } p r i v a t e long t i m e ( ) { return System . c u r r e n t T i m e M i l l i s ( ) ; } public O b j e c t i n v o k e ( O b j e c t proxy , Method method , Object [ ] a r g s ) throws Throwable { args ) ; System . o u t . p r i n t l n ( + + method . getName ( ) + + t i m e ( ) ) ; O b j e c t r e t u r n V a l u e = method . i n v o k e ( t a r g e t , return r e t u r n V a l u e ; System . o u t . p r i n t l n ( + method . getName ( ) + + t i m e ( ) ) ;
Listing 4.3 pokazuje sposb, w jaki obiekty proxy umoliwiaj grupowanie pewnych problemw i kompleksowe ich rozwizywanie. W wierszach 14 17 tworzony jest obiekt porednika, ktry implementuje interfejs IService. Ostatni argument metody newProxyInstance() pozwala na przekazanie nowotworzonemu porednikowi kodu, ktry powinien zosta wykonany w momencie wywoania dowolnej metody ma obiekcie porednika. cile rzecz ujmujc jest to dowolna klasa implementujca interfejs InvocationHandler. W naszym przykadzie jest to klasa zdeniowana w wierszach 23 42. Jako argument konstruktora klasa LoggerHandler przyjmuje dowolny obiekt. Zatem przekazujc w konstruktorze obiekt klasy Service moemy w metodzie invoke(...) kontrolowa wywoania metod naszej usugi. Nasze nowe proxy bdzie delegowao wszystkie wywoania metod do usugi waciwej, a przy okazji moemy dopisa linijki diagnostyczne, o ktre nam chodzio. Dziki temu prostemu zabiegowi udekorowalimy nasz usug wzbogacajc nieco jej funkcjonalno. W ten sposb udao si cakiem sporo osign. Kod programu nie zawiera dodatkowych linijek, kod usugi pozosta nietknity, loguje wywoania metod niezalenie od ich iloci - po dodaniu nowych zachowa si dokadnie tak, jak tego oczekujemy. Jedyne, co musielimy zrobi to skorzysta z obiektu porednika i napisa fragment kodu realizujcy podan przez nas funkcjonalno. Idea dynamicznych porednikw to podstawa, na ktrej zbudowany zosta framework AOP w Spring Framework. Zanim jednak przejdziemy do omwienia
45
szczegw tej czci Spring Framework warto powici jeszcze kilka chwil samym aspektom.
Aspekty W terminologii AOP powiedzielibymy, e wyodrbnilimy pewien aspekt, ktry by moe wymaga szczeglnego potraktowania. Aspekt jest wic wydzielon funkcjonalnie czci programu, pewnym moduem, ktry moemy bezkolizyjnie i bezinwazyjnie poczy z innymi moduami (aspektami). Realizuje on okrelone zadanie i koncentruje si tylko na problemie (ang. concern), ktrego cile dotyczy (np. trzymajc si naszego przykadu bdzie to np. logowanie wywoywania metod). Wszdzie tam, gdzie mamy do czynienia z zazbianiem, czy przecinaniem si pewnych zada moemy zastosowa aspekty w miejsce tradycyjnego tworzenia hierarii klas i zawiych powiza midzy nimi. Problem przecinania si zada (ang. cross-cutting concerns) dotyczy niemal kadej aplikacji. Np. aplikacja musi by zarwno wydajna, jak i bezpieczna (wydajno i bezpieczestwo mona potraktowa jako przykady aspektw). Zadania te mona sobie wyobrazi jako pewne paszczyzny, ktre dopiero w aplikacji znajduj pewien punkt przecicia - spotykaj si, by wsplnie realizowa kompleksowe zadanie. Aspekty i AOP sprawiaj, e
46
realizacja takich zada jest prosta, znacznie prostsza ni w tradycyjnym obiektowym podejciu, opartym na hierarchii klas. Aspekty umoliwiaj lepsz enkapsulacj (usuga biznesowa nie musi wiedzie, czy logowane s wywoania jej metod, a modu logujcy nie musi wiedzie, co waciwie loguje) oraz zwikszaj w znacznym stopniu ponowne wykorzystanie raz napisanego kodu (nasz aspekt, jako samodzielnie funkcjonujcy modu, moemy zastosowa z dowoln inn usug biznesow).
Instrukcje W jaki sposb dodalimy nowy aspekt do kodu naszej usugi? Uylimy obiektu proxy, ktry otoczy wywoanie kadej metody biznesowej usugi pewnym dodatkowym kodem. w kod nazwany jest w wiecie aspektw instrukcj (ang. advice). W dalszej czci tego rozdziau przekonamy si, e istnieje kilka typw instrukcji, ktrych moemy uywa w zalenoci od potrzeb. Dziki instrukcjom moemy wpywa na przebieg wykonywania programu poprzez wzbogacenie kodu, podmienianie go, albo odpowiednie reagowanie, w przypadku pojawienia si wyjtku.
Punkty zcze Punkt zczenia (ang. join point) to dowolne miejsce w kodzie programu gwnego (w naszym przykadzie jest to kod usugi), w ktrzym styka si on z aspektem. Punktem zczenia w naszej aplikacji jest wywoanie metody biznesowej. W tym miejscu aplikacja powinna oprcz wasnego kodu wykona rwnie kod instrukcji (jednej lub wicej). Wywoanie metody to pewnie najprostszy, ale oczywicie nie jedyny moliwy punkt zczenia. Inne przykady to m.in.:
4.2. Aspekty pod lup wykonanie jakiego szczeglnego kawaka kodu, wystpienie wyjtku, etc.
47
Punkt przecicia Ostatnim wanym pojciem z zakresu AOP jest punkt przecicia (ang. pointcut), ktry jest niczym innym, jak tylko zbiorem punktw zcze. Punkt przecicia okrela, w ktrych miejscach aspekt spotyka si z kodem gwnym. W przypadku naszej przykadowej usugi punktem przecicia s wszystkie jej metody. Rne narzdzia AOP stosuj rne notacje umoliwiajce deniowanie punktw przeci. Jednym z powszechniej stosowanych sposobw jest uycie wyrae regularnych celem wskazania metod nalecych do danego punktu przecicia. Np. wyraenie set* oznacza moe wszystkie metody ustawiajce (settery).
Obiekt proxy Podstawowym i najbardziej naturalnym narzdziem, ktre ju poznalimy, jest obiekt proxy. Niezaprzeczaln zalet tego rozwizania jest jego prostota i wsparcie ze strony samego jzyka Java. Podejcie to nie jest jednak pozbawione wad, z ktrych najwaniejsz jest brak moliwoci tworzenia obiektw proxy dla konkretnych klas. Jzyk Java przewiduje tylko i wycznie tworzenie obiektw proxy dla interfejsw.
48
Drug ciemn stron stosowania obiektw dynamicznych porednikw jest ich nisza ni czystych klas wydajno. Zwizany z utworzeniem i dziaaniem obiektu proxy dodatkowy narzut czasowy, moe, przy bardzo czstych wywoaniach metod, nie by obojtny dla oglnej wydajnoci aplikacji1 .
Generowanie byte-codeu Nie zawsze stosowanie prostych obiektw proxy jest wystarczajce. Nietrudno wyobrazi sobie sytuacj, gdy usuga nie implementuje adnego interfejsu i nie mamy jej kodu rdowego (np. jest to zakupiona komercyjna, zamknita biblioteka). Czy mona w takiej sytuacji zastosowa AOP? Mona, cho trzeba uciec si do nieco bardziej wyranowanej metody, a mianowicie sztuczki okrelanej mianem generowania byte-codeu. Korzystajc z pomocy takich bibliotek jak np. CGLIB (http://cglib.sf.net) mona w locie wygenerowa kod binarny klasy, ktra oprcz wasnej funkcjonalnoci zostanie wzbogacona o funkcjonalno pochodzc z kodu instrukcji. Ten sposb nie tylko eliminuje problem wydajnoci, ale rwnie pozwala w duym stopniu rozszerzy moliwoci aspektw. Wreszcie moliwe staje si: stworzenie porednika dla konkretnej klasy, a nie tylko dla interfejsu, dodanie interfejsw do klas, ktre pierwotnie ich nie implementuj, deniowane punktw zcze np. wewntrz metod.
Wariacje na temat AOP Wyej wymienione narzdzia realizacji AOP wystpuj w rnych wariantach. Niektre projekty realizuj AOP na poziomie instancji, inne na poziomie caej klasy. Ten drugi przypadek wymaga zastosowania dedykowanej
1
cho trzeba przyzna, e z kadym nowym wydaniem JVM wydajno tego rozwiza-
nia ronie
49
adowarki klas (ang. classloader ), ktra w miejsce zwykej instancji klasy utworzy instancj wzbogacon o kod instrukcji. Innym przykadem moe by wygenerowanie wzbogaconego kodu Java, jeszcze przed kompilacj programu, cho od tego rozwizania konsekwentnie si odchodzi ze wzgldu nie niewygod stosowania i moliwo osignicia tych samych efektw przy znacznie mniejszych nakadach pracy.
Diagnozowanie i monitorowanie Mielimy ju okazj pozna jedno zastosowanie aspektw, czyli dodawanie do kodu linijek diagnostycznych informujcych o stanie aplikacji. W ten sposb mona monitorowa wydajno i stan programu. Przewaga aspektw nad specjalistycznymi bibliotekami do logowania zdarze polega na tym, e kod bazowy aplikacji nie zawiera dodatkowych, zaciemniajcych algorytm linii. Logowanie zdarze realizowane jest w sposb przezroczysty. Co wicej, aspekt diagnostyczny mona np. wyczy w produkcyjnej instalacji aplikacji (jeli powodowaby zbyt due obcienie), czy te zamieni go na, bardziej w takiej sytuacji przydatny, aspekt monitorujcy.
Kontrola dostpu Wyobramy sobie sytuacj, w ktrej dostp do wybranych czci aplikacji wymaga specjalnych uprawnie, np. uytkownik powinien by zalogowany i posiada odpowiedni rol. Jak to osign? W tradycyjnym podejciu naleaoby przed wejciem lub tu po wejciu do chronionych metod sprawdzi czy spenione s warunki bezpieczestwa.
50
Kada chroniona metoda posiadaaby podobny lub identyczny kod gwarantujcy spenienie wymogw bezpieczestwa. Dziki aspektom moliwe jest wydzielenie takiego kodu wartownika do swoistej czarnej skrzynki, a poprzez odpowiednie zdeniowanie punktw przeci proste staje si rozpicie parasola ochronnego nad wszystkimi chronionymi fragmentami kodu bazowego. Co wane, dzieje si to w sposb cakowicie transparentny dla aplikacji.
Transakcje Kolejne, bodaj najpowszechniejsze, zastosowanie AOP to wydzielenie do niezalenego moduu caego kodu zwizanego z zarzdzaniem transakcjami. W przypadku transakcji bazodanowych oznacza to, e kod bazowy aplikacji wykonuje tylko i wycznie zapytania SQL zwizane z operacjami na rekordach (bezporednio lub z wykorzystaniem bibliotek O/R), nie przejmujc si szczeglnie wspbienoci. Transakcyjno zapewniana jest przez odpowiednio skonstruowany aspekt. Punktami zcze bd w takim przypadku wszystkie metody load(...), save(...), delete(...), etc., ktre dokonuj trwaych modykacji danych. Metody te stan si dziki aspektowi duo czytelniejsze, nie bd zawieray niekoczcych si blokw try {BEGIN ... COMMIT} catch {ROLLBACK}. Bd tylko staray si wykona swoje podstawowe zadanie, a zapewnienie wycznego dostpu do rda danych i stosown reakcj na ewentualne wyjtki zapewni aspekt.
Pami podrczna Aspektw mona rwnie uy do dodania do aplikacji pamici podrcznej (ang. cache), przyspieszajcej jej dziaanie. Aplikujc odpowiedni aspekt w kodzie bazowym mona przechwyci wywoanie niektrych czasochonnych metod, sprawdzi argumenty wejciowe metody, a nastpnie poszuka w pamici podrcznej, czy dla tych agrumentw nie zostaa ju wczeniej wyliczona i zapamitana warto wynikowa.
51
Jeli tak, to mona zwrci t warto bezporednio, bez wykonywania czasochonnego algorytmu. Znowu moe dzia si to w sposb niezauwaalny dla aplikacji.
Inne zastosowania
Zastosowa aspektw moe by oczywicie znacznie wicej. Przytoczone przykady su jedynie do lepszego zobrazowania caej idei i nadania jej realnego ksztatu. Dziki aspektom moliwe staje si pisanie przejrzystych, eleganckich i modularnych aplikacji, dlatego warto rozway to podejcie przed napisaniem wikszego fragmentu kodu - by moe AOP stanowi bdzie najprostsze i optymalne rozwizanie. Nawet jeli nie wiemy dokadnie, w jakim kierunku tworzona przez nas aplikacja bdzie w przyszoci ewoluowa, aspekty mog okaza si jedyn drog do szybkiego dodawania nowej funkcjonalnoci, bez potrzeby wprowadzania rewolucyjnych zmian w kodzie bazowym.
52
adna ksika, w ktrej poruszana jest tematyka programowania aspektowego, nie moe pomin znaczenia pierwszego i najwaniejszego projektu AOP, jakim zapewne jest AspectJ2 . AspectJ jest najpopularniejszym frameworkiem AOP, stworzonym m.in. przez samego autora koncepcji AOP Gregora Kiczalesa. Jest to najprawdopodobniej najbardziej zaawansowany projekt tego typu, oferujcy najszersze spektrum moliwoci. AspectJ rozszerza jzyk Java o dodatkowe sowa kluczowe i skadni umoliwiajc deniowanie aspektw. Kod napisany w AspectJ jest integrowany z byte-codem aplikacji bazowej w procesie okrelanym mianem weaving. Weaving polega na zlokalizowaniu na podstawie denicji zawartych w kodzie AspectJ punktw przeci i odpowiednim zmodykowaniu bytecodeu aplikacji bazowej. W wyniku tego procesu generowany jest nowy byte-code, wzbogacony o kod pochodzcy z instrukcji. Jeli weaving zostanie wykonany tu po skompilowaniu klas aplikacji bazowej i przed uruchomieniem JVM to mwimy o kompilacji statycznej. Drugim sposobem jest manipulowanie byte-codem ju w czase pracy aplikacji, co okrelane jest mianem load-time weaving. Stworzenie rozszerzenia jzyka Java i dodatkowy krok kompilacji, jakim jest weaving, mog by postrzegane jako najwiksza wada AspectJ. Autorzy projektu zadbali jednak o to, aby tworzenie aspektw nie byo dla programisty zbyt uciliwe. Dostpna jest wtyczka do rodowiska Eclipse AspectJ Development Tools3 (AJDT), dziki ktrej korzystanie z AspectJ staje si prostsze. Osoby szerzej zainteresowane zagadnieniem AOP powinny koniecznie zapozna si z tym projektem.
2 3
http://eclipse.org/aspectj http://www.eclipse.org/ajdt/
53
Podobnie jak wiele innych dziedzin AOP rwnie przeszed przez faz bujnego rozkwitu. Nie dziao si to moe na a tak wielk skal, jak mielimy (i mamy nadal!) okazj podziwia w przypadku frameworkw do tworzenia aplikacji internetowych, ale i tak wiat Javy doczeka si co najmniej kilkunastu projektw dedykowanych AOP. Spord bardziej znanych warto wymieni:
Wszystkie wyej wymienione rozwizania stosuj odmienne podejcie ni AspectJ. Nie rozszerzaj one jzyka Java, ani te nie tworz wasnego. Operuj wycznie na czystych obiektach POJO, kod wskazwek to zwyke klasy, a konguracja AOP przewanie realizowana jest w pliku XML. S to wic rozwizania atwiejsze w uyciu, gdy nie wymagaj adnych dodatkowych narzdzi. Ostatnie dwa projekty (JBossAOP oraz Spring Framework AOP) wydaj si zdobywa coraz wiksz popularno, co po czci wynika z tego, e zwizane s cile z bardzo popularnymi na rynku produktami. Oczywicie nie jest to gwny powd ich szerokiego stosowania - w rzeczywistoci s to potne narzdzia, ktre, jak si przekonamy podczas omawiania Spring Framework AOP, umoliwiaj osignicie zdumiewajcych rezultatw przy jednoczesnym zachowaniu przejrzystoci kodu. Po tym, nieco dugim, wstpnie moemy przystpi do zagbienia si w AOP w Spring Framework, ktry, zaraz po beanach i kontenerze IoC, jest trzecim najwaniejszym skadnikiem tej platformy.
54
4.3.1. org.springframework.aop.framework.ProxyFactoryBean
ProxyFactoryBean to specjalny bean, ktry potra utworzy instancj dynamicznego porednika dla dowolnego obiektu. Jest to wic podstawowe narzdzie realizacji AOP w Spring Framework.
Podsumowanie
Czas na podsumowanie
55
56
Listings
3.1. Pierwszy bean HelloWorld . . . . . . . . . . . . . . . . . . . 3.2. Minimalny plik konguracyjny w wersji XML . . . . . . . . . 3.3. Minimalny plik konguracyjny w wersji *.properties . . . . . 3.4. Przykad wykorzystania beanu HelloWorld . . . . . . . . . . . 3.5. Bardziej szczegowy plik konguracyjny . . . . . . . . . . . . 3.6. Klasa bez konstruktora domylnego . . . . . . . . . . . . . . . 3.7. Konguracja waciwoci beanw . . . . . . . . . . . . . . . . 3.8. Deniowanie kolekcji . . . . . . . . . . . . . . . . . . . . . . . 3.9. Metody zgodne ze specykacj JavaBean . . . . . . . . . . . . 3.10. Zagniedony bean . . . . . . . . . . . . . . . . . . . . . . . . 3.11. Interfejs InitializingBean . . . . . . . . . . . . . . . . . . . . . 3.12. Interfejs DisposableBean . . . . . . . . . . . . . . . . . . . . . 3.13. Deklaratywne deniowanie cyklu ycia . . . . . . . . . . . . . 3.14. Przykad konguracji beanu z polem typu java.util.Date . . . 3.15. Metoda ustawiajca pole date . . . . . . . . . . . . . . . . . . 3.16. Rejestrowanie wasnych edytorw waciwoci . . . . . . . . . 3.17. Klasa bez konstruktora domylnego . . . . . . . . . . . . . . . 3.18. Konguracja kontruktora . . . . . . . . . . . . . . . . . . . . 3.19. Interfejs org.springframework.beans.factory.FactoryBean . . . 57 17 17 17 18 18 20 21 21 22 22 24 24 25 26 26 26 27 27 28
Listings 3.20. Fabryki beanw . . . . . . . . . . . . . . . . . . . . . . . . . . 3.21. Przykadowe beany - NameProvider . . . . . . . . . . . . . . 3.22. Przykadowe beany - NameWriter . . . . . . . . . . . . . . . . 3.23. Wstrzykiwanie zalenoci przy pomocy setterw . . . . . . . . 3.24. Wstrzykiwanie zalenoci przy pomocy konstruktorw . . . . 3.25. Dopasowywanie zalenoci wg nazwy . . . . . . . . . . . . . . 3.26. Dopasowywanie zalenoci wg typu . . . . . . . . . . . . . . . 3.27. Dopasowywanie zalenoci wg konstruktorw . . . . . . . . . 3.28. Domylna polityka autodopasowania . . . . . . . . . . . . . . 4.1. Usuga, ktrej wywoania chcemy logowa . . . . . . . . . . . 4.2. Rozwizanie oczywiste . . . . . . . . . . . . . . . . . . . . . . 4.3. Dynamiczny porednik . . . . . . . . . . . . . . . . . . . . . .
58 28 31 31 32 32 35 36 37 38 42 42 43
Spis rysunkw
59
Spis rysunkw
60
Spis tablic
61