V prvom článku nášho seriálu o servletoch sme si objasnili niektoré základné princípy fungovania servletu. Povedali sme si niečo o Java Servlet API, čo je súbor tried a rozhraní na prácu so servletmi. No a nakoniec som ilustroval, že servlety sú viacvláknové komponenty. V tomto článku si objasníme, ako vyzerá životný cyklus servletu.

Životný cyklus servletu

Životný cyklus servletu je zabezpečovaný prostredníctvom metód init(), service() rešpektíve doGet()/doPost() a destroy(), ktoré nájdete v rozhraní Servlet. Teraz sa zameriame na jednotlivé metódy.

Inicializácia servletu – init()

Servlet je spustený a umiestnený do pamäte dynamicky. Teda až vtedy, keď vznikne prvá požiadavka na jeho služby. Prípadne môže byť server nakonfigurovaný tak, aby sa určité servlety inicializovali pri jeho štarte alebo reštarte. V oboch prípadoch je serverom zavolaná metóda init(), ktorá zabezpečí počiatočnú inicializáciu servletu. Táto metóda môže byť zavolaná len jediný raz pre každú inštanciu objektu. Skôr ako by servlet obslúžil akúkoľvek požiadavku, musí byť zavolaná. Môže napríklad vytvoriť spojenie s databázou, nastaviť počiatočné premenné, načítať súbor properties a podobne. Implementácia tejto metódy nie je podmienkou, programátor ju môže vynechať. Ak ju vynechá, zavolá sa metóda init() nadradenej triedy.

Úplne najjednoduchšia forma tejto metódy je bezparametrická, bez návratovej hodnoty (to platí aj pre ostatné formy). Bežná forma tejto metódy akceptuje ako parameter objekt triedy ServletConfig. Tento objekt sprístupňuje dvojice inicializačných parametrov typu meno/hodnota, špecifických pre konkrétny servlet. Zároveň nám umožňuje pristupovať k objektu triedy ServletContext, ktorý drží informácie o prostredí v ktorom servlet beží. Metódu init() môže programátor predefinovať, práve z dôvodu vlastných inicializačných nastavení (napr. spomínané spojenie s databázou).

Súčasťou webovskej aplikácie môže byť aj špeciálny súbor web.xml. Slúži na niekoľko vecí a jednou z nich je aj inicializácia servletu. Neskôr si ukážeme ako servlet zaregistrovať a nastaviť mu nejaké vstupné parametre, práve pomocou tohto súboru.

public void init(ServletConfig config)
     throws ServletException {
  super.init(config); //najprv je nutné zavolať metódu predka
  … inicializácia …
}

Obsluha požiadaviek – service() [doGet(), doPost()]

Akonáhle je servlet korektne inicializovaný, môže začať obsluhovať požiadavky. Každá požiadavka je reprezentovaná objektom triedy ServletRequest a príslušná odpoveď objektom ServletResponse z príslušného Java Servlet API. V predošlej časti som spomínal, že sa budeme zaoberať výhradne servletmi pracujúcimi nad protokolom HTTP. Preto ekvivalentom pre požiadavku je objekt HttpServletRequest a pre odpoveď HttpServletResponse.

Objekt HttpServletRequest obsahuje všetky informácie od klienta, vrátane informácií o prostredí (IP adresa, verzia prehliadača, podporované formáty MIME atď.) a všetkých vstupných dát, ktoré môžu byť zadané užívateľom (prostredníctvom formulára). Tento objekt samozrejme obsahuje vhodné metódy na prístup k jednotlivým informáciám, napríklad getParameter(), getParameterNames(), getAttribute(), setAttribute(), getCookies(), getRequestURL() a ďalšie.

Objekt HttpServletResponse je dynamicky vytvorená odpoveď (napríklad HTML stránka) poslaná klientovi. Samozrejme, že vytvorená odpoveď závisí od zaslanej požiadavky. Odpoveďou servletu však nemusí byť len HTML stránka ale aj čistý text, XML dokument, obrázok. A dokonca odpoveďou môže byť aj presmerovanie na iné URL, servlet, JSP a podobne.

