/topjava

Java Enterprise: Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery

Primary LanguageJava

Онлайн проект Topjava

  • Не стоит стремиться прочитать все ссылки урока, их можно использовать как справочник. Гораздо важнее пройти основной материал урока и сделать Домашнее Задание
  • Обязательно посмотри правила работы с патчами на проекте
  • Делать Apply Patch лучше по одному, непосредственно перед видео на эту тему, а при просмотре видео сразу отслеживать все изменения кода проекта по изменению в патче (Version Control->Local Changes-> Ctrl+D)
  • Код проекта обновляется и не всегда совпадает с видео (можно увидеть как развивался проект). Изменения в проекте указываю после соответствующего патча.

Материалы занятия (скачать все патчи можно через Download/Скачать папки patch)

image

correction Рефакторинг проекта

Apply 1_0_fix.patch

  • переименовал классы UserMeal* в более красивые Meal*
  • удалил себя из шапки классов
  • обновил версию maven-compiler-plugin

hw Разбор домашнего задания HW0:

Apply 1_1_HW0_stream.patch

Apply 1_2_HW0_cycle.patch

Изменения в проекте: map.getOrDefault заменил на map.merge, for заменены на forEach

question Вопросы по HW0

что делает метод Map.merge ?

никогда не надо лениться зайти в код Map.merge (Ctrl+мышка на методе) и почитать javadoc (Ctrl+Q). Когда привыкать, если не сейчас?

что означает Integer::sum ?

это ссылка на метод, сокращенная форма лямбды. IDEA иногда выделяет желтым и предлагает замену (соглашаться по Alt+Enter). Например m->m.getCalories() заменяется на Meal::getCalories.

почему не использовать в TimeUtil методы isBefore/isAfter ?

это строгие (excluded) сравнения, а нам также нужны краевые значения

В MealsUtil у нас где-то есть ключевое слово final, где-то нет. В чем разница?

Я участвовал в одном проекте, где final был обязательным (в сеттингах IDEA галочка стояла). Но это скорее исключение, чем правило в проектах java (в Java 8 вообще ввели эффективный final, те по факту). Во всех новомодных языках переменные final по умолчанию, а в java нужно помнить и везде добавлять, утомительно. Но если приучитесь - хуже не будет. Я обычно ставлю там, где важно по смыслу (если не забываю).

Почему у User.registered тип Date, а Meal.dateTime LocalDateTime ?

Время регистрации - абсолютное (конкретный момент), а время еды по бизнес логике относительно (те не зависит от часового пояса)

Apply 1_3_HW0_optional2.patch

Занятие 1:

video 5. Maven.

  • Нам нужна 8-я версия Tomcat (например последняя 8.5.16)
  • Устанавливать Tomcat лучше простым скачиванием архива apache-tomcat-8.5.16[-windows-xxx].zip и копированием из него в каталог без пробелов и русских букв (пример С:\java\apache-tomcat-8.5.16). Не используйте Tomcat 8.5.13 и 9.0.0.M19 IDEA из за бага Tomcat

Apply 1_4_switch_to_war.patch

  • Обновил сервлеты до версии 3.1 (Tomcat 8 использует это API, хотя для нас не принципиально, т.к. мы никакие фичи 3.x Servlet API не используем)
  • Переименовал userList.jsp в users.jsp
  • Сервлет добавляется в следующем патче, те в web.xml он будет подчеркиваться красным.

Apply 1_5_add_servlet_api.patch

Apply 1_6_forward_to_redirect.patch

<user username="tomcat" password="tomcat" roles="tomcat,manager-gui,admin-gui"/>

Apply 1_7_logging.patch

Установите переменную окружения на TOPJAVA_ROOT на корень проекта и перезапустите IDEA. Слеши в пути должны быть в стиле unix (/)

  • изменения в проекте: убрал LoggerWrapper и логирую напрямую в логгер SLF4J. При логгировании через вспомогательный класс, в логе теряется имя исходного класса.
  • удалил зависимость jul-to-slf4j. Она нам не нужена и, согласно видео Владимира Красильщика про логирование, она замедляет работу
  • Не делать конкатенацию строк при логгировании сообщений, если уровень логирования в конфигурации выставлен выше уровня логирования в коде

Проверочные вопросы:

  • Что нужно изменить в pom.xml, чтобы перейти с logback на log4j ?

question Ваши вопросы

Почему private static final Logger log а не LOG ?

