17 marca 2008

Słów kilka o transakcjach w Spring Framework

Czy to wpływ Waldiego, czy w ogóle ogólnej euforii możliwości Spring Framework wśród lokalnych programistów i projektantów, z którymi udaje mi się podzielić moimi spostrzeżeniami dotyczącymi Springa i podobieństw do serwera aplikacyjnego Java EE, kontynuuję swoją krucjatę o przywrócenie równowagi w wykorzystaniu funkcjonalności Spring Framework i Korporacyjnej Javy (Java EE) z Wicket w jednym. A warto? - możesz zapytać. Korporacyjna Java to zestaw wielu usług gotowych do użycia bez większego wysiłku umysłowego acz mocno organiczonych w porównaniu z funkcjonalnościami oferowanymi przez Springa, tj. wstrzeliwanie zależności w dowolny komponent mojej aplikacji webowej korzystającej z Wicket. Spring ze swoimi możliwościami postrzegany jest przeze mnie jak klocki Lego. Można wiele, podobnie jak z klockami Lego, ale kosztuje to wiele (przez koszt rozumiem ilość poświęconego czasu na jego poprawne zestawienie). W tym sensie jego bogate acz "rozproszone" możliwości mogą być zabójcze dla wielu, niekoniecznie początkujących programistów, którzy postawieni przed wyborem swojej platformy dla następnej aplikacji z pewnością wybiorą serwer aplikacyjny Java EE ze względu na jego "gotowość" (oczywiście znajdzie się wielu odważnych, którzy początkowe trudności w zestawieniu serwera aplikacji z użyciem Springa zrekompensują sobie późniejszą elastrycznością architektury). Możnaby powiedzieć, że serwer aplikacyjny Java EE to aplikacja oparta o Spring Framework ze wszystkimi jego usługami już zestawionymi do poprawnej realizacji wymagań stawianych przez specyfikację Java EE. Zresztą tego można już oczekiwać od nadchodzącej specyfikacji Java EE 6 (JSR 316: Java Platform, Enterprise Edition 6 Specification), w której uczestniczą architekci Springa. Wystarczy tych dywagacji na tą chwilę, bo zaczynam brnąć w kierunku, którego jeszcze wolałbym nie obierać ;-)

Wracając do tematu przewodniego, czyli zarządzaniu transakcjami w Spring Framework to udało mi się pozbyć kolejnej zależności od świata zewnętrznego w mojej aplikacji bez straty oferowanej przez nią funkcjonalności. Niedługo trwało, abym całkiem przypadkowo natrafił na rozwiązanie kwestii konieczności istnienia springowej adnotacji @Transactional do oznaczenia metod klasy jako transakcyjnej, o której wspominałem w notatce Apache Wicket z JPA z pomocą Spring Framework i Apache Maven 2, gdzie pisałem:

Najciekawszym elementem artykułu, jakkolwiek o Wicket, jest sama konfiguracja Springa, w taki sposób, że w żaden sposób nie wpłynął na inne pliki aplikacji. W zasadzie mógłbym wyrzucić Springa z aplikacji i nie wymagałoby to żadnej zmiany (poza klasą pl.jaceklaskowski.wicket.model.OsobaDaoImpl, gdzie użyłem adnotacji @Transactional, bez której babranie się z konfiguracją xmlową byłoby dla mnie marnotrawstem czasu)


Wystarczyło zapoznać się z sekcją 12.7. Transaction Management w dokumentacji Spring Framework 2.5.2, abym ostatnią rzecz, jaka wskazywała na użycie Spring Framework w aplikacji - adnotację @Transactional - zdjął z klasy pl.jaceklaskowski.wicket.model.OsobaDaoImpl.

Zmiana w pliku konfiguracyjnym Springa - applicationContext.xml polegała na dodaniu następujących elementów:
  xmlns:aop="http://www.springframework.org/schema/aop"

http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd

<aop:config>
<aop:pointcut id="osobaDaoMethods"
expression="execution(* pl.jaceklaskowski.wicket.model.OsobaDao.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="osobaDaoMethods" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="create*" propagation="REQUIRED"/>
<tx:method name="*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
Oczywiście koniecznym byłoby dodanie również kilku(nastu?) projektów zależnych związanych z obsługą AOP w Spring, jednakże Maven sprowadził temat do dodania spring-aspects jako jedynej koniecznej zależności projektu w pom.xml.
 <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
Jest jeszcze projekt spring-aop, który mógłby wskazywać, że to właśnie jego należałoby dodać, ale testy wskazały, że tak nie jest i to właśnie spring-aspects jest tym, którego potrzebuję.

Dla kompletności podaję pełen plik applicationContext.xml, który pozwolił mi na wykorzystanie Spring Framework bez jego interferencji z moimi klasami aplikacji.
 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<context:annotation-config />
<bean id="osobaDao" class="pl.jaceklaskowski.wicket.model.OsobaDaoImpl" />
<bean id="wicketApplication" class="pl.jaceklaskowski.wicket.WicketDemoApplication" autowire="byName">
<!-- wszystkie właściwości ustawiane po nazwie - wiele dzieje się poza naszą kontrolą -->
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="hsqlDatasource" />
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
</bean>
<bean id="hsqlDatasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:wicket" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="hsqlDatasource" />
</bean>
<aop:config>
<aop:pointcut id="osobaDaoMethods"
expression="execution(* pl.jaceklaskowski.wicket.model.OsobaDao.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="osobaDaoMethods" />
</aop:config>
<tx:annotation-driven />
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="create*" propagation="REQUIRED"/>
<tx:method name="*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
</beans>
Jeśli kiedykolwiek przyjdzie mi do głowy pomysł pozbycia się Springa z aplikacji to zadanie sprowadza się teraz do usunięcia pliku applicationContext.xml. Niesamowite, jakie możliwości drzemią w Springu. Dodając do tego bezinwazyjne oznaczenie klasy encji jako encji z użyciem orm.xml (to już część Java EE) połączenie dwu środowisk - serwera aplikacyjnego Java EE 5 oraz Spring Framework - wydaje się być obowiązkową architekturą dla większości aplikacji z jakimi ostatnio miałem przyjemność pracować. I jak tu zasnąć po takich wrażeniach?! ;-)

