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?! ;-)