Pokazywanie postów oznaczonych etykietą spring. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą spring. Pokaż wszystkie posty

niedziela, 29 czerwca 2008

Spring modules cache

Witam ponownie.
Pamiętacie aplikację akwarium? Aplikacja to trochę słowo na wyrost, bo w zasadzie stanowi ją jedna strona, która wylicza ile rybek pluska się w akwarium.

Wszystko działa, ale wyliczanie trochę trwa. Zaraz się tym zajmiemy, korzystając z dodatku do Springa: Spring Modules.
Spring Modules wprowadza między innymi mechanizm pozwalający na zapamiętywanie w pamięci podręcznej wyników wywołań wybranych metod (method cache). W przypadku, gdy któraś z funkcji wykonuje się długo, a dla zadanych parametrów wejściowych jej parametry wyjściowe są zawsze takie same, możemy wyniki jej wykonania przechować w pamięci podręcznej.
Trzeba jednak, trochę uważać. Przy wykorzystaniu mechanizmu method cache, tylko wejście i wyjście danej funkcji się zgadza. Wywołanie funkcji zostaje pominięte, natomiast rezultat jest zwracany z pamięci podręcznej.
Co ciekawe, te wszystkie czary można uruchomić bez napisania jakiejkolwiek linijki kodu w jawie. Trzeba tylko zmienić konfigurację. Ale do rzeczy...
Po uruchomieniu aplikacji akwarium i wpisaniu adresu strony:
http://localhost:8080/aquarium/info.htm
możemy zauważyć, że strona ładuje się kilka sekund. W dodatku zawsze zwraca inny wynik, coś ok 200000.



Odpowiedzialna jest za to klasa

package pl.matt.aquarium.manager.impl;

import pl.matt.aquarium.manager.AquariumManager;

/**
* @author mateusz
*
*/
public class AquariumManagerImpl implements AquariumManager {

/**
* pojemność akwarium w centymetrach sześciennych
*/
private int cubicCentimetres;
/**
* minimalna gęstość rybki
*/
private int minimalDensity;;

@Override
public int getFishCount() {
int count = 0;
for (int index = 0; index < cubicCentimetres; index++) {
if (getDensity(index) > minimalDensity) {
count++;
}
}
return count;
}

/**
* @param centimetreIndex
* @return gęstość wybranego fragmentu akwarium
*/
private int getDensity(int centimetreIndex) {
return (int) (Math.random() * 100);
}

public void setCubicCentimetres(int cubicCentimetres) {
this.cubicCentimetres = cubicCentimetres;
}

public void setMinimalDensity(int minimalDensity) {
this.minimalDensity = minimalDensity;
}

}

a dokładniej jej metoda public int getFishCount().

Pierwsza rzecz, jaką trzeba zrobić, aby dodać Spring Modules do naszego projektu to dodanie nowego repozytorium z którego skorzysta maven2. W tym celu trzeba wprowadzić kilka zmian w pliku settings.xml. Zazwyczaj jest on w katalogu ~/.m2/ lub /etc/maven2/. W sekcji profiles trzeba dopisać nowy profil:

<profiles>
<profile>
<id>standard-extra-repos</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>jboss</id>
<url>http://repository.jboss.com/maven2</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</profile>
</profiles>


Następnie dodaję kilka nowych zależności do pliku pom.xml

<dependency>
<groupId>org.springmodules</groupId>
<artifactId>spring-modules-cache</artifactId>
<version>0.7</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>apache-oro</groupId>
<artifactId>jakarta-oro</artifactId>
<version>2.0.8</version>
</dependency>


Cały plik pom.xml wygląda następnująco:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pl.matt</groupId>
<artifactId>aquarium</artifactId>
<packaging>war</packaging>
<version>1.0</version>
<name>aquarium Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.9</version>
</dependency>
<dependency>
<groupId>org.springmodules</groupId>
<artifactId>spring-modules-cache</artifactId>
<version>0.7</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>apache-oro</groupId>
<artifactId>jakarta-oro</artifactId>
<version>2.0.8</version>
</dependency>
</dependencies>
<build>
<finalName>aquarium</finalName>
<directory>/tmp/target</directory>
<outputDirectory>/tmp/target/classes</outputDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>


generalnie nic ciekawego. Czas wziąć na warsztat serce i mózg aplikacji springowej. W moim przypadku jest to plik springapp-servlet.xml.

Dodaję ziarna cacheManager i cacheProviderFacade określając położenie pliku konfiguracyjnego ehcache.xml.