Используются ли еще где-то в реальной разработке JSP, или это уже устаревшая технология? Заменит ли ее JSF (https://javatalks.ru/topics/38037)?

JSF и JSP- разные ниши и задачи. JSP- шаблонизатор, JSF- МVС фреймворк. Из моего опыта- с JSP сталкивался в 60% проектов. Его прямая замена: http://www.thymeleaf.org (в Spring-Boot по умолчанию), но в уже запущенных проектах встречется достаточно редко. JSP не умирает, потому что просто и дешево. Кроме того включается в большнство веб-контейнеров (в Tomcat его реализация Jasper)

JSF- sun-овский еще фреймворк, с которым я ни разу не сталкивался и особого желания нет. Вот он как раз, по моему мнению, активно замещается хотя бы javascript фреймворками (angular, ember, react).

А зачем мы использовали logback? Почему SLF4J нас не устроило? Почему реализация логирования не log4j?

SLF4J-API это API. Там есть только пустая реализация org.slf4j.helpers.NOPLogger (можно посмотреть в исходниках). Logback для новых проектов стал стандарт. spring-petclinic и spring-boot используют его по умолчанию.


hw Домашнее задание HW01 (делаем ветку HW01 от последнего патча в master)

На проекте мы учимся мыслить и работать как Java разработчики уже сейчас, потом это будет гораздо сложнее и стоить дороже. Вот на мой взгляд хорошие советы новичкам. От себя я добавлю:

  • Учись грамотно формулировать проблему. Проблема "у меня не работает" может иметь тысячи причин. В процессе формулирования очень часто приходит ее решение.
  • Учись исследовать проблему. Внимательное чтение логов и умение дебажить - основные навыки разработчика. Обычно самый верх самого нижнего эксепшена- причина ошибки, туда нужно ставить брекпойнт.
  • Грамотно уделяй время каждой проблеме. Две крайности - сразу бросаться за помощью и бится над ней часами. Пробуй решить ее сам и в зависимости от проблемы выделяй на это разумное время.

1. Реализовать сервлет с отображением в таблице списка еды (в памяти и БЕЗ учета пользователя)

Деплоиться в Tomcat лучше как war exploded: нет упаковки в war и при нажатой кнопке Update Resources on Frame Deactivation можно обновляться css, html, jsp без передеплоя. При изменении web.xml, добавлении методов, классов необходим redeploy.

  • 1.1 По аналогии с UserServlet добавить MealServlet и meals.jsp
    • Задеплоить приложение (war) в Tomcat c applicationContext=topjava (приложение должно быть доступно по http://localhost:8080/topjava)
    • Попробовать разные деплои в Tomcat, remote и local debug
  • 1.2 Сделать отображения списка еды в JSP, цвет записи в таблице зависит от параметра exceed (красный/зеленый).
    • 1.2.1 Список еды захардкодить (те проинициализировать в коде, желательно чтобы в проекте инициализация была только в одном месте)
    • 1.2.2 Время выводить без 'T'
    • 1.2.3 Список выводим БЕЗ фильтрации по startTime/endTime
    • 1.2.4 Вариант реализации:
      • из сервлета преобразуете еду в List<MealWithExceeded>;
      • кладете список в запрос (request.setAttribute);
      • делаете forward на jsp для отрисовки таблицы (при redirect атрибуты теряются).
      • в JSP для цикла можно использовать JSTL tag forEach. Для подключения JSTL в pom.xml и шапку JSP нужно добавить:
    <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>jstl</artifactId>
       <version>1.2</version>
    </dependency>

    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    ...

Optional

2. Реализуем в ПАМЯТИ CRUD (create/read/update/delete) для еды

Пример: Simple CRUD using Servlet/JSP

  • 2.1 Хранение в памяти будет одна из наших CRUD реализаций (позже будет JDBC, JPA и DATA-JPA).
  • 2.2 Работать с реализацией CRUD через интерфейс, который не должен ничего знать о деталях реализации (Map, DB или что-то еще).
  • 2.3 Добавить поле id в Meal/ MealWithExceed и реализовать генерацию счетчика, УЧЕСТЬ МНОГОПОТОЧНОСТЬ СЕРВЕЛТОВ
  • 2.4 Сделать форму редактирования в JSP: AJAX/JavaScript использовать НЕ надо, делаем через <form method="post"> и doPost() в сервлете.
  • 2.5 Для ввода дат и времени можно использовать html5 типы, хотя они поддерживаются не всеми браузерами (протестировать свой браузер). В конце курса мы добавим DateTimePicker jQuery plugin, который будет работать на всех браузерах.

После выполнения ДЗ обязательно проверьте решение на ошибки

question Вопросы по HW1

Не попадаю на страничку/брекпойнт в сервлете.

  • внимательно проверь url и applicationContext (Application Context должен быть тот же, что и url приложения: wiki IDEA)
  • посомтрите в task manager: возможно запущено несколько JVM и они мешают друг другу. Лишние java приложения убить.

Приложение не видит TOPJAVA_ROOT.

После выставления переменной окружения IDEA нужно рестартовать. Слеши в пути должны быть в стиле unix (/). Проверить, видит ли java переменную окружения можно так: System.getenv("TOPJAVA_ROOT"). Еще вариант: добавить -DTOPJAVA_ROOT=... в опции запуска приложения, тогда она доступна из java как System.getProperty("TOPJAVA_ROOT").

Проблемы с кодировкой в POST (кракозябры).

Возможное решение - выставьте кодироку ДО первого чтения из request:

protected void doPost(HttpServletRequest request, ...) {
    request.setCharacterEncoding("UTF-8");

Если сервлет тыкают несколько пользователей / несколько браузеров, какого должно быть поведение? Нужно ли что-то делать с сессиями?

В Optional нужно делать реализацию хранения многопоточной. Cессии пока не используем (начнутся, когда будет прикручивать авторизацию).

Для чего нам нужна многопоточная реализация коллекции, если каждый пользователь видит только себя?

Реализация хранения в памяти у нас одна на всех. Те коллекция шарится между пользователями, а они в разных потоках ее дергают. Если несколько потоков одновременно будут изменять коллекцию без учета многопоточности (нарпимер один будет удалять, второй вставлять), мы получим ConcurrentModificationException

Предпочтительнее ли создавать новый объект Meal при каждом update?

Если при обновлении не создавать копию, то сохраненный в памяти объект может кто-то попортить. Вопрос скорее доверия к коду- если проект большой и людей над ним трудится много, то вероятнее нужно копировать.

Почему теряются атрибуты при передаче на сервлет: http://localhost:8080/topjava/meals?action=add&... и req.getAttribute("action") = null ?

См. Difference between getAttribute() and getParameter(). Отсюда также следует, что при редиректе атрибуты теряются.

Зачем нужен в jsp <jsp:useBean id=".." scope="request" type=".."/> ?

jsp:useBean нужен IDEA для автодополнений - она понимает тип переменной, которая уже доступна в JSP (например через setAttribute). И еще эта переменная становится доступной в java вставках. Для вывода в JSP это тэг не обязателен. Если тип переменной JSP не совпадает с тем, что в jsp:useBean, будет ошибка.


error Типичные ошибки

  • 1 Если в названии класса есть Meal, не нужно использовать слово meal в методах класса.
  • 2 Привыкайте писать коментарии к чекину: одной фразой что вы сделали в нем. Например: Meals CRUD implementation
  • 3 Хранение в памяти и операции с ней должны выполняться просто и эффективно
  • 4 Хранить нужно Meal и конвертировать ее в MealWithExceed когда отдаем список на отображение в JSP. Иначе при редактировании любой записи или изменении юзером своей нормы caloriesPerDay нужно будет пересчитывать все записи юзера.
  • 5 Стили color можно применять ко всей строке таблицы tr, а не каждой ячейке.
  • 6 DateTimeFormatter можно сделать один заранее (он потокобезопасный в отличие от SimpleDateFormatter), а не создавать новый при каждом запросе.
  • 7 Работать с CRUD надо через интерфейс.
  • 8 Реализаций хранения будет несколько, нужно учитывать это в названии класса имплементации работы в памяти.
  • 9 В web.xml принято группировать сервлет со своим маппингом
  • 10 Не размещайте никакую логику (форматирование, счетчики) в бинах, где хранятся только данные (Meal, MealWithExceed)
  • 11 Еще раз: детали реализации в памяти не должны быть никому видны. Те НЕ НАДО счетчик размещать в Meal или MealServlet или MealsUtil, в базе же он будет по другому генерится.
  • 12 volatile при ++ не помогает от многопоточности. Почему?
  • 13 Обратите также внимание на то, чтобы реализация вашей коллекции для хранения еды была также многопоточной.
  • 14 Не делайте дублирование кода MealsUtil. Возможно вам пригодятся константы LocalTime.MIN и LocalTime.MAX
  • 15 Не дублируйте строки в jsp. Посмотрите на тернарный оператор.
  • 16 после операции delete в браузере должен быть url http:\\localhost:8080\topjava\meals
  • 17 Перед чекином проверяйте свой ченджлист (Ctrl+D на файле из Local Changes - посмотреть что поменялось). Если там только пробелы/переводы строк, не надо его комитить - делайте файлу Git->revert.
  • 18 Учтите в названии реализации CRUD, что
    • 18.1 у нас будет несколько реализаций (не только в памяти)
    • 18.2 у нас будет 2 CRUD (для еды и пользователей). А в реальном проекте их намного больше.
  • 19 Сессии НЕ использовать! При редиректе все атрибуты (req.getAttribute()) теряются (см. вопрос выше). Сценарий редиректа:
    • 1 из сервлета делаем редирект (снова на сервлет, не на JSP!)
    • 2 снова заходим в сервлет
    • 3 кладем нужные атрибуты и делаем forward на jsp
    • 4 если очень хочется передать параметры из 1. в 2. можно сделать их через параметры запроса (например meals?id=5) и доставать через reg.getParameter(id). В моей реализации такого не потребовалось.