📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP
В асинхронных или веб-сервисах на реактиве нужно быть осторожным: SecurityContext
не “переходит” автоматически в новые потоки. Для этого используют SecurityContextRepository
и специальные методы в WebFlux.
⚠️ Важно: не храните SecurityContext
в сессии, если у вас stateless-приложение (REST API). Вместо сессии используйте JWT или OAuth 2.0.
🧠 5. Авторизация: FilterSecurityInterceptor & AccessDecisionManagerFilterSecurityInterceptor
запускается в конце цепочки фильтров и проверяет доступ к URL. Он запрашивает у SecurityMetadataSource
список необходимых ролей для данного эндпоинта (Spring на основании @PreAuthorize
, HttpSecurity
конфигурации или XML). Затем передаёт дело в AccessDecisionManager
(по умолчанию AffirmativeBased), который опрашивает список AccessDecisionVoter
(например, RoleVoter
для проверок ролей, WebExpressionVoter
для SpEL).
FilterSecurityInterceptor
└─> SecurityMetadataSource (что нужно: ROLE_ADMIN)
└─> AccessDecisionManager.vote()
├─ RoleVoter.vote() → совпадает?
└─ WebExpressionVoter.vote() → SpEL-выражения?
AffirmativeBased
отпускает запрос (по умолчанию). Можно менять стратегию на Consensus
или Unanimous
.@WithMockUser(roles = "ADMIN")
и проверять, что нужный эндпоинт доступен.
@EnableMethodSecurity // (Spring Boot 3+) вместо @EnableGlobalMethodSecurity
public class SecurityConfig { ... }
// Где-то в сервисе:
@PreAuthorize("hasRole('ADMIN') and #id == principal.id")
public void deleteUser(Long id) { ... }
@PreAuthorize
/ @PostAuthorize
/ @Secured
/ @RolesAllowed
— все используют тот же механизм Voter’ов, но проверяют уже на методах сервиса.PasswordEncoder
с алгоритмами Argon2 или BCrypt:
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}
SecurityContextPersistenceFilter
хранит контекст в сессии. Удобно для монолитов с классическим web-приложением.SessionCreationPolicy.STATELESS
, используем BearerTokenAuthenticationFilter
, аутентификация и авторизация проверяются по JWT в каждом запросе.SecurityFilterChain
с oauth2ResourceServer().jwt()
.spring-boot-starter-oauth2-resource-server
.issuer-uri
или jwk-set-uri
в application.yml
.
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://keycloak.example.com/realms/myrealm
POST
, PUT
, DELETE
). Для stateless-API его обычно отключают:
http.csrf().disable();
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
logging.level.org.springframework.security=DEBUG
«Я слышу свой код»: как работает Java-программист, потерявший зрение
Константин Евтеев собирает Java-код с помощью диктора NVDA, редактирует его в Блокноте и передает на Linux по SSH через самописные bash-скрипты. После потери зрения он не потерял интереса к жизни и желания быть полезным и выстроил собственную инженерную экосистему: оглавления по строкам .txt-файлами, навигация по main и маленьким методам, отладка на слух.
https://habr.com/ru/companies/axiomjdk/articles/913748/
👉@BookJava
🚀 Подборка Telegram каналов для программистов
Системное администрирование, DevOps 📌
/channel/bash_srv Bash Советы
/channel/win_sysadmin Системный Администратор Windows
/channel/sysadmin_girl Девочка Сисадмин
/channel/srv_admin_linux Админские угодья
/channel/linux_srv Типичный Сисадмин
/channel/devopslib Библиотека девопса | DevOps, SRE, Sysadmin
/channel/linux_odmin Linux: Системный администратор
/channel/devops_star DevOps Star (Звезда Девопса)
/channel/i_linux Системный администратор
/channel/linuxchmod Linux
/channel/sys_adminos Системный Администратор
/channel/tipsysdmin Типичный Сисадмин (фото железа, было/стало)
/channel/sysadminof Книги для админов, полезные материалы
/channel/i_odmin Все для системного администратора
/channel/i_odmin_book Библиотека Системного Администратора
/channel/i_odmin_chat Чат системных администраторов
/channel/i_DevOps DevOps: Пишем о Docker, Kubernetes и др.
/channel/sysadminoff Новости Линукс Linux
1C разработка 📌
/channel/odin1C_rus Cтатьи, курсы, советы, шаблоны кода 1С
/channel/DevLab1C 1С:Предприятие 8
/channel/razrab_1C 1C Разработчик
/channel/buh1C_prog 1C Программист | Бухгалтерия и Учёт
/channel/rabota1C_rus Вакансии для программистов 1С
Программирование C++📌
/channel/cpp_lib Библиотека C/C++ разработчика
/channel/cpp_knigi Книги для программистов C/C++
/channel/cpp_geek Учим C/C++ на примерах
Программирование Python 📌
/channel/pythonofff Python академия.
/channel/BookPython Библиотека Python разработчика
/channel/python_real Python подборки на русском и английском
/channel/python_360 Книги по Python
Java разработка 📌
/channel/BookJava Библиотека Java разработчика
/channel/java_360 Книги по Java Rus
/channel/java_geek Учим Java на примерах
GitHub Сообщество 📌
/channel/Githublib Интересное из GitHub
Базы данных (Data Base) 📌
/channel/database_info Все про базы данных
Мобильная разработка: iOS, Android 📌
/channel/developer_mobila Мобильная разработка
/channel/kotlin_lib Подборки полезного материала по Kotlin
Фронтенд разработка 📌
/channel/frontend_1 Подборки для frontend разработчиков
/channel/frontend_sovet Frontend советы, примеры и практика!
/channel/React_lib Подборки по React js и все что с ним связано
Разработка игр 📌
/channel/game_devv Все о разработке игр
Библиотеки 📌
/channel/book_for_dev Книги для программистов Rus
/channel/programmist_of Книги по программированию
/channel/proglb Библиотека программиста
/channel/bfbook Книги для программистов
БигДата, машинное обучение 📌
/channel/bigdata_1 Big Data, Machine Learning
Программирование 📌
/channel/bookflow Лекции, видеоуроки, доклады с IT конференций
/channel/rust_lib Полезный контент по программированию на Rust
/channel/golang_lib Библиотека Go (Golang) разработчика
/channel/itmozg Программисты, дизайнеры, новости из мира IT
/channel/php_lib Библиотека PHP программиста 👨🏼💻👩💻
/channel/nodejs_lib Подборки по Node js и все что с ним связано
/channel/ruby_lib Библиотека Ruby программиста
/channel/lifeproger Жизнь программиста. Авторский канал.
QA, тестирование 📌
/channel/testlab_qa Библиотека тестировщика
Шутки программистов 📌
/channel/itumor Шутки программистов
Защита, взлом, безопасность 📌
/channel/thehaking Канал о кибербезопасности
/channel/xakep_2 Хакер Free
Книги, статьи для дизайнеров 📌
/channel/ux_web Статьи, книги для дизайнеров
Математика 📌
/channel/Pomatematike Канал по математике
/channel/phis_mat Обучающие видео, книги по Физике и Математике
/channel/matgeoru Математика | Геометрия | Логика
Excel лайфхак📌
/channel/Excel_lifehack
/channel/mir_teh Мир технологий (Technology World)
Вакансии 📌
/channel/sysadmin_rabota Системный Администратор
/channel/progjob Вакансии в IT
🗑 Понимание различных сборщиков мусора в Java
🔵 Serial Garbage Collector: Лучший вариант для однопоточных приложений с небольшими кучами. Он использует один поток для выполнения как малых, так и больших сборок мусора, что приводит к значительным паузам, но минимальной нагрузке на систему.
🔵 Parallel Garbage Collector: Подходит для приложений с высокими требованиями к пропускной способности. Использует несколько потоков для выполнения как малых, так и больших сборок мусора, уменьшая время пауз, но при этом увеличивая использование CPU.
🔵 Concurrent Mark-Sweep (CMS) Garbage Collector: Разработан для минимизации пауз за счёт выполнения основной части работы по сборке мусора параллельно с выполнением приложений. Подходит для приложений, где критически важна низкая задержка.
🔵 G1 Garbage Collector: Сбалансированный сборщик мусора, который стремится обеспечить предсказуемое время пауз, разделяя кучу на регионы и выполняя сборку мусора поэтапно. Является хорошим выбором по умолчанию для большинства приложений.
🔵 Z Garbage Collector и Shenandoah: Сборщики мусора с ультранизкой задержкой, разработанные для работы с большими кучами. Основная часть работы по сборке мусора выполняется параллельно, что позволяет минимизировать время пауз даже при очень больших кучах.
👉@BookJava
👩💻 Хотите выйти за пределы стандартных подходов в Java-разработке? Разобраться в JVM, многопоточности и современных фреймворках?
🔥 Актуальное обучение, курс «Java Developer. Professional» — это 96 часов практики, детальный разбор технологий, код-ревью от опытных экспертов и работа с Spring WebFlux, Kafka, Kubernetes.
После обучения вы сможете разрабатывать сложные Java-приложения уровня Middle+, понимать работу JVM изнутри и писать чистый, оптимизированный код.
🎁 Дарим промокод, который дает скидку на обучение - JAVA_06
➡️ Пройдите вступительное тестирование и получите скидку: https://vk.cc/cMonpN
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
🧠 Сегодня разберём Saga — паттерн, который часто всплывает на собеседованиях уровня сеньор, особенно если речь о микросервисах.
📌 Зачем нужна Saga?
Когда нужно выполнить бизнес-операцию, затрагивающую несколько микросервисов, важно обеспечить целостность данных. Классические транзакции (@Transactional
) тут не подходят — нет распределённого ACID. Saga решает эту проблему через последовательность локальных транзакций с возможностью отката (compensation).
💡 Виды Saga
1. Choreography
Нет единого оркестратора. Каждый сервис реагирует на события предыдущих через очередь (Kafka, RabbitMQ).
* Простой flow
* Меньше точек отказа
2. Orchestration
Есть выделенный "оркестратор", который координирует выполнение саги, рассылая команды сервисам.
* Более явный контроль
* Лучше трассировка
⚖️ Плюсы
* Обеспечивает согласованность между сервисами
* Высокая отказоустойчивость: каждый шаг можно компенсировать
* Гибкость — легко расширять и модифицировать бизнес-логику
⚠️ Минусы
* Сложнее реализовать, чем простые транзакции
* Не моментальная консистентность — возможны временные аномалии
* Нужно проектировать compensating transactions для каждого шага
* Тяжело тестировать крайние случаи и "сорванные" шаги
🔁 Альтернативы
* Distributed Transactions (XA, 2PC) — редко используются из-за сложности и слабой поддержки в современных cloud-системах.
* Event Sourcing — другой паттерн работы с изменениями, но сложнее для чтения.
* Idempotency + Retry — иногда достаточно, если бизнес-процесс допускает.
💡 Совет: для большинства бизнес-операций с межсервисным взаимодействием, где требуется отмена — Saga остаётся самым практичным выбором. В Spring есть библиотека Axon Framework, также стоит посмотреть на Camunda.
👉@BookJava
🧠 Как быстро написать компаратор в Java и не забыть про null
Часто нужно сортировать коллекции по какому-то полю. Вместо старых анонимных классов используем лямбды и статические методы из Comparator
:
List<User> users = ...;
users.sort(Comparator
.comparing(User::getName, Comparator.nullsLast(String::compareToIgnoreCase))
.thenComparingInt(User::getAge)
);
comparing
— сравнивает по полю (например, name).nullsLast
или nullsFirst
— удобно обрабатывать возможные null.thenComparingInt
— дополнительная сортировка (например, по возрасту).NullPointerException
во время сортировки может неожиданно прилететь в проде.📊 Приглашаем на открытый урок: Создание RLE архиватора на Java
Хотите узнать, как работают алгоритмы сжатия данных?
В нашем вебинаре мы шаг за шагом создадим архиватор на Java, используя алгоритм RLE. Вы разработаете интерфейс программы и поймете, как реализовать самые эффективные методы сжатия.
Протестировав алгоритм на реальных данных, вы увидите, когда он работает наилучшим образом. Присоединяйтесь к нам и погрузитесь в мир программирования и алгоритмов!
🗓 28 мая в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Алгоритмы и структуры данных».
🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - Algo5
👉 Регистрация на вебинар: https://vk.cc/cMl0ID
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
🧠 10 правил безопасного Java-приложения:
1️⃣ Валидация на всех уровнях.
📌 Всегда проверяй входные данные (API, формы, запросы к БД). Используй Bean Validation (@Valid
, @NotNull
).
2️⃣ Используй PreparedStatement.
⚠️ Никогда не конкатенируй SQL-запросы напрямую, чтобы избежать SQL-инъекций.
3️⃣ Не раскрывай детали исключений.
📌 Отправляй клиенту общий код ошибки, а детали логируй.
4️⃣ Скрывай конфигурацию.
💡 Используй application.yml
с переменными окружения для чувствительных данных (пароли, ключи API).
5️⃣ Ограничивай доступ.
📌 Spring Security — твой друг. Используй @PreAuthorize
и грамотно настраивай роли и права доступа.
6️⃣ Безопасная сериализация.
⚠️ Избегай Java-стандартной сериализации. Предпочитай JSON (Jackson) с явными DTO.
7️⃣ Используй актуальные версии библиотек.
📌 Регулярно проверяй зависимости (dependency-check
), чтобы избежать известных уязвимостей.
8️⃣ Грамотно логируй.
💡 Не логируй пароли, токены и персональные данные. Используй SLF4J с Logback, настрой уровень логов.
9️⃣ Безопасные куки и заголовки.
📌 Используй атрибуты куки: Secure
, HttpOnly
, SameSite
. Spring Security поможет тебе настроить это быстро.
🔟 Настрой Security Headers.
📌 Используй заголовки:
* X-Frame-Options: DENY
* X-Content-Type-Options: nosniff
* Content-Security-Policy
(CSP)
Безопасность — это не разовая задача, а процесс 💡
👉@BookJava
Spring Framework в деталях
SimpleJdbcInsert - Spring Framework JDBC
АОП в Spring Framework
XML-конфигурация АОП в Spring Framework
Транзакции - Spring Framework в деталях
источник
👉@BookJava
🔐 Основы сжатия данных: создаем RLE архиватор
Приглашаем на открытый урок.
🗓 28 мая в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Алгоритмы и структуры данных».
На этом вебинаре мы начнем создавать собственный архиватор на Java. Разработаем базовую структуру программы с пользовательским интерфейсом и реализуем алгоритм RLE (кодирование длин серий) для сжатия данных. Изучим как базовую, так и улучшенную версию RLE.
Протестируем эффективность алгоритма на разных типах файлов и увидим, когда этот простой метод сжатия работает наиболее эффективно.
Практическое погружение в мир алгоритмов сжатия данных для всех, кто интересуется программированием и структурами данных.
🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - Algo5
👉 Регистрация на вебинар: https://vk.cc/cMenHb
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
💻 Модели межсервисного взаимодействия
Изучите различные модели взаимодействия между микросервисами и выберите оптимальный подход для вашего проекта
Приглашаем на открытый урок.
🗓 28 мая в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Software Architect».
На вебинаре вы узнаете:
✔️ Основные принципы и типы межсервисного взаимодействия.
✔️ Синхронные и асинхронные модели взаимодействия: плюсы и минусы.
✔️ Использование API Gateway и Service Mesh для управления трафиком.
✔️ Паттерны и лучшие практики для надежного и масштабируемого взаимодействия.
✔️ Примеры успешных реализаций межсервисного взаимодействия в реальных проектах.
Вебинар будет полезен:
- Разработчикам, работающим с микросервисной архитектурой.
- Архитекторам ПО, стремящимся оптимизировать межсервисное взаимодействие.
- Backend и Fullstack разработчикам, заинтересованным в улучшении взаимодействия между сервисами.
- DevOps-инженерам, отвечающим за развертывание и управление микросервисами.
🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - SoftwareArc_06
👉 Регистрация на вебинар: https://vk.cc/cMcrjZ
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
🧠 Dependency Inversion Principle (D в SOLID)
Зависимости должны идти от высокоуровневой политики к низкоуровневым деталям, а не наоборот. Абстракции — хозяева, реализации — обслуживающий персонал.
📌 Коротко о сути
* Модули верхнего уровня (бизнес-логика) зависят только от интерфейсов/абстракций.
* Модули нижнего уровня (база, сеть, файлы) также зависят от тех же абстракций.
* Сами абстракции не знают ничего о деталях, тем самым разрывая «бетонную» сцепку между слоями.
💡 Мини-пример (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-магия
}
}
final
= явная зависимость.TransferPort
, а не JdbcTransferRepository
).class
. Если появится второй вариант — быстро вынесешь интерфейс (IDE поможет).Три похожих слова в Java — 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
, вызывался перед удалением объекта сборщиком мусора.AutoCloseable
и try-with-resources
.
@Override
protected void finalize() throws Throwable {
System.out.println("До свидания...");
}
final
— про нельзя менятьfinally
— про всегда выполнитсяfinalize
— про устаревший и бесполезный метод👩💻 Хотите научиться эффективно работать с многопоточными приложениями на Java?
На открытом уроке мы разберем потокобезопасные очереди JDK, которые являются обязательной частью многопоточных приложений. Вы узнаете, как они устроены, какие внутренние механизмы лежат в их основе и как правильно их использовать в своих проектах.
🗓 26 мая в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Java Developer. Professional».
Освоив принципы работы потокобезопасных очередей, вы сможете создать более производительные и стабильные многопоточные приложения. Эти знания откроют новые возможности для вашего профессионального роста в Java-разработке.
🔗 Ссылка на регистрацию: https://vk.cc/cM8APp
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
📌 Spring Security: основная архитектура
🧠 1. SecurityFilterChain & FilterChainProxy
Spring Security строит всё вокруг цепочки фильтров (FilterChainProxy). При запросе к приложению запрос проходит через набор фильтров, каждый из которых отвечает за свой кусок логики:
* ⚙️ ChannelProcessingFilter – перенаправление на HTTPS, если нужно.
* ⚙️ SecurityContextPersistenceFilter – загружает/сохраняет SecurityContext
(где хранится Authentication
).
* ⚙️ UsernamePasswordAuthenticationFilter – обрабатывает форму логина (если вы используете formLogin).
* ⚙️ BasicAuthenticationFilter – поддерживает HTTP Basic (для REST).
* ⚙️ BearerTokenAuthenticationFilter (Spring Boot 3+) – для JWT/OAuth2 Bearer-токенов.
* ⚙️ ExceptionTranslationFilter – перехватывает AccessDeniedException и AuthenticationException, перенаправляет на страницу логина или возвращает 401.
* ⚙️ FilterSecurityInterceptor – проверяет, есть ли у аутентифицированного пользователя разрешение (ROLE_*) для доступа к ресурсу.
Каждый фильтр решает конкретную задачу, и порядок важен: если, например, фильтр авторизации (FilterSecurityInterceptor) стоит раньше, чем фильтр аутентификации, вы получите неожиданный отказ.
💡 Современный подход (Spring Boot 3+):
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.oauth2ResourceServer(oauth2 -> oauth2.jwt()); // JWT из OIDC/JWK
return http.build();
}
UsernamePasswordAuthenticationFilter
(или другой аутентификатор) получает учётные данные, он создает UsernamePasswordAuthenticationToken
с неверифицированными (unauthenticated) флагом. Затем передаёт этот токен в AuthenticationManager
:
UsernamePasswordAuthenticationFilter → AuthenticationManager.authenticate()
AuthenticationManager
по умолчанию — это ProviderManager
, который хранит список AuthenticationProvider
(например, DaoAuthenticationProvider
для UserDetailsService или JwtAuthenticationProvider
для токенов). Каждый Provider
пытается аутентифицировать токен, и если успешно, возвращает уже аутентифицированный Authentication
с authorities.AuthenticationProvider
и зарегистрируйте его перед DaoAuthenticationProvider
.DaoAuthenticationProvider
опирается на UserDetailsService
(или ReactiveUserDetailsService
в WebFlux), чтобы получить UserDetails
(имя, пароль, роли, статус аккаунта). В Java 17+ можно пользоваться Map.of(...)
или List.of(...)
, но в реальных проектах лучше хранить в БД через JPA/Hibernate.
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository repo;
@Override
public UserDetails loadUserByUsername(String username) {
UserEntity user = repo.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
return User.withUsername(user.getUsername())
.password(user.getPassword())
.authorities(user.getRoles().toArray(new String[0]))
.accountLocked(!user.isAccountNonLocked())
.build();
}
}
SecurityContext
в SecurityContextHolder
. По умолчанию используется стратегия MODE_THREADLOCAL
, т.е. контекст привязан к текущему потоку.Читать полностью…
SecurityContextHolder.getContext().setAuthentication(authenticatedToken);
🧠 Чем Spring Native Image отличается от обычного Spring-приложения?
📌 Обычный Spring:
* Запускается на JVM, динамически загружает классы, использует рефлексию.
* Медленный старт (секунды), выше потребление памяти.
* Подходит для сложной логики с динамическим поведением (настройки, рефлексия, прокси).
📌 Spring Native Image (GraalVM):
* Компиляция в нативный бинарник.
* ⚡️ Мгновенный старт (миллисекунды), низкое потребление памяти.
* Отсутствие динамики: ограничения в рефлексии, прокси и динамической загрузке.
💡 Когда что использовать?
* Native Image — идеален для микросервисов и Serverless-приложений.
* Обычный JVM Spring — когда важна максимальная гибкость и динамика.
⚠️ Помни, что Native Image требует больше усилий по настройке и ограничений на библиотеки.
👉@BookJava
Как не завалить приложение из-за N+1 запросов в Hibernate 🧨
N+1 проблема — больной зуб почти любого Java-разработчика. Hibernate делает жизнь проще, пока не сталкиваешься с этим 👻
📌 Суть проблемы
Допустим, у тебя есть сущности User
и связанные с ними сущности Order
. При запросе пользователей:
List<User> users = userRepository.findAll(); // Один запрос
for(User user : users) {
List<Order> orders = user.getOrders(); // +1 запрос на каждого пользователя!
}
@Query("SELECT u FROM User u JOIN FETCH u.orders")
List<User> findAllWithOrders();
@EntityGraph(attributePaths = {"orders"})
List<User> findAll();
spring.jpa.properties.hibernate.default_batch_fetch_size=20
Серия статей: «Дюк, вынеси мусор!»
Все, что нужно знать о сборке мусора в Java
🔹 Если работа с JVM для тебя не просто слова, а параметры вроде Xmx
и Xms
ты прописываешь с закрытыми глазами, самое время разобраться, что же реально происходит с памятью в Java и как новые сборщики мусора могут повлиять на производительность твоего приложения.
🗂 В этой серии — подробный разбор всех современных GC, доступных в Java HotSpot VM: от базовых до самых продвинутых. Для каждого — объяснение принципов, сценарии применения, плюсы и минусы, а также практические советы по настройке.
Список статей:
1️⃣ Введение
2️⃣ Serial GC и Parallel GC
3️⃣ CMS и G1
4️⃣ ZGC
5️⃣ Epsilon GC
6️⃣ Shenandoah GC
👉@BookJava
🧠 Сейчас расскажу о распределённых транзакциях в Java и почему их стоит использовать с осторожностью.
Когда твое приложение работает с несколькими источниками данных (например, двумя базами или базой + очередью), часто появляется соблазн обернуть всё в одну большую транзакцию с помощью JTA (Java Transaction API) и XA-ресурсов. Но на практике это почти всегда антипаттерн.
📌 Почему?
* XA-транзакции медленные и сложно отлаживаются.
* Они ломают горизонтальное масштабирование: один сбой — откат по всем участникам.
* Могут приводить к блокировкам и deadlock-ам, если инфраструктура нестабильна.
💡 Современные альтернативы:
1. Transaction outbox pattern — сначала пишем событие/задачу в outbox-таблицу в той же БД, где меняем данные, и только потом публикуем во внешний сервис.
2. Event sourcing — строим архитектуру так, чтобы любые изменения происходили через событийную модель (и проще компенсировать сбои).
3. Idempotency & retries — делаем операции идемпотентными и безопасными к повтору.
⚠️ Не используешь JTA без реальной необходимости. Spring Boot 3+ по умолчанию уже не настраивает JTA — и правильно делает.
Если всё же нужен XA:
* Используй Atomikos, Narayana или Bitronix — это современные провайдеры.
* Но всегда сначала подумай: "Могу ли я упростить архитектуру и уйти от XA?"
Пример из кода на Spring Boot:
// НЕ рекомендуется:
@EnableTransactionManagement
public class Config {
@Bean
public JtaTransactionManager transactionManager() {
return new JtaTransactionManager();
}
}
// Лучше outbox + отдельный publisher
Ищем Java-разработчика в команду онлайн-рекомендаций AI VK 🤖
Будем вместе разрабатывать высоконагруженные микросервисы на Java. Кроме кода доверим коллеге принимать архитектурные и технические решения, гибко настраивать ML-эксперименты и рекомендательный пайплайн.
Если любите технически сложные задачи и хотите работать с большими данными, ждём ваше резюме на сайте VK Team!
Сегодня покажу вам интересный костыль, который до сих пор живёт в JDK и влияет на сортировку списков.
🧠 Если ты когда-нибудь пробовал сортировать LinkedList через Collections.sort()
, знай — под капотом происходит скрытая магия.
📌 Collections.sort(list)
сначала проверяет, какой это список. Если это RandomAccess-list (например, ArrayList) — используется быстрый сортировщик (TimSort) прямо по массиву.
⚠️ Но если это LinkedList (или любой не-рандом-доступ список), внутри происходит... преобразование списка в массив! Сначала все элементы копируются в массив, потом сортируются, а затем результат обратно заливается в твой список.
// Да, это реально работает так:
List<Integer> list = new LinkedList<>();
// ...заполняем...
Collections.sort(list);
Двоичная Java: CDS, CRaC и AOT для ускорения запуска и прогрева JVM
В этой статье вы узнаете о повышении перфоманса Java-приложений. Разберём:
🔹Как работает HotSpot.
🔹Процессы исполнения байт-кода и в чём же основные проблемы.
🔹Технологии для повышения перформанса: CDS, CRaC и AOT.
🔹Два мира сборки приложений: статический и динамический.
Поговорим о двух болях, которые есть в Java-приложениях — это время запуска и время достижения пиковой производительности.
https://habr.com/ru/companies/axiomjdk/articles/911568/
👉@BookJava
🔥 Хардкорный тест для разработчиков, тимлидов и архитекторов!
💻 Ответьте на 11 вопросов и узнайте, достаточно ли у вас знаний, чтобы пройти онлайн-курс «Software Architect» в OTUS по спец.цене.
🦾 Курс поможет прокачать весь арсенал навыков, необходимых архитектору ПО.
❇️ Пройти тест - https://vk.cc/cMj0Ns
💣 Знание продвинутых техник построения архитектуры — это топ-компетенции для программистов в 2025 году. За 4 месяца обучения вы изучите тактики работы с атрибутами качества и архитектурные решения, а также узнаете, как проектировать архитектуру мобильных приложений, микросервисов, баз данных и ML архитектуру пайплайнов.
🎁 Для получения спец.цены используйте промокод, который дает скидку на обучение - SoftwareArc_06
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
🧠 Проверяешь аргументы вручную? Используй мощь системы типов Java!
📌 Ситуация: часто в методах видишь явные проверки вроде:
public void process(User user) {
if (user == null || user.getName() == null) {
throw new IllegalArgumentException("User или его имя не могут быть null");
}
// код обработки...
}
import java.util.Objects;
public void process(User user) {
Objects.requireNonNull(user, "User не должен быть null");
Objects.requireNonNull(user.getName(), "Имя пользователя не должно быть null");
// код обработки...
}
import lombok.NonNull;
public void process(@NonNull User user) {
// Lombok автоматически вставит проверку на null
}
📌 Обход коллекций в Java: делаем правильно!
Часто вижу, как разработчики по привычке используют старый подход с циклом for
. Сегодня напомню оптимальные способы перебора коллекций в Java.
🧠 1. Foreach (Java 5+)
Коротко, ясно, подходит для большинства задач:
for (String item : collection) {
// обработка элемента
}
collection.forEach(item -> {
// обработка элемента
});
collection.stream()
.filter(Objects::nonNull)
.map(String::toUpperCase)
.forEach(System.out::println);
collection.forEach()
.📌 Stream API: забудьте про peek()
Метод 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()
.Ищете эффективные инструменты для создания 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
🧠 Ленивая инициализация через 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()
.AtomicReference
и updateAndGet
, но это уже длиннее и менее выразительно.@Lazy
. Но вне Spring, в обычных Java-приложениях или утилитах — Supplier
с memoize()
идеален.Что такое механизм try-with-resources?
Механизм 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 будет закрыт автоматически, даже если произойдёт исключение
BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
try (reader) {
System.out.println(reader.readLine());
}