🧠 Внезапная ловушка с
Вы знали, что вызов метода с
📌 Почему так происходит?
Spring AOP работает через прокси. Когда вы вызываете метод
Пример:
⚠️ Итог: никакой транзакции, и вы получите баг, который сложно заметить: данные не откатываются, lazy-loading кидает
💡 Как правильно:
1. Вынести transactional-метод в другой бин:
2. Или получить прокси текущего бина через
📌 Для второго варианта не забудьте включить:
⚠️ Важно: Не используйте
👉@BookJava
@Transactional
и self-invocation
в SpringВы знали, что вызов метода с
@Transactional
внутри того же класса не активирует транзакцию? Это один из самых частых подводных камней.📌 Почему так происходит?
Spring AOP работает через прокси. Когда вы вызываете метод
this.someTransactionalMethod()
, вы обходите прокси и вызываете метод напрямую — без обёртки, которая включает транзакцию.Пример:
@Service
public class UserService {
public void createUser() {
// транзакция не начнётся
this.saveUser();
}
@Transactional
public void saveUser() {
// ожидаем, что тут будет транзакция, но её нет
}
}
⚠️ Итог: никакой транзакции, и вы получите баг, который сложно заметить: данные не откатываются, lazy-loading кидает
LazyInitializationException
, и т.д.💡 Как правильно:
1. Вынести transactional-метод в другой бин:
@Service
public class UserSaver {
@Transactional
public void saveUser() { ... }
}
2. Или получить прокси текущего бина через
AopContext
:
public void createUser() {
((UserService) AopContext.currentProxy()).saveUser();
}
📌 Для второго варианта не забудьте включить:
@EnableAspectJAutoProxy(exposeProxy = true)
⚠️ Важно: Не используйте
AopContext
без крайней необходимости. Предпочтительнее делегировать логику в отдельный бин — это чище и легче тестируется.👉@BookJava
👍4👎2
👩💻 SpELые приложения на Spring
Присоединяйтесь к открытому уроку, узнайте, как динамически выражать и обрабатывать данные в Spring-приложениях.
🗓 21 мая в 19:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Разработчик на Spring Framework».
О чём поговорим:
✔️ Разоберем, для чего нужен SpEL.
✔️ Рассмотрим, в каких проектах Spring его можно встретить.
Кому будет интересно:
Spring-разработчикам, Java-бэкенд-инженерам, архитекторам ПО, IT-специалистам и студентам, заинтересованным в технологиях Spring.
В результате урока:
Узнаете, для чего нужен SpEL и где его можно применять.
🔗 Ссылка на регистрацию: https://vk.cc/cLY8ET
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Присоединяйтесь к открытому уроку, узнайте, как динамически выражать и обрабатывать данные в Spring-приложениях.
🗓 21 мая в 19:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Разработчик на Spring Framework».
О чём поговорим:
Кому будет интересно:
Spring-разработчикам, Java-бэкенд-инженерам, архитекторам ПО, IT-специалистам и студентам, заинтересованным в технологиях Spring.
В результате урока:
Узнаете, для чего нужен SpEL и где его можно применять.
🔗 Ссылка на регистрацию: https://vk.cc/cLY8ET
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1
🧠 Как не словить
Одна из самых частых ошибок при работе с JPA:
📌 Причина: лениво загружаемая коллекция (
💡 Как избежать?
✅ Решение 1:
Убедитесь, что вы обращаетесь к ленивым коллекциям внутри метода с
⚠️ Не используйте
✅ Решение 2: Fetch Join
Подгрузите нужные данные сразу через
📌 Плюс: 1 запрос вместо N (N+1 проблема решается).
📌 Минус: может быть избыточная загрузка, особенно с большими коллекциями.
✅ Решение 3: DTO проекция
Лучший способ в большинстве случаев — проецировать сразу в DTO:
📌 Выгружает только нужные данные. Быстро, безопасно, эффективно.
💬 Ленивая инициализация — ок, если вы контролируете границы транзакций.
Проекции и
👉@BookJava
LazyInitializationException
в Spring Boot + HibernateОдна из самых частых ошибок при работе с JPA:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection
📌 Причина: лениво загружаемая коллекция (
LAZY
) обращается к БД вне транзакции — например, в слое контроллера или после закрытия Session
.💡 Как избежать?
✅ Решение 1:
@Transactional
в сервисеУбедитесь, что вы обращаетесь к ленивым коллекциям внутри метода с
@Transactional
:
@Transactional
public UserDto getUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow();
// OK: коллекция friends будет инициализирована в транзакции
return new UserDto(user.getName(), user.getFriends());
}
⚠️ Не используйте
@Transactional
в контроллерах — это плохая практика.✅ Решение 2: Fetch Join
Подгрузите нужные данные сразу через
JOIN FETCH
:
@Query("SELECT u FROM User u LEFT JOIN FETCH u.friends WHERE u.id = :id")
Optional<User> findByIdWithFriends(@Param("id") Long id);
📌 Плюс: 1 запрос вместо N (N+1 проблема решается).
📌 Минус: может быть избыточная загрузка, особенно с большими коллекциями.
✅ Решение 3: DTO проекция
Лучший способ в большинстве случаев — проецировать сразу в DTO:
@Query("""
SELECT new com.example.UserDto(u.name, f.name)
FROM User u
LEFT JOIN u.friends f
WHERE u.id = :id
""")
List<UserDto> findUserWithFriendNames(@Param("id") Long id);
📌 Выгружает только нужные данные. Быстро, безопасно, эффективно.
💬 Ленивая инициализация — ок, если вы контролируете границы транзакций.
Проекции и
fetch join
— ваши лучшие друзья, если нужен контроль и производительность.👉@BookJava
👍5
🔥 Java и производительность — новая тема от онлайн-конференции Podlodka Java Crew
С 26 по 30 мая вас ждет тематическая неделя, посвящённая продвинутым практикам оптимизации Java-приложений.
В программе:
— Доклад Владимира Плизги (Tibbo System) о подходах к профилированию и инструментах, которые действительно работают;
— Воркшоп по JMH от Григория Кошелева (Контур) — научитесь писать микробенчмарки правильно;
— Круглый стол с Антоном Курако (Т-Банк) и Михаилом Поливахой (Spring АйО) — сравнение Spring, Micronaut, Quarkus и Kora через призму производительности;
— Опыт команды НСПК по нагрузочному тестированию в бою — расскажет Павел Митин.
А ещё — JFR, корутины, Kubernetes и десятки инсайтов из продакшена.
🎯 Неделя для тех, кто держит перформанс под контролем.
🔗 Подключайся: podlodka.io/javacrew
С 26 по 30 мая вас ждет тематическая неделя, посвящённая продвинутым практикам оптимизации Java-приложений.
В программе:
— Доклад Владимира Плизги (Tibbo System) о подходах к профилированию и инструментах, которые действительно работают;
— Воркшоп по JMH от Григория Кошелева (Контур) — научитесь писать микробенчмарки правильно;
— Круглый стол с Антоном Курако (Т-Банк) и Михаилом Поливахой (Spring АйО) — сравнение Spring, Micronaut, Quarkus и Kora через призму производительности;
— Опыт команды НСПК по нагрузочному тестированию в бою — расскажет Павел Митин.
А ещё — JFR, корутины, Kubernetes и десятки инсайтов из продакшена.
🎯 Неделя для тех, кто держит перформанс под контролем.
🔗 Подключайся: podlodka.io/javacrew
🧠 Трюк с
Когда тебе нужно обрабатывать события в рамках транзакции, мы часто пишем:
⚠️ Но есть нюанс:
📌 Альтернатива: обычный
💡 Сниппет:
📎 Плюсы:
— Лучше контроль: ты сам решаешь, когда обрабатывать (до/после/вне транзакции)
— Можно централизовать поведение через utility-метод
— Гибкость: логика обработки не зависит от аннотаций Spring'а
⚠️ Минус: чуть больше кода, но понятнее поведение.
👉@BookJava
@EventListener
в Spring — убираем лишний @TransactionalEventListener
Когда тебе нужно обрабатывать события в рамках транзакции, мы часто пишем:
@TransactionalEventListener
public void handleEvent(MyEvent event) {
// ...
}
⚠️ Но есть нюанс:
@TransactionalEventListener
по умолчанию срабатывает после коммита. Иногда это не очевидно и вызывает баги, особенно если ожидаешь, что событие обработается внутри транзакции.📌 Альтернатива: обычный
@EventListener
, но вместе с TransactionSynchronizationManager
.💡 Сниппет:
@Component
public class MyEventHandler {
@EventListener
public void handle(MyEvent event) {
if (TransactionSynchronizationManager.isActualTransactionActive()) {
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// обработка события после коммита
}
}
);
} else {
// fallback: нет активной транзакции — выполняем сразу
}
}
}
📎 Плюсы:
— Лучше контроль: ты сам решаешь, когда обрабатывать (до/после/вне транзакции)
— Можно централизовать поведение через utility-метод
— Гибкость: логика обработки не зависит от аннотаций Spring'а
⚠️ Минус: чуть больше кода, но понятнее поведение.
👉@BookJava
👍9
🧠
Оба способа хороши для конфигурации, но используют их по-разному. И если ты всё ещё везде пихаешь
📌
✅ Хорошо для единичных значений
❌ Плохо для сложных структур, списков, валидации
❌ Трудно покрыть тестами (без
❌ Нет биндинга по префиксу → нет группировки
📌
💡 Используй с
✅ Удобно группировать и документировать
✅ Работает с вложенными структурами, коллекциями
✅ Поддерживает JSR-303 валидацию (
✅ Легче мокать в тестах
✅ Интеграция с Spring Boot Actuator (
⚠️ Не смешивай: не нужно тянуть
💬 Если конфигурация простая —
👉@BookJava
@Value
vs @ConfigurationProperties
— не выбирай наобумОба способа хороши для конфигурации, но используют их по-разному. И если ты всё ещё везде пихаешь
@Value
, держи краткий гайд, когда лучше что:📌
@Value
— просто, но не гибко:
@Value("${my.prop}")
private String value;
✅ Хорошо для единичных значений
❌ Плохо для сложных структур, списков, валидации
❌ Трудно покрыть тестами (без
TestPropertySource
)❌ Нет биндинга по префиксу → нет группировки
📌
@ConfigurationProperties
— сила и масштаб:
@ConfigurationProperties(prefix = "app.feature")
public class FeatureProperties {
private boolean enabled;
private List<String> items;
}
💡 Используй с
@EnableConfigurationProperties
или аннотируй как @Component
✅ Удобно группировать и документировать
✅ Работает с вложенными структурами, коллекциями
✅ Поддерживает JSR-303 валидацию (
@Validated
)✅ Легче мокать в тестах
✅ Интеграция с Spring Boot Actuator (
/actuator/configprops
)⚠️ Не смешивай: не нужно тянуть
@Value
внутрь @ConfigurationProperties
— это антипаттерн.💬 Если конфигурация простая —
@Value
норм. Но как только появляется структура, коллекции, логика — всегда используй @ConfigurationProperties
.👉@BookJava
👍8❤1
🚀 Открой для себя идеальный путь к лидерству с карьерным тестом от ОЭЗ «Алабуга»! 🌟
Мечтаете о карьере в крупной компании, где ваш потенциал раскроется на полную? Наш тест поможет вам определить вашу уникальную лидерскую роль. Может быть, именно вы станете тем лидером, который выведет команду на новый уровень?
После прохождения теста вы можете заполнить заявку и получить приглашение на эксклюзивную лидерскую программу. Участие в программе открывает реальные перспективы трудоустройства в ОЭЗ «Алабуга», предоставляя шанс начать путь к профессиональному признанию.
Сделайте первый шаг к своему будущему сегодня! Пройдите тест, подайте заявку и начните строить свою карьеру вместе с нами. 🎯
Мечтаете о карьере в крупной компании, где ваш потенциал раскроется на полную? Наш тест поможет вам определить вашу уникальную лидерскую роль. Может быть, именно вы станете тем лидером, который выведет команду на новый уровень?
После прохождения теста вы можете заполнить заявку и получить приглашение на эксклюзивную лидерскую программу. Участие в программе открывает реальные перспективы трудоустройства в ОЭЗ «Алабуга», предоставляя шанс начать путь к профессиональному признанию.
Сделайте первый шаг к своему будущему сегодня! Пройдите тест, подайте заявку и начните строить свою карьеру вместе с нами. 🎯
⚡1👍1🍾1
Double-brace инициализация в Java — это идиома, которая используется для инициализации коллекций (и иногда других объектов) в краткой форме. Она выглядит как две открывающие фигурные скобки подряд
Как это работает:
Double-brace инициализация — это комбинация двух конструкций:
1. Анонимный внутренний класс:
Создаётся новый безымянный подкласс
2. Инициализатор экземпляра:
Это блок, который выполняется при создании объекта. В него можно вставлять вызовы методов (например,
Преимущества:
* Компактный и удобочитаемый синтаксис для заполнения коллекций.
* Можно использовать в полях
Недостатки:
1. Создаётся лишний анонимный класс — это увеличивает количество байткода и может мешать сериализации.
2. Утечки памяти — если такой класс находится внутри внешнего класса, он может неявно хранить ссылку на него.
3. Читаемость — не все разработчики знают, как это работает, и это может сбивать с толку.
4. Нарушение принципов OOP — логика инициализации размещается в конструкторе, который не явно виден.
Альтернативы:
Java 8+ (через
Статический метод инициализации:
Java 9+ (immutable):
Вывод:
Double-brace инициализация — это удобный, но потенциально опасный трюк, который не рекомендуется использовать в продакшене. Лучше предпочесть более читаемые и безопасные альтернативы, особенно с учётом новых возможностей Java 8+.
👉@BookJava
{{
и имеет специфическое поведение. Пример:
import java.util.*;
List<String> list = new ArrayList<String>() {{
add("one");
add("two");
add("three");
}};
Как это работает:
Double-brace инициализация — это комбинация двух конструкций:
1. Анонимный внутренний класс:
new ArrayList<String>() { ... }
Создаётся новый безымянный подкласс
ArrayList
.2. Инициализатор экземпляра:
{{ ... }}
Это блок, который выполняется при создании объекта. В него можно вставлять вызовы методов (например,
add()
).Преимущества:
* Компактный и удобочитаемый синтаксис для заполнения коллекций.
* Можно использовать в полях
final
, например:
private static final Set<String> set = new HashSet<>() {{
add("A");
add("B");
}};
Недостатки:
1. Создаётся лишний анонимный класс — это увеличивает количество байткода и может мешать сериализации.
2. Утечки памяти — если такой класс находится внутри внешнего класса, он может неявно хранить ссылку на него.
3. Читаемость — не все разработчики знают, как это работает, и это может сбивать с толку.
4. Нарушение принципов OOP — логика инициализации размещается в конструкторе, который не явно виден.
Альтернативы:
Java 8+ (через
Stream
и Collectors
):
List<String> list = Stream.of("one", "two", "three")
.collect(Collectors.toList());
Статический метод инициализации:
public static List<String> createList() {
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
return list;
}
Java 9+ (immutable):
List<String> list = List.of("one", "two", "three");
Set<String> set = Set.of("A", "B");
Вывод:
Double-brace инициализация — это удобный, но потенциально опасный трюк, который не рекомендуется использовать в продакшене. Лучше предпочесть более читаемые и безопасные альтернативы, особенно с учётом новых возможностей Java 8+.
👉@BookJava
❤6👍3
👩💻 Как использовать SpEL в приложениях на Spring?
Присоединяйтесь к открытому уроку «SpELые приложения на Spring» и узнайте, как динамически выражать и обрабатывать данные в Spring-приложениях.
SpEL (Spring Expression Language) — это мощный инструмент для внедрения динамических выражений, который широко применяется в проектировании приложений на Spring.
🗓 21 мая в 19:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Разработчик на Spring Framework».
Что вас ждёт:
✔️ Погружение в SpEL и его возможности.
✔️ Применение SpEL в реальных Spring-проектах.
✔️ Понимание, как Spring использует SpEL для обработки и динамических вычислений.
Урок для Spring-разработчиков, Java-бэкенд-инженеров и архитекторов ПО.
🔗 Ссылка на регистрацию: https://vk.cc/cM6W9C
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Присоединяйтесь к открытому уроку «SpELые приложения на Spring» и узнайте, как динамически выражать и обрабатывать данные в Spring-приложениях.
SpEL (Spring Expression Language) — это мощный инструмент для внедрения динамических выражений, который широко применяется в проектировании приложений на Spring.
🗓 21 мая в 19:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Разработчик на Spring Framework».
Что вас ждёт:
Урок для Spring-разработчиков, Java-бэкенд-инженеров и архитекторов ПО.
🔗 Ссылка на регистрацию: https://vk.cc/cM6W9C
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
📌 picocli — это современная библиотека для создания CLI-приложений на Java. Она упрощает разработку командных интерфейсов, обеспечивая:
* Автоматическую генерацию
* Поддержку подкоманд (как в
* Аргументы, параметры, опции с короткими и длинными флагами (
* Интеграцию с GraalVM (подходит для нативной компиляции)
* Поддержку аннотаций (аннотируй POJO — и готово!)
* Автоматическую валидацию аргументов
* Цветной вывод и гибкое форматирование
* Интерактивный режим и автодополнение
Проект активно развивается, полностью документирован и используется в сотнях продакшн-проектов. Если ты ищешь мощную и простую в использовании CLI-библиотеку на Java — picocli отличный выбор.
https://github.com/remkop/picocli
👉@BookJava
* Автоматическую генерацию
--help
и --version
* Поддержку подкоманд (как в
git commit
, git push
)* Аргументы, параметры, опции с короткими и длинными флагами (
-v
, --verbose
)* Интеграцию с GraalVM (подходит для нативной компиляции)
* Поддержку аннотаций (аннотируй POJO — и готово!)
* Автоматическую валидацию аргументов
* Цветной вывод и гибкое форматирование
* Интерактивный режим и автодополнение
Проект активно развивается, полностью документирован и используется в сотнях продакшн-проектов. Если ты ищешь мощную и простую в использовании CLI-библиотеку на Java — picocli отличный выбор.
https://github.com/remkop/picocli
👉@BookJava
👍7❤2
Что такое механизм try-with-resources?
Механизм
📌 Поддерживается с Java 7
📌 Ресурсы должны реализовывать интерфейс
💡 Пример:
🧠 Почему это важно:
* Уменьшает boilerplate-код
* Исключает утечки ресурсов
* Упрощает обработку исключений
⚠️ Совет:
С Java 9 можно использовать уже объявленные переменные, если они final или effectively final:
👉@BookJava
Механизм
try-with-resources
в Java — это конструкция, которая упрощает работу с ресурсами, требующими закрытия (например, файлы, сокеты, соединения с БД и т.д.). Он автоматически закрывает ресурсы после завершения блока try
, избавляя от необходимости писать finally
вручную.📌 Поддерживается с Java 7
📌 Ресурсы должны реализовывать интерфейс
AutoCloseable
💡 Пример:
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
// reader будет закрыт автоматически, даже если произойдёт исключение
🧠 Почему это важно:
* Уменьшает boilerplate-код
* Исключает утечки ресурсов
* Упрощает обработку исключений
⚠️ Совет:
С Java 9 можно использовать уже объявленные переменные, если они final или effectively final:
BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
try (reader) {
System.out.println(reader.readLine());
}
👉@BookJava
👍8
На открытом уроке мы разберем потокобезопасные очереди JDK, которые являются обязательной частью многопоточных приложений. Вы узнаете, как они устроены, какие внутренние механизмы лежат в их основе и как правильно их использовать в своих проектах.
🗓 26 мая в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Java Developer. Professional».
Освоив принципы работы потокобезопасных очередей, вы сможете создать более производительные и стабильные многопоточные приложения. Эти знания откроют новые возможности для вашего профессионального роста в Java-разработке.
🔗 Ссылка на регистрацию: https://vk.cc/cM8APp
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
🧠 Ленивая инициализация через
Когда нужно отложить создание тяжёлого объекта до первого обращения, многие вспоминают double-checked locking:
⚠️ Многословно, хрупко, легко ошибиться. Есть лучше.
📌 Современный подход — использовать
💡
Плюсы:
— Читается за секунду
— Потокобезопасно
— Нет дублирования кода
— Легко тестировать и заменять
🔁 Альтернатива в чистой Java: использовать
Если используешь Spring — можно просто обернуть бин в
👉@BookJava
Supplier
— элегантная альтернатива double-checked lockingКогда нужно отложить создание тяжёлого объекта до первого обращения, многие вспоминают double-checked locking:
private volatile SomeHeavyObject obj;
public SomeHeavyObject getObj() {
if (obj == null) {
synchronized (this) {
if (obj == null) {
obj = new SomeHeavyObject();
}
}
}
return obj;
}
⚠️ Многословно, хрупко, легко ошибиться. Есть лучше.
📌 Современный подход — использовать
Supplier
с ленивой инициализацией:
private final Supplier<SomeHeavyObject> lazyObj = Suppliers.memoize(SomeHeavyObject::new);
public SomeHeavyObject getObj() {
return lazyObj.get();
}
💡
Suppliers.memoize
— из Guava. Он гарантирует потокобезопасную инициализацию один раз при первом вызове get()
.Плюсы:
— Читается за секунду
— Потокобезопасно
— Нет дублирования кода
— Легко тестировать и заменять
🔁 Альтернатива в чистой Java: использовать
AtomicReference
и updateAndGet
, но это уже длиннее и менее выразительно.Если используешь Spring — можно просто обернуть бин в
@Lazy
. Но вне Spring, в обычных Java-приложениях или утилитах — Supplier
с memoize()
идеален.👉@BookJava
👍6
Три похожих слова в Java —
🔒
Ключевое слово. Используется для ограничений:
*
*
*
📌 Особенно важно для immutability и thread-safety.
🧯
Блок в конструкции
💡 Используется для освобождения ресурсов: закрытия потоков, соединений и т.д.
⚰️
Метод из
⚠️ УСТАРЕЛ с Java 9, удалён в Java 18. Не используй.
🔪 Непредсказуем, плохо работает, тормозит GC. Вместо него —
🧠 Важно не путать:
*
*
*
👉@BookJava
final
, finally
, finalize
— но смысл у них совершенно разный. Разберёмся 🧠🔒
final
Ключевое слово. Используется для ограничений:
*
final class
— нельзя наследовать.*
final method
— нельзя переопределить.*
final variable
— нельзя изменить значение (один раз присвоил — всё).📌 Особенно важно для immutability и thread-safety.
final int x = 10;
x = 20; // ошибка компиляции
🧯
finally
Блок в конструкции
try-catch-finally
. Выполняется всегда, даже если был return
или exception
.💡 Используется для освобождения ресурсов: закрытия потоков, соединений и т.д.
try {
// что-то может выбросить исключение
} catch (Exception e) {
// обработка ошибки
} finally {
// всегда выполнится
}
⚰️
finalize()
Метод из
Object
, вызывался перед удалением объекта сборщиком мусора.⚠️ УСТАРЕЛ с Java 9, удалён в Java 18. Не используй.
🔪 Непредсказуем, плохо работает, тормозит GC. Вместо него —
AutoCloseable
и try-with-resources
.
@Override
protected void finalize() throws Throwable {
System.out.println("До свидания...");
}
🧠 Важно не путать:
*
final
— про нельзя менять*
finally
— про всегда выполнится*
finalize
— про устаревший и бесполезный метод👉@BookJava
👍5
Ищете эффективные инструменты для создания DSL?
Узнайте, как Kotlin может упростить разработку с помощью JsonBuilder!
Приглашаем на открытый урок.
🗓 22 мая в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Kotlin Backend Developer. Professional».
На открытом уроке вы разберете, как Kotlin позволяет создавать DSL (Domain-Specific Languages), оптимизируя процесс разработки. Мы покажем теорию и практику создания DSL на примере JsonBuilder.
Вы не только научитесь создавать собственные DSL, но и освоите замыкания и extension-методы Kotlin, которые дадут вам дополнительные преимущества при написании чистого и гибкого кода.
🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - Kotlin5
👉 Регистрация на вебинар: https://vk.cc/cMaDZP
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Узнайте, как Kotlin может упростить разработку с помощью JsonBuilder!
Приглашаем на открытый урок.
🗓 22 мая в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Kotlin Backend Developer. Professional».
На открытом уроке вы разберете, как Kotlin позволяет создавать DSL (Domain-Specific Languages), оптимизируя процесс разработки. Мы покажем теорию и практику создания DSL на примере JsonBuilder.
Вы не только научитесь создавать собственные DSL, но и освоите замыкания и extension-методы Kotlin, которые дадут вам дополнительные преимущества при написании чистого и гибкого кода.
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
🧠 Dependency Inversion Principle (D в SOLID)
Зависимости должны идти от высокоуровневой политики к низкоуровневым деталям, а не наоборот. Абстракции — хозяева, реализации — обслуживающий персонал.
📌 Коротко о сути
* Модули верхнего уровня (бизнес-логика) зависят только от интерфейсов/абстракций.
* Модули нижнего уровня (база, сеть, файлы) также зависят от тех же абстракций.
* Сами абстракции не знают ничего о деталях, тем самым разрывая «бетонную» сцепку между слоями.
💡 Мини-пример (Java 17+, Spring Boot 3+)
BillingService может жить в модульном jar без spring-email-starter и SMTP-кода — протестировать его теперь элементарно.
⚠️ Где рождаются проблемы
1. Путаница DI container ≠ DIP
IoC/DI-фреймворк (Spring, CDI) — лишь удобный способ «сращивать» зависимости, но принцип работает и без контейнера (чистый constructor injection).
2. Абстракции ради галочки
Интерфейс OneImplService с единственной реализацией ломает читаемость, тесты и автоконфиг 📉.
➜ Создавай абстракцию, когда реально нужны сменяемость, тестируемость или расширяемость.
3. Утечки деталей
Если интерфейс таскает DTO из слоя хранения, ты всё ещё «протёк» к базе. Держи контракты чистыми.
4. Слепая вера в фреймворк
Жизненный цикл бинов, прокси, lazy-init — магия мешает понимать, кто кем владеет.
➜ Всегда можешь собрать объект вручную в юнит-тесте. Если сложно — запах нарушения DIP.
5. Слишком много уровней абстракций
«Контроллер → сервис → менеджер → порт → адаптер → репозиторий» превращает код в матрёшку. Дизайн важнее количества слоёв.
📝 Практические советы
* Используй constructor injection по умолчанию. Поле
* Группируй интерфейсы по use-case, а не по технологии (например,
* В тестах не мокай фреймворк — мокай контракт.
* Для одноразовых реализаций начни с
* Проверка себя: можно ли запустить модуль верхнего уровня без нижнего? Если да — DIP соблюдён.
💬 Итог
DIP — это не про «везде интерфейсы» и не про «подключи Spring». Это про правильное направление зависимостей, которое делает код гибким, тестируемым и не заложником технологий. А проблемы возникают, когда путают инструмент с принципом и забывают, что абстракция должна прятать детали, а не выпячивать их.
👉@BookJava
Зависимости должны идти от высокоуровневой политики к низкоуровневым деталям, а не наоборот. Абстракции — хозяева, реализации — обслуживающий персонал.
📌 Коротко о сути
* Модули верхнего уровня (бизнес-логика) зависят только от интерфейсов/абстракций.
* Модули нижнего уровня (база, сеть, файлы) также зависят от тех же абстракций.
* Сами абстракции не знают ничего о деталях, тем самым разрывая «бетонную» сцепку между слоями.
💡 Мини-пример (Java 17+, Spring Boot 3+)
// 1️⃣ Абстракция — контракт
public interface NotificationSender {
void send(Message msg);
}
// 2️⃣ Верхний уровень — бизнес-служба
@Service
public class BillingService {
private final NotificationSender sender;
public BillingService(NotificationSender sender) {
this.sender = sender; // зависим от контракта
}
public void bill(Client c) {
// ...
sender.send(new Message("Invoice #42"));
}
}
// 3️⃣ Низкий уровень — деталь
@Component
public class EmailSender implements NotificationSender {
public void send(Message msg) {
// SMTP-магия
}
}
BillingService может жить в модульном jar без spring-email-starter и SMTP-кода — протестировать его теперь элементарно.
⚠️ Где рождаются проблемы
1. Путаница DI container ≠ DIP
IoC/DI-фреймворк (Spring, CDI) — лишь удобный способ «сращивать» зависимости, но принцип работает и без контейнера (чистый constructor injection).
2. Абстракции ради галочки
Интерфейс OneImplService с единственной реализацией ломает читаемость, тесты и автоконфиг 📉.
➜ Создавай абстракцию, когда реально нужны сменяемость, тестируемость или расширяемость.
3. Утечки деталей
Если интерфейс таскает DTO из слоя хранения, ты всё ещё «протёк» к базе. Держи контракты чистыми.
4. Слепая вера в фреймворк
Жизненный цикл бинов, прокси, lazy-init — магия мешает понимать, кто кем владеет.
➜ Всегда можешь собрать объект вручную в юнит-тесте. Если сложно — запах нарушения DIP.
5. Слишком много уровней абстракций
«Контроллер → сервис → менеджер → порт → адаптер → репозиторий» превращает код в матрёшку. Дизайн важнее количества слоёв.
📝 Практические советы
* Используй constructor injection по умолчанию. Поле
final
= явная зависимость.* Группируй интерфейсы по use-case, а не по технологии (например,
TransferPort
, а не JdbcTransferRepository
).* В тестах не мокай фреймворк — мокай контракт.
* Для одноразовых реализаций начни с
class
. Если появится второй вариант — быстро вынесешь интерфейс (IDE поможет).* Проверка себя: можно ли запустить модуль верхнего уровня без нижнего? Если да — DIP соблюдён.
💬 Итог
DIP — это не про «везде интерфейсы» и не про «подключи Spring». Это про правильное направление зависимостей, которое делает код гибким, тестируемым и не заложником технологий. А проблемы возникают, когда путают инструмент с принципом и забывают, что абстракция должна прятать детали, а не выпячивать их.
👉@BookJava
👍10👎1
📌 Stream API: забудьте про peek()
Метод
⚠️ Почему не стоит полагаться на peek()?
*
* Поведение зависит от терминальной операции: например, при некоторых оптимизациях JDK (особенно в параллельных стримах) вызов
💡 Пример опасного кода:
🧠 Лучший подход: используйте
✅ Правильная замена:
Если нужно выполнить действие над каждым элементом, используйте явный и безопасный подход:
Или вообще вынесите логику за пределы стрима.
🧹 Держите код понятным и безопасным — забудьте про
👉@BookJava
Метод
peek()
в Java Stream API выглядит удобным для отладки, но его использование в реальном коде часто приводит к проблемам.⚠️ Почему не стоит полагаться на peek()?
*
peek()
— промежуточная операция, которая не гарантирует вызов функции для каждого элемента стрима.* Поведение зависит от терминальной операции: например, при некоторых оптимизациях JDK (особенно в параллельных стримах) вызов
peek()
может быть пропущен или работать непредсказуемо.💡 Пример опасного кода:
List<String> names = Stream.of("Java", "Spring", "Hibernate")
.peek(System.out::println) // 👈 анти-паттерн!
.collect(Collectors.toList());
🧠 Лучший подход: используйте
peek()
исключительно для дебага, но никогда — для изменения состояния или важных операций.✅ Правильная замена:
Если нужно выполнить действие над каждым элементом, используйте явный и безопасный подход:
List<String> names = Stream.of("Java", "Spring", "Hibernate")
.map(name -> {
log.info("Processing: {}", name); // или другая логика
return name;
})
.collect(Collectors.toList());
Или вообще вынесите логику за пределы стрима.
🧹 Держите код понятным и безопасным — забудьте про
peek()
.👉@BookJava
👍7