Как я провалила курс по спрингу
В декабре прошлого года я проводила адвент-календарь по java. 21 день почти тысяча человек разбирали нюансы java core. В конце я спросила: какую тему ещё разобрать, где есть непонятки? Ответ был почти единогласным — Spring.
И я поставила цель на 2023 — сделать что-нибудь полезное по спрингу. Не для начинающих, с практическими фишками и лучшими практиками.
За 8 месяцев так ничего и не сделала😞
Я не склонна к самобичеванию, и попробовала разобраться, почему так получилось. Ответ лежал на поверхности.
Все силы идут на курс по многопоточке. Чуть появляется свободное время — бегу его улучшать. Хотя давно можно остановиться. Отзывы прекрасные, доходимость высокая. Хороший баланс между пользой и нагрузкой. Аналогов до сих пор нет ни в СНГ, ни в англоязычном мире.
И кажется, это отличный момент пойти дальше.
Следующий поток будет в октябре, и это будет последний раз в текущем виде. Дальше я пройдусь по бэклогу, сделаю рефакторинг, и курс останется только в варианте без обратной связи.
А со следующего года снова займусь образовательным творчеством. Руки чешутся взяться за спринг и другие темы!
Всё это похоже на чувства от принятого оффера. Когда на старом месте всё получается, и коллеги лапочки, но есть чувство, что можешь больше. Новый проект обещает что-то интересное, и ты с надеждой устремляешься в путь.
Так что кто хотел на курс с обратной связью — откладывать больше нельзя. Через пару недель объявлю набор, и в начале октября начнём🚀
В декабре прошлого года я проводила адвент-календарь по java. 21 день почти тысяча человек разбирали нюансы java core. В конце я спросила: какую тему ещё разобрать, где есть непонятки? Ответ был почти единогласным — Spring.
И я поставила цель на 2023 — сделать что-нибудь полезное по спрингу. Не для начинающих, с практическими фишками и лучшими практиками.
За 8 месяцев так ничего и не сделала😞
Я не склонна к самобичеванию, и попробовала разобраться, почему так получилось. Ответ лежал на поверхности.
Все силы идут на курс по многопоточке. Чуть появляется свободное время — бегу его улучшать. Хотя давно можно остановиться. Отзывы прекрасные, доходимость высокая. Хороший баланс между пользой и нагрузкой. Аналогов до сих пор нет ни в СНГ, ни в англоязычном мире.
И кажется, это отличный момент пойти дальше.
Следующий поток будет в октябре, и это будет последний раз в текущем виде. Дальше я пройдусь по бэклогу, сделаю рефакторинг, и курс останется только в варианте без обратной связи.
А со следующего года снова займусь образовательным творчеством. Руки чешутся взяться за спринг и другие темы!
Всё это похоже на чувства от принятого оффера. Когда на старом месте всё получается, и коллеги лапочки, но есть чувство, что можешь больше. Новый проект обещает что-то интересное, и ты с надеждой устремляешься в путь.
Так что кто хотел на курс с обратной связью — откладывать больше нельзя. Через пару недель объявлю набор, и в начале октября начнём🚀
Pattern matching: зачем?
Последние годы в джаву активно добавляется группа фич на тему pattern matching.
Тагир Валеев из Intellij IDEA в этом докладе рассказывает, какие это сложные фичи, как много челленджей стояло перед командой разработки. Но не говорит, зачем он вообще нужен.
Архитектор java Brian Goetz пишет, что pattern matching — не просто синтаксический сахар, а движение в сторону data oriented programming. Но это слабо применимо к большинству корпоративных систем, где доминирует ООП и изредка встречается функциональное программирование.
Большая часть моих знакомых считает паттерн матчинг "стрелкой вместо двоеточия в свиче":)
Так что сегодня расскажу, для каких задач пригодится pattern matching. В следующем посте обсудим реализацию в java.
Формат входных данных в энтерпрайзе обычно чётко определён. Если входящее сообщение не подходит по формату — это ошибка со стороны клиента.
Но бывает, что система работает со множеством источников данных, и выделить общий формат очень сложно.
Например, вы парсите чужие сайты/документы/дампы и достаёте оттуда что-то полезное. Знаю один проект, где бизнес-данные вытаскивают из логов(!) другой системы🤯
В итоге входные данные выглядят как
Паттерн — схема того, что мы ищем. Паттерн для поиска координат может выглядеть так:
▫️
▫️
Дальше идём по набору данных и проверяем их на соответствие паттерну.
Традиционно для такой задачи используется связка
Вот что нужно написать, чтобы проверить, является ли координатами массив
Ещё пример:
С if-ами эта конструкция будет гораздо объёмнее
Резюме
✅ Pattern matching нужен, когда мы пытаемся найти что-то знакомое в слабо- или неструктурированных данных.
✅ Большинство
✅ Разумеется, применить pattern matching можно и в других сценариях, но здесь видится наибольший профит в корпоративном царстве ООП.
В следующем посте распишу возможности pattern matching конкретно в джаве.
Спойлер:пока не впечатляет😑
Последние годы в джаву активно добавляется группа фич на тему pattern matching.
Тагир Валеев из Intellij IDEA в этом докладе рассказывает, какие это сложные фичи, как много челленджей стояло перед командой разработки. Но не говорит, зачем он вообще нужен.
Архитектор java Brian Goetz пишет, что pattern matching — не просто синтаксический сахар, а движение в сторону data oriented programming. Но это слабо применимо к большинству корпоративных систем, где доминирует ООП и изредка встречается функциональное программирование.
Большая часть моих знакомых считает паттерн матчинг "стрелкой вместо двоеточия в свиче":)
Так что сегодня расскажу, для каких задач пригодится pattern matching. В следующем посте обсудим реализацию в java.
Формат входных данных в энтерпрайзе обычно чётко определён. Если входящее сообщение не подходит по формату — это ошибка со стороны клиента.
Но бывает, что система работает со множеством источников данных, и выделить общий формат очень сложно.
Например, вы парсите чужие сайты/документы/дампы и достаёте оттуда что-то полезное. Знаю один проект, где бизнес-данные вытаскивают из логов(!) другой системы🤯
В итоге входные данные выглядят как
[Object, Object, Object]
. Паттерн — схема того, что мы ищем. Паттерн для поиска координат может выглядеть так:
▫️
[Double, Double]
— ищём массив из двух чисел с плавающей запятой▫️
[(-90;90), (-180,180)]
— массив с двумя числами в указанных диапазонахДальше идём по набору данных и проверяем их на соответствие паттерну.
Традиционно для такой задачи используется связка
if + instanceOf
. Вариант рабочий, но читаемость ужасная. Вот что нужно написать, чтобы проверить, является ли координатами массив
[Object, Object] data
:if (data.length == 2 && data[0] instanceOf Double && data[1] instanceOf Double) {В pattern matching многие проверки убираются под капот, и код выглядит симпатичнее:
double n = (Double) data[0];
double e = (Double) data[1];
if (n ≥ -90 && n≤ 90 && e ≥ -180 && e ≤ 180) {
// что-то делаем
}
}
switch(data) {Близкий родственник паттерн матчинга — регулярные выражения. Строка — это набор символов, внутри этого набора ищутся паттерны. Можно сделать ту же работу через if, но регулярка удобнее. Плюс в строке элементы однородные (символы), а паттерн матчинг работает с разными типами данных.
case [(-90; 90), (-180, 180)]:
// что-то делаем
}
Ещё пример:
double discount = switch(transaction) {Здесь ищем список из 5 элементов, где первый — строка "vip", второй и пятый — число. Если нашли — можно сразу работать с полем
case ["vip", Long, _, _, Double sum] → sum*0,9
case _ → 0
}
sum
. Если не нашли — используем паттерн по умолчанию _
С if-ами эта конструкция будет гораздо объёмнее
Резюме
✅ Pattern matching нужен, когда мы пытаемся найти что-то знакомое в слабо- или неструктурированных данных.
✅ Большинство
instanceOf
и if
отправляются под капот, и мы получаем более компактный код. ✅ Разумеется, применить pattern matching можно и в других сценариях, но здесь видится наибольший профит в корпоративном царстве ООП.
В следующем посте распишу возможности pattern matching конкретно в джаве.
Спойлер:
Что будет напечатано в консоли? (используется Java 20)
Anonymous Poll
24%
Even Even
4%
Even Even Other
51%
Even Other Even
8%
Even Other Even Other
13%
Ошибка компиляции в одном из методов
Pattern matching: синтаксис
В прошлый раз мы обсудили pattern matching в вакууме. Сегодня обсудим, как он выглядит в java с учётом 21 версии, и чего не хватает в текущей реализации.
Под зонтик pattern matching относят много фич: sealed классы, records, обновление switch и instanceOf. Sealed и records в контексте pattern matching — специфичные кейсы data oriented programming, не будем о них. Cфокусируемся на более популярных сценариях:)
Итак, как pattern matching воплощается в синтаксисе:
1️⃣ Компактный instanceOf
Раньше для неизвестного типа выполнялись две операции: instanceOf и явное приведение типа:
В логическом И можно использовать переменную в том же if:
✅
❌
У древнейшей конструкции доступен новый синтаксис:
▫️ Стрелочка вместо двоеточия
▫️ Break в конце case по умолчанию, и его можно не писать
▫️ Можно объединить несколько case в один
Старый синтаксис никуда не делся, им можно пользоваться:
🔥 Ответ на вопрос перед постом: выведется
3️⃣ Присвоение элемента через switch
Раньше в case принимались только константы. Теперь можно проверить тип переменной:
❌ Нет работы с массивами
Один из кейсов pattern matching — работа с набором данных, в которых мы ищем определённые признаки. Такого в java пока нет:
❌ Нет паттернов по полям класса
Под капот спрятан только instanceOf, остальные условия выглядят как в обычном if. Вся работа с полями происходит через методы:
Резюме
В текущем виде pattern matching выглядит слабо, особенно по сравнению с другими языками. Покрыты только базовые кейсы, в таком виде область применения очень ограничена.
Возможно это лишь промежуточный этап. Посмотрим, как фича будет развиваться и использоваться✨
В прошлый раз мы обсудили pattern matching в вакууме. Сегодня обсудим, как он выглядит в java с учётом 21 версии, и чего не хватает в текущей реализации.
Под зонтик pattern matching относят много фич: sealed классы, records, обновление switch и instanceOf. Sealed и records в контексте pattern matching — специфичные кейсы data oriented programming, не будем о них. Cфокусируемся на более популярных сценариях:)
Итак, как pattern matching воплощается в синтаксисе:
1️⃣ Компактный instanceOf
Раньше для неизвестного типа выполнялись две операции: instanceOf и явное приведение типа:
if (obj instanceof String) {Теперь эти операции объединены. Рядом с instanceOf объявляем имя переменной и сразу ей пользуемся:
String str = (String) obj;
// используем str
}
if (obj instanceof String str) {Тонкий момент— переменная определяется только при успешном выполнении instanceOf, поэтому есть нюанс с областью видимости.
// используем str
}
В логическом И можно использовать переменную в том же if:
✅
if (obj instanceof String s && s.length() > 5)
Логическое ИЛИ такого не позволяет, будет ошибка компиляции:❌
if (obj instanceof String s || s.length() > 5)
2️⃣ Компактный switchУ древнейшей конструкции доступен новый синтаксис:
▫️ Стрелочка вместо двоеточия
▫️ Break в конце case по умолчанию, и его можно не писать
▫️ Можно объединить несколько case в один
Старый синтаксис никуда не делся, им можно пользоваться:
switch (value) {В "новом стиле" это будет так:
case 1:
case 3: println("Odd"); break;
case 2:
case 4: println("Even"); break;
default: println("Other");
}
switch (value) {Важный момент — break добавляется по умолчанию только в вариант "со стрелочками". В "двоеточиях" break всё ещё добавляется вручную.
case 1,3 -> println("Odd");
case 2,4 -> println("Even");
default -> println("Other")
}
🔥 Ответ на вопрос перед постом: выведется
Even Other Even
. Чтобы работало как надо, нужно переписать на стрелочки или добавить break.3️⃣ Присвоение элемента через switch
int value = switch(…) {…};4️⃣ Проверка в switch по типам и сложные case
Раньше в case принимались только константы. Теперь можно проверить тип переменной:
switch (obj) {Если произошёл мэтч, для переменной сразу доступна доп. информация, и можно добавить условия в case:
case Integer i -> …
case Long l -> …
case Double d -> …
}
switch (response) {А теперь самое интересное! Обсудим, чего в текущей реализации нет:
case String s when s.length() == 10 -> …
}
❌ Нет работы с массивами
Один из кейсов pattern matching — работа с набором данных, в которых мы ищем определённые признаки. Такого в java пока нет:
double discount = switch(transaction) {В других языках такой функционал есть. Например, List patterns в С# или Matching sequences в питоне.
case ["vip", Long, _, _, Double sum] → sum*0,9
case _ → 0
}
❌ Нет паттернов по полям класса
Под капот спрятан только instanceOf, остальные условия выглядят как в обычном if. Вся работа с полями происходит через методы:
switch (figure) {В том же питоне запись гораздо компактнее:
case Square s when s.getLength() != 10 → …
}
match point:JEP Record Patterns мог быть как раз об этом, ведь records позиционируются как лаконичные контейнеры данных. Но увы.
case (0, 0): …
case (0, y): …
case _: …
Резюме
В текущем виде pattern matching выглядит слабо, особенно по сравнению с другими языками. Покрыты только базовые кейсы, в таком виде область применения очень ограничена.
Возможно это лишь промежуточный этап. Посмотрим, как фича будет развиваться и использоваться✨
Анонс курса по многопоточке🥳
Старт: 2 октября
Длительность: 9 недель
Кто давно ждал и уже готов → https://fillthegaps.ru/mt
Теперь подробнее. У курса две основные задачи:
✅ Научиться писать хороший многопоточный код
Разберём типовые энтерпрайзные задачи, огромное количество кейсов, лучших практик и возможных ошибок. Сравним производительность разных решений для разных ситуаций
✅ Подготовимся к собеседованиям, где требуется concurrency. Обсудим стандартные и нестандартные вопросы, порешаем тестовые задания
Что говорят ученики:
👨🦱 "Курс понравился тем, что он "от разработчиков разработчикам": примеры реальных библиотек для разбора, приближенные к реальным задачи для кодинга"
👨🦱 "Курс очень интенсивный, охватывает не только многопоточку, но и смежные темы, учит разным лайфхакам полезным для практического использования, обращает внимание на темы, которые легко или упустить, изучая тему самостоятельно, или вообще можно никогда не узнать без курса"
👨🦱 "Есть очень много свежей информации, которую сконцентрировано в едином источнике не получить"
👨🦱 "Это не с нуля совсем курс, и больше про правду разработки, разбавленную вопросами с собесов, а не про чистые знания."
Океан отзывов можно почитать тут
Для какого уровня курс?
Middle и выше
Сколько стоит?
🔸 Без обратной связи: 32000 руб.
🔸 С обратной связью: мест нет
Разницу между тарифами можно почитать тут. Этот поток — последний с обратной связью.
✔️ Есть рассрочка на 3 и 6 месяцев
✔️ Принимаются карты любых банков
✔️ Курс можно оплатить за счёт компании
✔️ Налоговый вычет 13%
Аналогов у курса нет. С каждым потоком программа становится лучше, задания интереснее, а учёба приятнее. Если хотите разобраться с многопоточкой, и вам близок мой стиль изложения — записывайтесь, будет очень полезно!
https://fillthegaps.ru/mt
Старт: 2 октября
Длительность: 9 недель
Кто давно ждал и уже готов → https://fillthegaps.ru/mt
Теперь подробнее. У курса две основные задачи:
✅ Научиться писать хороший многопоточный код
Разберём типовые энтерпрайзные задачи, огромное количество кейсов, лучших практик и возможных ошибок. Сравним производительность разных решений для разных ситуаций
✅ Подготовимся к собеседованиям, где требуется concurrency. Обсудим стандартные и нестандартные вопросы, порешаем тестовые задания
Что говорят ученики:
👨🦱 "Курс понравился тем, что он "от разработчиков разработчикам": примеры реальных библиотек для разбора, приближенные к реальным задачи для кодинга"
👨🦱 "Курс очень интенсивный, охватывает не только многопоточку, но и смежные темы, учит разным лайфхакам полезным для практического использования, обращает внимание на темы, которые легко или упустить, изучая тему самостоятельно, или вообще можно никогда не узнать без курса"
👨🦱 "Есть очень много свежей информации, которую сконцентрировано в едином источнике не получить"
👨🦱 "Это не с нуля совсем курс, и больше про правду разработки, разбавленную вопросами с собесов, а не про чистые знания."
Океан отзывов можно почитать тут
Для какого уровня курс?
Middle и выше
Сколько стоит?
🔸 Без обратной связи: 32000 руб.
🔸 С обратной связью: мест нет
Разницу между тарифами можно почитать тут. Этот поток — последний с обратной связью.
✔️ Есть рассрочка на 3 и 6 месяцев
✔️ Принимаются карты любых банков
✔️ Курс можно оплатить за счёт компании
✔️ Налоговый вычет 13%
Аналогов у курса нет. С каждым потоком программа становится лучше, задания интереснее, а учёба приятнее. Если хотите разобраться с многопоточкой, и вам близок мой стиль изложения — записывайтесь, будет очень полезно!
https://fillthegaps.ru/mt
Любимые подписчики, поздравляю вас с днём разработчика!
Мы все разные, с разным опытом, интересами и бэкграундом:
👦🏻 Кто-то пришёл в профессию по зову сердца, кто-то ради денег
👩 Кто-то горит работой, а кто-то уже выгорел
🧔🏻♀️ Кто-то работает 10 лет и видел всё. Кто-то только делает первые шаги
Но каждый прошёл огромный путь. Изучил тонны информации, научился тысяче вещей. Гибкий ум, критическое мышление, способность быстро обучаться и разбираться в сложном — наши сильные стороны.
Cегодня отличный день, чтобы признать свои заслуги и сказать себе: "я умничка". Признать, что делаешь сложную работу, и хорошо с ней справляешься.
Ребята, вы прекрасны! Вы очень умные и талантливые, каждый из вас.
Не будьте критичны к себе, почаще отдыхайте. Не стесняйтесь просить большую зарплату. Берегите своё желание учиться и покорять новые высоты❤️
С праздником🥳
Мы все разные, с разным опытом, интересами и бэкграундом:
👦🏻 Кто-то пришёл в профессию по зову сердца, кто-то ради денег
👩 Кто-то горит работой, а кто-то уже выгорел
🧔🏻♀️ Кто-то работает 10 лет и видел всё. Кто-то только делает первые шаги
Но каждый прошёл огромный путь. Изучил тонны информации, научился тысяче вещей. Гибкий ум, критическое мышление, способность быстро обучаться и разбираться в сложном — наши сильные стороны.
Cегодня отличный день, чтобы признать свои заслуги и сказать себе: "я умничка". Признать, что делаешь сложную работу, и хорошо с ней справляешься.
Ребята, вы прекрасны! Вы очень умные и талантливые, каждый из вас.
Не будьте критичны к себе, почаще отдыхайте. Не стесняйтесь просить большую зарплату. Берегите своё желание учиться и покорять новые высоты❤️
С праздником🥳
Какая версия Java сейчас основная на вашем проекте?
Anonymous Poll
19%
8
1%
9 или 10
28%
11
2%
12-16
44%
17
6%
18+
Сегодня релиз Java 21🥳
Главные фичи новой версии:
🔸 Виртуальные потоки
В чём суть: для многих задач производительность ограничивается количеством потоков в ОС. Чтобы обойти это ограничение и использовать потоки на максимум, придумали множество способов — асинхронные библиотеки, реактивное программирование и тд. Вариант рабочий, но код усложняется.
Виртуальные потоки позволяют писать простой код и не упираться в ограничения ОС.
Ближайший аналог виртуальных потоков — горутины в Go. Собственно, горутины — основная причина, почему многие сервисы написаны на Go.
Более дальний аналог — корутины в Kotlin. Они имитируют виртуальные потоки на уровне библиотеки и несут большие накладные расходы. В java виртуальные потоки реализованы на уровне JVM, поэтому их производительность гораздо выше.
В теории виртуальные потоки принесут пользу большинству приложений. Посмотрим, что будет на практике.
🔸 Pattern matching
В этих постах (один, два) я подробно расписала, зачем нужен pattern matching в целом, и как он реализован в java. Вкратце: нужен не всем, реализация в джаве так себе
🔸 Sequenced collections
Новые методы в коллекциях:
Интересные preview фичи:
🔹 String templates — интерполяция строк, возможность писать
🔹 Scoped Values — аналог ThreadLocal c ограниченной областью действия
🔹 Structured Concurrency — способ организации подзадач в многопоточной среде
Остальные фичи очень специфичные и вряд ли пригодятся большинству разработчиков:
▫️ Generational ZGC — в сборщик добавили поколения, как следует из названия. Молодые объекты будут собираться чаще, и сборщик будет работать эффективнее
▫️ Record Patterns — records можно использовать внутри case
▫️ Foreign Function & Memory API (Third Preview) — методы для работы с нативным кодом и управлению памятью за пределами JVM. Это нужно для приложений, которые хотят сами управлять размещением объектов в памяти и не зависеть от сборщика мусора.
▫️ Unnamed Patterns and Variables (Preview) — можно не указывать имя переменной, если оно не нужно. Вместо него ставить
▫️ Deprecate the Windows 32-bit x86 Port for Removal — перестать работать с Windows 32-bit x86, в будущем удалить
▫️ Prepare to Disallow the Dynamic Click Me Load More of Agents
Агент — компонент, который изменяет классы при загрузке или меняет уже загруженные классы в JVM. Используется для мониторинга, профайлинга и других служебных целей
▫️ Key Encapsulation Mechanism API — методы для защиты ключей симметричного шифрования
▫️ Unnamed Classes and Instance Main Methods (Preview)
Видимо чтобы короче писать Hello World:
Главные фичи новой версии:
🔸 Виртуальные потоки
В чём суть: для многих задач производительность ограничивается количеством потоков в ОС. Чтобы обойти это ограничение и использовать потоки на максимум, придумали множество способов — асинхронные библиотеки, реактивное программирование и тд. Вариант рабочий, но код усложняется.
Виртуальные потоки позволяют писать простой код и не упираться в ограничения ОС.
Ближайший аналог виртуальных потоков — горутины в Go. Собственно, горутины — основная причина, почему многие сервисы написаны на Go.
Более дальний аналог — корутины в Kotlin. Они имитируют виртуальные потоки на уровне библиотеки и несут большие накладные расходы. В java виртуальные потоки реализованы на уровне JVM, поэтому их производительность гораздо выше.
В теории виртуальные потоки принесут пользу большинству приложений. Посмотрим, что будет на практике.
🔸 Pattern matching
В этих постах (один, два) я подробно расписала, зачем нужен pattern matching в целом, и как он реализован в java. Вкратце: нужен не всем, реализация в джаве так себе
🔸 Sequenced collections
Новые методы в коллекциях:
getFirst()
, getLast()
, reversed()
и другие. Подробный пост тутИнтересные preview фичи:
🔹 String templates — интерполяция строк, возможность писать
String str = "Hello, ${name}!";Подробнее в этом посте
🔹 Scoped Values — аналог ThreadLocal c ограниченной областью действия
🔹 Structured Concurrency — способ организации подзадач в многопоточной среде
Остальные фичи очень специфичные и вряд ли пригодятся большинству разработчиков:
▫️ Generational ZGC — в сборщик добавили поколения, как следует из названия. Молодые объекты будут собираться чаще, и сборщик будет работать эффективнее
▫️ Record Patterns — records можно использовать внутри case
▫️ Foreign Function & Memory API (Third Preview) — методы для работы с нативным кодом и управлению памятью за пределами JVM. Это нужно для приложений, которые хотят сами управлять размещением объектов в памяти и не зависеть от сборщика мусора.
▫️ Unnamed Patterns and Variables (Preview) — можно не указывать имя переменной, если оно не нужно. Вместо него ставить
_
Например, в case:case Point(int x, _) → …Или при ловле исключения:
catch (IllegalStateException _)Было бы удобно сделать такое для лямбд, но для этого есть отдельный JEP, который сделают чёрт знает когда
▫️ Deprecate the Windows 32-bit x86 Port for Removal — перестать работать с Windows 32-bit x86, в будущем удалить
▫️ Prepare to Disallow the Dynamic Click Me Load More of Agents
Агент — компонент, который изменяет классы при загрузке или меняет уже загруженные классы в JVM. Используется для мониторинга, профайлинга и других служебных целей
▫️ Key Encapsulation Mechanism API — методы для защиты ключей симметричного шифрования
▫️ Unnamed Classes and Instance Main Methods (Preview)
Видимо чтобы короче писать Hello World:
class HelloWorld {
void main() {
System.out.println("Hello, World!");
}
}
Вот такой релиз. Конечно, самая ожидаемая фича — это виртуальные потоки. Если планируете в ближайшее время внедрять их, отпишитесь по впечатлениям, мне очень интересно😊Scoped Value (preview)
Неделю назад вышла java 21. Cегодня разберу интересную фичу в стадии превью — Scoped Value.
Ближайший аналог Scoped Value — ThreadLocal. Это когда мы объявляем переменную
В бизнес-логике это редко нужно, но фреймворки активно пользуются этим классом. Spring Security использует ThreadLocal для хранения информации о текущем пользователе. Давайте на этом кейсе посмотрим недостатки ThreadLocal, и что предлагает ScopedValue.
Как работает секьюрити:
1️⃣ Когда приходит новый запрос, Spring вытаскивает информацию о пользователе и записывает в ThreadLocal переменную:
3️⃣ В конце работы с запросом удаляем информацию из ThreadLocal переменной
Что в итоге:
✅ Не надо передавать Principal в параметрах
❌ Надо явно очищать значение ThreadLocal переменной в конце работы
❌ В любом месте можно вызвать set/remove и всё сломать
❌ Подход несовместим с виртуальными потоками
Scoped Value намерен решить проблемы выше. Как это выглядит:
Кроме
Что получаем:
✅ Видимость переменной задаётся для конкретного вызова метода
✅ У ScopedValue нет метода set, переменную нельзя обнулить/поменять внутри блока
✅ Код совместим с виртуальными потоками
Что вызывает вопросы:
🤔 Сценарии использования
Неизменяемый аналог ThreadLocal, совместимый с Project Loom точно нужен, но не вижу смысла задавать область видимости настолько гранулярно
🤔 Нельзя использовать несколько ScopedValue без использования вложенности. Хотя это легко реализовать по аналогии с try-with-resources
⚒ Где использовать: пока вижу только как замену ThreadLocal при переходе на виртуальные потоки.
Фича сейчас в стадии превью, посмотрим, как она будет развиваться. Если будет, конечно:)
Неделю назад вышла java 21. Cегодня разберу интересную фичу в стадии превью — Scoped Value.
Ближайший аналог Scoped Value — ThreadLocal. Это когда мы объявляем переменную
ThreadLocal<Integer> value;и для каждого потока будет своё независимое значение value.
В бизнес-логике это редко нужно, но фреймворки активно пользуются этим классом. Spring Security использует ThreadLocal для хранения информации о текущем пользователе. Давайте на этом кейсе посмотрим недостатки ThreadLocal, и что предлагает ScopedValue.
Как работает секьюрити:
1️⃣ Когда приходит новый запрос, Spring вытаскивает информацию о пользователе и записывает в ThreadLocal переменную:
public static ThreadLocal<Principal> PRINCIPAL = …2️⃣ Бизнес-логика. В любом месте кода можно узнать, кто выполняет запрос:
void serve(Request request, Response response) {
…
var principal = ADMIN;
PRINCIPAL.set(principal);
…}
var principal = PRINCIPAL.get();Обычно каждый запрос обрабатывается в своём потоке, поэтому данные между запросами не пересекаются.
3️⃣ В конце работы с запросом удаляем информацию из ThreadLocal переменной
Что в итоге:
✅ Не надо передавать Principal в параметрах
❌ Надо явно очищать значение ThreadLocal переменной в конце работы
❌ В любом месте можно вызвать set/remove и всё сломать
❌ Подход несовместим с виртуальными потоками
Scoped Value намерен решить проблемы выше. Как это выглядит:
public static ScopedValue<Principal> PRINCIPAL = …Переменная
void serve(Request request, Response response) {
…
var principal = ADMIN;
ScopedValue.where(PRINCIPAL, principal)
.run(() -> process(request, response));
…}
PRINCIPAL
со значением principal будет доступна только внутри конкретного вызова метода process
. Достать значение внутри process
:var principal = PRINCIPAL.get()
;Кроме
run
есть метод call
, который возвращает значение из переданной функции:var result = ScopedValue.where(Server.PRINCIPAL, guest)Сначала кажется, что для java синтаксис Scoped Value очень необычный — как будто переменная главнее основного действия. Но такое в java уже есть, вспомните try-with-resources.
.call(() -> getResult());
Что получаем:
✅ Видимость переменной задаётся для конкретного вызова метода
✅ У ScopedValue нет метода set, переменную нельзя обнулить/поменять внутри блока
✅ Код совместим с виртуальными потоками
Что вызывает вопросы:
🤔 Сценарии использования
Неизменяемый аналог ThreadLocal, совместимый с Project Loom точно нужен, но не вижу смысла задавать область видимости настолько гранулярно
🤔 Нельзя использовать несколько ScopedValue без использования вложенности. Хотя это легко реализовать по аналогии с try-with-resources
⚒ Где использовать: пока вижу только как замену ThreadLocal при переходе на виртуальные потоки.
Фича сейчас в стадии превью, посмотрим, как она будет развиваться. Если будет, конечно:)
IDEA: как не потерять важные места в коде
В огромном проекте всегда есть места, которые хочется отметить или быстро находить.
Раньше это делали так:
🔴 Ставили неактивный брейкпойнт в нужном месте. В принципе нормально, но иногда сложно вспомнить, что где находится
⭐️ Добавляли файл в favorites. Файл добавляется целиком, что не очень удобно
Затем в IDEA убрали favorites и добавили закладки. Супер удобно, ни одна важная строчка теперь не потеряется.
▫️ Курсор на нужной строке → F11 → появляется закладка
▫️ Правый щёлк по закладке → Rename bookmark… → ввести что-то осмысленное
▫️ Посмотреть закладки: View → Tool Windows → Bookmarks (или Shift + F11)
В огромном проекте всегда есть места, которые хочется отметить или быстро находить.
Раньше это делали так:
🔴 Ставили неактивный брейкпойнт в нужном месте. В принципе нормально, но иногда сложно вспомнить, что где находится
⭐️ Добавляли файл в favorites. Файл добавляется целиком, что не очень удобно
Затем в IDEA убрали favorites и добавили закладки. Супер удобно, ни одна важная строчка теперь не потеряется.
▫️ Курсор на нужной строке → F11 → появляется закладка
▫️ Правый щёлк по закладке → Rename bookmark… → ввести что-то осмысленное
▫️ Посмотреть закладки: View → Tool Windows → Bookmarks (или Shift + F11)
Postgres и Kafka, часть 1
Возвращаюсь к ведению канала, и на этой неделе вас ждут 2 огненных поста🔥🔥 о взаимодействии Postgres и Kafka.
Эта пара безумно популярна на большинстве проектов👯♀️ База хранит данные, кафка отвечает за коммуникации между сервисами. Сегодня разберу одну проблему в их отношениях и расскажу вариант решения. А в следующем посте обсудим ещё 2 способа.
Рассмотрим проблему на простом примере.
Пользователь создал заказ, сервис принял запрос. Сервис добавил заказ в базу, и хочет рассказать другим сервисам о новом заказе. Что-то вроде такого:
В чём проблема?
Мы обращаемся к двум отдельным компонентам — базе данных и брокеру сообщений. Каждый из них в любой момент может отвалиться, например, пропадёт связь по сети. В зависимости от порядка строк в saveOrder возможны 2 негативных исхода:
😢 запись в базу сделали, сообщение не отправили
😢 отправили сообщение, но запись в БД не прошла
Получим несоответствие. Поэтому иногда хочется, чтобы события выполнились атомарно: либо оба успешно завершаются, либо ни одно из них.
Большинство разработчиков нетерпеливо скажут: "Что тут думать, нужен transaction outbox!!1". Но если спросить 10 человек, что они под этим понимают, получится 10 разных ответов.
В лучших традициях канала обсудим всё простыми словами:) Очень грубо все решения можно назвать так:
1️⃣ Убираем кафку
2️⃣ Убираем БД
3️⃣ Добавляем координатор
Сегодня рассмотрим первый вариант, в следующем посте — остальные два.
Вариант 1: убираем кафку
У Postgres есть механизм notify/listen, который отправляет уведомления заинтересованным лицам. И вместо отправки сообщений через кафку мы возьмём механизм подписки внутри БД.
База становится единственным компонентом и выполняет оба действия (сохранить в таблицу, уведомить заинтересованных) в одной транзакции.
Чтобы не решать проблемы с координацией двух компонентов, мы переложили всю работу на один.
✅ Образцовая транзакция: атомарность и доставка exactly once
✅ Минимальная задержка между сохранением в базу и уведомлением
❌ Ограниченная функциональность уведомлений
❌ Размытие ответственности — часть уведомлений делает Kafka, часть — Postgres
❌ Увеличение нагрузки на БД
Последний пункт — главный ограничитель, поэтому подход "база делает всё" не очень популярен.
Реализация
Можно взять spring-integration-jdbc и для отправки сообщений, и для получения уведомлений. Документация максимально скудная, дополнительные детали есть в этой статье (под VPN)
В следующем посте обсудим ещё 2 варианта🔥
Возвращаюсь к ведению канала, и на этой неделе вас ждут 2 огненных поста🔥🔥 о взаимодействии Postgres и Kafka.
Эта пара безумно популярна на большинстве проектов👯♀️ База хранит данные, кафка отвечает за коммуникации между сервисами. Сегодня разберу одну проблему в их отношениях и расскажу вариант решения. А в следующем посте обсудим ещё 2 способа.
Рассмотрим проблему на простом примере.
Пользователь создал заказ, сервис принял запрос. Сервис добавил заказ в базу, и хочет рассказать другим сервисам о новом заказе. Что-то вроде такого:
public Order saveOrder(…) {Другие сервисы, подписанные на orders, получат сообщение и что-то сделают. Посчитают скидки, обновят статистику, запишут заказ в свою БД и тд.
Order saved = orderRepo.save(…);
kafkaTemplate.send("orders",new OrderCreated(…));
}
В чём проблема?
Мы обращаемся к двум отдельным компонентам — базе данных и брокеру сообщений. Каждый из них в любой момент может отвалиться, например, пропадёт связь по сети. В зависимости от порядка строк в saveOrder возможны 2 негативных исхода:
😢 запись в базу сделали, сообщение не отправили
😢 отправили сообщение, но запись в БД не прошла
Получим несоответствие. Поэтому иногда хочется, чтобы события выполнились атомарно: либо оба успешно завершаются, либо ни одно из них.
Большинство разработчиков нетерпеливо скажут: "Что тут думать, нужен transaction outbox!!1". Но если спросить 10 человек, что они под этим понимают, получится 10 разных ответов.
В лучших традициях канала обсудим всё простыми словами:) Очень грубо все решения можно назвать так:
1️⃣ Убираем кафку
2️⃣ Убираем БД
3️⃣ Добавляем координатор
Сегодня рассмотрим первый вариант, в следующем посте — остальные два.
Вариант 1: убираем кафку
У Postgres есть механизм notify/listen, который отправляет уведомления заинтересованным лицам. И вместо отправки сообщений через кафку мы возьмём механизм подписки внутри БД.
База становится единственным компонентом и выполняет оба действия (сохранить в таблицу, уведомить заинтересованных) в одной транзакции.
Чтобы не решать проблемы с координацией двух компонентов, мы переложили всю работу на один.
✅ Образцовая транзакция: атомарность и доставка exactly once
✅ Минимальная задержка между сохранением в базу и уведомлением
❌ Ограниченная функциональность уведомлений
❌ Размытие ответственности — часть уведомлений делает Kafka, часть — Postgres
❌ Увеличение нагрузки на БД
Последний пункт — главный ограничитель, поэтому подход "база делает всё" не очень популярен.
Реализация
Можно взять spring-integration-jdbc и для отправки сообщений, и для получения уведомлений. Документация максимально скудная, дополнительные детали есть в этой статье (под VPN)
В следующем посте обсудим ещё 2 варианта🔥
Postgres и Kafka, часть 2
В прошлом посте обсуждали, как атомарно записать данные в базу и отправить сообщение в Kafka. Чтобы на случай проблем с сетью компоненты не противоречили друг другу.
Сложность в том, что это БД и месседж брокер — разные сервисы и обеспечить их атомарную работу сложно. Есть 3 подхода к решению проблемы:
1️⃣ Убираем кафку
2️⃣ Убираем БД
3️⃣ Добавляем координатор
Первый пункт уже рассмотрели, в этом посте обсудим варианты 2 и 3.
Вариант 2: убираем БД
При сохранении сущности не делаем сразу запись в базу, только отправляем событие:
В случае проблем с сетью стратегия сервиса такая:
1️⃣ Отправь сообщение, пока не получится
2️⃣ Получив сообщение, пиши в БД, пока не получится
Вместо координации двух компонентов мы добиваемся исполнения гарантий на каждом шаге.
✅ Доступны все возможности Kafka: роутинг, разные гарантии доставки, репликация сообщений
✅ БД не становится узким местом
❌ Самое долгое время между отправкой сообщения и сохранением в БД среди всех вариантов
❌ Возможна неконсистентность данных и нарушение порядка изменений
Другие сервисы могут получить сообщение об изменениях раньше, чем "основной" сервис.
Если развивать вариант "убираем БД" дальше, придём к Event Sourcing. Это подход, в котором основной источник данных — события, а не БД. Но это уже другая история🧚♀️
Вариант 3: добавляем координатор
Оставляем БД исходную задачу по хранению данных, а для отслеживания изменений добавляем отдельный компонент. Общая схема выглядит так:
🔸 В базе данных что-то меняется
🔸 Координатор обнаруживает это изменение
🔸 Транслирует изменение в кафку
База может записывать изменения в отдельную таблицу с помощью триггера, materialized view или логической репликации. Координатор может смотреть на таблицу, а может читать логи БД.
Всё это многообразие объединяется термином Change Data Capture (CDC).
✅ Чёткие зоны ответственности. Postgres хранит данные, Kafka шлёт сообщения, координатор координирует
✅ Огромная гибкость, множество вариантов реализации
❌ Возможна дупликация сообщений
❌ Сложность системы увеличивается
Теперь у нас не 2 компонента (БД и Kafka), а гораздо больше. Больше инстансов, настроек, мониторинга и потраченных человеко-часов.
Реализация
Большинство инструментов базируются на Kafka Connect, в том числе популярная CDC система Debezium. Здесь и остановимся, пока всё просто и понятно:) Оценить, насколько глубока кроличья нора, можно в документации Debezium.
Резюме
Для слаженной работы Postgres и Kafka есть три основных подхода. Они отличаются
✍️ гарантиями доставки сообщения
✍️ нагрузкой на БД
✍️ временем между сохранением в БД и доставкой сообщения
✍️ сложностью
✍️ дополнительными возможностями
Выбираем решение по приоритетам, требованиям и нагрузке и ставим огоньки хорошему посту🔥
В прошлом посте обсуждали, как атомарно записать данные в базу и отправить сообщение в Kafka. Чтобы на случай проблем с сетью компоненты не противоречили друг другу.
Сложность в том, что это БД и месседж брокер — разные сервисы и обеспечить их атомарную работу сложно. Есть 3 подхода к решению проблемы:
1️⃣ Убираем кафку
2️⃣ Убираем БД
3️⃣ Добавляем координатор
Первый пункт уже рассмотрели, в этом посте обсудим варианты 2 и 3.
Вариант 2: убираем БД
При сохранении сущности не делаем сразу запись в базу, только отправляем событие:
public void saveOrder(…) {Сервис прекращает быть "главным" и становится обычным потребителем событий. Получает сообщение — делает запись в БД. Другие сервисы тоже получают сообщение и что-то делают.
kafkaTemplate.send("orders", new OrderCreated(…));
}
В случае проблем с сетью стратегия сервиса такая:
1️⃣ Отправь сообщение, пока не получится
2️⃣ Получив сообщение, пиши в БД, пока не получится
Вместо координации двух компонентов мы добиваемся исполнения гарантий на каждом шаге.
✅ Доступны все возможности Kafka: роутинг, разные гарантии доставки, репликация сообщений
✅ БД не становится узким местом
❌ Самое долгое время между отправкой сообщения и сохранением в БД среди всех вариантов
❌ Возможна неконсистентность данных и нарушение порядка изменений
Другие сервисы могут получить сообщение об изменениях раньше, чем "основной" сервис.
Если развивать вариант "убираем БД" дальше, придём к Event Sourcing. Это подход, в котором основной источник данных — события, а не БД. Но это уже другая история🧚♀️
Вариант 3: добавляем координатор
Оставляем БД исходную задачу по хранению данных, а для отслеживания изменений добавляем отдельный компонент. Общая схема выглядит так:
🔸 В базе данных что-то меняется
🔸 Координатор обнаруживает это изменение
🔸 Транслирует изменение в кафку
База может записывать изменения в отдельную таблицу с помощью триггера, materialized view или логической репликации. Координатор может смотреть на таблицу, а может читать логи БД.
Всё это многообразие объединяется термином Change Data Capture (CDC).
✅ Чёткие зоны ответственности. Postgres хранит данные, Kafka шлёт сообщения, координатор координирует
✅ Огромная гибкость, множество вариантов реализации
❌ Возможна дупликация сообщений
❌ Сложность системы увеличивается
Теперь у нас не 2 компонента (БД и Kafka), а гораздо больше. Больше инстансов, настроек, мониторинга и потраченных человеко-часов.
Реализация
Большинство инструментов базируются на Kafka Connect, в том числе популярная CDC система Debezium. Здесь и остановимся, пока всё просто и понятно:) Оценить, насколько глубока кроличья нора, можно в документации Debezium.
Резюме
Для слаженной работы Postgres и Kafka есть три основных подхода. Они отличаются
✍️ гарантиями доставки сообщения
✍️ нагрузкой на БД
✍️ временем между сохранением в БД и доставкой сообщения
✍️ сложностью
✍️ дополнительными возможностями
Выбираем решение по приоритетам, требованиям и нагрузке и ставим огоньки хорошему посту🔥
8 ошибок начинающих разработчиков
Рассмотрим типичную историю начинающего разработчика👶
Чтобы стать программистом, он долго учился. Прошёл много курсов, писал учебные проекты. У них маленькая кодовая база, один пользователь, а главная задача — отработать алгоритм или попробовать фреймворк.
В этих условиях у стажёра сформировался стиль написания кода.
После собеседования стажёр начинает работать в крупном проекте вместе с другими разработчиками. И здесь очень важно перестроиться: многие паттерны, нормальные в учебных проектах, уже не подходят.
В этом посте опишу 8 таких паттернов на простых примерах.
1️⃣ Процедурный стиль
Задача: получить список заказов пользователя. Новичок скорее всего напишет такой код:
Во входной параметр
В системах со сложной бизнес-логикой такой подход усложняет чтение, тестирование и возможную параллелизацию.
Как лучше: использовать выходные параметры, не менять входные данные, давать осмысленные имена методам:
2️⃣ Сложные универсальные методы
Задача: по-разному считать скидку для новых пользователей и пользователей с картами лояльности.
Новички часто используют принцип Don't Repeat Yourself на максималках и пишут универсальный метод с кучей параметров и десятком if внутри:
Как лучше: сфокусированные методы для разных ситуаций
3️⃣ Длинные методы
Сложно читать и тестировать, страшно менять.
4️⃣ Любовь к статическим методам
Как лучше: небольшие методы, связанные с конкретным классом. Связность ниже, ошибок меньше.
5️⃣ Сложное проектирование
Задача: завести три типа пользователей: новые, обычные и VIP. Новички скорее всего сделают интерфейс, 3 класса и статический класс с фабричными методами и билдером.
Как лучше: как можно проще. Например, один класс пользователя с полем Тип. Усложнять при реальной необходимости
PS Все "как лучше" не всегда лучше. Но думаю, идея понятна.
6️⃣ Нулевое или минимальное покрытие тестами
как следствие больших сложных методов и недостаточной инкапсуляции.
7️⃣ Низкий уровень ответственности
Пункт не относится к разработке, но очень актуален для начинающих. Проявляется в двух формах:
🔸 Непонятно, что происходит. Человек сидит и молчит до последнего, пока не спросишь статус задачи или про возможные трудности. Он умалчивает проблемы или переносит на других:
— Что с задачей, которую я тебе дала 3 дня назад?
— Я не понял, куда смотреть, потом меня HR позвал бумаги подписывать, потом я настраивал гит, увидел другую задачу и переключился на неё.
🔸 Код не решает проблему, а заметает симптомы:
— Приходил пустой параметр, и я выставил дефолтный. Тесты мешали сделать пул-реквест, и я их отключил.
8️⃣ Слабые коммуникативные навыки
— Как ты починил баг с расчётом ставки?
— Там через геттер фабричный метод нашёл, и потом докер с постгрёй поднял посмотреть, в логах был фильтр урезанный, я письмо отправил тебе в цц, но вроде скоуп не тот или тот, короче, запушил
— В чём была ошибка?
— Там два двоеточия вылезло
— Где?
— В дебаге
— 🤯
Эти ошибки ожидаемы в начале работы, я тоже их совершала🙂 Чем быстрее вы перестроитесь на командный стиль разработки, тем вероятнее пройдёте испытательный срок и быстрее вольётесь в проект.
Рассмотрим типичную историю начинающего разработчика👶
Чтобы стать программистом, он долго учился. Прошёл много курсов, писал учебные проекты. У них маленькая кодовая база, один пользователь, а главная задача — отработать алгоритм или попробовать фреймворк.
В этих условиях у стажёра сформировался стиль написания кода.
После собеседования стажёр начинает работать в крупном проекте вместе с другими разработчиками. И здесь очень важно перестроиться: многие паттерны, нормальные в учебных проектах, уже не подходят.
В этом посте опишу 8 таких паттернов на простых примерах.
1️⃣ Процедурный стиль
Задача: получить список заказов пользователя. Новичок скорее всего напишет такой код:
List list = new ArrayList();
process(user, list);
Во входной параметр
list
потом запишется результат. Такой стиль часто идёт из учебных заданий по алгоритмам, где экономится каждый бит и максимально сокращается количество объектов.В системах со сложной бизнес-логикой такой подход усложняет чтение, тестирование и возможную параллелизацию.
Как лучше: использовать выходные параметры, не менять входные данные, давать осмысленные имена методам:
List orders = getOrders(user);
2️⃣ Сложные универсальные методы
Задача: по-разному считать скидку для новых пользователей и пользователей с картами лояльности.
Новички часто используют принцип Don't Repeat Yourself на максималках и пишут универсальный метод с кучей параметров и десятком if внутри:
getDiscount(user, true, true, limit, true)
Как лучше: сфокусированные методы для разных ситуаций
getDiscountNew(user);
getDiscountLoyal(user, limit)
3️⃣ Длинные методы
Сложно читать и тестировать, страшно менять.
4️⃣ Любовь к статическим методам
Как лучше: небольшие методы, связанные с конкретным классом. Связность ниже, ошибок меньше.
5️⃣ Сложное проектирование
Задача: завести три типа пользователей: новые, обычные и VIP. Новички скорее всего сделают интерфейс, 3 класса и статический класс с фабричными методами и билдером.
Как лучше: как можно проще. Например, один класс пользователя с полем Тип. Усложнять при реальной необходимости
PS Все "как лучше" не всегда лучше. Но думаю, идея понятна.
6️⃣ Нулевое или минимальное покрытие тестами
как следствие больших сложных методов и недостаточной инкапсуляции.
7️⃣ Низкий уровень ответственности
Пункт не относится к разработке, но очень актуален для начинающих. Проявляется в двух формах:
🔸 Непонятно, что происходит. Человек сидит и молчит до последнего, пока не спросишь статус задачи или про возможные трудности. Он умалчивает проблемы или переносит на других:
— Что с задачей, которую я тебе дала 3 дня назад?
— Я не понял, куда смотреть, потом меня HR позвал бумаги подписывать, потом я настраивал гит, увидел другую задачу и переключился на неё.
🔸 Код не решает проблему, а заметает симптомы:
— Приходил пустой параметр, и я выставил дефолтный. Тесты мешали сделать пул-реквест, и я их отключил.
8️⃣ Слабые коммуникативные навыки
— Как ты починил баг с расчётом ставки?
— Там через геттер фабричный метод нашёл, и потом докер с постгрёй поднял посмотреть, в логах был фильтр урезанный, я письмо отправил тебе в цц, но вроде скоуп не тот или тот, короче, запушил
— В чём была ошибка?
— Там два двоеточия вылезло
— Где?
— В дебаге
— 🤯
Эти ошибки ожидаемы в начале работы, я тоже их совершала🙂 Чем быстрее вы перестроитесь на командный стиль разработки, тем вероятнее пройдёте испытательный срок и быстрее вольётесь в проект.
С новым годом!
Любимые подписчики, пусть в следующем году выгорят только бенгальские огни, а работа принесёт море удовольствия и денег. Пусть жизнь за пределами работы вас вдохновляет и наполняет❤️
Я к концу года очень устала, и на канал не осталось сил. Но отдых творит чудеса, поэтому скоро вернусь в строй. Вы тоже отдохните как следует, вы это заслужили!
С наступающим новым годом!🎄
Любимые подписчики, пусть в следующем году выгорят только бенгальские огни, а работа принесёт море удовольствия и денег. Пусть жизнь за пределами работы вас вдохновляет и наполняет❤️
Я к концу года очень устала, и на канал не осталось сил. Но отдых творит чудеса, поэтому скоро вернусь в строй. Вы тоже отдохните как следует, вы это заслужили!
С наступающим новым годом!🎄
Как вредит ChatGPT
ChatGPT всё глубже проникает в жизнь, но не всегда улучшает её в лучшую сторону. За последние полгода появились две неприятные тендеции, которые больно бьют по начинающим разработчикам.
Нейросети — прекрасный инструмент, и в работе с ним есть два важных этапа:
👆 задать вопрос
✌️ провалидировать ответ
Второй пункт очень важен. ЧатЖПТ и другие нейросетки — не гигантская сеть знаний. Это искусственная модель, которая пытается угадать ответ, а задача пользователя — провалидировать его. Но бывает, что этап валидации пропускается, и мы получаем
Следствие 1: низкий уровень знаний после курсов
Иногда ученики курсов ленятся разбираться в непонятном. Тогда они спрашивают ответ у нейросети и сдают его на проверку.
Пользы от такого "обучения" нет. Лучше ошибиться и разобрать ошибку с преподавателем, чем получить формальный зачёт и забить не возможный пробел.
На курсе многопоточки я не принимала ответы нейросети и подробно разбирала затык ученика. Но не все преподаватели так же заботятся об усвоении знаний, ведь с виду ответы выглядят отлично.
Так что скоро увидим больше выпускников курсов с формальным дипломом и поверхностными знаниями. Ситуация на рынке джунов и так печальная, а станет ещё хуже😔
✍️ Что делать:
Не используйте нейросети для обучения. Если что-то непонятно — спросите преподавателя, поищите статьи на хабре/baeldung, почитайте документацию.
Особенно это касается практики. Во многих вещах надо "набить руку", и в процессе работы всплывают все непонятные моменты. А готовый ответ выветрится из головы через секунду.
Следствие 2: низкое качество контента в "Java" каналах
Вы точно видели такие каналы — посты с рандомными фактами и обильная реклама курсов. Раньше в основе контента был копипаст из устаревших статей, переводы и куски из документации. Не очень интересно, но кому-то норм.
Сейчас чаще появляются посты, сделанные при участии ChatGPT. Качество контента не выросло, но теперь в постах проскальзывают ошибки.
Парочка примеров:
🤔 Пост о StringBuffer и противоречивые абзацы насчёт потокобезопасности:
Класс является потокобезопасным, т. е. может использоваться в многопоточных приложениях.
Методы StringBuffer не синхронизированы, поэтому для многопоточного доступа нужно вручную синхронизировать доступ с помощью synchronized блока.
🤔 Пост о методе compareAndSwap():
Такого метода в атомиках вообще нет. В описании снова находим противоречие — метод возвращает то ли результат операции, то ли текущее значение переменной.
Хорошо, если в комментариях найдётся молодец, который укажет на ошибку. Но комментарии читают не всегда, а админам безразлично, что публиковать. Создавать контент с ChatGPT быстро и дешево, в будущем его будет больше.
При использовании нейросети человек отдаёт себе отчёт, что это нейросеть, и может засомневаться в ответе. А посты в "Java" канале реже подвергаются сомнению.
✍️ Что делать:
Отписаться от таких каналов. В интернете петабайты материалов для новичков, в которых точно нет ошибок. Даже устаревший текст лучше неверной информации.
И конечно, читать каналы разработчиков, которые пишут авторский контент и делятся опытом. Люди тоже ошибаются, но качество таких постов гораздо выше. Не забывайте про лайки и обратную связь, это правда очень мотивирует писать дальше🥰
ChatGPT всё глубже проникает в жизнь, но не всегда улучшает её в лучшую сторону. За последние полгода появились две неприятные тендеции, которые больно бьют по начинающим разработчикам.
Нейросети — прекрасный инструмент, и в работе с ним есть два важных этапа:
👆 задать вопрос
✌️ провалидировать ответ
Второй пункт очень важен. ЧатЖПТ и другие нейросетки — не гигантская сеть знаний. Это искусственная модель, которая пытается угадать ответ, а задача пользователя — провалидировать его. Но бывает, что этап валидации пропускается, и мы получаем
Следствие 1: низкий уровень знаний после курсов
Иногда ученики курсов ленятся разбираться в непонятном. Тогда они спрашивают ответ у нейросети и сдают его на проверку.
Пользы от такого "обучения" нет. Лучше ошибиться и разобрать ошибку с преподавателем, чем получить формальный зачёт и забить не возможный пробел.
На курсе многопоточки я не принимала ответы нейросети и подробно разбирала затык ученика. Но не все преподаватели так же заботятся об усвоении знаний, ведь с виду ответы выглядят отлично.
Так что скоро увидим больше выпускников курсов с формальным дипломом и поверхностными знаниями. Ситуация на рынке джунов и так печальная, а станет ещё хуже😔
✍️ Что делать:
Не используйте нейросети для обучения. Если что-то непонятно — спросите преподавателя, поищите статьи на хабре/baeldung, почитайте документацию.
Особенно это касается практики. Во многих вещах надо "набить руку", и в процессе работы всплывают все непонятные моменты. А готовый ответ выветрится из головы через секунду.
Следствие 2: низкое качество контента в "Java" каналах
Вы точно видели такие каналы — посты с рандомными фактами и обильная реклама курсов. Раньше в основе контента был копипаст из устаревших статей, переводы и куски из документации. Не очень интересно, но кому-то норм.
Сейчас чаще появляются посты, сделанные при участии ChatGPT. Качество контента не выросло, но теперь в постах проскальзывают ошибки.
Парочка примеров:
🤔 Пост о StringBuffer и противоречивые абзацы насчёт потокобезопасности:
Класс является потокобезопасным, т. е. может использоваться в многопоточных приложениях.
Методы StringBuffer не синхронизированы, поэтому для многопоточного доступа нужно вручную синхронизировать доступ с помощью synchronized блока.
🤔 Пост о методе compareAndSwap():
Такого метода в атомиках вообще нет. В описании снова находим противоречие — метод возвращает то ли результат операции, то ли текущее значение переменной.
Хорошо, если в комментариях найдётся молодец, который укажет на ошибку. Но комментарии читают не всегда, а админам безразлично, что публиковать. Создавать контент с ChatGPT быстро и дешево, в будущем его будет больше.
При использовании нейросети человек отдаёт себе отчёт, что это нейросеть, и может засомневаться в ответе. А посты в "Java" канале реже подвергаются сомнению.
✍️ Что делать:
Отписаться от таких каналов. В интернете петабайты материалов для новичков, в которых точно нет ошибок. Даже устаревший текст лучше неверной информации.
И конечно, читать каналы разработчиков, которые пишут авторский контент и делятся опытом. Люди тоже ошибаются, но качество таких постов гораздо выше. Не забывайте про лайки и обратную связь, это правда очень мотивирует писать дальше🥰
От JDBC до Spring Data: часть 1
Общение с базой данных связано с огромным количеством технологий — навскидку вспоминаем JDBC, JPA, Hibernate и Spring Data. Они тесно переплетены, и не всегда люди чётко понимают, что есть что и зачем нужно.
В ближайших постах разложу по полочкам основные технологии по работе с данными.
❓Почему всё так сложно? Почему нельзя сохранить всё как есть?
Приложение использует оперативную память и представляет данные в виде объектов. Можно работать с любым объектом в любой момент — чтение и запись происходят быстро. Минус оперативки — когда приложение завершается, память освобождается, и данные пропадают.
Чтобы данные пережили приложение, они записываются в постоянную память. Хранением и организацией данных занимается БД.
В оперативке данные лежат кое-как — где место нашлось, там объект и создаётся. В постоянной памяти данных много, всё упорядочено и оптимизировано. Поэтому модели данных в БД и приложении иногда отличаются.
А ещё БД — это отдельное приложение. В итоге для сохранения/извлечения данных нужна куча дополнительной работы:
🔸 открыть соединение
🔸 составить SQL запрос
🔸 передать в запросе данные из приложения
🔸 преобразовать ответ БД в java объект
🔸 закрыть соединение
JDBC
— стандартные java методы, которые выполняют все пункты выше. Все инструменты по работе с БД под капотом используют как раз JDBC.
Работа с соединениями и преобразование данных — утомительная работа. Поэтому и появляются библиотеки, облегчающие этот труд.
Spring JDBC
берёт на себя работу с соединениями. Разработчик всё так же составляет запросы, передаёт параметры и преобразует ответы в java объекты.
ORM
Object Relational Mapping — преобразование данных (mapping) из java объектов в сущности БД и обратно.
Формально, ORM — просто название процесса. В случае JDBC весь ОRМ разработчик делает вручную.
На практике под ORM подразумевают ORM библиотеку/фреймворк — какой-нибудь инструмент, который берёт на себя часть работы по запросам и преобразованию данных.
Hibernate
— самая популярная ORM библиотека. Составляет простые SQL запросы и преобразует данные. Упростил жизнь многим и заслужил их любовь❤️
В хибернейте не всё идеально:
▪️ Работа с соединениями (сессиями) остаётся на пользователе
▪️ Для корректной работы надо знать внутрянку (dirty session, как разруливаются отношения и тд).
Сложно не признать, что Hibernate великолепен. Ворвался на олимп ORM библиотек в 2001 году и до сих пор оттуда не слезает🏆
JPA
Сейчас большинство приложений базируются на спринге, но 10-15 лет назад приложения часто опирались на Java ЕЕ. В те года ORM Java ЕЕ выглядел сложно — для каждой сущности требовались несколько классов и кучка интерфейсов.
Hibernate выглядел привлекательно, но нельзя просто взять и добавить библиотеку в проект. Во вселенной Java EE всё работает через спецификации — стандартные интерфейсы.
Поэтому появилась новая спека по ORM — Java Persistence API или JPA. С небольшими отличиями почти полностью списана с хибернейта. Вскоре Hibernate подстроился под JPA и стал использоваться в Java EE.
Итого
⭐️ JDBC — базовое API по работе с БД
⭐️ ORM — преобразование данных между приложением и БД. На практике под “у нас на проекте ORM” имеют в виду, что используется ORM библиотека, например, Hibernate
⭐️ JPA — спецификация по ORM. Набор интерфейсов, аннотаций и описание, как всё должно работать. Не включает в себя конкретную реализацию
⭐️ Hibernate — популярная ORM библиотека, реализующая JPA
В следующем посте распишу вариации Spring Data, и почему материалы по хибернейт могут не соответствовать реальности.
Общение с базой данных связано с огромным количеством технологий — навскидку вспоминаем JDBC, JPA, Hibernate и Spring Data. Они тесно переплетены, и не всегда люди чётко понимают, что есть что и зачем нужно.
В ближайших постах разложу по полочкам основные технологии по работе с данными.
❓Почему всё так сложно? Почему нельзя сохранить всё как есть?
Приложение использует оперативную память и представляет данные в виде объектов. Можно работать с любым объектом в любой момент — чтение и запись происходят быстро. Минус оперативки — когда приложение завершается, память освобождается, и данные пропадают.
Чтобы данные пережили приложение, они записываются в постоянную память. Хранением и организацией данных занимается БД.
В оперативке данные лежат кое-как — где место нашлось, там объект и создаётся. В постоянной памяти данных много, всё упорядочено и оптимизировано. Поэтому модели данных в БД и приложении иногда отличаются.
А ещё БД — это отдельное приложение. В итоге для сохранения/извлечения данных нужна куча дополнительной работы:
🔸 открыть соединение
🔸 составить SQL запрос
🔸 передать в запросе данные из приложения
🔸 преобразовать ответ БД в java объект
🔸 закрыть соединение
JDBC
— стандартные java методы, которые выполняют все пункты выше. Все инструменты по работе с БД под капотом используют как раз JDBC.
Работа с соединениями и преобразование данных — утомительная работа. Поэтому и появляются библиотеки, облегчающие этот труд.
Spring JDBC
берёт на себя работу с соединениями. Разработчик всё так же составляет запросы, передаёт параметры и преобразует ответы в java объекты.
ORM
Object Relational Mapping — преобразование данных (mapping) из java объектов в сущности БД и обратно.
Формально, ORM — просто название процесса. В случае JDBC весь ОRМ разработчик делает вручную.
На практике под ORM подразумевают ORM библиотеку/фреймворк — какой-нибудь инструмент, который берёт на себя часть работы по запросам и преобразованию данных.
Hibernate
— самая популярная ORM библиотека. Составляет простые SQL запросы и преобразует данные. Упростил жизнь многим и заслужил их любовь❤️
В хибернейте не всё идеально:
▪️ Работа с соединениями (сессиями) остаётся на пользователе
▪️ Для корректной работы надо знать внутрянку (dirty session, как разруливаются отношения и тд).
Сложно не признать, что Hibernate великолепен. Ворвался на олимп ORM библиотек в 2001 году и до сих пор оттуда не слезает🏆
JPA
Сейчас большинство приложений базируются на спринге, но 10-15 лет назад приложения часто опирались на Java ЕЕ. В те года ORM Java ЕЕ выглядел сложно — для каждой сущности требовались несколько классов и кучка интерфейсов.
Hibernate выглядел привлекательно, но нельзя просто взять и добавить библиотеку в проект. Во вселенной Java EE всё работает через спецификации — стандартные интерфейсы.
Поэтому появилась новая спека по ORM — Java Persistence API или JPA. С небольшими отличиями почти полностью списана с хибернейта. Вскоре Hibernate подстроился под JPA и стал использоваться в Java EE.
Итого
⭐️ JDBC — базовое API по работе с БД
⭐️ ORM — преобразование данных между приложением и БД. На практике под “у нас на проекте ORM” имеют в виду, что используется ORM библиотека, например, Hibernate
⭐️ JPA — спецификация по ORM. Набор интерфейсов, аннотаций и описание, как всё должно работать. Не включает в себя конкретную реализацию
⭐️ Hibernate — популярная ORM библиотека, реализующая JPA
В следующем посте распишу вариации Spring Data, и почему материалы по хибернейт могут не соответствовать реальности.
От JDBC до Spring Data: часть 2
В этом посте расскажу про популярные модули Spring Data и подскажу важный нюанс при изучении Hibernate.
Spring Data JPA
Hibernate вызвал вау-эффект тем, что взял на себя маппинг и простейшие SQL запросы.
Spring Data JPA пошёл дальше и избавил разработчика от унылых конфигов и возни с сессиями, плюс генерирует более сложные SQL запросы.
Всё очень круто (без шуток), но есть нюанс.
Ванильный Hibernate подразумевает, что пользователь знаком с деталями реализации — умело работает с кэшами 1 и 2 уровня, различает
А вот JPA определяет только интерфейс доступа к данным. Поэтому в Spring Data JPA многие хибернейт фичи не используются.
Пример — ленивая загрузка коллекций и кэш 1 уровня. Spring Data в общем случае при каждом обращении к репозиторию создаёт новую сессию. Кэширования в итоге нет, а при загрузке коллекций ловим эксепшн.
Кэш 2 уровня и EntityGraph поправят ситуацию, но это уже продвинутый уровень:) Недостаточно пользоваться абстракцией "репозиторий", надо знать и Hibernate, и как Spring использует Hibernate.
Практический совет — если что-то читаете по хибернейту, уточняйте, как это работает в Spring Data и работает ли вообще.
Для простых сервисов Spring Data JPA существенно упрощает жизнь. Для сложных тоже, но требует больше знаний.
Spring Data JDBC
— альтернатива Spring Data JPA. Под капотом у него JDBC без посредничества Hibernate.
Интерфейс такой же — пользователь работает с репозиторием и размечает классы аннотациями типа @Id или @Column.
JDBC проще, у него нет кэшей, ленивой загрузки, каскадных операций и автоматического сохранения. Код становится предсказуемым, но многие вещи нужно делать явно.
Отдельного внимания заслуживает работа с зависимыми сущностями в DDD стиле. А в этом докладе показан наглядный пример и больше различий Spring Data JPA/JDBC.
Важный момент! Не путайте две библиотеки:
🌸 Spring JDBC упрощает работу с соединениями. Запросы, маппинг сущностей, управление транзакциями пишет разработчик
🌹 Spring Data JDBC даёт следующий уровень абстрации — репозиторий. Работа c запросами, маппингом и транзациями упрощается за счёт аннотаций
MyBatis
часто упоминается как альтернатива Hibernate. Называет себя persistence framework, а не ORM, но занимается тем же — помогает писать меньше кода по перегону данных между БД и приложением.
Основное отличие MyBatis от хибернейта — все SQL-запросы пишутся явно, и внутри можно писать if и foreach блоки.
MyBatis в целом ничего, но редко встречается. Причины просты:
❌ Нет Spring Data модуля, только Spring Boot Starter. Писать руками нужно гораздо больше
❌ В MyBatis есть аннотации, но документация и большинство статей используют XML. Выглядит несовременно👨🦳
Итого
⭐️ Spring Data * берёт на себя конфиги, работу с сессиями, генерацию некоторых запросов
⭐️ Spring Data JPA упрощает работу с Hibernate
⭐️ Spring Data JDBC предлагает похожий интерфейс, но на основе JDBC
⭐️ MyBatis для тех, кто хочет чего-то другого
Что выбрать?
Функционально Spring Data JPA/JDBC и MyBatis похожи, но со своими нюансами. Адекватных и современных бенчмарков в интернете нет. Статьи вроде "Hibernate vs MyBatis" очень поверхностные, не тратьте на них время.
На практике выбор делается почти случайно. Что затащат в проект на старте, то и используется:)
В этом посте расскажу про популярные модули Spring Data и подскажу важный нюанс при изучении Hibernate.
Spring Data JPA
Hibernate вызвал вау-эффект тем, что взял на себя маппинг и простейшие SQL запросы.
Spring Data JPA пошёл дальше и избавил разработчика от унылых конфигов и возни с сессиями, плюс генерирует более сложные SQL запросы.
Всё очень круто (без шуток), но есть нюанс.
Ванильный Hibernate подразумевает, что пользователь знаком с деталями реализации — умело работает с кэшами 1 и 2 уровня, различает
persist
/save
/merge
, использует нужные типы данных и тд.А вот JPA определяет только интерфейс доступа к данным. Поэтому в Spring Data JPA многие хибернейт фичи не используются.
Пример — ленивая загрузка коллекций и кэш 1 уровня. Spring Data в общем случае при каждом обращении к репозиторию создаёт новую сессию. Кэширования в итоге нет, а при загрузке коллекций ловим эксепшн.
Кэш 2 уровня и EntityGraph поправят ситуацию, но это уже продвинутый уровень:) Недостаточно пользоваться абстракцией "репозиторий", надо знать и Hibernate, и как Spring использует Hibernate.
Практический совет — если что-то читаете по хибернейту, уточняйте, как это работает в Spring Data и работает ли вообще.
Для простых сервисов Spring Data JPA существенно упрощает жизнь. Для сложных тоже, но требует больше знаний.
Spring Data JDBC
— альтернатива Spring Data JPA. Под капотом у него JDBC без посредничества Hibernate.
Интерфейс такой же — пользователь работает с репозиторием и размечает классы аннотациями типа @Id или @Column.
JDBC проще, у него нет кэшей, ленивой загрузки, каскадных операций и автоматического сохранения. Код становится предсказуемым, но многие вещи нужно делать явно.
Отдельного внимания заслуживает работа с зависимыми сущностями в DDD стиле. А в этом докладе показан наглядный пример и больше различий Spring Data JPA/JDBC.
Важный момент! Не путайте две библиотеки:
🌸 Spring JDBC упрощает работу с соединениями. Запросы, маппинг сущностей, управление транзакциями пишет разработчик
🌹 Spring Data JDBC даёт следующий уровень абстрации — репозиторий. Работа c запросами, маппингом и транзациями упрощается за счёт аннотаций
MyBatis
часто упоминается как альтернатива Hibernate. Называет себя persistence framework, а не ORM, но занимается тем же — помогает писать меньше кода по перегону данных между БД и приложением.
Основное отличие MyBatis от хибернейта — все SQL-запросы пишутся явно, и внутри можно писать if и foreach блоки.
MyBatis в целом ничего, но редко встречается. Причины просты:
❌ Нет Spring Data модуля, только Spring Boot Starter. Писать руками нужно гораздо больше
❌ В MyBatis есть аннотации, но документация и большинство статей используют XML. Выглядит несовременно👨🦳
Итого
⭐️ Spring Data * берёт на себя конфиги, работу с сессиями, генерацию некоторых запросов
⭐️ Spring Data JPA упрощает работу с Hibernate
⭐️ Spring Data JDBC предлагает похожий интерфейс, но на основе JDBC
⭐️ MyBatis для тех, кто хочет чего-то другого
Что выбрать?
Функционально Spring Data JPA/JDBC и MyBatis похожи, но со своими нюансами. Адекватных и современных бенчмарков в интернете нет. Статьи вроде "Hibernate vs MyBatis" очень поверхностные, не тратьте на них время.
На практике выбор делается почти случайно. Что затащат в проект на старте, то и используется:)
Выполнится ли до конца задача, отправленная в экзекьютор? Длительность задачи - около 500мс
Выполнится ли до конца задача, отправленная в экзекьютор? Длительность задачи - около 500мс
Anonymous Poll
25%
Да, если она начала выполнение до вызова cancel
16%
Нет
59%
Невозможно определить