Библиотека джависта | Java, Spring, Maven, Hibernate
23.1K subscribers
2.27K photos
49 videos
46 files
3.22K links
Все самое полезное для Java-разработчика в одном канале.

Список наших каналов: https://xn--r1a.website/proglibrary/9197

Для обратной связи: @proglibrary_feeedback_bot

По рекламе: @proglib_adv

РКН: https://gosuslugi.ru/snet/67a5bbda1b17b35b6c1a55c4
Download Telegram
⚡️ Параллельные стримы: ускорение или нет?

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

Добавление parallelStream() бездумно — это не "оптимизация", а лотерея с шансом на баги и падение.

Когда не использовать

— При небольшом наборе данных (<10 000 элементов) затраты на управление потоками могут превышать прирост скорости.
— Операции sorted(), distinct() или limit() требуют полного знания данных, что снижает эффективность параллельного выполнения.
— Вложенные parallelStream() в CompletableFuture или ExecutorService могут привести к конкуренции за ресурсы и неожиданному падению производительности.

✔️ Когда использовать

— Обработка больших объёмов данных (100 000+ элементов).
— Операции независимы и ресурсоёмки, например, сложные вычисления, парсинг файлов, загрузка данных из сети.

🔍 Важная особенность

parallelStream() использует ForkJoinPool.commonPool(). Если есть другие задачи, использующие этот же пул, они могут начать конкурировать за потоки, замедляя всё приложение.

💬 Делитесь в комментах интересными кейсами

══════ Навигация ══════
ВакансииЗадачиСобесы

🐸 Библиотека джависта

#CoreJava #лучшее2025
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🔥21👏1
👑 Магия IntelliJ IDEA: покрытие тестами

Запускаешь тесты, они зелёные, и ты думаешь всё ок? А потом баг на проде в ветке, которую никто не проверил. IDEA умеет показывать, что реально покрыто тестами.

🔹 Что это

Run with Coverage — запуск тестов с визуализацией покрытия кода. Видишь прямо в редакторе, какие строки покрыты тестами, а какие нет.

🔹 Как запустить

— Ctrl+Shift+F10 → выбрать "Run with Coverage".
— Или правой кнопкой на тест/класс → "More Run/Debug" → "Run with Coverage".
— Или через иконку щита рядом с кнопкой Run.

🔹 Что показывает

→ Зелёная полоска слева от кода — строка выполнилась.
→ Красная полоска — строка не покрыта тестами.
→ Жёлтая полоска — ветка покрыта частично (if выполнился, else — нет).
→ Окно Coverage — статистика по классам, пакетам, методам.

🔹 Фишки

→ Кликни на класс в Coverage window → увидишь его code coverage.
→ Кликни на жёлтую полоску → IDEA покажет, какая именно ветка не покрыта.
→ Сортируй классы по проценту покрытия — найдёшь слабые места.
→ Экспортируй отчёт (HTML) через кнопку в Coverage панели.

🔹 Зачем это нужно


— Находишь непокрытые edge cases.
— Видишь, что тест не проверяет все ветки if/else.
— Не гадаешь, а точно знаешь, что покрыто.
— Быстро понимаешь, где добавить тесты.

══════ Навигация ══════
ВакансииЗадачиСобесы

🐸 Библиотека джависта

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🔥4👏1
Как настроить production-ready логирование в Spring Boot

Логи в проде — это не просто System.out.println(). Это структурированные данные, correlation ID, асинхронная запись и ротация. Разбираем настройку от А до Я.

1️⃣ Выбираем стек: Logback + SLF4J

Spring Boot из коробки использует Logback. Добавляем зависимости:
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.4</version>
</dependency>

ИЛИ

gradleimplementation 'net.logstash.logback:logstash-logback-encoder:7.4'


Это даст JSON-формат логов для удобного парсинга в ELK/Grafana.

2️⃣ Создаём logback-spring.xml

Кладём в src/main/resources:

<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeContext>false</includeContext>
<includeMdc>true</includeMdc>
</encoder>
</appender>

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/application.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/application-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>

<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>


Логи ротируются по размеру и времени, старые архивируются в gzip.

3️⃣ Добавляем Correlation ID

Создаём фильтр для трекинга запросов:

@Component
public class CorrelationIdFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String correlationId = request.getHeader("X-Correlation-ID");
if (correlationId == null) {
correlationId = UUID.randomUUID().toString();
}
MDC.put("correlationId", correlationId);
response.setHeader("X-Correlation-ID", correlationId);
try {
chain.doFilter(request, response);
} finally {
MDC.clear();
}
}
}


Теперь каждый лог привязан к конкретному запросу. В JSON-логе будет поле correlationId.

4️⃣ Настраиваем уровни логирования в application.yml

logging:
level:
root: INFO
com.yourcompany: DEBUG
org.springframework.web: WARN
org.hibernate.SQL: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n"


Для прода root: INFO, для дебага поднимаем до DEBUG только нужные пакеты.

5️⃣ Асинхронная запись логов

Добавляем в logback-spring.xml:

<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE"/>
<queueSize>512</queueSize>
<discardingThreshold>0</discardingThreshold>
</appender>

<root level="INFO">
<appender-ref ref="ASYNC_FILE"/>
</root>


Логи пишутся в отдельном потоке и не блокируют бизнес-логику.

6️⃣ Интеграция с мониторингом

Для отправки в Logstash/Fluentd добавляем TCP-аппендер:

<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>logstash.example.com:5000</destination>
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>


══════ Навигация ══════
ВакансииЗадачиСобесы

🐸 Библиотека джависта

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍15🔥2👏2🥱1
Что такое JIT-компилятор?

JIT (Just-In-Time) компилятор — это компонент JVM, который компилирует байт-код в машинный код непосредственно во время выполнения программы, а не до старта приложения. Его задача — улучшить производительность, оптимизируя код, исходя из реальных условий работы программы.

JIT компилирует только те части кода, которые реально исполняются, и может применять различные оптимизации для ускорения работы приложения. Это позволяет сочетать гибкость интерпретируемого байт-кода и производительность нативного кода.

🐸 Библиотека собеса по Java

#core
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥1👏1
Roadmap для Java-разработчика: интеграция ИИ-агентов в Enterprise

ИИ-агенты — это не только область Python. В корпоративной разработке на Java архитектура и надёжность агентов выходят на первый план.

Ваш путь внедрения ИИ:

— изучение принципов взаимодействия с LLM через API;

— интеграция агентских паттернов в существующие `JVM`-сервисы;

— создание систем с разделением ответственности между агентами;

— мониторинг и отладка автономных процессов.

Курс «Разработка ИИ-агентов» даст необходимые навыки для проектирования интеллектуальных надстроек над сложными системами.

Повысить квалификацию в ИИ

При регистрации до 19 января действует акция «3 в 1»: один курс покупаете, два получаете в подарок.

Дедлайн: 19 января.
😁1
🖥 Когда Kafka транзакции действительно нужны

Kafka транзакции не панацея, а узкоспециализированный инструмент.

🔹 Что гарантируют Kafka транзакции:

— Атомарную публикацию в несколько топиков (все сообщения видны или ни одно).
— Связывание read → process → write в одну операцию для консьюмеров-продюсеров.

🔹 Что НЕ гарантируют:


— Откат изменений во внешних системах (БД, API, email).
— Защиту от дубликатов, если downstream-сервисы не настроены правильно.

🔹 Два реальных сценария использования транзакций:

1️⃣ Публикация в несколько топиков без возможности дедупликации

Отправка события обработки заказа и событие в audit-топик с временной меткой. Если сервис упадёт между отправками, то при повторе время не совпадёт — история будет некорректной.

Транзакция гарантирует, что оба сообщения либо станут видимы консьюмерам одновременно, либо ни одно из них не будет доставлено.

2️⃣ Схема read → process → write без дедупликации

Если сервис упадёт после отправки сообщений, но до коммита оффсета, downstream-сервисы увидят дубликаты.

Транзакции связывают отправку и коммит в одну атомарную операцию.

💡 Ключевой вывод: большинство систем прекрасно работают без транзакций, используя дедупликацию и идемпотентность.

Транзакции добавляют накладные расходы и подходят только для специфических кейсов.

══════ Навигация ══════
ВакансииЗадачиСобесы

🐸 Библиотека джависта

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍83🔥2👏1
🔍 Просто о сложном: идемпотентность

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

Простыми словами: повторный вызов не меняет состояние системы после первого успешного выполнения.
Классический пример — HTTP методы: GET, PUT, DELETE идемпотентны, POST — нет.

🔹 Ключевые моменты

▪️ Идемпотентная операция: setStatus(ACTIVE) — сколько раз ни вызови, статус будет ACTIVE.
▪️ Неидемпотентная операция: balance += 100 — каждый вызов увеличивает баланс.
▪️ Идемпотентность ≠ отсутствие побочных эффектов (это чистота функций).
▪️ Критична для распределённых систем: retry-механизмы, очереди сообщений, API.
▪️ Защищает от дублирующих запросов при сетевых сбоях.

🔹 Под капотом

В реальных системах идемпотентность достигается через:

→ Idempotency Key — клиент генерирует уникальный ключ и передаёт в запросе. Сервер проверяет: если операция с таким ключом уже выполнялась — возвращает закешированный результат.
→ Версионирование — оптимистичные блокировки через версии записей (JPA @Version).
→ Уникальные идентификаторы — вместо "создай заказ" отправляем "создай заказ с ID=xyz". Повторный запрос с тем же ID игнорируется.
→ Статус-машины — переходы между состояниями: если уже в целевом состоянии, ничего не делаем.

🔹 Подводные камни

— Idempotency key нужно хранить ограниченное время (обычно 24 часа).
— Нужна атомарность проверки и выполнения.
— Сложность при асинхронной обработке.
— Не все бизнес-операции можно сделать идемпотентными.
— Overhead на хранение и проверку ключей.

✔️ Когда использовать

