Библиотека джависта | Java, Spring, Maven, Hibernate
22.5K subscribers
2.36K photos
52 videos
48 files
3.44K links
Все самое полезное для Java-разработчика в одном канале.

Список наших каналов: https://t.me/proglibrary/9197

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

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

РКН: https://gosuslugi.ru/snet/67a5bbda1b17b35b6c1a55c4
Download Telegram
👑 IntelliJ IDEA: Evaluate Expression

Открывается через Run → Evaluate Expression или Alt + F8 (Option + F8 на macOS).

🔹 Что это вообще такое


Небольшое окно с полем ввода, в котором можно написать любое Java-выражение и мгновенно получить результат. Код выполняется в контексте текущего фрейма — видны все локальные переменные, this, поля класса.

🔹 Примеры из реальной жизни


▪️ Вызов метода на живом объекте

userService.findById(42L)

Не нужно писать тест — просто вызываете метод и смотрите, что он реально вернёт с текущими данными в БД.

▪️ Проверка сложного условия
order.getItems().stream()
.anyMatch(i -> i.getPrice().compareTo(BigDecimal.ZERO) < 0)

Подозреваете, что в заказе есть товары с отрицательной ценой? Проверяете прямо сейчас, не добавляя логов.

▪️ Изменение переменной на лету

user.setRole(Role.ADMIN)

Хотите проверить, как поведёт себя код с другим значением — меняете стейт прямо в памяти и продолжаете выполнение. Приложение не перезапускается.

▪️ Десериализация или парсинг

objectMapper.writeValueAsString(response)

Получаете красивый JSON из объекта прямо в дебаггере — удобно проверить, что именно уйдёт клиенту.

🔹 Продвинутые трюки

— В окне Evaluate есть переключатель с однострочного режима на Code Fragment — там можно писать многострочный код с переменными, условиями и циклами.

— Результат вычисления отображается как обычная переменная в дереве дебаггера. Можно раскрыть, посмотреть вложенные поля, скопировать значение.

⚠️ Evaluate выполняет код реально — если вызовете метод с side-effect (запись в БД, отправка события), это произойдёт по-настоящему.

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

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍114👏1
✔️ Java-тест: утечка в фильтре

Memory leak, который живёт неделями и не воспроизводится локально 👇

📦 Задание — code review


Написали фильтр для аудита запросов. В продакшне через несколько дней — OutOfMemoryError.

@Component
public class AuditFilter implements Filter {

private static final ThreadLocal<AuditContext> CONTEXT =
new ThreadLocal<>();

@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {

AuditContext ctx = new AuditContext(
((HttpServletRequest) request).getRequestURI(),
System.currentTimeMillis()
);
CONTEXT.set(ctx);

try {
chain.doFilter(request, response);
} finally {
auditLog(CONTEXT.get());
}
}

public static AuditContext current() {
return CONTEXT.get();
}

private void auditLog(AuditContext ctx) {
// пишем в БД...
}
}


▪️ Объясни

— Почему объекты в ThreadLocal не собираются GC, даже если запрос завершён.
— Какая одна строчка кода здесь отсутствует, и где именно она должна быть.

* Есть ли риски при использовании ThreadLocal в virtual threads (Project Loom)?

Ставьте → 🔥, если нравится формат. Если нет → 🌚

💬 Решения под спойлер. Сравним, какое будет лучше.

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

#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12👍21
🧵 GC: как выбрать сборщик мусора и не пожалеть

Выбирал ли ты GC хоть раз осознанно? Часто запустил приложение — работает, и ладно. Но когда начинаются проблемы с латентностью или throughput, первый вопрос от SRE: «А какой у вас GC?».

🔹 Разберём что выбрать и почему.

Все GC балансируют между двумя вещами:

▪️ Throughput — сколько полезной работы делает приложение за единицу времени
▪️ Latency — насколько долго приложение паузится на GC

Идеального нет. Выбор всегда зависит от профиля нагрузки.

🔹 Serial и Parallel — если латентность не важна

-XX:+UseSerialGC / -XX:+UseParallelGC


Serial GC однопоточный. И Minor, и Major GC выполняет один поток, всё приложение стоит. Звучит как антиквариат, но на маленьком heap (< 256MB) и однопоточной среде накладные расходы на координацию потоков не нужны, Serial реально быстрее.

Parallel GC — то же самое, но сборку делают несколько потоков одновременно. STW никуда не девается, просто сама пауза короче за счёт параллелизма. На Major GC всё равно можешь получить сотни миллисекунд.

Где уместно: batch-обработка, утилиты, CLI-инструменты. Там где важен максимальный throughput и никто не ждёт response time.

🔹 G1 — дефолт с Java 9

-XX:+UseG1GC


G1 делит heap на регионы фиксированного размера (~1-32MB) и собирает их инкрементально. Не нужно собирать весь heap за раз — выбираются регионы с наибольшим количеством мусора (отсюда "Garbage First").

Главная настройка:
bash-XX:MaxGCPauseMillis=200  # целевое время паузы


Это не гарантия, это цель. G1 будет подстраивать размер собираемых регионов, чтобы уложиться.

Где уместно: большинство серверных приложений с heap от 4GB. Хороший баланс throughput/latency без тюнинга.

🔹 ZGC и Shenandoah — когда паузы недопустимы

-XX:+UseZGC / -XX:+UseShenandoahGC


Оба делают большую часть работы конкурентно — параллельно с работой приложения. STW-паузы измеряются единицами миллисекунд вне зависимости от размера heap.

Где уместно: real-time системы, trading, API с жёсткими SLA по p99 латентности. Платишь немного throughput — получаешь предсказуемые паузы.

🔹 Как выбирать на практике

Не угадывать, а мерить. Включаешь GC логи и смотришь что происходит:
bash-Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=20m

Дальше либо GCEasy, либо руками смотришь на паузы и частоту сборок.

🔹 Три вопроса которые определяют выбор

1. Какой размер heap?
До 4GB — G1 справится. Больше 16GB — смотри на ZGC, у G1 начинаются долгие Mixed GC.

2. Какие требования к латентности?
Есть SLA на p99 < 50ms — только ZGC или Shenandoah. Нет жёстких требований — G1.

3. Какой профиль аллокаций?
Много short-lived объектов — генерационный GC справится хорошо. Много long-lived крупных объектов — G1 может давать длинные паузы на evacuation, ZGC справится лучше.

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

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍83🔥1
🖥 JDK 21 или 25, что выбрать новичку

Вышла Java 25, которую все хотят попробовать. Но ставить ли её или взять проверенную Java 21 LTS?

В этом гайде — пошаговая установка Java на Windows с картинками, разбор различий между версиями, настройка JAVA_HOME и PATH.

Ориентирован для начинающих разработчиков, изучающих Java

🔗 Читайте в статье

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

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

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍1
🎁 Паттерн Decorator

Decorator — структурный паттерн, который добавляет объектам новое поведение, оборачивая их в объекты-обёртки. Альтернатива наследованию, когда комбинаций поведения много и они меняются в рантайме.

Использование

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

Преимущества

1️⃣ Гибкость комбинирования поведений

Каждый декоратор делает одно дело. Нужно кеширование + логирование + метрики? Оборачиваете в три слоя в любом порядке. Не нужны метрики — просто не добавляете этот слой.

2️⃣ Единая ответственность

Кеширование живёт в одном классе, логирование — в другом. Каждый декоратор легко читать, тестировать и заменять независимо.

3️⃣ Прозрачность для клиента

Декоратор реализует тот же интерфейс, что и оригинал. Клиентский код не знает, с чем работает — с оригиналом или с обёрткой.

⚠️ Порядок декораторов имеет значение. Метрики снаружи кеша видят кеш-хиты корректно. Метрики внутри — не видят их вообще.

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

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

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

Java-разработчик Middle - от 200 000 до 250 000 ₽ - гибрид (Новосибирск)

Java-разработчик - от 280 000 ₽ - удалёнка

Tech Lead (Java / Spark) - 400 000 ₽ - офис (Москва)

➡️ Еще больше топовых вакансий — в нашем канале Java jobs
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍1
✌🏻 У нас две новости — хорошая и плохая!

Хорошая: Ваших знаний, скорее всего, хватит, чтобы собрать рабочую демку AI-агента в Colab. 🫡

Плохая: Вы вряд ли выведете его в прод, не обанкротившись на токенах и не слив базу. 🤯