Teda aby som sa vrátil k metóde service(). V minulej časti som povedal, že pri každej požiadavke na servlet sa vytvorí nové vlákno a vďaka tomu môže servlet obslúžiť množstvo požiadaviek súčasne. Pri vytvorení nového vlákna sa zavolá práve metóda service(), ktorá neurobí nič iné len prekontroluje typ požiadavku. Môžu to byť tieto typy: GET, POST, PUT, DELETE, OPTIONS a TRACE. A podľa toho zavolá príslušnú metódu: doGet(), doPost(), doPut(), doDelete(), doOptions() alebo doTrace().

Vo väčšine prípadov v bežnej praxi sa stretnete len s doGet() a doPost(). V prípade, že väčšina požiadaviek je typu POST alebo GET, zvádza to k predefinovaniu metódy service() tak, aby sa postarala o oba typy. Toto sa však neodporúča. Oveľa lepšie je zadefinovať metódu doGet() tak, aby volala metódu doPost(). Samozrejme to platí vtedy, ak nepotrebujem, aby jednotlivé typy POST a GET boli spracovávané odlišným spôsobom. Ilustrujem to na nasledujúcej ukážke kódu:

public void doPost(HttpServletRequest req, HttpServletResponse res)
     throws ServletException, IOException {
  doGet(req, res);
}
public void doGet(HttpServletRequest req, HttpServletResponse res)
     throws ServletException, IOException {
  … nejaký kód …
}

Aj implementácia metódy service() nie je podmienkou, programátor ju môže vynechať. Zavolá sa z nadradenej triedy. Každopádne ak chcem, aby môj servlet vrátil nejakú odozvu, musím implementovať minimálne metódy doGet()/doPost().

Odstránenie inštancie servletu – destroy()

Toto je metóda, ktorú podobne ako init() nemusíte nutne implementovať. Ak to však urobíte, bude zavolaná ešte predtým, ako server uvoľní servlet z pamäte. Táto metóda má mať v podstate opačné účinky ako init(). Teda uzatvárať databázové spojenia, otvorené súbory, zapísať na disk stav niektorých objektov (perzistencia) a podobne.

public void destroy() {
  … „ukončovací“ kód …
}

Nakoniec by som životný cyklus servletu ilustroval na obrázku:

Životný cyklus servletu

SingleThreadModel

Už viackrát som spomenul, že server vytvára len jednu inštanciu servleta. A zároveň pri každej požiadavke vytvorí nové vlákno, ktoré spracuje požiadavky od klienta, vygeneruje odpoveď a predá ju serveru, ktorý ju pošle klientovi. Takto to funguje štandardne. Treba sa však zamyslieť, čo sa stane, ak niekoľko vlákien súčasne požiada o prístup k jednému zdieľanému zdroju. Týmto zdrojom môžu byť jednak databázy (tabuľky), súbory a tiež takzvané inštančné premenné alebo premenné inštancie. To sú premenné viazané na konkrétnu inštanciu triedy (servletu). Ak by prístup k týmto zdrojom nebol synchronizovaný, môžeme dostať „neočakávané“ hodnoty. V prípade, že na náš servlet nebude adresovaných priveľa požiadaviek, môže servlet implementovať SingleThreadModel.

public class MyServlet extends HttpServlet
     implements SingleThreadModel {
}

Znamená to, že bude vytvorená len jedna inštancia a jedno vlákno a všetky požiadavky na servlet budú zoradené do fronty (zníženie výkonu pri veľkom počte súčasných prístupov) a vybavované postupne, alebo bude vytvorená vždy nová inštancia pre každú požiadavku (veľké pamäťové nároky). Treba dobre zvážiť použitie tohto modelu.

V ďalšej časti si vytvoríme jednoduchý servlet, a vysvetlíme si ako urobiť deployment (nasadenie) nášho servletu.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Žádný příspěvek v diskuzi

Odpovědět