— REST API с критичными операциями (платежи, создание заказов).
— Kafka consumers — защита от повторной обработки при rebalance.
— Интеграция с внешними системами через retry.
— Scheduled jobs, которые могут запуститься дважды.
— Распределённые транзакции (Saga pattern).
— Webhook обработчики.
— Любые операции с денежными средствами.

Не нужно:

— Внутренние CRUD операции без side effects.
— Операции чтения (они идемпотентны по умолчанию).
— Высоконагруженные операции, где overhead критичен.
— Простые внутренние методы без внешних вызовов.

🔧 Бонус-трюк: в Spring можно создать аннотацию @Idempotent и реализовать через AOP с использованием Redis для хранения idempotency keys. Получается декларативная идемпотентность на уровне методов.

══════ Навигация ══════
ВакансииЗадачиСобесы

🐸 Библиотека джависта

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🔥41👏1
💻 Kafka транзакции в Spring Boot: от конфига до кода

Разберём практическую реализацию транзакций на конкретном примере.

1️⃣ Конфигурация продюсера

propertiesspring.kafka.producer.properties.enable.idempotence=true
spring.kafka.producer.properties.transactional.id=order-service-${random.value}-


2️⃣ Конфигурация консьюмера

propertiesspring.kafka.consumer.isolation-level=read_committed


3️⃣ Добавляем TransactionManager

@Bean
public KafkaTransactionManager<String, Object> kafkaTransactionManager(
ProducerFactory<String, Object> producerFactory
) {
return new KafkaTransactionManager<>(producerFactory);
}


4️⃣ Используем @Transactional

@Transactional
public void publishOrderResult(OrderRequest request) {
String orderId = UUID.randomUUID().toString();
OrderPlacedEvent event = new OrderPlacedEvent(
orderId,
request.email(),
request.productName()
);

kafkaTemplate.send("order-placed", orderId, event);
kafkaTemplate.send("order-audit", orderId, event);
// Обе отправки атомарны — либо обе успешны, либо обе откатятся
}


⚠️ Важно: если в приложении есть и Kafka и JPA TransactionManager:

Вариант 1 — пометить как @Primary:

@Primary
@Bean
public KafkaTransactionManager<String, Object> kafkaTransactionManager(...) {...}


Вариант 2 — явно указать:

@Transactional("kafkaTransactionManager")
public void publishOrderResult(...) {...}


⚠️ В большинстве систем дедупликация и идемпотентность — более простое и эффективное решение.

══════ Навигация ══════
ВакансииЗадачиСобесы

🐸 Библиотека джависта

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥3👏1
🔥 Field vs Constructor Injection: зомби-объекты в production

Казалось бы, избитая тема. Но копните глубже @Autowired — и обнаружите, что ваши объекты после new существуют в состоянии "клинической смерти".

Автор разбирает не "удобство тестов" (это мелочи), а фундаментальные проблемы: нарушение контракта конструктора, проблемы с JMM, хрупкость при AOT-компиляции для GraalVM, race conditions в мультипоточке.

Рекомендую прочитать, если хотите понять почему Spring Framework 6 окончательно выбрал constructor injection, а не просто "так все делают".

🔗 Читайте подробнее

══════ Навигация ══════
ВакансииЗадачиСобесы

🐸 Библиотека джависта

#coreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥5👏2
😮 Топ-вакансий для джавистов за неделю

Backend Engineer (Java/Kotlin) — от 600 до 3 500 $ — офис (Минск)

Middle Java-разработчик — 240 000 - 260 000₽ — удалёнка

Java Developer — от 5 000 $ — удалёнка

➡️ Еще больше топовых вакансий — в нашем канале Java jobs
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥2👏1
«Этот манёвр будет стоить нам 51 год...»

— или 20% от вашего бюджета на самообразование. В Java-мире архитектурные паттерны и алгоритмы — это то, что отличает мидла от сеньора. Не ждите особого случая, забирайте базу прямо сейчас.

Успейте купить курсы Proglib Academy по старой цене до понедельника:

— Разработка ИИ-агентов
— Математика для разработки AI-моделей
— ML для старта в Data Science
— Математика для Data Science
— Специалист по ИИ
— Алгоритмы и структуры данных
— Программирование на Python
— Основы IT для непрограммистов
— Архитектуры и шаблоны проектирования

Взять по старой цене

⚠️ Цены вырастут 19 января
Structured Concurrency API — кто-то юзает в проде?

Наш подписчик спрашивает:

Перешли на Java 21, Virtual Threads зашли отлично. Теперь смотрю на Structured Concurrency API (JEP 462, пока в preview).
У нас классика: один запрос → параллельно дергаем несколько микросервисов → агрегируем ответ. Structured Concurrency пока preview feature. Стоит ли уже переходить или рано?


💬 Кто-то пробовал в реальных проектах? Какие впечатления?

P.S. Если хотите задать вопрос, заполните нашу гугл-форму. Это займет 5 минут.

🐸 Библиотека джависта

#DevLife
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🤔21🔥1