1. interface Servlet

Сьогодні розпочинаємо нову та цікаву тему — сервлети. Саме додавання сервлетів до Java привело до того, що Java зараз — де-факто стандарт для великих серверних застосунків. 80% всього корпоративного програмного забезпечення у світі пишеться на Java. А у Китаї так і всі 100%. То що ж таке сервлети?

Сервлет — це саме те, що перетворює Java-програму на вебсервіс і дозволяє обробляти запити від клієнтів. А справа була так…

У 90-ті роки, одразу після появи всесвітньої павутини, з'явилися веб-клієнти (браузери) та веб-сервери. Веб-сервери зазвичай просто роздавали через інтернет файл, які в них зберігалися: html-сторінки, скрипти, картинки тощо.

У якийсь момент усі дійшли до висновку, що треба було б зробити обидві сторони розумнішими. У HTML-сторінки додали JavaScript, а серверам додали плагіни — спеціальні скрипти, які викликалися у відповідь на певні запити і дозволяли зробити поведінку сервера більш гнучкою та розумною.

Так ось сервлет – це такий Java-плагін, який вбудовувався до Java веб-сервера і дозволяв йому виконувати Java-код після запиту на певні сторінки. А цей Java-код, що представлено класом, успадкованим від класу Servlet, робив те, що запланували його розробники.

Як ти вже знаєш, найпопулярніший Java веб-сервер – це Tomcat. Названий, до речі, на честь кота Тома з мультфільму "Том і Джеррі".

Як же Tomcat взаємодіє із сервлетами? Насправді цей процес стандартизований і називається життєвим циклом сервлету. У ньому сервлет — це об'єкт, що завантажується, а веб-сервер — це контейнер сервлетів.

Якщо сервлет ще не завантажений, то:

  1. Клас сервлету завантажується контейнером.
  2. Контейнер створює екземпляр класу (об'єкт) сервлету.
  3. Контейнер викликає метод init() об'єкта сервлета. Метод викликається лише один раз.

Стандартний цикл роботи – обслуговування клієнтського запиту:

  • Кожен запит обробляється в окремому потоці.
  • Контейнер викликає метод service() сервлета і передає туди об'єкти ServletRequest і ServletResponse.
  • Для завершення роботи сервлета викликається метод destroy() об'єкта сервлета. Викликається він лише один раз.

Причин завершення роботи сервлету може бути дуже багато:

  • Програміст перезапускає веб-сервер, потрібно правильно завершити роботу всіх сервлетів.
  • Програміст завантажує нову версію сервлета, стару потрібно правильно вивантажити.
  • Тощо.

Запам'ятай головне: веб-сервер та його сервлети повинні без збоїв та перезавантаження працювати місяцями, обслуговувати тисячі запитів за хвилину. Тому код і завантаження, і роботи, і вивантаження сервлета завжди потрібно писати дуже якісно.

2. Клас HttpServlet

Клас Servlet існує для стандартизації роботи сервлета та контейнера. Безпосередньо із цим класом програмісти не працюють. Ну або працюють дуже рідко. Найчастіше використовується клас HttpServlet, успадкований від Servlet.

Цей клас має кілька методів, які нам знадобляться. Ти будеш часто їх використовувати:

Метод Опис
1 init() Викликається один раз під час завантаження сервлета
2 destroy() Викликається один раз при розвантаженні сервлета
3 service(HttpRequest, HttpResponse) Викликається для кожного нового запиту до сервлета
4 doGet(HttpRequest, HttpResponse) Викликається для кожного нового GET-запиту до сервлета
5 doPost(HttpRequest, HttpResponse) Викликається для кожного нового запиту POST до сервлета
6 doHead(HttpRequest, HttpResponse) Викликається для кожного нового HEAD-запиту до сервлета
7 doDelete(HttpRequest, HttpResponse) Викликається для кожного нового DELETE-запиту до сервлета
8 doPut(HttpRequest, HttpResponse) Викликається для кожного нового PUT-запиту до сервлета

Методи init() та destroy() успадковані від класу Servlet. Тому якщо ти вирішиш перевизначити їх у своєму сервлеті, тобі потрібно буде також викликати їхню реалізацію з базового класу. Для цього використовується команда super.ім'яМетоду().

Приклад сервлету:


public class FirstHttpServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // Отримуємо параметр “secret” із запиту
        String secret = request.getParameter("secret");
 
        // Кладемо параметр “secret” до Http-сесії
        HttpSession session = request.getSession(true);
        session.setAttribute("secret", secret);
 
        // Друкуємо HTML як відповідь для браузера
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            out.println("<html>");
            out.println("<head>");
            out.println("<title>Заголовок</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>Приклад сервлета "+ secret +"</h1>");
            out.println("</body>");
            out.println("</html>");
        } finally {
            out.close();
        }
    }
}

3. Метод service(HttpServletRequest, HttpServletResponse)

Якщо дивитися на обробку клієнтського запиту з погляду сервлета, все виглядає приблизно так.

Для кожного запиту клієнта контейнер (веб-сервер) створює об'єкти HttpServletRequest і HttpServletResponse, а потім викликає метод service(HttpServletRequest request, HttpServletResponse response) у відповідного сервлета. До нього передаються ці об'єкти, щоб метод міг взяти потрібні дані request’а і покласти результат роботи до response.

Метод service() має реалізацію за замовчуванням. Якщо її не перевизначити, виконуватиметься саме вона. Ось що він робить.

Метод service() визначає з request'а тип HTTP-метода (GET, POST, …) і викликає метод, що відповідає запиту.

Метод Опис
1 service(HttpRequest, HttpResponse) Викликається для кожного нового запиту до сервлета
2 doGet(HttpRequest, HttpResponse) Викликається для кожного нового GET-запиту до сервлета
3 doPost(HttpRequest, HttpResponse) Викликається для кожного нового запиту POST до сервлета
4 doHead(HttpRequest, HttpResponse) Викликається для кожного нового HEAD-запиту до сервлета
5 doDelete(HttpRequest, HttpResponse) Викликається для кожного нового DELETE-запиту до сервлета
6 doPut(HttpRequest, HttpResponse) Викликається для кожного нового PUT-запиту до сервлета

У своєму класі ти можеш або перевизначити один метод service(), або не чіпати його, але тоді перевизначати методи doGet(), doPost()… за потребою.