Spring Framework Tomasz Dobek t.dobek@students.mimuw.edu.pl
Plan prezentacji Spring z lotu ptaka Kontener Spring IoC Spring AOP Menedżer transakcji w Springu Spring DAO Testy integracyjne Podsumowanie
Spring Lekki kontener do budowy aplikacji Nie opisuje go żadna specyfikacja Najnowsza wersja 2.0 Istnieje też równoległy projekt Spring Webflow Spring powstał jako alternatywa dla EJB 2.1
Motywacja projektu Spring Wymuszamy dobry styl programowania Nie uzależniamy kodu aplikacji od naszego API Wspieramy testowanie modułów (unit- testing) poza kontenerem Wysoka konfigurowalność projektów Architekt może skroić Springa do swoich potrzeb
Składniki Spring Kontener IoC AOP DAO (JDBC, Hibernate, JPA, Toplink... ) Transakcje Framework MVC
Spring z lotu ptaka
Spring IoC
Kontener IoC Inversion of Control Zależności pomiędzy obiektami nie znajdują się w kodzie klas tych obiektów. Zależnościami zarządza kod zewnętrzny z punktu widzenia aplikacji (tu: kontenera). Wymusza to od nas myślenie interfejsami. Do realizacji tej techniki Spring używa Dependency Injection i Dependency Lookup
Przykład DI przez settery <beans> <bean id= bla class= org.bla.blaimpl > <property name= intval value= 1 /> </bean> </beans> Public class BlaImpl implements BlaIntf { private int intval; Public void setintval(int value) { this.intval = value; } }
DI przez konstruktor <beans> <bean id= bla class= org.bla.blaimpl2 > <constructor-arg type= int value= 23 /> </bean> </beans> Public class BlaImpl2 implements BlaIntf { private int intval; public BlaImpl2(int val) { this.intval = val;...
DI przez fabrykę <beans> <bean id= factorybean class=...... </bean> <bean id= instancebean class=... factory-bean= factorybean factory-method= createinstance >... </bean> </beans>
Zależności od Beanów <beans> <bean id= Bean1 class= foo1intf >... </bean> <bean id= Bean2 class= foo2intf > <property name= collaborator > <ref id= Bean1 /> </property> </bean> </beans>
Dependency lookup Możemy sami poprosić kontener w naszym kodzie o Beana Interfejsy kontenera: BeanFactory ApplicationContext WebApplicationContext
DL - przykład Public static void main(string[] args) { } InputStream is = new FileInputStream("beans.xml"); BeanFactory factory = new XmlBeanFactory(is);
Zasięg Beanów Singleton (domyślny) Jedna instancja na kontener Prototype Każde żądanie tworzy nową instancję Request Session Global Session
Przykład - Eclipse Pobierz szkielet projektów do Eclipse a /home/tmp/td209515/spring- workspace.tar.gz Rozpakuj i uruchom Eclipse'a wskazując rozpakowany katalog jako workspace Stwórz implementację interfejsu Stwórz plik konfiguracyjny Uruchom Main.java
Zaawansowane możliwości Leniwe ładowanie beanów Definiowanie własnych zakresów Definiowanie własnych znaczników w pliku konfiguracyjnym ApplicationContext Lokalizacje Sterowanie beanów zdarzeniami Auto-wiring
Spring AOP Framework
Spring AOP wprowadzenie Własny framework AOP Używa czystej Javy Ograniczona funkcjonalność Na Spring AOP zbudowana jest większość funkcjonalności Springa Spring AOP nie jest konkurencją dla np. AspectJ
AOP - przypomnienie Aspect Join point Advice Before advice After (returning/throwing/finally) advice Around advice Pointcut
Konfiguracja Spring AOP Aspekty traktowane są na równych prawach z innymi beanami. Sposoby konfiguracji: Adnotacje @AspectJ Konfiguracja w pliku XML z innymi Beanami
Konfiguracja - adnotacje @Aspect Public class BeforeExample { @Before( com.xyz.myapp.bla.operation() ) public void doaccesscheck() {... } } // przy metodzie Bla.operation() definiujemy @Pointcut <beans>... <aop:aspectj-autoproxy/> </beans>
Konfiguracja - XML <beans> <aop:config> <aop:aspect id= myaspect ref= abean > <aop:pointcut id= businessservice expression= execution(* com.xyz.myapp.service.*.* (..)) /> <aop:before pointcut-ref= businessservice method= doaccesscheck /> </aop:aspect> </aop:config>... </beans>
Realizacja Spring AOP Obiekt AOP Proxy pośredniczy w wywołaniach metod naszego obiektu Jeśli występuje join point wywoływany jest w odpowiedni sposób kod aspektu
Przykład Do poprzedniego przykładu dodaj do interfejsu 1 metodę Napisz implementację drukującą coś na konsoli Zdefiniuj aspekt pointcut i advice uruchamiający tą metodę przy wywołaniu dowolnej metody Zobacz efekt
Zarządca transakcji w Springu
Transakcje w Springu Te same poziomy ISOLATION co w JTA Te same wartości PROPAGATION co w JTA Lepsze zarządzanie wyjątkami niż w JTA Wspiera JDBC, Hibernate, JPA, JDO Deklaratywnie i/lub programistycznie Brak zależności od serwera aplikacji
Używanie transakcji Interfejs PlatformTransactionManager public interface PlatformTransactionManager { TransactionStatus gettransaction(transactiondefinition definition) throws TransactionException; void commit(transactionstatus status) throws TransactionException; void rollback(transactionstatus status) throws TransactionException; }
Używanie transakcji (2) Interfejs TransactionDefinition Isolation (domyślnie: DEFAULT) Propagation (domyślnie: REQUIRED) Timeout Read-only Oczywiście wszystko konfigurujemy jako Beany z użyciem AOP
Transakcje - konfiguracja <aop:config> <aop:pointcut id="defaultserviceoperation" expression="execution(* x.y.service.*service.*(..))"/> <aop:pointcut id="notxserviceoperation" expression="execution(* x.y.service.ddl.defaultddlmanager.*(..))"/> <aop:advisor pointcut-ref="defaultserviceoperation" adviceref="defaulttxadvice"/> <aop:advisor pointcut-ref="notxserviceoperation" advice-ref="notxadvice"/> </aop:config> <jee:jndi-lookup id="datasource" jndi-name="jdbc/jpetstore"/> <bean id="txmanager class="org.springframework.transaction.jta.jtatransactionmanager" />
Konfiguracja (c.d.) <bean id="fooservice" class="x.y.service.defaultfooservice"/> <bean id="anotherfooservice class="x.y.service.ddl.defaultddlmanager"/> <tx:advice id="defaulttxadvice"> <tx:attributes> <tx:method name="get*" read-only="true"/> <tx:method name="*"/> </tx:attributes> </tx:advice> <tx:advice id="notxadvice"> <tx:attributes> <tx:method name="*" propagation="never"/> </tx:attributes> </tx:advice>... </beans>
Konfiguracja - adnotacje @Transactional public class DefaultFooService implements FooService { Foo getfoo(string fooname); Foo getfoo(string fooname, String barname); void insertfoo(foo foo); void updatefoo(foo foo); } <bean id="fooservice" class="x.y.service.defaultfooservice"/> <tx:annotation-driven transaction-manager="txmanager"/> <bean id="txmanager class= (...).DataSourceTransactionManager"> <property name="datasource" ref="datasource"/> </bean>
Realizacja transakcji Do realizacji użyto oczywiście IoC i AOP
Transakcje - ciekawostki Atrybut rollback-for Atrybut no-rollback-for Transakcje programistyczne: Korzystamy z klasy TransactionTemplate Podajemy co chcemy zrobić w transakcji i o nic się nie martwimy return transactiontemplate.execute(new TransactionCallback() { public Object dointransaction(transactionstatus status) { updateoperation1(); return resultofupdateoperation2(); } });
Spring DAO
Spring DAO Integracja wielu technologii do łączenia się z warstwą danych: JDBC, Hibernate, Toplink, JDO, JPA, ibatis SQL Maps Zunifikowany zestaw wyjątków Wiele użytecznych klas Wykonywanie najczęstszych czynności Wplatanie wywołań API danej technologii
Nowa hierarchia wyjątków
Przykład - Hibernate Mamy przykładowy mapping Hibernate Definiujemy interfejs dostępu do danych Implementujemy go używając jednej z technologi (tu: Hibernate) Konfigurujemy beany. Uruchamiamy testy. Jeśli zmienimy technologie, zmieniamy konfigurację i implementacje interfejsu
Konfiguracja źródła danych <bean id="mydatasource" class="org.apache.commons.dbcp.basicdatasource" destroymethod="close"> <property name="driverclassname" value="org.hsqldb.jdbcdriver"/> <property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/> <property name="username" value="sa"/> <property name="password" value=""/> </bean> <bean id="mysessionfactory" class="org.springframework.orm.hibernate3.localsessionfactorybean"> <property name="datasource" ref="mydatasource"/> <property name="mappingresources"> <list> <value>product.hbm.xml</value> </list> </property> <property name="hibernateproperties"> <value> hibernate.dialect=org.hibernate.dialect.hsqldialect </value> </property> </bean> </beans>
Klasa HibernateTemplate Klasa opakowująca wywołania wewnątrz API Hibernate Umożliwia wykonywanie poleceń w Hibernate API przez callbacki. Inicjujemy ją w naszej implementacji DAO przez Dependency Injection
Konfiguracja Beanów <bean id="myproductdao" class="product.productdaoimpl"> <property name="sessionfactory" ref="mysessionfactory"/> </bean> public class ProductDaoImpl implements ProductDao { private HibernateTemplate hibernatetemplate; public void setsessionfactory(sessionfactory sessionfactory) { this.hibernatetemplate = new HibernateTemplate(sessionFactory); } public Collection loadproductsbycategory(string category) throws DataAccessException { return this.hibernatetemplate.find("from test.product product where product.category=?", category); } }
Testowanie aplikacji
Testowanie Spring Framework wspiera TDD Pisanie testów modułów jest proste (obiekty POJO) może być poza kontenerem Spring ułatwia pisanie testów integracyjnych.
Interfejsy do pisania testów AbstractDependencyInjectionSpringC ontexttests AbstractTransactionalDataSourceSpri ngcontexttests
Wsparcie dla testów integracyjnych Cache owanie kontenera IoC AbstractDependencyInjectionSpringContext Tests ::getconfiglocations() Wykorzystanie auto-wiring by type do konfigurowania beanów testowych Zawieranie testów w transakcjach i wycofywanie ich pod koniec testu
Pisanie beanów testujących Wystarczy napisać settery do pól Nie przeładujemy kontenera chyba że wywołamy setdirty() Transakcje: możemy zatwierdzić wołając setcomplete() Mamy też dostęp do źródła danych przez JdbcTemplate Mamy za darmo dostęp do applicationcontext
Testy integracyjne - przykład Mamy aplikację z dostępem do bazy danych Chcemy sprawdzić testami poprawność konfiguracji (Hibernate, beany, obiekty DAO)
Kolejne ułatwienia - adnotacje @DirtiesContext @ExpectedException (SomeException.class) @NotTransactional @Repeat(np. 10) Takie testy wymagają dziedziczenia z AbstractAnnotationAwareTransactionalT ests!!!
Testy integracyjne - przykład public final class HibernateTitleDaoTests extends AbstractDependencyInjectionSpringContextTests { private HibernateTitleDao titledao; public void settitledao(hibernatetitledao titledao) { this.titledao = titledao; } public void testloadtitle() throws Exception { Title title = this.titledao.loadtitle(new Long(10)); assertnotnull(title); } protected String[] getconfiglocations() { return new String[] { "classpath:com/foo/daos.xml" }; } }
Przykład c.d. - konfiguracja <beans> <bean id="titledao" class="com.foo.dao.hibernate.hibernatetitledao"> <property name="sessionfactory" ref="sessionfactory"/> </bean> <bean id="sessionfactory" class="org.springframework.orm.hibernate3.localsessionfactorybean">... </bean> </beans>
Ciekawostki wersja 2.0 Obsługa języków dynamicznych JRuby, Groovy, BeanShell Portlet MVC Pełne wsparcie dla JMS. Biblioteka tagów JSP. Uproszczona konfiguracja w XML
Podsumowanie Spring ułatwia budowanie aplikacji opartej na wielu technologiach. Spring wspiera tworzenie dobrze zaprojektowanych aplikacji. Spring nie jest inwazyjny (kod). Spring wspiera TDD. Spring jest lekki.
Spring, a EJB3 EJB3 rozwiązał wiele problemów, które wcześniej były rozwiązane w Springu Czy Spring jest nadal potrzebny?
Pytania Jeśli starczy czasu Bądź na maila : t.dobek@students.mimuw.edu.pl