🧠
Многие знают, что можно заинжектить список строк из
Но знаете, что вы получите?
⚠️ ОШИБКУ.
📌 Решение — использовать
💡 Профит:
- работает с
- валидация через
- легко покрыть тестами;
- меньше магии.
⚠️
📲 Мы в MAX
👉@BookJava
@Value в Spring — это ловушка, если вы используете списки или map'ыМногие знают, что можно заинжектить список строк из
application.yml вот так:
app:
langs:
- en
- fr
- de
@Value("${app.langs}")
private List<String> langs;
Но знаете, что вы получите?
⚠️ ОШИБКУ.
@Value не умеет парсить YAML-массивы. Он ожидает строку, и даже с CSV-строкой (en,fr,de) — всё не так очевидно: Spring не применяет ConversionService для списков.📌 Решение — использовать
@ConfigurationProperties:
app:
langs:
- en
- fr
- de
@ConfigurationProperties(prefix = "app")
@Component
public class AppProps {
private List<String> langs;
// геттеры/сеттеры
}
💡 Профит:
- работает с
List, Map, вложенными объектами;- валидация через
@Validated и @NotEmpty;- легко покрыть тестами;
- меньше магии.
⚠️
@Value хорош для простых скаляров. Всё остальное — через @ConfigurationProperties.📲 Мы в MAX
👉@BookJava
❤3
🧠 JVM-разработчики, стягиваемся в Москву 29 августа
В последнюю субботу лета бэкенд- и JVM-комьюнити соберется на JVM Day, который устраивает Т-Банк. Ждут всех разработчиков, архитекторов и тимлидов, которые работают с Java, Kotlin и Scala.
В программе:
– Андрей Кулешов расскажет о том, что изменилось в мире JVM за год.
– Сергей Петрелевич разберет Mechanical Sympathy.
– Александр Ланцов покажет, какие модели конкурентности актуальны после появления Loom.
– Антон Курако объяснит, почему красивые показатели бенчмарков могут вводить в заблуждение.
А после докладов во дворе штаб-квартиры Т-Банка пройдут:
– Игры на Sega и ИТ-Квиз.
– PowerPoint Party и открытый микрофон с историями ошибок.
– Диджей-сет на виниле под открытым небом.
Места ограничены, регистрироваться тут. Пишите в комментариях, если идете на конфу.
📲 Мы в MAX
👉@BookJava
В последнюю субботу лета бэкенд- и JVM-комьюнити соберется на JVM Day, который устраивает Т-Банк. Ждут всех разработчиков, архитекторов и тимлидов, которые работают с Java, Kotlin и Scala.
В программе:
– Андрей Кулешов расскажет о том, что изменилось в мире JVM за год.
– Сергей Петрелевич разберет Mechanical Sympathy.
– Александр Ланцов покажет, какие модели конкурентности актуальны после появления Loom.
– Антон Курако объяснит, почему красивые показатели бенчмарков могут вводить в заблуждение.
А после докладов во дворе штаб-квартиры Т-Банка пройдут:
– Игры на Sega и ИТ-Квиз.
– PowerPoint Party и открытый микрофон с историями ошибок.
– Диджей-сет на виниле под открытым небом.
Места ограничены, регистрироваться тут. Пишите в комментариях, если идете на конфу.
📲 Мы в MAX
👉@BookJava
🤩13👍10😁10
💡 Не делай этого в
Сейчас расскажу, почему инициализировать важную бизнес-логику в
Типичный пример:
🧨 Проблема:
Если внутри будет ошибка (например, БД недоступна) — приложение может упасть, или что хуже — запуститься в полурабочем состоянии.
📌 Кроме того:
* ❌ Нет контроля над порядком выполнения таких методов;
* ❌ Нельзя легко переиспользовать эту логику (например, вручную перезагрузить кеш);
* ❌ В тестах или dev-среде — такие вызовы часто мешают.
✅ Современный подход — использовать
📌 Альтернатива — аннотация
📦 В Spring Boot это нативный и рекомендованный способ выполнения кода после старта.
🧠 Резюме:
🔹
🔹 Бизнес-логику и I/O — в
📲 Мы в MAX
👉@BookJava
@PostConstruct — особенно в продеСейчас расскажу, почему инициализировать важную бизнес-логику в
@PostConstruct — плохая идея.Типичный пример:
@Component
public class CacheLoader {
private final SomeService service;
public CacheLoader(SomeService service) {
this.service = service;
}
@PostConstruct
public void init() {
service.loadDataIntoCache(); // ⚠️ обращение к БД
}
}
🧨 Проблема:
@PostConstruct вызывается до того, как приложение полностью поднялось.Если внутри будет ошибка (например, БД недоступна) — приложение может упасть, или что хуже — запуститься в полурабочем состоянии.
📌 Кроме того:
* ❌ Нет контроля над порядком выполнения таких методов;
* ❌ Нельзя легко переиспользовать эту логику (например, вручную перезагрузить кеш);
* ❌ В тестах или dev-среде — такие вызовы часто мешают.
✅ Современный подход — использовать
ApplicationListener:
@Component
public class CacheLoader implements ApplicationListener<ApplicationReadyEvent> {
private final SomeService service;
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
service.loadDataIntoCache(); // 👍 вызывается только после старта
}
}
📌 Альтернатива — аннотация
@EventListener:
@EventListener(ApplicationReadyEvent.class)
public void onReady() {
// безопасно загружаем данные
}
📦 В Spring Boot это нативный и рекомендованный способ выполнения кода после старта.
🧠 Резюме:
🔹
@PostConstruct — только для простой инициализации бинов.🔹 Бизнес-логику и I/O — в
@EventListener(ApplicationReadyEvent.class).📲 Мы в MAX
👉@BookJava
👍6🔥2
🚀 Подборка полезных IT каналов в Max
Системное администрирование, DevOps 📌
https://max.ru/i_odmin Все для системного администратора
https://max.ru/bash_srv Bash Советы
https://max.ru/sysadminof Книги для админов, полезные материалы
https://max.ru/i_odmin_book Библиотека Системного Администратора
https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др.
https://max.ru/tipsysdmin Типичный Сисадмин
https://max.ru/channel_win_sysadmin Системный Администратор Windows
https://max.ru/channel_linux_admin Linux: Системный администратор
https://max.ru/channel_linuxmod Linux
https://max.ru/channel_i_linux Системный администратор
https://max.ru/channel_devopslib DevOps, SRE, Sysadmin
https://max.ru/channel_devops_star DevOps Star (Звезда Девопса)
Excel лайфхак 📌
https://t.me/Excel_lifehack Excel лайфхак
Английский с нуля 🇬🇧
https://max.ru/UchuEnglish
1C разработка 📌
https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С
https://max.ru/channel_DevLab1C 1С:Предприятие 8
Программирование C++📌
https://max.ru/cpp_lib Библиотека C/C++ разработчика
https://max.ru/channel_cpp_geek C++ geek
Программирование Go📌
https://max.ru/golang_lib Библиотека Go (Golang) разработчика
Программирование React📌
https://max.ru/react_lib React
Программирование Rust📌
https://max.ru/channel_rust_lib
Программирование Python 📌
https://max.ru/python_of Python академия.
https://max.ru/BookPython Библиотека Python разработчика
Java разработка 📌
https://max.ru/bookjava Библиотека Java разработчика
https://max.ru/channel_java_geek Java Geek
GitHub Сообщество 📌
https://max.ru/githublib Интересное из GitHub
Базы данных (Data Base) 📌
https://max.ru/database_info Все про базы данных
Фронтенд разработка 📌
https://max.ru/frontend_1 Подборки для frontend разработчиков
Библиотеки 📌
https://max.ru/programmist_of Книги по программированию
https://max.ru/proglb Библиотека программиста
https://max.ru/bfbook Книги для программистов
Программирование 📌
https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций
https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT
https://max.ru/php_lib Библиотека PHP программиста 👨🏼💻👩💻
Шутки программистов 📌
https://max.ru/itumor Шутки программистов
Защита, взлом, безопасность 📌
https://max.ru/thehaking Канал о кибербезопасности
https://max.ru/xakkep_1 Хакер Free
Книги, статьи для дизайнеров 📌
https://max.ru/odesigners Статьи, книги для дизайнеров
Математика 📌
https://max.ru/Pomatematike Канал по математике
https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике
Вакансии в IT📌
https://max.ru/progjob
https://max.ru/channel_rabotait
Мир технологий 📌
https://max.ru/mir_teh Канал для любознательных
Городские📌
https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга
https://max.ru/mockva_life Свежие новости Москвы
https://max.ru/piterspb Питер Новости: Санкт-Петербург / СПБ / ДТП
https://max.ru/channel_krasnodar_novosty Краснодар Новости
https://max.ru/channel_novosibirsk_novosti Новосибирск
https://max.ru/channel_samara_novosti Новости Самары
https://max.ru/channel_ekaterinburg_novosti Новости Екатеринбурга
https://max.ru/channel_kazan_novosti Новости Казани
https://max.ru/channel_omsk_novosti Новости Омска
https://max.ru/channel_moskva_24 Москва 24
Системное администрирование, DevOps 📌
https://max.ru/i_odmin Все для системного администратора
https://max.ru/bash_srv Bash Советы
https://max.ru/sysadminof Книги для админов, полезные материалы
https://max.ru/i_odmin_book Библиотека Системного Администратора
https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др.
https://max.ru/tipsysdmin Типичный Сисадмин
https://max.ru/channel_win_sysadmin Системный Администратор Windows
https://max.ru/channel_linux_admin Linux: Системный администратор
https://max.ru/channel_linuxmod Linux
https://max.ru/channel_i_linux Системный администратор
https://max.ru/channel_devopslib DevOps, SRE, Sysadmin
https://max.ru/channel_devops_star DevOps Star (Звезда Девопса)
Excel лайфхак 📌
https://t.me/Excel_lifehack Excel лайфхак
Английский с нуля 🇬🇧
https://max.ru/UchuEnglish
1C разработка 📌
https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С
https://max.ru/channel_DevLab1C 1С:Предприятие 8
Программирование C++📌
https://max.ru/cpp_lib Библиотека C/C++ разработчика
https://max.ru/channel_cpp_geek C++ geek
Программирование Go📌
https://max.ru/golang_lib Библиотека Go (Golang) разработчика
Программирование React📌
https://max.ru/react_lib React
Программирование Rust📌
https://max.ru/channel_rust_lib
Программирование Python 📌
https://max.ru/python_of Python академия.
https://max.ru/BookPython Библиотека Python разработчика
Java разработка 📌
https://max.ru/bookjava Библиотека Java разработчика
https://max.ru/channel_java_geek Java Geek
GitHub Сообщество 📌
https://max.ru/githublib Интересное из GitHub
Базы данных (Data Base) 📌
https://max.ru/database_info Все про базы данных
Фронтенд разработка 📌
https://max.ru/frontend_1 Подборки для frontend разработчиков
Библиотеки 📌
https://max.ru/programmist_of Книги по программированию
https://max.ru/proglb Библиотека программиста
https://max.ru/bfbook Книги для программистов
Программирование 📌
https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций
https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT
https://max.ru/php_lib Библиотека PHP программиста 👨🏼💻👩💻
Шутки программистов 📌
https://max.ru/itumor Шутки программистов
Защита, взлом, безопасность 📌
https://max.ru/thehaking Канал о кибербезопасности
https://max.ru/xakkep_1 Хакер Free
Книги, статьи для дизайнеров 📌
https://max.ru/odesigners Статьи, книги для дизайнеров
Математика 📌
https://max.ru/Pomatematike Канал по математике
https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике
Вакансии в IT📌
https://max.ru/progjob
https://max.ru/channel_rabotait
Мир технологий 📌
https://max.ru/mir_teh Канал для любознательных
Городские📌
https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга
https://max.ru/mockva_life Свежие новости Москвы
https://max.ru/piterspb Питер Новости: Санкт-Петербург / СПБ / ДТП
https://max.ru/channel_krasnodar_novosty Краснодар Новости
https://max.ru/channel_novosibirsk_novosti Новосибирск
https://max.ru/channel_samara_novosti Новости Самары
https://max.ru/channel_ekaterinburg_novosti Новости Екатеринбурга
https://max.ru/channel_kazan_novosti Новости Казани
https://max.ru/channel_omsk_novosti Новости Омска
https://max.ru/channel_moskva_24 Москва 24
MAX
Системный Администратор | Sysadmin Windows & Linux Server. …
Купить рекламу: https://telega.in/m/i_odmin
Блог практикующего админа. Настройка Windows Server, Active Directory (AD), GPO и терминальных серверов (RDP). Раб…
Блог практикующего админа. Настройка Windows Server, Active Directory (AD), GPO и терминальных серверов (RDP). Раб…
🤮2💩2🤡2
🧠 Spring Boot: как НЕ попасть в ловушку с
Когда используете
📌 Если таких задач несколько или они работают долго — остальные ждут. Это создаёт бутылочное горлышко и приводит к неожиданным задержкам.
💡 Решение: настроить кастомный executor:
Теперь все
⚠️ Не забывайте: если задача критична к ресурсам или зависит от внешних сервисов — добавьте внутреннюю защиту от повторного выполнения, например, с помощью Redis lock или базы.
✅ Подключение пула — must-have для production-проектов, где
📲 Мы в MAX
👉@BookJava
@Scheduled и многопоточностьюКогда используете
@Scheduled для периодических задач в Spring Boot, важно понимать: по умолчанию все задачи выполняются в одном потоке.
@Scheduled(fixedRate = 5000)
public void syncData() {
// долгая операция
}
📌 Если таких задач несколько или они работают долго — остальные ждут. Это создаёт бутылочное горлышко и приводит к неожиданным задержкам.
💡 Решение: настроить кастомный executor:
@Configuration
@EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar registrar) {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(5); // количество параллельных задач
scheduler.setThreadNamePrefix("scheduled-task-");
scheduler.initialize();
registrar.setTaskScheduler(scheduler);
}
}
Теперь все
@Scheduled - методы будут использовать пул потоков, а не один.⚠️ Не забывайте: если задача критична к ресурсам или зависит от внешних сервисов — добавьте внутреннюю защиту от повторного выполнения, например, с помощью Redis lock или базы.
✅ Подключение пула — must-have для production-проектов, где
@Scheduled выполняет реальные задачи, а не просто println.📲 Мы в MAX
👉@BookJava
👍4🔥1
🧠 Spring Boot: правильно логируем
Сейчас покажу простой, но часто упускаемый момент при обработке ошибок в Spring Boot.
Если у вас есть глобальный
❌ Плохо:
✅ Хорошо:
📌
* Сохранять stacktrace для дебага;
* Не терять вложенные причины (
* Работать с лог-агрегаторами (ELK, Grafana, etc).
💡 Если вы используете Slf4j и формат
⚠️ И не забывайте: глобальные хендлеры — это хорошо, но не глушите все ошибки без разбора. Лучше создавать разные
📲 Мы в MAX
👉@BookJava
@ExceptionHandlerСейчас покажу простой, но часто упускаемый момент при обработке ошибок в Spring Boot.
Если у вас есть глобальный
@ExceptionHandler, убедитесь, что вы не теряете stacktrace при логировании.❌ Плохо:
@ExceptionHandler(MyException.class)
public ResponseEntity<String> handle(MyException ex) {
log.error("MyException occurred: {}", ex.getMessage()); // stacktrace теряется!
return ResponseEntity.status(500).body("Error");
}
✅ Хорошо:
@ExceptionHandler(MyException.class)
public ResponseEntity<String> handle(MyException ex) {
log.error("MyException occurred", ex); // stacktrace будет видно в логе
return ResponseEntity.status(500).body("Error");
}
📌
log.error(String, Throwable) — правильный способ логировать исключения. Это позволяет:* Сохранять stacktrace для дебага;
* Не терять вложенные причины (
getCause());* Работать с лог-агрегаторами (ELK, Grafana, etc).
💡 Если вы используете Slf4j и формат
log.error("message: {}", ex.getMessage()), вы теряете почти всю полезную информацию об ошибке.⚠️ И не забывайте: глобальные хендлеры — это хорошо, но не глушите все ошибки без разбора. Лучше создавать разные
@ExceptionHandler под каждую категорию исключений.📲 Мы в MAX
👉@BookJava
👍3🔥1
🧠 Неочевидный performance-трюк с
Многие используют
📌 Что делает
* Подсказывает
* Это позволяет избежать затрат на грязную проверку (dirty checking).
* Не создаётся snapshot состояний сущностей → меньше памяти и операций.
💡 Пример:
⚠️ А теперь важно: если вы случайно измените сущность в таком методе, Hibernate проигнорирует изменения — потому что readOnly намекает: "не трогай".
📉 В реальном приложении с большим количеством запросов к БД, особенно читающих, такой подход даёт ощутимый буст — меньше нагрузки на ORM, меньше GC, быстрее ответы.
📌 Где применять:
* Методы только для чтения.
* REST-эндпоинты GET.
* Сервис-методы, возвращающие DTO и не модифицирующие Entity.
⚠️ Где не надо:
* Методы с lazy-loading и последующими модификациями.
* Там, где возможно случайное изменение Entity (например, через mapper'ы).
👉 Используйте
📲 Мы в MAX
👉@BookJava
@Transactional(readOnly = true)Многие используют
@Transactional(readOnly = true) просто по инерции. Но вы знали, что в Spring это влияет не только на семантику, но и на производительность?📌 Что делает
readOnly = true:* Подсказывает
Hibernate, что внутри транзакции не будет изменений сущностей.* Это позволяет избежать затрат на грязную проверку (dirty checking).
* Не создаётся snapshot состояний сущностей → меньше памяти и операций.
💡 Пример:
@Transactional(readOnly = true)
public List<User> findActiveUsers() {
return userRepository.findByActiveTrue();
}
⚠️ А теперь важно: если вы случайно измените сущность в таком методе, Hibernate проигнорирует изменения — потому что readOnly намекает: "не трогай".
📉 В реальном приложении с большим количеством запросов к БД, особенно читающих, такой подход даёт ощутимый буст — меньше нагрузки на ORM, меньше GC, быстрее ответы.
📌 Где применять:
* Методы только для чтения.
* REST-эндпоинты GET.
* Сервис-методы, возвращающие DTO и не модифицирующие Entity.
⚠️ Где не надо:
* Методы с lazy-loading и последующими модификациями.
* Там, где возможно случайное изменение Entity (например, через mapper'ы).
👉 Используйте
@Transactional(readOnly = true) не как декор, а как инструмент для оптимизации.📲 Мы в MAX
👉@BookJava
👍2🔥1
🧠 Трюк с
Когда вы выносите конфигурацию в отдельный модуль или создаёте библиотеку с
📌 Spring Boot 3+ по умолчанию НЕ сканирует пакеты вне стартового (
Пример:
И вы такие:
🔥 Но
💡 Потому что
📌 Решения:
1. Ручной импорт конфигурации:
2. Сделать конфигурацию
3. Переместить
⚠️ Часто баг проявляется неявно: контекст стартует, но бины "теряются", и вы получаете
✅ После миграции на Spring Boot 3+ обязательно проверьте, что нужные конфигурации действительно подхватываются. Особенно, если раньше они подключались "магически".
📲 Мы в MAX
👉@BookJava
@Configuration и @ComponentScan: как не словить баг при миграции на Spring Boot 3+Когда вы выносите конфигурацию в отдельный модуль или создаёте библиотеку с
@Configuration-классами — не забывайте:📌 Spring Boot 3+ по умолчанию НЕ сканирует пакеты вне стартового (
main).Пример:
// Внутри библиотеки
@Configuration
public class MyLibConfig {
@Bean
public MyService myService() {
return new MyService();
}
}
И вы такие:
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
🔥 Но
MyService не создаётся! Почему?💡 Потому что
@ComponentScan по умолчанию сканирует ТОЛЬКО package текущего класса и ниже.📌 Решения:
1. Ручной импорт конфигурации:
@SpringBootApplication
@Import(MyLibConfig.class)
public class App { ... }
2. Сделать конфигурацию
@AutoConfiguration и подключить через spring.factories или META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports — актуально для библиотек.3. Переместить
MyLibConfig в подпакет стартового класса (не всегда возможно или удобно).⚠️ Часто баг проявляется неявно: контекст стартует, но бины "теряются", и вы получаете
NoSuchBeanDefinitionException в рантайме.✅ После миграции на Spring Boot 3+ обязательно проверьте, что нужные конфигурации действительно подхватываются. Особенно, если раньше они подключались "магически".
📲 Мы в MAX
👉@BookJava
👍2🔥2
🔧 Maven vs. Gradle: что выбрать разработчику?
Когда речь заходит о сборке Java-проектов, выбор обычно падает на два главных инструмента: Maven и Gradle. Оба давно стали стандартом индустрии, но каждый имеет свои особенности. Разберёмся, что выбрать 👇
☕ Maven — проверенная классика
✅ Строгая структура: легче читать и сопровождать
✅ Надёжность и предсказуемость сборки
✅ Большое комьюнити и множество плагинов
⚠️ XML-конфигурация громоздкая
⚠️ Медленнее по сравнению с Gradle
⚡ Gradle — гибкость и скорость
✅ Поддержка Kotlin и Groovy DSL
✅ Инкрементальные сборки и кэширование → быстрее
✅ Более гибкий подход к настройке
⚠️ Порог входа выше
⚠️ Иногда сложно отлаживать конфигурацию
💡 Вывод:
* 🔹 Выбирай Maven, если важны стабильность, простота и читаемость.
* 🔹 Выбирай Gradle, если хочешь максимум производительности и гибкости.
🎯 В крупных проектах Gradle становится всё популярнее, особенно при использовании Kotlin. Но в enterprise-среде Maven по-прежнему правит бал.
📲 Мы в MAX
👉@BookJava
Когда речь заходит о сборке Java-проектов, выбор обычно падает на два главных инструмента: Maven и Gradle. Оба давно стали стандартом индустрии, но каждый имеет свои особенности. Разберёмся, что выбрать 👇
☕ Maven — проверенная классика
✅ Строгая структура: легче читать и сопровождать
✅ Надёжность и предсказуемость сборки
✅ Большое комьюнити и множество плагинов
⚠️ XML-конфигурация громоздкая
⚠️ Медленнее по сравнению с Gradle
⚡ Gradle — гибкость и скорость
✅ Поддержка Kotlin и Groovy DSL
✅ Инкрементальные сборки и кэширование → быстрее
✅ Более гибкий подход к настройке
⚠️ Порог входа выше
⚠️ Иногда сложно отлаживать конфигурацию
💡 Вывод:
* 🔹 Выбирай Maven, если важны стабильность, простота и читаемость.
* 🔹 Выбирай Gradle, если хочешь максимум производительности и гибкости.
🎯 В крупных проектах Gradle становится всё популярнее, особенно при использовании Kotlin. Но в enterprise-среде Maven по-прежнему правит бал.
📲 Мы в MAX
👉@BookJava
👍4🔥1
2 сентября(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика.
Как это будет:
📂 Сергей Чамкин, старший разработчик из Uzum, ex-WildBerries, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Cергей будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Сергею
Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.
Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_sh_bot
Реклама.
О рекламодателе.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
💡 Чем опасен
Расписание в Spring через
📌 Пример проблемы:
🧨 Каждые 10 секунд метод запускается заново. Если выполнение предыдущего ещё не закончено, начнётся второй поток, который заберёт те же
В итоге — дублирование обработки, гонки, повреждение данных.
📉 Особенно критично при долгих задачах или высокой нагрузке.
✅ Решение — обернуть метод в транзакцию + использовать блокировки:
📌 Или добавить флаг “locked”, чтобы явно помечать взятые задачи.
💡 Лучше использовать
🧠 Подумайте о том, чтобы заменить
* Spring Batch (если сложные джобы)
* Spring Integration / Flowable / Camunda (если нужны гарантии и retry)
* Quartz (если нужен контроль и очереди)
📲 Мы в MAX
👉@BookJava
@Scheduled(fixedRate) без @Transactional?Расписание в Spring через
@Scheduled — удобный способ запускать задачи по таймеру. Но часто разработчики забывают про транзакции, особенно с fixedRate, и попадают в ловушку.📌 Пример проблемы:
@Scheduled(fixedRate = 10_000)
public void cleanUp() {
List<Job> jobs = jobRepository.findAllByStatus(PENDING);
jobs.forEach(job -> {
job.setStatus(PROCESSING);
jobRepository.save(job);
});
}
🧨 Каждые 10 секунд метод запускается заново. Если выполнение предыдущего ещё не закончено, начнётся второй поток, который заберёт те же
PENDING -записи.В итоге — дублирование обработки, гонки, повреждение данных.
📉 Особенно критично при долгих задачах или высокой нагрузке.
✅ Решение — обернуть метод в транзакцию + использовать блокировки:
@Transactional
@Scheduled(fixedRate = 10_000)
public void cleanUp() {
List<Job> jobs = jobRepository.findAllByStatusForUpdate(PENDING); // SELECT ... FOR UPDATE
jobs.forEach(job -> {
job.setStatus(PROCESSING);
jobRepository.save(job);
});
}
📌 Или добавить флаг “locked”, чтобы явно помечать взятые задачи.
💡 Лучше использовать
@Scheduled(fixedDelay) — он ждёт завершения предыдущего запуска. Это безопаснее по умолчанию.🧠 Подумайте о том, чтобы заменить
@Scheduled на:* Spring Batch (если сложные джобы)
* Spring Integration / Flowable / Camunda (если нужны гарантии и retry)
* Quartz (если нужен контроль и очереди)
📲 Мы в MAX
👉@BookJava
❤2👍2🔥1
🧠 Spring Boot: ленивые зависимости через
Иногда сервису не нужно всегда инжектить другую зависимость при старте — только иногда по ходу работы. Но
💡 Решение: использовать
Пример:
📌
▫️ не создаёт бин сразу — ленивый доступ;
▫️ позволяет проверить наличие бина (
▫️ можно использовать
⚠️ Это не альтернатива DI. Это способ контролировать создание и использование бинов вручную, когда это действительно нужно.
📈 Отлично помогает:
▫️ при борьбе с циклическими зависимостями;
▫️ для optional-бинов;
▫️ чтобы ускорить старт приложения.
📲 Мы в MAX
👉@BookJava
ObjectProviderИногда сервису не нужно всегда инжектить другую зависимость при старте — только иногда по ходу работы. Но
@Autowired всё равно тянет её сразу, даже если она вам пока не нужна. Это бьёт по времени старта и может вызвать циклические зависимости.💡 Решение: использовать
ObjectProvider<T>.Пример:
@Service
public class NotificationService {
private final ObjectProvider<EmailSender> emailSenderProvider;
public NotificationService(ObjectProvider<EmailSender> emailSenderProvider) {
this.emailSenderProvider = emailSenderProvider;
}
public void sendEmailIfEnabled(String to, String body) {
if (featureEnabled()) {
EmailSender sender = emailSenderProvider.getIfAvailable();
if (sender != null) {
sender.send(to, body);
}
}
}
}
📌
ObjectProvider:getIfAvailable() / ifAvailable(...));stream() — для коллекций бинов.⚠️ Это не альтернатива DI. Это способ контролировать создание и использование бинов вручную, когда это действительно нужно.
📈 Отлично помогает:
📲 Мы в MAX
👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🧠 Как не словить
Одна из самых частых ошибок при работе с JPA:
📌 Причина: лениво загружаемая коллекция (
💡 Как избежать?
✅ Решение 1:
Убедитесь, что вы обращаетесь к ленивым коллекциям внутри метода с
⚠️ Не используйте
✅ Решение 2: Fetch Join
Подгрузите нужные данные сразу через
📌 Плюс: 1 запрос вместо N (N+1 проблема решается).
📌 Минус: может быть избыточная загрузка, особенно с большими коллекциями.
✅ Решение 3: DTO проекция
Лучший способ в большинстве случаев - проецировать сразу в DTO:
📌 Выгружает только нужные данные. Быстро, безопасно, эффективно.
Ленивая инициализация - ок, если вы контролируете границы транзакций.
Проекции и
📲 Мы в MAX
👉@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 - ваши лучшие друзья, если нужен контроль и производительность.📲 Мы в MAX
👉@BookJava
👍2
Приглашаем на открытый урок.
🗓 21 сентября в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Java разработчик. Продвинутый уровень».
Разберем, как браузер общается с сервером, и создадим работающий HTTP-сервис без Spring и сторонних библиотек.
О чем поговорим:
🔗 Ссылка на регистрацию: https://vk.cc/d1sLwn
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🧠 Трюк с
Когда тебе нужно обрабатывать события в рамках транзакции, мы часто пишем:
⚠️ Но есть нюанс:
📌 Альтернатива: обычный
💡 Сниппет:
📎 Плюсы:
— Лучше контроль: ты сам решаешь, когда обрабатывать (до/после/вне транзакции)
— Можно централизовать поведение через utility-метод
— Гибкость: логика обработки не зависит от аннотаций Spring'а
⚠️ Минус: чуть больше кода, но понятнее поведение.
📲 Мы в MAX
👉@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'а
⚠️ Минус: чуть больше кода, но понятнее поведение.
📲 Мы в MAX
👉@BookJava
👍2🔥1