<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml" />
</bean>
<bean id="cacheProviderFacade" class="org.springmodules.cache.provider.ehcache.EhCacheFacade">
<property name="cacheManager" ref="cacheManager" />
</bean>


Ziarno cachingInterceptor pozwala wskazać metody, których wyniki wywołań będą przechowywane w pamięci podręcznej. Wskazuję metody zaczynające się od liter get interfejsu pl.matt.aquarium.manager.AquariumManager. Czyli de facto tylko metodę getFishCount().

<bean id="cachingInterceptor"
class="org.springmodules.cache.interceptor.caching.MethodMapCachingInterceptor">
<property name="cacheProviderFacade" ref="cacheProviderFacade" />
<property name="cachingModels">
<props>
<prop key="pl.matt.aquarium.manager.AquariumManager.get*">cacheName=testCache</prop>
</props>
</property>
</bean>

Wyniki będą przechowywane we fragmencie pamięci nazwanym testCache. Zaraz go zdefiniuję, ale najpierw jeszcze jedno ziarno konfigurujące przechowywanie wyników wywołań metod w pamięci podręcznej:

<bean
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<idref local="aquariumManager" />
</list>
</property>
<property name="interceptorNames">
<list>
<value>cachingInterceptor</value>
</list>
</property>
</bean>


Cały plik springapp-servlet.xml wygląda tak:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!--
the application context definition for the springapp DispatcherServlet
-->
<bean name="/info.htm" class="pl.matt.aquarium.InfoController"
autowire="byName" />

<!-- freemarker config -->
<bean id="freemarkerConfig"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/freemarker/" />
</bean>
<!--
View resolvers can also be configured with ResourceBundles or XML
files. If you need different view resolving based on Locale, you have
to use the resource bundle resolver.
-->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true" />
<property name="prefix" value="" />
<property name="suffix" value=".ftl" />
<!--
if you want to use the Spring FreeMarker macros, set this property to
true
-->
<property name="exposeSpringMacroHelpers" value="true" />
</bean>
<bean id="aquariumManager" class="pl.matt.aquarium.manager.impl.AquariumManagerImpl">
<property name="cubicCentimetres" value="20000000" />
<property name="minimalDensity" value="98" />
</bean>

<!-- cache -->
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml" />
</bean>
<bean id="cacheProviderFacade" class="org.springmodules.cache.provider.ehcache.EhCacheFacade">
<property name="cacheManager" ref="cacheManager" />
</bean>
<bean id="cachingInterceptor"
class="org.springmodules.cache.interceptor.caching.MethodMapCachingInterceptor">
<property name="cacheProviderFacade" ref="cacheProviderFacade" />
<property name="cachingModels">
<props>
<prop key="pl.matt.aquarium.manager.AquariumManager.get*">cacheName=testCache</prop>
</props>
</property>
</bean>
<bean
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<idref local="aquariumManager" />
</list>
</property>
<property name="interceptorNames">
<list>
<value>cachingInterceptor</value>
</list>
</property>
</bean>
</beans>


Pozostaje jeszcze skonfigurować Ehcache, który będzie u mnie zarządzał pamięcią podręczną. Plik ehcache.xml tworzę w katalogu resources.

<?xml version="1.0" encoding="UTF-8"?>

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd">
<diskStore path="java.io.tmpdir/cache" />

<defaultCache
maxElementsInMemory="9999"
eternal="false"
timeToIdleSeconds="777"
timeToLiveSeconds="777"
overflowToDisk="true"
diskSpoolBufferSizeMB="30"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="600"
memoryStoreEvictionPolicy="LRU"
/>

<cache name="testCache"
maxElementsInMemory="9999"
eternal="false"
timeToIdleSeconds="777"
timeToLiveSeconds="777"
overflowToDisk="true"
diskSpoolBufferSizeMB="30"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="600"
memoryStoreEvictionPolicy="LRU"
/>

</ehcache>


Plik zawiera konfigurację dwóch fragmentów pamięci podręcznej. Nie wchodząc w szczegóły, konfiguracja fragmentu defaultCache jest obowiązkowa. Natomiast testCache będzie pamiętał wywołania metody getFishCount().
I to tyle. Czas sprawdzić, czy działa. Buduję aplikację, wchodzę na stronę:
http://localhost:8080/aquarium/info.htm
Ładuje się długo. Wchodzę ponownie. Tym razem strona załadowała się błyskawicznie. W dodatku liczba rybek pozostała niezmieniona. Miodzio - i to wszystko bez ani jednej linijki kodu w jawie. Całą aplikację akwarium znajdziesz tutaj.

