sobota, 26 lipca 2008

Spring+Seam - integracja dla ubogich

Wczoraj opisałem dosyć ogólnie wszystkie warstwy. Teraz pora na obniżenie poziomu abstrakcji; zobaczmy jak to wszystko ze sobą skleić przy pomocy Springa.

Oczywiście można zadać pytanie: po co sie wydurniać? Czy używanie Springa to nie nakładanie gaci przez głowę? Przecież Seam wspiera injekcje, bijeckje, outjekcje...

Założenie jest takie, że nie chcemy widzieć Seama w warstwach niższych niż warstwa prezentacji aby nie wiązać się z nim zbyt mocno mając na uwadze ogromną skalę czasową projektu. Spring pozwala na eleganckie zarządzanie transakcjami i bezpieczeństwem (Spring Security - dawniej ACEGI). Wogóle... Spring to Spring:)
Poza tym kwestia nieszczęsnych adnotacji. Wg mnie używanie adnotacji w celu Dependency Injection to chromy pomysł - zdradza on chyba niezrozumienie idei DI. Prosty kontrprzykład: w celu zmiany wstrzykiwanej instancji muszę przekompilować kod. O ile da się z tym żyć, to niestety gdybym chciał wstrzykiwać przykładowo różnie strategie wyliczania podatków u różnych klientów to przydały by się chyba jakieś dyrektywy kompilacji w Javie hehehe. Natomiast trzymanie deklaracji wstrzykiwania w np XMLu pozwala na trzymanie jednej wersji kodu - systemy różnych klientów różnią się jedynie paroma XMLami. Że o konfiguracjach na potrzeby testów nie wspomnę...

Do rzeczy: załóżmy, że mamy klasę juzkejsu i chcemy zadeklarować ją w Springu:

<bean id="beanId" class="Klasa">
<property name="jakiesDAO"><ref bean="jakiesDAO"/></property>
</bean>

Teraz aby zrobić z naszego beana komponent Seamowy wystarczy do definicji beana dodać:
scope="prototype" - ponieważ Springowe singletony niezbyt nadają się na stanowe komponenty;)
<seam:component scope="CONVERSATION"/> - aby zadeklarować, że dany bean jest komponentem Seam o danym zasięgu.
Zatem całość wygląda tak:

<bean id="beanId" class="Klasa" scope="prototype">
<seam:component scope="CONVERSATION"/>
<property name="jakiesDAO"><ref bean="jakiesDAO"/></property>
</bean>


Oczywiście aby parser XML mógł zrozumieć co to znaczy seam:component potrzebujemy zmodyfikować plik konfiguracyjny Springa:


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:seam="http://jboss.com/products/seam/spring-seam"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://jboss.com/products/seam/spring-seam
http://jboss.com/products/seam/spring-seam-2.0.xsd
">
<!-- bean definitions -->
</bean>


Aby całość "zatrybiła", czyli aby nasze Springowe beany były widoczne w kontekście Seam i JSF musimy zadeklarować Resolvera w faces-config.xml:

<faces-config>
<application>
<el-resolver>
org.springframework.web.jsf.el.SpringBeanFacesELResolver
</el-resolver>
</application>
</faces-config>


Od tej pory w warstwie prezentacji (Czy to adnotacje @In w backing beanach czy wyrażenia EL w faceletach) możemy posługiwać się identyfikatorem naszego beana.


Oczywiście nasz przykład możemy rozszerzyć o managera transakcji i Proxy, który opakowuje nasze obiekty UC w celu obsłużenia transakcji w transparentny sposób:


<bean id="beanId" scope="prototype" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" >
<seam:component scope="CONVERSATION" class="Klasa"/>
<property name="proxyTargetClass" value="true"/>

<property name="transactionManager"><ref bean="txManager"/></property>
<property name="transactionAttributes">...</property>
<property name="target">
<bean id="beanId" class="Klasa" >
<property name="jakiesDAO"><ref bean="jakiesDAO"/></property>
</property>
</bean>


Jednak tym razem musimy dodać:
class do seam:component oraz paramter proxyTargetClass ponieważ wspaniały Seam nie obsługuje Dynamic Proxy:/
Niestety nie da się wyciągnąć transaction proxy jako abstrakcyjny bean w celu zmniejszenia ilości kodu ponieważ mamy wówczas paskudny wyjątek:/


Prawie gotowe. Prawie ponieważ aby zaznaczyć początek i koniec Seamowej konwersacji muszę uzyć adnotacji @Begin i @End. Mogę je dopisać w metodach backing beanów, które reagują na czynności użytkownika. Niestety zakładam, że wiele różnych kliknięć może rozpocząć konwersację (np odpalić metodę init z klasy juzkesju) - więc mamy bałagan. Lepiej byłby otagować dwie metody juzkejsu - wszystko jasno, czysta i porządnie. Niestety nie mogę sobie pozwolić na brudzenie corowych warstw jakimiś adnotacjami jakiegoś frameworka prezentacji. Póki co wymyśliłem takie oto rozwiązanie:
W warstwie prezentacji tworzę klasę wrappera dla klasy juzkejsu: wrapper implementuje tan sam interfejs co UC, wrapper ma wstrzykniętą implementację danego UC (tą zadeklarowaną w Springu). Wrapper służy tylko do tego aby to jego metody (a nie klasy UC) brudzić adnotacjami. Hmm gdy wymyślę coś lepszego to napiszę...

//============================
Przedstawiona integracja jest wyjściem (aczkolwiek niewielkim) poza zakres "Poor man's integration" z rozdziału 15.2 doskonałej książki "Seam in Action" Dana Allena. Szczerze ją polecam wszystkim tym, którzy chcą zgłębić Seam trochę bardziej niż potrzeba do wyklikania formularzyków w apliakcji typu przeglądarka do bazy. Autor na prawdę rzetelnie opisuje poruszane zagadnienia. Szczególne wrażenie zrobił na mnie właśnie rozdział poświęcony integracji Springa z Seam. Oprócz gotowej do przepisania konfiguracji autor dokładnie omawia szczegóły architektoniczne obu frameworków i przyczyny dla których integracja wygląda tak a nie inaczej. Możecie się z niej dowiedzieć jak zintegrować frameworki na głębszym poziomie (wychodząc poza poor's man), tak aby wspólnie zarządzały persystencją i transakcjami. Mi niestety "nowoczesne" podejście do persystencji nie odpowiada z powodu drastycznych wymagań niefunckjonalnych...

Brak komentarzy: