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.

Brak komentarzy: