bookjava | Unsorted

Telegram-канал bookjava - Библиотека Java разработчика

10986

📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP

Subscribe to a channel

Библиотека Java разработчика

🧠 Коллекторы и toList() в Java 16+: можно ли заменить collect(Collectors.toList()) на просто .toList()?

Да, но с нюансами.

📌 Короткий ответ:
Если ты используешь Java 16+, можешь заменить:


List<String> list = stream.collect(Collectors.toList());


на:


List<String> list = stream.toList();


💡 Но будь осторожен. Вот 3 ключевых отличия:


1️⃣ Немодифицируемость

* .toList() возвращает немодифицируемый список (immutable).
* Collectors.toList() возвращает modifiable ArrayList.


var list1 = List.of("a", "b");
var list2 = list1.stream().toList();
list2.add("c"); // 💥 UnsupportedOperationException

var list3 = list1.stream().collect(Collectors.toList());
list3.add("c"); // ✅ OK



2️⃣ Тип возвращаемого списка

* Collectors.toList() — это ArrayList (или его сабкласс).
* .toList() — это неопределённый тип внутри JDK (часто List.of под капотом).

Если ты делаешь что-то вроде:


if (list instanceof ArrayList) ...


то поведение может измениться.


3️⃣ Параллельные стримы

.toList() оптимизирован для параллельных стримов: может работать быстрее, но также может повлиять на порядок, если ты этого явно не контролируешь.


⚠️ Когда НЕ стоит заменять:

* Если ты мутируешь список после получения.
* Если ты полагаешься на конкретный тип (например, ArrayList).
* Если ты используешь Java < 16.


Когда заменить можно:

* Если тебе нужен read-only список.
* Если ты не изменяешь коллекцию.
* Если важна сжатость и выразительность.


📌 Резюме:

Заменяй collect(Collectors.toList()) на .toList(), только если тебе действительно не нужен изменяемый список. Это безопасно при соблюдении условий, но может привести к неожиданным багам в тестах и проде, если забыть про неизменяемость.


👉@BookJava

Читать полностью…

Библиотека Java разработчика

🧠 Record (Java 16+) + pattern matching для instanceof (Java 14+) в Java 17+ позволяют писать лаконичный и безопасный код:

📌 Запись DTO с валидацией через компактный конструктор:


public record User(String name, String email) {
public User {
Objects.requireNonNull(name, "name не должен быть null");
if (!email.contains("@")) {
throw new IllegalArgumentException("Неверный email: " + email);
}
}
}


– автоматические toString(), equals(), hashCode() без лишнего кода.

🧠 Проверка и приведение типов в одном выражении:


Object obj = …;
if (obj instanceof User u) {
System.out.println("Привет, " + u.name());
}


– нет лишних кастов, код чище и безопаснее.

💡 Совет: для полей — коллекций или массивов — избегайте поверхностной мутабельности:


public record Team(String name, List<String> members) {
public Team {
members = List.copyOf(members);
}
}


– таким образом members нельзя изменить извне.

⚠️ Антипаттерн: не используйте public record X(List<String> list) без копирования — рискуете нарушить неизменяемость!

👉@BookJava

Читать полностью…

Библиотека Java разработчика

👩‍💻 JPQL: как писать запросы, которые не сломают Hibernate

Узнайте, как писать JPQL-запросы, которые ускорят Hibernate в 5 раз, избегая критических ошибок, тормозящих 80% проектов!

Приглашаем на открытый урок

🗓 19 июня в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Java Developer. Professional».

🎯 О чём поговорим:
✔️- JPQL vs SQL: почему ваши запросы ломают Hibernate и как их переписать так, чтобы БД не «умирала» под нагрузкой.
✔️ Тайные ловушки: антипаттерны JPQL, генерирующие N+1 SELECT и тормозящие приложение, и методы их поиска в коде.
✔️ Оптимизация на максимум: как использовать JOIN FETCH, подзапросы и кэширование в JPQL для мгновенного ускорения Hibernate.

👥 Кому будет интересно:
Java-разработчикам, использующим Hibernate, системным архитекторам и инженерам по оптимизации производительности.

💡В результате урока вы:
Научитесь писать эффективные JPQL-запросы, избегать распространённых ошибок и значительно ускорять работу Hibernate-приложений.

🎁 Дарим промокод, который дает скидку на обучение - JAVA_06

🔗 Ссылка на регистрацию: https://vk.cc/cMKvog

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

Читать полностью…

Библиотека Java разработчика

⁉️ Монолит или микросервисы? Руководство для архитекторов, которые ценят свои нервы

Приглашаем на открытый урок.

🗓 17 июня в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Software Architect».

📌 Что будет на вебинаре:
✔️ Как не попасть в ловушку “модных” микросервисов;
✔️ Разбор признаков, что пора выходить из монолита;
✔️ Архитектурные паттерны для перехода к микросервисам (Strangler Fig, BFF, Self-contained systems);
✔️ Организационные и технические риски — что точно пойдёт не так и как это предсказать;
✔️ Роль DevOps, CI/CD и мониторинга в выборе архитектуры.

👥 Для кого этот вебинар:
- Разработчиков Backend и FullStack, участвующих в архитектурных решениях;
- Архитекторов ПО, которые планируют масштабирование приложений;
- Тимлидов и DevOps-инженеров, выстраивающих процесс разработки и доставки;
- Технических менеджеров, выбирающих стратегию развития продукта.

🎯 После вебинара вы:
- Получите пошаговое руководство по выбору архитектуры под ваш проект;
- Научитесь оценивать реальные риски и стоимость микросервисов;
- Поймёте, как внедрять архитектурные изменения без сбоев и хаоса;
- Увидите, как принимать взвешенные архитектурные решения, сохраняя технический контроль и производительность команды.

💡 Идеальный вебинар для тех, кто хочет перестать "архитектурить на ощущениях" и начать действовать стратегически.

🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - SoftwareArc_06

👉 Регистрация на вебинар: https://vk.cc/cMHSzz

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

Читать полностью…

Библиотека Java разработчика

⚡️ Быстрые альтернативы HashMap: EnumMap, массивы и примитивные коллекции


🧠 EnumMap вместо HashMap (enum-ключи)
📌 EnumMap<K, V> хранит данные в массиве → нет хеширования и boxing’а.


EnumMap<Status, String> map = new EnumMap<>(Status.class);
map.put(Status.STARTED, "Запущен");
String status = map.get(Status.STARTED);


⚠️ Работает только для enum-ключей.


🧠 Прямой массив для плотных int-ключей
📌 Используйте массив вместо Map, если ключи — диапазон [0…MAX] и известны заранее.


int MAX = 1000;
var cache = new String[MAX + 1];
cache[42] = "ответ";
String result = cache[42];


💡 O(1), без коллизий и аллокаций объектов.
⚠️ Память линейно зависит от MAX.


🧠 Специализированные коллекции для примитивов
📌 Библиотеки fastutil, HPPC, Trove и др.


var fastMap = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<String>();
fastMap.put(42, "ответ");
String res = fastMap.get(42);


💡 Избегаете boxing/unboxing и получаете быстрее операции.


🧠 Switch-expression / tableswitch
📌 Для фиксированного набора целых или enum-ключей генерируйте switch, а не Map.


String handle(int code) {
return switch (code) {
case 100 -> "OK";
case 200 -> "Created";
default -> "Other";
};
}


💡 JIT компилирует switch в tableswitch или lookupswitch — молниеносно.


💡 Выбирайте стратегию под конкретную задачу:

- EnumMap — enum-ключи
- Массив — плотные int-диапазоны
- fastutil/HPPC — примитивы с большим диапазоном
- Switch — фиксированный набор значений

👉@BookJava

Читать полностью…

Библиотека Java разработчика

🚀 Spring WebFlux с Server-Sent Events 🚀

Улучшите свои приложения в режиме реального времени с помощью #SpringWebFlux и Server-Sent Events! 🔥

👉@BookJava

Читать полностью…

Библиотека Java разработчика

🎯 Java-хаки: динамический вывод с помощью printf()

Статья посвящена методу printf() в Java, который используется для создания форматированной строки вывода. В ней рассматривается синтаксис метода, различные спецификаторы формата (например, %d, %f, %s и т.д.) и то, как с их помощью управлять отображением чисел, строк и других типов данных в консоли.

Приведены примеры использования System.out.printf() с пояснениями по флагам, ширинe и точности форматирования, а также показано, как легко создавать динамический и читаемый вывод в приложениях на Java.

https://springframework.guru/java-output-printf-method/

👉@BookJava

Читать полностью…

Библиотека Java разработчика

Как масштабировать машинные модели и работать с огромными объемами данных? Откройте для себя возможности Spark ML на открытом уроке от OTUS!

Spark ML — это мощный инструмент для масштабируемого машинного обучения, который позволяет обучать модели на больших данных, не переходя на специализированные ML-системы. Мы покажем, как интеграция с Spark SQL и DataFrame API упрощает ETL-подготовку данных и фичуризацию для реальных проектов.

Убедитесь, как Spark ML решает задачи отказоустойчивости и распределённых вычислений, позволяя вам легко строить промышленные ML-пайплайны.

Посетите открытый урок 11 июня в 20:00 МСК в преддверие старта курса «Spark Developer» и получите скидку на обучение: https://vk.cc/cMyLJ3

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

Читать полностью…

Библиотека Java разработчика

👩‍💻 Java — один из самых востребованных языков, но не каждый разработчик умеет использовать его возможности по максимуму.

На курсе «Java Developer. Professional» вы научитесь создавать современные Java-приложения, освоите Spring WebFlux и Kafka, а также разберётесь в работе JVM изнутри.

Пройдите тест, проверьте, достаточно ли у вас знаний для обучения на курсе:.

🎁 Дарим промокод, который дает скидку на обучение - JAVA_06

На курсе вас ждёт практическая работа с кодом, детальные разборы, ревью от экспертов и подходы, позволяющие писать эффективный и чистый код.

Начните свой путь к уровню Middle+ и используйте Java на 100%.

➡️ Пройти вступительный тест курса: https://vk.cc/cMxZwr

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

Читать полностью…

Библиотека Java разработчика

Optional.stream() появился в Java 9 и позволяет превратить Optional<T> в Stream<T> длины 0 или 1. Зачем это нужно?

📌 Когда применять?

◾️ 🧠 Интеграция с цепочками Stream API: если у вас есть коллекция Optional<T>, можно собрать все непустые значения без дополнительных проверок:


List<Optional<User>> userOptionals = …;
List<User> users = userOptionals.stream()
.flatMap(Optional::stream) // из каждого Optional либо 1 элемент, либо пусто
.collect(Collectors.toList());


Без Optional.stream() пришлось бы делать что-то вроде filter(Optional::isPresent).map(Optional::get).

◾️ 🧠 При операциях над вложенными опционалами: когда в потоке у вас Optional<Something> и вы хотите «сливать», а не оставлять пустые обёртки.

💡 Совет:

Если вы строите конвейер обработки данных, а на каком-то шаге может не быть значения — Optional.stream() поможет аккуратно пропустить «пустышки» и не ломать последующие операции.


⚠️ Но вот в чём «подводные камни» и почему нельзя злоупотреблять:

1. Потеря явности

◾️ Когда вы где-то просто хотите проверить: есть ли значение в Optional, — использование stream() создаёт впечатление, что у вас реально коллекция элементов, хотя всего лишь 1 или 0. Для простых случаев ifPresent(), map(), orElse() читается понятнее.


// Менее канонично:
optionalValue.stream().forEach(v -> doSomething(v));
// Лучше:
optionalValue.ifPresent(v -> doSomething(v));


2. Ненужные накладные расходы

◾️ Каждый вызов Optional.stream() создаёт объект стрима и небольшую внутреннюю структуру, что на горячем участке кода (в tight loop) может сказаться на производительности. Если вместо него можно обойтись map().orElse(), задумайтесь о легковесном варианте.

3. Скрытые баги

◾️ Если вы по ошибке используете Optional.stream() в одиночном случае (не в контексте объединения множества опционалов), код может стать менее очевидным. Например:


// Что тут происходит?
Stream.of(opt1, opt2, opt3)
.flatMap(Optional::stream)
.findFirst();


Казалось бы, надо искать первый непустой, но читающий код может не сразу понять логику: а вдруг нужно просто взять любое значение, а не первой в списке? Лушче явно:


Optional<User> result = opt1.isPresent() ? opt1
: opt2.isPresent() ? opt2
: opt3;


4. Лишняя сложность

◾️ В ситуациях, когда Optional появляется из map/filter в одном стриме, а потом вы вновь оборачиваете результат в Optional, лучше сразу строить последовательность через flatMap и filter без промежуточных Optional.

💡 Пример «полезного» применения:


List<Order> orders = getOrders();

// Для каждого заказа пытаемся получить пользователя из БД,
// но он может быть не найден (Optional<User>).
List<Optional<User>> maybeUsers = orders.stream()
.map(o -> userRepository.findById(o.getUserId()))
.toList();

// Теперь формируем список уже «существующих» юзеров:
List<User> users = maybeUsers.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());


Здесь Optional.stream() полностью оправдан: сразу избавляемся от «пустых» опционалов.

💡 Анти-паттерн:

Не используйте Optional.stream() внутри метода, который ожидает ровно одно значение или бросает исключение, если опционал пуст.



// Плохо:
User user = optionalUser.stream()
.findFirst()
.orElseThrow(() -> new NotFoundException("User not found"));

// Лучше так:
User user = optionalUser
.orElseThrow(() -> new NotFoundException("User not found"));


В первом случае мы заводим стрим без смысла, во втором — прямой и понятный код.

Итого:

◾️ 🧠 Используйте Optional.stream() только когда действительно нужно объединить несколько Optional-ов в один Stream и пропустить пустые.
◾️ ⚠️ В одиночных сценариях проверки и извлечения значения он избыточен и даже снижает читабельность и производительность.

👉@BookJava

Читать полностью…

Библиотека Java разработчика

🧠 Конфигурация Spring Boot 3 через record и @ConstructorBinding

Вместо традиционных @Data + пустого конструктора можно сразу использовать Java 17 record для настройки свойств:

📌 Почему это полезно?

🔴Полная иммутабельность: поля конфигов больше нельзя случайно перезаписать.
🔴Минимум «шаблонного» кода: не нужны геттеры, сеттеры, toString(), equals() и т.д.
🔴Чёткая связь с Java 17+ и актуальными best practices.

💡 Как сделать:

1. Подключаем зависимость:


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>


Это нужно, чтобы IDE и Spring метаинфу подхватили.

2. Создаём record с аннотацией:


import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;

@ConstructorBinding
@ConfigurationProperties(prefix = "app.mail")
public record MailProperties(
String host,
int port,
String username,
String password
) {}


3. Регистрируем бин в Spring:


import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(MailProperties.class)
public class AppConfig { }


4. Конфигурируем в application.yml (или .properties):


app:
mail:
host: smtp.example.com
port: 587
username: user@example.com
password: secret123


⚠️ Обратите внимание:

🔴Без @ConstructorBinding Spring не сможет смотать значения в record’ы.
🔴Уберите все сеттеры и по умолчанию конструктор генерируется автоматически.
🔴Если вам нужна валидация свойств, добавьте @Validated и JSR-303 аннотации (@NotNull, @Min и т.д.).

🧠 Что получилось?

🔴Минимум «мусора» в коде: один блок record заменил класс с 4 полями, геттерами и конструктором.
🔴Полная типобезопасность и поддержка автокомплита при обращении к полям.
🔴Быстрый переход на Java 17+ подходы без потери функциональности.

💡 Дополнительный лайфхак:
Если вам нужно разделить конфиги по окружениям (dev/prod), просто создайте два record’а с разными префиксами или используйте @Profile. В Spring Boot 3 этот подход «из коробки» работает наилучшим образом.

👉@BookJava

Читать полностью…

Библиотека Java разработчика

В асинхронных или веб-сервисах на реактиве нужно быть осторожным: SecurityContext не “переходит” автоматически в новые потоки. Для этого используют SecurityContextRepository и специальные методы в WebFlux.

⚠️ Важно: не храните SecurityContext в сессии, если у вас stateless-приложение (REST API). Вместо сессии используйте JWT или OAuth 2.0.

🧠 5. Авторизация: FilterSecurityInterceptor & AccessDecisionManager
FilterSecurityInterceptor запускается в конце цепочки фильтров и проверяет доступ к URL. Он запрашивает у SecurityMetadataSource список необходимых ролей для данного эндпоинта (Spring на основании @PreAuthorize, HttpSecurity конфигурации или XML). Затем передаёт дело в AccessDecisionManager (по умолчанию AffirmativeBased), который опрашивает список AccessDecisionVoter (например, RoleVoter для проверок ролей, WebExpressionVoter для SpEL).


FilterSecurityInterceptor
└─> SecurityMetadataSource (что нужно: ROLE_ADMIN)
└─> AccessDecisionManager.vote()
├─ RoleVoter.vote() → совпадает?
└─ WebExpressionVoter.vote() → SpEL-выражения?


Если хотя бы один голос “grant”, AffirmativeBased отпускает запрос (по умолчанию). Можно менять стратегию на Consensus или Unanimous.

💡 Трюк: чтобы локально протестировать SecurityContext, можно в тестах использовать аннотацию @WithMockUser(roles = "ADMIN") и проверять, что нужный эндпоинт доступен.

🧠 6. Аннотации & Method Security
Помимо URL-уровня, есть методная проверка:


@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’ов, но проверяют уже на методах сервиса.
* 💡 Совет: включайте методную безопасность только там, где действительно нужна тонкая грануляция.

🧠 7. Хранение паролей & PasswordEncoder
С Java 17+ используйте PasswordEncoder с алгоритмами Argon2 или BCrypt:


@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}


⚠️ Никогда не храните пароли в открытом виде и не используйте MD5/SHA-1 — они считаются небезопасными.

🧠 8. Stateless vs Stateful

* Stateful (Сессии): Spring создаёт HTTP-сессию, а SecurityContextPersistenceFilter хранит контекст в сессии. Удобно для монолитов с классическим web-приложением.
* Stateless (JWT/OAuth2): убираем SessionCreationPolicy.STATELESS, используем BearerTokenAuthenticationFilter, аутентификация и авторизация проверяются по JWT в каждом запросе.

💡 Современный стек (Spring Boot 3+):

1. Настраиваем SecurityFilterChain с oauth2ResourceServer().jwt().
2. Подключаем spring-boot-starter-oauth2-resource-server.
3. Указываем issuer-uri или jwk-set-uri в application.yml.


spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://keycloak.example.com/realms/myrealm


🧠 9. Подпись запросов & CSRF

* По умолчанию CSRF включён для “форменных” запросов (POST, PUT, DELETE). Для stateless-API его обычно отключают:


http.csrf().disable();

* Если используете формы, не забудьте добавить в шаблон:


<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>


⚠️ Внимание: не отключайте CSRF, если ваше приложение использует сессии и cookie на фронтенде!

💡 Совет по отладке: включите логирование фильтров:


logging.level.org.springframework.security=DEBUG


Тогда в логах вы увидите, как проходит запрос через каждый фильтр и где происходит отказ.

👉@BookJava

Читать полностью…

Библиотека Java разработчика

«Я слышу свой код»: как работает Java-программист, потерявший зрение

Константин Евтеев собирает Java-код с помощью диктора NVDA, редактирует его в Блокноте и передает на Linux по SSH через самописные bash-скрипты. После потери зрения он не потерял интереса к жизни и желания быть полезным и выстроил собственную инженерную экосистему: оглавления по строкам .txt-файлами, навигация по main и маленьким методам, отладка на слух.

https://habr.com/ru/companies/axiomjdk/articles/913748/

👉@BookJava

Читать полностью…

Библиотека Java разработчика

🚀 Подборка 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 разработчика

🗑 Понимание различных сборщиков мусора в Java

🔵 Serial Garbage Collector: Лучший вариант для однопоточных приложений с небольшими кучами. Он использует один поток для выполнения как малых, так и больших сборок мусора, что приводит к значительным паузам, но минимальной нагрузке на систему.

🔵 Parallel Garbage Collector: Подходит для приложений с высокими требованиями к пропускной способности. Использует несколько потоков для выполнения как малых, так и больших сборок мусора, уменьшая время пауз, но при этом увеличивая использование CPU.

🔵 Concurrent Mark-Sweep (CMS) Garbage Collector: Разработан для минимизации пауз за счёт выполнения основной части работы по сборке мусора параллельно с выполнением приложений. Подходит для приложений, где критически важна низкая задержка.

🔵 G1 Garbage Collector: Сбалансированный сборщик мусора, который стремится обеспечить предсказуемое время пауз, разделяя кучу на регионы и выполняя сборку мусора поэтапно. Является хорошим выбором по умолчанию для большинства приложений.

🔵 Z Garbage Collector и Shenandoah: Сборщики мусора с ультранизкой задержкой, разработанные для работы с большими кучами. Основная часть работы по сборке мусора выполняется параллельно, что позволяет минимизировать время пауз даже при очень больших кучах.

👉@BookJava

Читать полностью…

Библиотека Java разработчика

VK Weekend Offer: отправьте заявку, пройдите интервью и получите офер!

28–29 июня VK проведёт Weekend Offer для бэкендеров с опытом от трёх лет. Участников со знанием Java, Go, Python или C++ ждут технические собеседования, знакомство с продуктами и, если всё сложится, офер уже в конце выходных.

Ребята много лет создают облачные решения, системы рекомендаций и поисковые движки — всё с миллионами пользователей в проде — и сейчас ищут новых коллег. Поэтому оставляйте заявку до 25 июня, чтобы попасть в команду за выходные!

Подробности — на сайте.

Читать полностью…

Библиотека Java разработчика

🧠 JPA Batch Insert: ускоряем и защищаем от OOM

📌 Настройка Hibernate
Добавьте в application.yml или properties:


spring:
jpa:
properties:
hibernate.jdbc.batch_size: 50 # размер пакета
hibernate.order_inserts: true # группировка INSERT’ов
hibernate.order_updates: true # группировка UPDATE’ов


Это позволит драйверу посылать пачками, а Hibernate — сортировать операции для максимальной эффективности.

💡 Сниппет для batch-пакетов


@Service
@RequiredArgsConstructor
public class OrderService {
private final EntityManager em;
private static final int BATCH_SIZE = 50;

@Transactional
public void saveAll(List<Order> orders) {
for (int i = 0; i < orders.size(); i++) {
em.persist(orders.get(i));
if (i > 0 && i % BATCH_SIZE == 0) {
em.flush();
em.clear(); // освобождаем persistence-context
}
}
em.flush();
em.clear();
}
}


flush() выталкивает пакеты в БД,
clear() освобождает ОЗУ от управляемых сущностей.

⚠️ Важные моменты

* GenerationType.IDENTITY отключает batching. Используйте @SequenceGenerator с allocationSize.
* При двусторонних связях (OneToMany) избегайте каскадного сохранения огромных графов — лучше сохранять “плоско” и затем связывать.
* Следите за JDBC-драйвером: не все поддерживают batch-вставки одинаково хорошо.

💡 Совет по мониторингу
Запустите приложение с -Dorg.hibernate.SQL=DEBUG и -Dhibernate.format_sql=true — вы увидите групповые INSERT вместо множества одиночных.

📌 Результат

* Скорость записи растёт в 5–10× (в зависимости от нагрузки).
* Память на стороне приложения остаётся стабильной, без роста Persistence Context.

👉@BookJava

Читать полностью…

Библиотека Java разработчика

📌 CRaC (Coordinated Restore at Checkpoint) — горячая JVM-фишка для сверхбыстрого cold-start: сохраняем состояние приложения после инициализации и моментально «восстанавливаем» при рестарте.

🧠 Как это работает

1. JVM создаёт снимок (checkpoint) всего heap- и native-состояния сразу после bootstrap и bean-инициализации.
2. При рестарте JVM грузит этот снимок вместо полной загрузки классов и прогрева JIT.

💡 Подключение в Java 21+

1. Включите экспериментальный модуль:


--add-modules jdk.crac
--enable-preview

2. Реализуйте CheckpointListener для чистки и восстановления ресурсов:


import jdk.crac.Core;
import jdk.crac.Control;
import jdk.crac.CheckpointListener;
import jdk.crac.Context;
import org.springframework.stereotype.Component;

@Component
public class CracHandler implements CheckpointListener {
@Override
public void beforeCheckpoint(Context<?> ctx) {
// 📌 Закрываем пулы, Flush в БД, отписываемся от очередей
}
@Override
public void afterRestore(Context<?> ctx) {
// 💡 Реинициализируем пулы, повторная регистрация listeners
}
}
// Регистрация слушателя
Core.getGlobalContext().register(new CracHandler());

3. Сборка и запуск:


# Сохраняем checkpoint
java \
--add-modules jdk.crac \
--enable-preview \
-XX:CRaCCheckpointToDir=crac-checkpoint \
-jar app.jar

# Восстанавливаем из него
java \
--add-modules jdk.crac \
--enable-preview \
-XX:CRaCRestoreFrom=crac-checkpoint \
-jar app.jar


⚠️ Ограничения и нюансы

* Не все native-библиотеки безопасны для снапшота.
* Тяжёлые background-потоки: до checkpoint лучше останавливать.
* Проверяйте на staging-окружении — subtle bugs могут всплыть только после restore.

📌 Зачем это нужно?

* 🚀 Ускоренный cold-start для Spring Boot 3+ сервисов (лучшая DevOps-интеграция в контейнерах и serverless).
* 💰 Экономия ресурсов в автоскейлируемых кластерах.

Простой CRaC-proof-of-concept позволит вам измерить прирост старта ваших микросервисов уже сегодня!

👉@BookJava

Читать полностью…

Библиотека Java разработчика

📌 Stream.toList() vs Collectors.toList() — безопасная замена?

🧠 В Java 16+ появился метод Stream.toList(), который собирает элементы потока в список. Раньше мы писали:


List<String> list = stream.collect(Collectors.toList());


Теперь можно укоротить до:


List<String> list = stream.toList();


💡 Главные отличия:

1️⃣ Неизменяемость
toList() возвращает unmodifiable List — любые add()/remove() вылетят UnsupportedOperationException.
Если нужен изменяемый список, продолжайте использовать collect(Collectors.toList()) или


stream.collect(Collectors.toCollection(ArrayList::new));


2️⃣ Null-элементы
toList() не допускает null и бросит NPE при встрече null в потоке. Collectors.toList() сохранит null без ошибок.

3️⃣ Спецификация
Stream.toList() гарантированно создаёт новый список с точным размером, а Collectors.toList() лишь «может» вернуть любой List (часто ArrayList, но без чётких гарантий).

⚠️ Если вам важна мутабельность или поддержка nullне меняйте на toList().

Если же нужен чистый readonly-список и вы уверены в отсутствии nullсмело переходите на toList() для более лаконичного и потенциально более эффективного кода.

Я перехожу на toList() везде, где нужен только чтение — получилось короче и понятнее.

👉@BookJava

Читать полностью…

Библиотека Java разработчика

Test Driven Development (TDD) in Java

Creating a Queue Abstract Data Type class
Introduction to Test Doubles Dummies and Stubs
Introduction to Test Doubles Spies
Introduction to Test Doubles Mocks
Introduction to Test Doubles - Fakes

источник

👉@BookJava

Читать полностью…

Библиотека Java разработчика

📚 Эффективное сжатие текста: код Хаффмана в действии

Приглашаем на открытый урок.

🗓 11 июня в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Алгоритмы и структуры данных».

На этом вебинаре мы продолжим разработку архиватора, реализовав код Хаффмана.

✔️ Рассмотрим, как построить дерево кодов, где частота появления символов определяет их битовое представление.
✔️ Интегрируем алгоритм в наш архиватор и проведем сравнительное тестирование с RLE.
✔️ Увидим, как эффективно работает код Хаффмана на текстовых файлах и других типах данных.

Отличная возможность изучить продвинутые древовидные структуры данных на практическом примере.

Развивайте алгоритмическое мышление, увеличивайте производительность программ.

🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - Algo5

👉 Регистрация на вебинар: https://vk.cc/cMzyUv

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

Читать полностью…

Библиотека Java разработчика

Структурированное логирование в Spring Boot 3.5.5

Spring Boot 3.5.5 приносит улучшенное структурированное логирование.
Чтобы его включить, добавьте следующее в ваш application.yml:

Это обеспечивает более чистые, структурированные логи, что делает их проще для разбора инструментами вроде ELK, Grafana или Datadog.

👉@BookJava

Читать полностью…

Библиотека Java разработчика

Bulkhead — это паттерн из мира устойчивых систем, цель которого — изолировать сбои в одном компоненте, чтобы они не “затопили” всю систему. Сейчас я покажу вам несколько способов его реализации и подсвечу неочевидный момент при работе с любыми паттернами.

🧠 Концепция: представьте корабль с отсековыми переборками (bulkheads). Если вода просачивается в один отсек, остальные остаются сухими, и судно всё ещё может плыть. В мире Java/Spring это означает: ограничивать ресурсы (пулы потоков, соединения, очереди) для каждого сервиса/метода, чтобы при пике нагрузки или ошибках нагрузка не разошлась по всей системе.

📌 Способ 1: отдельные пул-экзекьюторы


@Configuration
public class BulkheadConfig {
@Bean("serviceAExecutor")
public ThreadPoolTaskExecutor serviceAExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("svcA-");
executor.initialize();
return executor;
}
}


В коде контроллера или сервиса указываем:


@Service
public class ServiceA {
@Autowired @Qualifier("serviceAExecutor")
private Executor executor;

public CompletableFuture<String> callExternal() {
return CompletableFuture.supplyAsync(() -> {
// долгий/ненадежный вызов
return externalClient.fetchData();
}, executor);
}
}


⚠️ Помните: если пул заполнится, новые задачи будут либо ждать (до исчерпания queueCapacity), либо бросать RejectionException. Настройте RejectedExecutionHandler по необходимости.

📌 Способ 2: Resilience4j Bulkhead (Semaphore vs ThreadPool)


resilience4j.bulkhead.instances:
myServiceBulkhead:
maxConcurrentCalls: 5
maxWaitDuration: 100ms


В сервисе:


@Service
public class MyService {
private final Bulkhead bulkhead;

public MyService(BulkheadRegistry registry) {
this.bulkhead = registry.bulkhead("myServiceBulkhead");
}

public String process() {
return Bulkhead.decorateSupplier(bulkhead, () -> {
// защищенный вызов
return externalClient.process();
}).get();
}
}


💡 Если поставить maxConcurrentCalls слишком маленьким, часть запросов будет сразу отвергаться с BulkheadFullException. Неочевидный момент: нужно мониторить реальную нагрузку и подбирать значения, а не копировать из гугла.

👉 Также есть аннотационный стиль:


@Bulkhead(name = "myServiceBulkhead", type = Bulkhead.Type.SEMAPHORE)
public String annotatedProcess() { … }


или ThreadPool-вариант:


resilience4j.bulkhead.instances:
myThreadPoolBulkhead:
maxThreadPoolSize: 10
queueCapacity: 20



@Bulkhead(name = "myThreadPoolBulkhead", type = Bulkhead.Type.THREADPOOL)
public CompletionStage<String> asyncProcess() { … }


🧠 Неочевидный момент про паттерны в целом: внедрять Bulkhead “просто потому что модно” — плохо. Паттерн не заменяет мониторинг, трассировку или грамотную архитектуру. Он лишь ограничивает повреждения, но не показывает, где именно проблема. Если вы изолировали компонент в пул, а он всё равно падает, паттерн не скажет “почему”. Всегда сочетайте паттерны с метриками (Micrometer, Prometheus, Grafana) и логированием.

💡 Совет:

▫️Используйте отдельные пулы для медленных операций (например, внешних HTTP-вызовов) и отдельно для CPU-bound задач.
▫️На уровне базы данных тоже можно “бульхедом” выделять разные пулы соединений (например, HikariCP с разными конфигурациями) для тяжелых и легких запросов.
▫️При проектировании микросервисов отдавайте предпочтение Bulkhead на уровне отдельных сервисов: в Kubernetes это можно делать через limits/requests, Horizontal Pod Autoscaling и Circuit Breaker.

⚠️ Предупреждение: перебор с изоляцией приведет к недоиспользованию ресурсов. Если у вас слишком много мелких пулов, а нагрузка неравномерна, часть ресурсов простаивает. Поэтому сначала измерьте нагрузку, а потом разбивайте.

👉@BookJava

Читать полностью…

Библиотека Java разработчика

30 лет Java: от провалившегося гаджета до фундамента разработки ПО

Некоторые языки программирования, например Rust, Go и TypeScript, считаются крутыми. Другие, в том числе Cobol и Java, «скучны». Однако пусть Java, которому 23 мая этого года исполнилось тридцать лет, может, и не самый захватывающий язык, он остаётся одним из самых важных.

Путь Java начался 23 мая 1995 года, когда его выпустила компания Sun Microsystems. За прошедшее время благодаря удачному видению разработчиков и адаптивности он превратился из нишевого проекта для потребительской электроники в мощный фундамент энтерпрайз-, облачной и веб-разработки.

Хоть Java исполнилось тридцать, его история гораздо дольше. Корнями этот язык уходит в 1991 год, когда инженеры Sun Джеймс Гослинг, Майк Шеридан и Патрик Ноутон приступили к созданию языка для интерактивного телевидения и встроенных устройств. Этот проект назвали Green Project. Его цель заключалась не столько в написании нового языка, сколько в создании того, что бы мы сегодня назвали контроллером Интернета вещей. Ещё один разработчик Java Тим Линдхольм, описал его как «своего рода гибрид между КПК и универсальным пультом дистанционного управления».

https://habr.com/ru/articles/914970/

original https://www.zdnet.com/article/java-at-30-how-a-language-designed-for-a-failed-gadget-became-a-global-powerhouse/

👉@BookJava

Читать полностью…

Библиотека Java разработчика

🔍Тестовое собеседование с Java-разработчиком из Т1 Иннотех уже завтра

4 июня(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика.

Как это будет:
📂 Илья Аров, старший разработчик в Т1, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Илья будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Илье

Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.

Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_sh_bot

Реклама. ООО "ШОРТКАТ", ИНН: 9731139396, erid: 2VtzqvL6bDw

Читать полностью…

Библиотека Java разработчика

Релиз через два дня. Код готов. Почти...
Остались тесты. Ну, точнее — покрытие. Потому что QA уже дышит в затылок, а ты сидишь и выбираешь: спать или корпеть до утра.
Explyt Test умеет создавать тесты под твой код — сам. Быстро. В IDE. Без плясок.
Хочешь, чтобы релиз прошёл, а не пролетел? Попробуй бесплатно! 👉 explyt.ai

Читать полностью…

Библиотека Java разработчика

📌 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();
}


Таким образом вы сами управляете порядком фильтров и включаете только нужные.

🧠 2. AuthenticationManager & ProviderManager
Когда UsernamePasswordAuthenticationFilter (или другой аутентификатор) получает учётные данные, он создает UsernamePasswordAuthenticationToken с неверифицированными (unauthenticated) флагом. Затем передаёт этот токен в AuthenticationManager:


UsernamePasswordAuthenticationFilter → AuthenticationManager.authenticate()


AuthenticationManager по умолчанию — это ProviderManager, который хранит список AuthenticationProvider (например, DaoAuthenticationProvider для UserDetailsService или JwtAuthenticationProvider для токенов). Каждый Provider пытается аутентифицировать токен, и если успешно, возвращает уже аутентифицированный Authentication с authorities.

📌 Совет: если нужно добавить кастомную проверку (например, MFA), реализуйте свой AuthenticationProvider и зарегистрируйте его перед DaoAuthenticationProvider.

🧠 3. UserDetailsService & UserDetails
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();
}
}


🧠 4. SecurityContext & SecurityContextHolder
После успешной аутентификации фильтр устанавливает SecurityContext в SecurityContextHolder. По умолчанию используется стратегия MODE_THREADLOCAL, т.е. контекст привязан к текущему потоку.


SecurityContextHolder.getContext().setAuthentication(authenticatedToken);

Читать полностью…

Библиотека Java разработчика

🧠 Чем Spring Native Image отличается от обычного Spring-приложения?

📌 Обычный Spring:

* Запускается на JVM, динамически загружает классы, использует рефлексию.
* Медленный старт (секунды), выше потребление памяти.
* Подходит для сложной логики с динамическим поведением (настройки, рефлексия, прокси).

📌 Spring Native Image (GraalVM):

* Компиляция в нативный бинарник.
* ⚡️ Мгновенный старт (миллисекунды), низкое потребление памяти.
* Отсутствие динамики: ограничения в рефлексии, прокси и динамической загрузке.

💡 Когда что использовать?

* Native Image — идеален для микросервисов и Serverless-приложений.
* Обычный JVM Spring — когда важна максимальная гибкость и динамика.

⚠️ Помни, что Native Image требует больше усилий по настройке и ограничений на библиотеки.

👉@BookJava

Читать полностью…

Библиотека Java разработчика

Как не завалить приложение из-за N+1 запросов в Hibernate 🧨

N+1 проблема — больной зуб почти любого Java-разработчика. Hibernate делает жизнь проще, пока не сталкиваешься с этим 👻

📌 Суть проблемы
Допустим, у тебя есть сущности User и связанные с ними сущности Order. При запросе пользователей:


List<User> users = userRepository.findAll(); // Один запрос
for(User user : users) {
List<Order> orders = user.getOrders(); // +1 запрос на каждого пользователя!
}


Если пользователей 1000, то будет 1 + 1000 SQL-запросов. Это убийственно для производительности ⚠️

🛠 Как решить?

💡 Вариант 1: JOIN FETCH
Самый быстрый и простой путь — сразу подтянуть связанные сущности:


@Query("SELECT u FROM User u JOIN FETCH u.orders")
List<User> findAllWithOrders();


✅ Всего один SQL-запрос
❌ Может привести к дублированию строк, если связей много и они сложные

💡 Вариант 2: Entity Graph (Spring Data JPA)
Entity Graph — более гибкий подход:


@EntityGraph(attributePaths = {"orders"})
List<User> findAll();


✅ Удобно и понятно, нет дублей
✅ Можно легко настраивать под конкретный запрос

💡 Вариант 3: Batch fetching (настройка Hibernate)


spring.jpa.properties.hibernate.default_batch_fetch_size=20


Hibernate будет автоматически загружать связанные сущности пачками.

✅ Просто настроить и сразу эффект
❌ Всё равно несколько запросов, хотя и пачками

🧠 Когда что использовать?

▫️ JOIN FETCH — когда связи простые, а данных немного.

▫️ Entity Graph — если нужна гибкость и удобство.

▫️ Batch fetching — когда настроить проще, чем переписывать код.

Не забывай проверять SQL-запросы в логах и профилировать приложение 🔥

👉@BookJava

Читать полностью…

Библиотека Java разработчика

Серия статей: «Дюк, вынеси мусор!»
Все, что нужно знать о сборке мусора в 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

Читать полностью…
Subscribe to a channel