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:

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


Ciekawe linki

Osobiste