Для защиты от таких сценариев мы полностью пересобрали курс «Разработка AI-агентов». Теперь внутри плотная работа с экономикой ресурсов, дебаг через time-travel в LangGraph, извлечение данных из кривых сканов для RAG и комплаенс по 152-ФЗ.

Если всё ещё сомневаетесь, послушайте голосовое от спикера курса Влада Прошинского, где он объясняет, как правильно тестировать агентов перед релизом.


Программа курса, полный состав спикеров и другие подробности 👈🏻

ВАЖНО! До 5 апреля на курс действует скидка, но свободные места могут закончиться раньше.
🥱4
📨 Transactional Outbox c AI

Сценарий: сохранил сущность, опубликовал событие в Kafka, транзакция откатилась — событие уже ушло. Или наоборот: транзакция прошла, Kafka недоступна — событие потеряно.

Outbox Pattern решает это на уровне архитектуры. Промпт:

Ты Senior Java-разработчик. Сгенерируй реализацию Transactional Outbox Pattern для Spring Boot.

Требования:
- Сущность OutboxEvent: id (UUID), aggregateType, aggregateId, eventType,
payload (JSONB), createdAt, processedAt, status (PENDING/PROCESSED/FAILED)
- OutboxEventPublisher: сохраняет событие в той же транзакции что и бизнес-данные
- OutboxPoller: @Scheduled, читает PENDING-события, публикует в Kafka
- SELECT FOR UPDATE SKIP LOCKED при выборке — объясни зачем в комментарии
- @Transactional(propagation = REQUIRES_NEW) на polling-методе
- Flyway-миграция для таблицы

Стек: Java 21, Spring Boot 3.2, Spring Kafka, PostgreSQL, Flyway

Верни код сущности, репозитория, сервисов, SQL-миграцию, конфиг Kafka.


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

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

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
2🥱103👍1👾1
✔️ Java-тест: 1 запрос в коде = 10 000 запросов в БД

Код чистый, тесты быстрые, на реальных данных — pg_stat_activity в огне 👇

📦 Задание — code review

Ручка возвращает список заказов с информацией о товарах. Работает корректно, но DBA прибежал с графиком: каждый вызов делает тысячи запросов к БД.

@Entity
public class Order {
@Id
private Long id;
private Long userId;
private LocalDateTime createdAt;

@OneToMany(fetch = FetchType.LAZY, mappedBy = "order")
private List<OrderItem> items;
}

@Entity
public class OrderItem {
@Id
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
private Order order;

@ManyToOne(fetch = FetchType.LAZY)
private Product product;

private Integer quantity;
}

@RestController
@RequiredArgsConstructor
public class OrderController {

private final OrderRepository orderRepository;

@GetMapping("/orders")
public List<OrderDto> getOrders(@RequestParam Long userId) {
List<Order> orders = orderRepository.findByUserId(userId);

return orders.stream()
.map(order -> new OrderDto(
order.getId(),
order.getItems().stream()
.map(item -> new ItemDto(
item.getProduct().getName(),
item.getQuantity()
))
.toList()
))
.toList();
}
}


▪️ Объясни

— Точную механику N+1: где и сколько раз запросы уходят в БД в этом коде.
— Почему FetchType.EAGER «решит» проблему, но создаст другую.
— Как можно решить проблему.

Ставьте → 🔥, если нравится формат. Если нет → 🌚

💬 Решения под спойлер. Сравним, какое будет лучше.

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

#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥205
🐳 Магия Docker CLI

Контейнер запустился, но ведёт себя странно — не то сетевое окружение, не те переменные, непонятно что вообще внутри? Просто запустите docker inspect, и у вас будет полная картина без лишних инструментов.

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

— Возвращает полный JSON с внутренностями: переменные окружения, тома, сетевые настройки, лимиты ресурсов, entrypoint, restart policy.
— Работает не только с контейнерами — понимает образы, тома и сети.
— Незаменимо, когда нужно быстро понять, почему контейнер ведёт себя не так, как ожидается.

🔹 Как использовать

— Полный дамп: docker inspect my-container
— Только IP-адрес: docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-container
— Все переменные окружения: docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' my-container
— Смонтированные тома: docker inspect -f '{{json .Mounts}}' my-container | jq

Флаг -f принимает Go-шаблоны — вытащите ровно то поле, которое нужно, без парсинга тысячи строк JSON вручную.

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

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