7 komentarzy:

  1. No coz-od siebie dodam anegdote uslyszana od Alefa Arendsena. Podobno implementacja EJB3 w Weblogicu powstala jako "cwiczenie prkatyczne" ktore Rod Johnson i Arendsen wlasnie urzadzili sobie w ramach wykorzystywania mozliwosci Springa. Zajelo im to raptem kilka godzin, a trafilo gdzies do uszu ludzi z BEA-ktorzy wykorzystali to poczatkowo do testow, a potem zaczeli wcielac do samego serwera.

    Nie wiem, na ile to prawda, na ile anegdota - ale swietnie ilustruje mozliwosci springa. I nawet pomimo tego ze zawsze troche tego XML-a zostanie(szczegolnie w przypadku tych modulow Springa, ktore nie dorobily sie jeszcze wlasnych definicji przestrzeni nazw w XML), mysle ze warto(wystarczy tylko zobaczyc jak mocno inspirowane zestawieniem Spring+Hibernate byly JPA i EJB3 i przyjrzec sie temu, czym tak naprawde beda profile w JEE 6)

    OdpowiedzUsuń
  2. Witaj Jacku,

    temat ciekawy, zwłaszcza że niedawno zacząłem zabawę ze Springiem i doszło do tematu transakcji. Zanim zacznę zadawać głupie pytania, to może polecisz jakiś ciekawy artykuł, który dogłębnie rozwinie ten temat? Interesuje mnie zwłaszcza w aspekcie pracy w kontenerze j2ee (a konkretnie was 6.1), w połączeniu z hibernate'm. W zwykłych ejb'kach ustawianie propagacji transakcji było proste, w RAD'zie dało się wszystko wyklikać;) Tu widzę, że używasz AOP, a ja skłaniam się jednak ku annotacjom @Transactional. Mam małe problemy (niby wszystko działa, ale nie do końca wiem dlaczego - znając życie zapewne to kwestia konfiguracji :) (websphere'owy transactionManager, LocalSessionFactoryBean, OpenSessionInViewFilter...)

    PS. Jak poszła SCEA? Trzeba przyznać, że zadania były trudne bo zawierały mnóstwo błędów...

    OdpowiedzUsuń
  3. Artykuł powiadasz? Hmm, jedynie co przychodzi mi do głowy to dokumentacja Springa i Google - potencjalnie książki, ale nie spodziewałbym się wiele o tx w nich. Nie mam nic ponad to.

    Odnośnie WAS 6.1, to skorzystaj z rozszerzenia EJB3 (EJB3 Feature Pack), które dodaje jego obsługę w serwerze, który wciąż jest na poziomie Java EE 1.4. Testowałem i opisałem. Już niedługo "opisałem" ujrzy światło dzienne.

    Użycie AOP vs @Transactional wynikało z potrzeby uniezależnienia aplikacji od środowiska uruchomieniowego. Chciałem, aby aplikacja była czysta od jakichkolwiek dodatków czy to z EJB3, czy JPA, czy w końcu Spring. Udało się i stąd wydawało mi się słusznym, aby o tym napisać. Na razie Springa traktuję jako dodatek i nic poza tym. Docelowym środowiskiem jest serwer aplikacyjny Java EE 5. Ale do tego jeszcze trochę czasu upłynie. Przed tym krokiem chciałbym jeszcze wdrożyć OSGi i zobaczyć jego zalety w aplikacji webowej opartej o Wicket (jeśli w ogóle takie zalety będą widoczne, poza tymi standardowymi - większa modularyzacja, itp.). Gdybym wiedział, czego w WAS 6.1 dotykasz, jaką masz konfigurację serwera i aplikacji mógłbym pomóc więcej. Pisz na priv.

    Re PS. SCEAę zarzuciłem, kiedy otrzymałem diagramy UMLowe do rozrysowania i jedynie 2 tygodnie. To było dla mnie za wiele i po jednym dniu stwierdziłem, że poddaję się. Poszukałem sobie coś ciekawszego i tak SCEA poszła w zapomnienie. Widzę, że SCEA 5 jeszcze nie ma, ale na otarcie łez wydano SCWCD 5, więc teraz o tym myślę. Dodatkowo rozpoznaję praktycznie UMLa, więc może kiedy SCEA 5 wyjdzie, ja już będę gotów. Na razie nie jestem.

    OdpowiedzUsuń
  4. Cześć,

    Biorąc pod uwagę nasze przekomarzania, pewnie się tego Jacek spodziewasz, ale oczywiście IMHO linia podziału przebiega między Spring a EJB (dokładnie częścią EJB czyli Session Bean i Message Driven Bean, czyli bez JPA), a NIE między Spring a Java EE.
    Nie widzę też w Springu tego "wiele" na jego poprawne zestawienie (ani też nie widzę tego "bez większego wysiłku umysłowego" w EJB).
    A już w żadnej mierze - o czym zresztą jakiś czas temu na grupie WJUG było - nie można przeciwstawiać serwera aplikacyjnego i Spring. Kombinacja serwer aplikacyjny JEE + Spring jest bardzo mocną platformą na której można budować serwerowe aplikacje.
    Trzymałbym się też rzeczywistości, która jasno pokazuje, że "model Spring" w sposób absolutny zdominował "model EJB" i do "zabójstw" nie dochodzi ;-). Wręcz przeciwnie - dzisiaj wielu (większość ???) programistów szczególną uwagę przywiązuje do możliwości łatwego testowania swojego kodu - problem, którego akurat EJB3 (znowu dotyczy Session i Message-Driven Beans) wciąż nie rozwiązuje i nic "gotowego" tu nie oferuje.
    Aby podtrzymać DOBRĄ praktykę przekomarzania się, to przyznam, że Twojej analogii o app serwerze jako "aplikacji opartej o Spring" nie załapałem ;-).

    To w sumie też już było kilka razy poruszane, ale moim skromnym zdaniem w fajny sposób pokazałeś, że CZASAMI te zniesławione ostatnimi czasy deployment deskryptory (nawet XMLowe) mogą być lepszym podejściem (biorąc pod uwagę ZAŁOŻENIA) od będących-cool adnotacji. Wybór "adnotacje vs. zewnętrzna konfiguracja" jest kwestią indywidualną danego programisty. Czasem lepiej pasuje jedno, czasem drugie. Akurat mapowanie OR, czy konfiguracja transakcji należą moim zdaniem do tych, gdzie - zwłaszcza w dłuższej perspektywie - zewnętrzna konfiguracja ma przewagę nad adnotacjami. Marzy mi się takie rozszerzenie do IDE, w którym będzie refaktoring zamieniający jednym kliknięciem adnotacje na konfigurację i odwrotnie...

    Pozdrawiam,
    Waldek Kot

    OdpowiedzUsuń
  5. @popaprany,

    Z tą anegdotą, to coś chyba nie do końca. Bo w WebLogic Server implementacja EJB3 bazuje na 10-letniej już implementacji EJB. To co zostało zaimplementowane przy pomocy Spring, to wstrzeliwanie zależności (czyli stosunkowo niewielka część EJB3). Po prostu zdroworozsądkowo było nie wymyślać koła i skorzystać z tego co jest dobre. Zresztą Rod ani Alef nie mają dostępu do kodu źródłowego WLS, a Spring został już dawno wewnątrz BEA uznany za conajmniej równoprawny model programistyczny do EJB. Z tego co mi się udało zaobserwować, to najpierw ludzie z developmentu BEA postanowili użyć Spring jako kontenera aplikacyjnego w ramach przejścia na OSGi (mSA - micro Service Architecture), a potem, gdy mSA trafiło do WLS wręcz bezpośrednio wykorzystano funkcjonalność Spring DI do implementacji EJB3. Swoją drogą to odbyło się to w taki sposób, że BEA i Spring uruchomili projekt Pitchfork, który jest notabene niezależny od kontenera (choć jedyne mi znane w tej chwili implementacje Pitchfork to - produkcyjna - w WLS i - testowa - w Tomcat). W Pitchfork jednym z głównych developerów jest Michael Cheng, jeden z core developerów EJB3 w WLS, zresztą podobnie jak jednym z core developerów Spring-OSGi jest Andy Piper, dawniej architekt kontenera EJB w WLS oraz guru od CORBA i RMI-over-IIOP. A teraz dev lead produktu WebLogic Event Server, w którym za podstawowy model programowania przyjęto... Spring.
    Jak widać sporo tych zależności między Spring a BEA (w obu kierunkach).

    Szepnę tylko: stay tuned - there's more comming to the theathers near you ;-)

    Pozdrawiam,
    Waldek Kot

    OdpowiedzUsuń
  6. Panowie,

    Spring prowadzi do "spagetti xml".
    Uzywam spring-a od 2004 i moje pierwsze reakcje byly podobnie entuzjastyczne. Jednak juz w srednich projektach koniguracja springa to dobre 20 plikow, a odkad dodali import zrobila sie z tego niezla makabra.

    IoC i testowalnosc sa nie do zaprzeczenia, ale spring nie ma modularnosci. Poza tym XML nawet ten z "naming space" to przerost formy nad trescia.

    Przyszedl czas na cos lzejszego - Guice to odpowiedz!
    oczywiscie w polaczeniu z bibliotekami springa, ale nie z kontenerem.


    Btw. Aop w springu jest strasznie kiepskie, ale nie bede sie tu rozwodzil - wyatarczy spojrzec na koszmarny stack trace...

    OdpowiedzUsuń
  7. Jeżeli wgłebic się w dokumentacje Springa lub wpisy na blogach Spring teamu to jest tam jasno powiedziane:

    Anotacje są tylko dodatkowym sposobem budowania "blueprint" czyli inforamcji w jakie mamy komponenty, jak są ze sobą połączone i jak są obudowywane...

    Oprócz anotacji pozostaje jeszcze stary poczciwy XML albo Spring Java Configuration Project.

    A najpiekniejsze jest to że można robic mix tych konfiguracji

    OdpowiedzUsuń