Oczywiście do konfiguracji mechanizmu method cache można wykorzystać adnotacje, a zamiast Ehcache, JBoss Cache, Java Caching System (JCS) albo OSCache. Takie uroki Springa.

sobota, 7 czerwca 2008

wiosenna fasolka

Niniejszy wpis dedykuję panu Stefanowi Białasowi. Stefan Białas został w maju trenerem Jagiellonii Białystok. Dzisiaj został zwolniony z pracy. Czyżby dla tego, że drużyna słabo grała? Nie. Jagiellonia w tym czasie nie rozegrała żadnego spotkania.


To tyle, jeśli chodzi o didaskalia. Dzisiaj do naszego akwarium wrzucimy wiosenną fasolkę. Niby już lato, ale tak naprawdę, do aplikacji stworzonej we wpisie Spring MVC + Freemarker dodamy springowe ziarno (bean). Po co? No generalnie zaletą springowych ziaren jest to, że mogą one korzystać z dobrodziejstw springa, z których skorzystamy kiedyś w przyszłości. Niedalekiej. Poza tym stworzymy kolejną warstwę abstrakcji - warstwę biznesową. Same plusy.


Do rzeczy.
Aplikacja akwarium wyświetla ilość rybek w akwarium. Ilość ta jest obliczana w klasie InfoController. Dobrze byłoby odseparować wyliczanie tej wartości od klasy kontrolera i przenieść do warstwy biznesowej, której jeszcze nie mamy.
Tworzę zatem 2 paiety: pl.matt.aquarium.manager i pl.matt.aquarium.manager.impl. W pakiecie pl.matt.aquarium.manager będą biznesowe (akwarystyczne?) interfejsy, w pl.matt.aquarium.manager.impl ich implementacje.

Tworzę interfejs AquariumManager


package pl.matt.aquarium.manager;

/**
* @author mateusz
*
*/
public interface AquariumManager {

/**
* @return ilość rybek
*/
public int getFishCount();

}

zawiera on jedną metodę, która zwraca ilość rybek jakie pluskają w akwarium.

Tworzę też domyślną implementację powyższego interfejsu AquariumManagerImpl:


package pl.matt.aquarium.manager.impl;

import pl.matt.aquarium.manager.AquariumManager;

/**
* @author mateusz
*
*/
public class AquariumManagerImpl implements AquariumManager {

/**
* pojemność akwarium w centymetrach sześciennych
*/
private int cubicCentimetres;
/**
* minimalna gęstość rybki
*/
private int minimalDensity;;

@Override
public int getFishCount() {
int count = 0;
for (int index = 0; index < cubicCentimetres; index++) {
if (getDensity(index) > minimalDensity) {
count++;
}
}
return count;
}

/**
* @param centimetreIndex
* @return gęstość wybranego fragmentu akwarium
*/
private int getDensity(int centimetreIndex) {
return (int) (Math.random() * 100);
}

public void setCubicCentimetres(int cubicCentimetres) {
this.cubicCentimetres = cubicCentimetres;
}

public void setMinimalDensity(int minimalDensity) {
this.minimalDensity = minimalDensity;
}

}


Implementacja jest ciekawsza. Jest tam parametr cubicCentimetres;
który mówi o ilości centymetrów sześciennych (pojemności) akwarium. Co ciekawe, parametr nie jest zainicjalizowany w klasie, ale ma metodę modyfikującą (tzw. setter), więc jest nadzieja, że jednak jakoś będzie ustawiony. Ilość rybek nie zostanie już wylosowana, tylko wyliczona. Dla każdego centymetra sześciennego akwarium liczona jest jego gęstość (metoda private int getDensity(int centimetreIndex)), jeżeli jest większa niż minimalDensity (który również nie jest zainicjalizowany) uznaje się, że akurat tutaj mamy rybkę i licznik jest zwiększany.
Jak masz w akwarium duże ryby, mogą być policzone wielokrotnie. Zakładam, że masz ławicę neonków.


Teraz serce aplikacji springowej, czyli plik konfiguracyjny. W moim przypadku jest to springapp-servlet.xml.
Definiuję nowe ziarno:

<bean id="aquariumManager"
class="pl.matt.aquarium.manager.impl.AquariumManagerImpl">
<property name="cubicCentimetres" value="20000000" />
<property name="minimalDensity" value="98" />
</bean>

