Spring (12-14) 2008/2009
Z JASR Wiki
<<< Powrót do Tworzenie aplikacji wielowarstowych 2008/2009
Spis treści |
Wprowadzenie
Narzędzia
Do przykładu na zajęciach będzie potrzebny:
- NetBeans 6.5 http://www.netbeans.org/downloads/index.html
- JDK 6 (ew. 5) http://java.sun.com/javase/downloads/index.jsp
- GlassFish(V2 lub V3-zalecany) lub Tomcat 6.x
Opis
- NetBeans 6.5
- natywne, ograniczone wsparcie dla Spring[jest wtyczka – web MVC]
- brak wsparcia dla Spring Web Flow
- automatyczne uzupełnianie kodu i znaczników XML
- Eclipse + Spring IDE (wtyczka)
- wsparcie dla wszystkich projektów Spring
- diagramy zaleznosci ziaren
- uwzględnianie ziaren tworzonych w konfiguracji kontenera Spring
- diagramy dla Spring Web Flow
- rozwijany równoczesnie ze zrębem Spring
Wniosek - eclipse dużo bogatszy we wsparcie springa.
Czym jest Spring?
- Framework dla aplikacji J2EE (istnieje wersja .NET)
- Alternatywa dla „ociężałej” technologii EJB2 i dla Struts, które uznano za źle zaprojektowane i zaimplementowane
- Może być używany w różnego typu aplikacjach, ale największe wsparcie dla www na bazie J2EE
Komponenty Spring
Kontener IoC (Inversion of Control)
- przeniesienie na zewnątrz (np. obiektu) odpowiedzialności za kontrolę wybranych czynności, zgodnie z Hollywood'zką zasadą: 'don't call us, we will call you'. Podzbiorem Inversion of Control jest Dependency Injection. Dependency Injection jest powszechnie stosowaną techniką w Spring'u.
Mamy klase Movie
public class Movie {
private String director;
...
public Movie(String director, ...) {
this.director = director;
...
}
public String getDirector() {
return director;
}
...
}
Następnie klase Movies, która znajduje określone filmy i je zwraca. W tradycyjnym podejściu sami implementujemy szukanie filmów:
public class Movies {
public List<Movie> moviesDirectedBy(director: String) {
List<Movie> allMovies = finder.findAll();
for (m in allMovies) {
if (!movie.getDirector().equals(director))
allMovies.remove(m);
}
return allMovies;
}
}
public interface MovieFinder {
List<Movie> findAll();
}
Tymczasem, zdarza się, że chcemy zmieniac algorytm szukający zależnie od naszych potrzeb. W tym celu tworzymy atrybut finder, który jest ustawiany w konstruktorze.
public class Movies {
private MovieFinder finder;
public Movies() {
this.finder = new SemiColonDelimitedMovieFinder("movies.txt");
}
public List<Movie> moviesDirectedBy(director: String) {
List<Movie> allMovies = finder.findAll();
for (m in allMovies) {
if (!movie.getDirector().equals(director))
allMovies.remove(m);
}
return allMovies;
}
Obiekt klasy Movies, w czasie tworzenia, w konstruktorze, musi zdecydowac jakiego finder'a chce używac. Sam musi o to zadbac. Natomiast my chcemy zmieniac findera często i przy okazji chcemy zrzucic z klasy obowiązek zadbania o findera. Tutaj Spring przychodzi z pomocą:
public class Movies {
private MovieFinder finder;
public Movies() {
}
public void setFinder(MovieFinder finder) {
this.finder = finder;
}
public List<Movie> moviesDirectedBy(director: String) {
List<Movie> allMovies = finder.findAll();
for (m in allMovies) {
if (!movie.getDirector().equals(director))
allMovies.remove(m);
}
return allMovies;
}
}
public class SemiColonDelimitedMovieFinder implements MovieFinder {
private String filename;
public void setFilename(String filename) {
this.filename = filename;
}
}
Na podstawie pliku konfiguracyjnego xml, atrybut finder zostanie wstrzyknięty wtedy, gdy obiekt będzie chciał go użyc. W tym celu potrzebne są plik konfikuracyjny, kontener IoC i setter obiektu do którego kontener będzie wstrzykiwac.
<beans>
<bean id="MovieLister" class="spring.MovieLister">
<property name="finder">
<ref local="MovieFinder"/>
</property>
</bean>
<bean id="MovieFinder" class="spring.ColonMovieFinder">
<property name="filename">
<value>movies1.txt</value>
</property>
</bean>
</beans>
Szablon programowania aspektowego – AOP
- metodologia uzupełniająca tradycyjny paradygmat programowanie obiektowego, OOP – dobre do modelowania logiki biznesowej, ale niewygodne przy powstających podczas kodowania problemach przelotowych (np. walidacja parametrów wejściowych, transakcje, logowanie (wypisywanie))
Pojęcia związane z programowaniem aspektowym:
- Joinpoint – dokładnie określony punkt podczas wykonywania aplikacji np. (wywołanie metody, inicjalizacja klasy)
- Advice – podejmowana akcja w określonym joinpoincie, typy:
- before advice (przed wywołaniem np. metody)
- after returning advice (po powrocie z metody)
- after throwing advice(gdy metoda rzuci wyjątek)
- around advice (dookoła metody, tutaj można decydowac czy wywołac metode czy nie)
- rozwiazanie od Spring 2.X - anotacje przy metodach klas:
@Aspect
public class CalculatorLoggingAspect {
private Log log = LogFactory.getLog(this.getClass());
@Before("execution(* ArithmeticCalculator.add(..))")
public void logBefore() {
log.info("The method add() begins");
}
}
Szablon dostępu do danych – Data Access
Wsparcie dla:
- Określanie parametrów połączenia
- Otwieranie połączenia
- Określenie zapytania (jedyny z tych punktów, które programista sam musi w całości napisac)
- Przygotowanie i wykonanie zapytania
- Użycie pętli do odebrania wyników
- Obsłużenie wyjątków
- Transakcje
- Zamykanie połącznia
Baza danych
Przykładowa konfiguracja połącznia z bazą danych w Spring.
Konfiguracja połączenia z bazą
Fragment pliku applicationContext.xml
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver"/>
<property name="url" value="jdbc:derby://localhost:1527/test"/>
<property name="username" value="anyuser"/>
<property name="password" value="anypass"/>
</bean>
<!-- klasa wykonująca zapytania do bazy powinna dziedziczyc z SimpleJdbcDaoSupport np:
public class JdbcProductDao extends SimpleJdbcDaoSupport implements ProductDao {...
wtedy dziedziczymy również atrybut dataSource, który stanowi parametry połączenia z bazą,
ustawiamy property name dataSource, na referencje do beana ktorego stworzyliśmy powyżej,
tutaj akurat ma on tą samą nazwę - dataSource -->
<bean id="productDao" class="springapp.repository.JdbcProductDao">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- jesli używamy transakcji -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
Przykłady zapytań do bazy
Pobranie elementów z bazy
List<Product> products = getSimpleJdbcTemplate().query("select id, description, price from products", new ProductMapper());
//trzeba ustawic mapowanie kolumn bazy na obiekt
private static class ProductMapper implements ParameterizedRowMapper<Product> {
public Product mapRow(ResultSet rs, int rowNum) throws SQLException {
Product prod = new Product();
prod.setId(rs.getInt("id"));
prod.setDescription(rs.getString("description"));
prod.setPrice(new Double(rs.getDouble("price")));
return prod;
}
}
Zapisanie elementu
int count = getSimpleJdbcTemplate().update(
"update products set description = :description, price = :price where id = :id",
new MapSqlParameterSource().addValue("description", prod.getDescription())
.addValue("price", prod.getPrice())
.addValue("id", prod.getId()));
logger.info("Rows affected: " + count);
Szablon obsługi transakcji - Transaction management
Co daje Spring:
- lokalne i globalne transakcje(lokalne nie wymagają serwera aplikacji)
- obsługa zagnieżdżonych transakcji
- transaction safepoints (zapis stanu procesu, proces wykonuje operacje dopóki może, np. jeśli czeka na jakiś zasób tworzony jest safepoint)
- działa na niemal wszystkich platformach Javy
Dla porównania, JTA(Java Transaction API) wspiera tylko:
- zagnieżdżone transakcje
- globalne transakcje
... i wymaga serwera aplikacji
Można ustawic:
- jeden z 4ech znanych poziomow izolacji.(Uncommited,Commited,...)
- propagacje transakcji (Required,Supports,Mandatory...)
<bean id="clinicTarget"class="org.springframework.samples.petclinic.hibernate.HibernateClinic">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="clinic"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="target" ref="clinicTarget"/>
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="store*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
Szablon Model-Widok-Kontroler – MVC
Nie był planowany, ale zdecydowano się na jego stworzenie w reakcji na, jak to nazwali, kiepskiego Strutsa i braku innych frameworków. Spring MVC przypomina architekture Apache Struts, ale występują istotne różnice: -Serwlety DispatcherServlet współdzielą kontekst -Brak zależności aplikacji od szkieletu -Daleko posunięta separacja M-V-C -Model niezależny od Spring API lub Servlet API -Łatwe testowanie przy pomocy Junit
Najprostsze Spring Web MVC w Netbeans 6.5
Upewnic się, że wtyczka Spring Web MVC jest zainstalowana.
- File -> New Project
- Java Web -> Web Application
- Wybieramy dowolne 'Project Name'
- Jako serwer wybieramy GlassFish (v3 lub v2) lub Tomcat
- We Framework zaznaczamy Spring Web MVC 2.5. Jak zaznaczymy wybieramy nazwe głównego serwletu Spring'a, defaultowo jest to 'dispatcher'. Linijke niżej, wybieramy mapowanie url'i na ten serwlet. Defaultowo jest to *.htm.
- Klikamy next i dostajemy najprosztszą Springową aplikację Web MVC, można ją od razu uruchomic.
(więcej o MVC podczas praktycznej części prezentacji na zajęciach)
Ćwiczenia
Ćwiczenie 1
Stwórz zwykły formularz, który pobiera <imię> i po naciśnieciu submit, przechodzi do strony która wyświetla 'Hello <imię>'
Wskazówka
Ćwiczenie 2
Stwórz serwis, który pobiera z bazy danych liste produktów i wyświetla je na swojej stronie głównej. Produkty mają swoją cenę. Dodaj możliwośc zwiększania ceny wszystkich produktów na raz. Dodaj walidację -> nie można zwiększyc ceny o więcej niż 50% i o mniej niż 0%. Cena nie może byc pusta.
Do tego cwiczenia możesz potrzebowac bibliotek. Te które u mnie działały są dostępne http://students.mimuw.edu.pl/~mw235845/spring_jary/ UWAGA najnowszy antlr.jar nie współpracuje ze spring framework pod netbeans 6.5.
Wskazówka
Tutoriale