#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍4🔥1
🧵 Просто о сложном: Kubernetes

Не DevOps-гайд, не «как установить кластер». Именно то, с чем ты столкнёшься как разработчик когда твой сервис едет в K8s.

🔹 Что куда деплоится


Твой jar живёт в Docker-образе. Образ запускается как Pod — минимальная единица в K8s. Pod эфемерен: упал, рестартовал, переехал на другую ноду — это нормально, это не баг.

Нода — это физическая или виртуальная машина в кластере.

Кластер состоит из нескольких нод, на каждой из них K8s запускает поды. Когда нода падает или уходит на обслуживание — K8s автоматически перезапускает её поды на других нодах.

Как разработчику тебе не нужно знать на какой ноде живёт твой под, K8s сам решает это на основе requests ресурсов.

Чтобы управлять несколькими копиями пода используют Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
spec:
containers:
- name: order-service
image: order-service:1.4.2
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"


requests — сколько K8s резервирует для пода при планировании. limits — жёсткий потолок. Превысил memory limit — OOMKill. Превысил CPU limit — троттлинг.

Это напрямую влияет на JVM: именно от limits.memory нужно считать -XX:MaxRAMPercentage.

🔹 Как сервисы находят друг друга

Pod умирает и поднимается с новым IP. Поэтому обращаться к поду напрямую нельзя. Service — стабильный DNS-адрес и балансировщик поверх набора подов.

apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- port: 8080


Внутри кластера твой payment-service обращается к order-service просто по имени

K8s DNS резолвит это в нужный ClusterIP. Никаких Eureka, никакого Consul — для базового service discovery это не нужно.

🔹 K8s не знает что твой сервис готов


K8s видит что контейнер запустился. Но не знает что Spring контекст поднялся, Liquibase отработал и сервис готов принимать трафик.

livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10

readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 20
periodSeconds: 5


Readiness — сервис готов принимать трафик. Пока не прошёл, pod исключается из балансировки Service.

Liveness — сервис живой. Не прошёл несколько раз подряд, pod рестартует. Сюда не стоит вешать проверку зависимостей (БД, кафка) — если БД легла, рестарт сервиса не поможет.

Spring Boot Actuator отдаёт оба эндпоинта из коробки при management.endpoint.health.probes.enabled=true.

🔹 ConfigMap и Secret

Хардкодить урлы и пароли в application.properties внутри образа — плохая идея. Образ должен быть одинаковым для dev, staging и prod.

apiVersion: v1
kind: ConfigMap
metadata:
name: order-service-config
data:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/orders
SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:9092

---

apiVersion: v1
kind: Secret
metadata:
name: order-service-secret
data:
SPRING_DATASOURCE_PASSWORD: cGFzc3dvcmQ= # base64


В Deployment пробрасываешь как env-переменные — Spring Boot подхватывает автоматически через @Value или application.properties биндинг.

Kubernetes не требует от разработчика знания всего кластерного стека. Но понимать как твой сервис деплоится, как K8s решает готов ли он к трафику и как корректно завершает работу — это уже твоя зона ответственности, не DevOps.

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

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍62🔥1
👁 Паттерн Observer

Observer — поведенческий паттерн, который создаёт механизм подписки: одни объекты следят за событиями других и реагируют на них. Источник событий не знает, кто именно подписан и что произойдёт.

Использование

🔹 Когда изменение одного объекта должно автоматически обновлять другие — и вы не знаете заранее, сколько их.
🔹 Когда объекты должны уведомлять других без жёсткой связи между ними.
🔹 Когда набор реакций на событие может меняться в рантайме.

Преимущества

1️⃣ Слабая связанность

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

2️⃣ Динамическая подписка

Подписчики добавляются и удаляются в рантайме. Поведение системы меняется без перекомпиляции.

3️⃣ Соответствие принципу Open/Closed.

Новая реакция на событие — новый подписчик. Источник и существующие подписчики не меняются.

⚠️ Утечки памяти. Подписчик, который не отписался — не будет собран GC, пока жив источник.

В Spring: ApplicationEventPublisher + @EventListener — это Observer из коробки. Добавить @Async к слушателю и подписчик изолирован от основного потока.

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

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

#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍61🔥1