Definiując ziarno możemy ustawić jego stan (wartości zmiennych, co też robię. Wstrzyknę wartość tych parametrów metodami modyfikującymi (settery) ale można to zrobić też konstruktorem z parametrami.
To ziarno będzie singletonem. Domyślnie każde springowe ziarno jest singletonem.


Czas zmienić odrobinę klasę pl.matt.aquarium;.InfoController aby korzystała z logiki ukrytej za interfejsem AquariumManager


package pl.matt.aquarium;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import pl.matt.aquarium.manager.AquariumManager;

/**
* @author mateusz
*
*/
public class InfoController implements Controller {

private AquariumManager aquariumManager;

public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

int fishCount = aquariumManager.getFishCount();
return new ModelAndView("info", "fishCount", fishCount);
}

public void setAquariumManager(AquariumManager aquariumManager) {
this.aquariumManager = aquariumManager;
}

}


tu zmiany są raczej drobne. Znowu jest jakaś niezainicjalizowana zmienna. Jak się już zapewne domyślacie, wstrzykniemy ją w pliku konfiguracyjnym springa. Piszę "wstrzykniemy" bo ten bajer to nic innego jak Dependency Injection . Czyli zainicjalizowanie wartości ziarna poza nim samym.


Modyfikuję zatem ponownie plik springapp-servlet.xml.


zmieniam

<bean name="/info.htm" class="pl.matt.aquarium.InfoController" >

na

<bean name="/info.htm" class="pl.matt.aquarium.InfoController" autowire="byName" />

Spodziewałeś się, że zmienię na:

<bean name="/info.htm" class="pl.matt.aquarium.InfoController">
<property name="aquariumManager">
<ref bean="aquariumManager" />
</property>
</bean>

Tak też mogło być, ale tym sposobem poznaliśmy nową opcję, trochę niebezpieczną autowire. Opcja ta próbuje automatycznie wstrzyknąć wartości zmiennych, w naszym przypadku po nazwie (zmienna się nazywa aquariumManager więc wstrzyknięty jest bean o tej samej nazwie. Jeżeli dasz autowire="byType" spring spróbuje odnaleźć i wstrzyknąć ziarno implementujące interfejs AquariumManager czyli też będzie dobrze.


OK. Czas sprawdzić, czy wszystko działa. Strona się chwilę ładuje, ale działa.


Mam teraz 199 140 rybek. A wy?

Zauważcie, że po zmianie konfiguracji springa (plik springapp-servlet.xml) nie trzeba kompilować ponownie aplikacji. Wystarczy ją uruchomić ponownie i wszystko gra.

Wszystkie pliki projektu akwarium znajdziesz w tym archiwum.

wtorek, 3 czerwca 2008

Spring MVC + Freemarker

Witam. Dzisiejszy post dedykuję Panu Kazimierzowi z którym przepiłem 22100.

Specjalnie dla Pana Kazimierza zmierzymy się dziś ze Spring MVC.
Generalnie szkielety MVC dzielą się na 2 rodzaje. W jednym z każdą akcją (wysłaniem formularza) związana jest klasa Java, w drugim z każdą stroną związana jest klasa. Spring MVC należy do tych pierwszych. Do tych drugich np. JSF czy Apache Wicket.
Ma to jednak zasadniczą zaletę, strona nie jest związana ze stojącym za nią ziarnem (Java Bean) i


do jednej strony dane można wrzucać na wiele sposobów.

Niestety nie znalazłem prostego archetypu dla aplikacji webowej ze springiem, więc posłużymy się archetypem maven-archetype-webapp. Żeby było ciekawiej, tym razem nie z linii poleceń.
Zainstalowałem bowiem wtyczkę M2Eclipse do mojego Eclipsa 3.3.

1. Tworzenie aplikacji webowej

Teraz wybieram
File/New.../Project
i zaznaczam
Maven project

Następnie wybieram
Next
i zaznaczam odpowiedni archetyp, czyli maven-archetype-webapp.



Podaję Group Id na pl.matt i ArtifactId na aquarium (to będzie nazwa projektu). Wersję ustawiam na 1.0.

Klikam
Finish
i projekt gotowy.

Żeby stworzyć archiwum WAR i skompilować wszystkie klasy (których jeszcze nie ma), klikam prawym przyciskiem myszy (ppm) na projekcie, wybieram
Run As... / Maven install


Projekt zbudowany. Teraz wystarczy skopiować lub podlinkować wara z katalogu target do katalogu webapps Tomcata.
Uruchamiam Tomcata, wpisuję adres:
http://localhost:8080/aquarium/
i widzę piękne witaj świecie.

Aplikacja działa. Czas podłączyć Springa.

2. Konfiguracja SpringMVC
Aplikacja, która powstała jest bardzo prosta. Nie ma nawet żadnego serwletu. Dorzucę do niej springa. Ponieważ korzystam z mavena2 odbędzie się to przez modyfikację pliku pom.xml



<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pl.matt</groupId>
<artifactId>aquarium</artifactId>
<packaging>war</packaging>
<version>1.0</version>
<name>aquarium Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.9</version>
</dependency>
</dependencies>
<build>
<finalName>aquarium</finalName>
<directory>/tmp/target</directory>
<outputDirectory>/tmp/target/classes</outputDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>




Dodałem w sekcji dependencies zależności do springa, springa-webmvx, servlet API i freemarkera.
SpringMVC w warstwie prezentacji może korzystać z wielu technologii, m. in. JSP, szablonów Velocity i Freemarkera. Wybrałem tą ostatnią, bo jeszcze z niej nie korzystałem - może będzie ciekawiej.
Poczyniłem jeszcze pewne zmiany w sekcji build. Tagi directory i outputDirectory pozwalają wskazać katalog, gdzie maven będzie budował aplikację. Ustawiam je na katalog tmp, nie lubię jak mi się coś buduje w przestrzeni roboczej (workspace) środowiska Eclipse. Wtyczka (plugin) maven-compiler-plugin pozwala ustawić poziom kompilacji na Javę w wersji 1.6 (domyślnie jest 1.4).


Jeszcze trochę konfiguracji. Springa można podłączyć do aplikacji webowej na 2 sposoby. Za pomocą serwletu, przez który będą przechodziły żądania HTTP lub za pomocą filtra. Wybieram to pierwsze podejście.
Edytuję plik web.xml:


<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<servlet>
<servlet-name>springapp</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>springapp</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>

</web-app>


Dodałem servlet springapp, przez który przejdą wszystkie żądania http do zasobów o rozszerzeniu *.htm. Nimi zajmie się spring. Konfiguracja springa znajdzie się w pliku springapp-servlet.xml (ponieważ nasz serwlet nazwałem springapp). Plik ten trzeba dodać do katalogu WEB-INF



<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<!-- the application context definition for the springapp DispatcherServlet -->

<bean name="/info.htm" class="pl.matt.aquarium.InfoController" />

<!-- freemarker config -->
<bean id="freemarkerConfig"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath"
value="/freemarker/" />
</bean>

<!--

View resolvers can also be configured with ResourceBundles or XML files. If you need
different view resolving based on Locale, you have to use the resource bundle resolver.

-->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true" />
<property name="prefix" value="" />
<property name="suffix" value=".ftl" />

<!-- if you want to use the Spring FreeMarker macros, set this property to true -->
<property name="exposeSpringMacroHelpers" value="true" />

</bean>

</beans>



Co się tutaj dzieje? W pliku springapp-servlet.xml mamy 2 rzeczy.

<bean name="/info.htm" class="pl.matt.aquarium.InfoController" />
mówi Springowi, że rządania zasobu info.htm będzie realizowała klasa pl.matt.aquarium.InfoController.

Reszta to konfiguracja FreeMarkera. Raczej wiadomo o co chodzi.
Teraz czas stworzyć klasę zarządcy (controller we wzorcu MVC).



package pl.matt.aquarium;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

/**
* @author mateusz
*
*/
public class InfoController implements Controller {

public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

int fishCount = (int) (Math.random() * 100);
return new ModelAndView("info", "fishCount", fishCount);
}

}




Każdy zarządca ww SpringMVC implementuje
org.springframework.web.servlet.mvc.Controller. Mój, InfoController, sprawdza (losuje) ile mam rybek w akwarium. Wylosowaną wartość przekazuje do warstwy prezentacji, o nazwie "info".
Stworzę więc i ją - plik info.ftl w katalogu webapp/freemarker

<html>
<body>
<h2>Masz ${fishCount} rybek</h2>
</body>
</html>

Proste, prawda? Żadnych taglibów, ani innych bajerów. Praktycznie czysty HTML. Za fishCount zostanie oczywiście podstawiona wartość wylosowana w klasie InfoController.

Czas to wszystko uruchomić. Ponownie daję maven install. Jeżeli nie podlinkowałeś wara, musisz go ponownie skopiować do katalogu webapps Tomcata.
i wchodzę na adres:
http://localhost:8080/aquarium/info.htm

na koniec konkurs. Ile macie rybek?
Ja mam 25.