TGViewer
Channel Public Channel
Библиотека джависта | Java, Spring, Maven, Hibernate

Библиотека джависта | Java, Spring, Maven, Hibernate

@javaproglib

Все самое полезное для Java-разработчика в одном канале.

Учиться у нас: clc.to/AATM8w

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

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

РКН: https://gosuslugi.ru/snet/67a5bbda1b17b35b6c1a55c4
Subscribers
22K
Photos
2.5K
Videos
57
Links
3.7K

Showing posts older than #7448 · Back to latest

Older Posts 20 shown
Post #7444 2.89K
☕️ Java-хак: не используй Optional.get() напрямую

Классическая ошибка, которую порой делают даже опытные разработчики:
Optional<User> user = userRepository.findById(id);
return user.get(); // 💀 NoSuchElementException если пусто


Правильно — используй альтернативы:
// Дефолтное значение
user.orElse(User.guest())

// Ленивое создание дефолта (если создание дорогое)
user.orElseGet(() -> userService.createDefault())

// Своё исключение с контекстом
user.orElseThrow(() -> new UserNotFoundException(id))

// Выполнить действие только если есть значение
user.ifPresent(u -> eventBus.publish(new UserLoadedEvent(u)))


Optional.get() — это тот же NPE, только под другим соусом. Optional нужен именно для того, чтобы явно обработать отсутствие значения.

Если пишешь get(), спроси себя: а зачем тут вообще Optional?

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

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

#CoreJava
  • 👍 12
  • ❤ 2
  • 🔥 1
Post #7443 2.64K
✏️ Spring Proxying и self-invocation

Spring AOP использует прокси. То есть вместо настоящего бина в контейнер помещается его обёртка. Эта обёртка перехватывает вызовы и применяет к ним различные действия, такие как логирование, управление транзакциями, проверка безопасности или другие, заданные пользователем.

Способ создания такой обёртки выбирается автоматически, но у разных способов есть свои особенности и ограничения.

🔵 JDK Dynamic Proxy vs CGLIB

JDK proxy работает только с интерфейсами. Он создаёт объект, который реализует тот же интерфейс, и все вызовы идут через специальный обработчик (InvocationHandler). Внутри есть оригинальный объект, но снаружи виден только прокси.

CGLIB работает по-другому: он создаёт подкласс вашего класса прямо во время работы программы и переопределяет методы, вставляя в них перехват. Поэтому CGLIB не может работать с final-методами и классами, т.к. подкласс не может переопределить final-метод.

Начиная с Spring Boot 2.0, CGLIB используется по умолчанию даже для классов с интерфейсами (spring.aop.proxy-target-class=true). Раньше, если был интерфейс, использовался JDK proxy.

🔵 Self-invocation

Тут всё становится не так очевидно. Разберём такой код:

@Service
public class OrderService {

public void placeOrder() {
// какая-то логика
sendNotification();
}

@Async
public void sendNotification() {
// должно выполняться асинхронно
}
}


sendNotification() помечен аннотацией @Async, но при вызове из placeOrder() он будет выполняться синхронно. Потому что placeOrder() вызывает this.sendNotification(), то есть напрямую метод оригинального объекта, минуя прокси. Перехватывать нечего, аспект не срабатывает.

То же самое с @Transactional, @Cacheable, любым AOP-аспектом.

🔵 Как это исправить

1. Самое простое и правильное решение — вынести метод в отдельный бин. Тогда вызов пойдёт через прокси другого бина, и перехват сработает.

2. Можно получить прокси самого себя через контекст:

@Service
public class OrderService {

@Autowired
private ApplicationContext context;

public void placeOrder() {
context.getBean(OrderService.class).sendNotification();
}

@Async
public void sendNotification() { ... }
}


3. Использовать AopContext.currentProxy():
((OrderService) AopContext.currentProxy()).sendNotification();


Для этого потребуется @EnableAspectJAutoProxy(exposeProxy = true). Выглядит не очень красиво, но иногда оправдано, если рефакторинг слишком сложен.

4. Перейти от Spring AOP к AspectJ с compile-time или load-time weaving. Тогда аспекты вшиваются прямо в байткод, прокси не нужен, и self-invocation работает как надо. Но это уже совсем другая история, требующая более сложной настройки.

🔵 Почему это важно

Ошибки, связанные с self-invocation, не всегда легко воспроизвести. В тестах, где бин заменяется моком, поведение может быть другим. А в интеграционных тестах с поднятым контекстом – проявляются.

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

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

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

#CoreJava
  • 👍 15
  • ❤ 6
  • 🔥 3
Post #7442 2.84K
⚠️ Одна строка кода заблокировала 102 потока в проде

Вводные: сервис на 800 RPS, SLA соблюдается, никаких предупреждений нет. Но в это время 102 потока молча стоят в очереди к одному локу.

Причина – вызов DatatypeFactory.newInstance(). На первый взгляд безобидно, но внутри он запускает ServiceLoader, который обращается к URLClassPath.getLoader(), а этот метод синхронизирован. Каждый раз при вызове, с каждым запросом. Миллионы раз в час.

Решение – всего одна строка статической инициализации final.

Как это было обнаружено, почему ClassLoader работает именно так, и как кэш на Caffeine усугубил проблему, можно прочитать в статье на Хабре.

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

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

#CoreJava
  • 👍 8
  • 🔥 1
  • 👏 1
Post #7440 2.71K
За год мы провели три потока курса по ИИ-агентам, а теперь запускаем масштабное обновление!

В новом, четвёртом потоке мы учли все пожелания студентов, добавили большой блок про AgentOps и сместили фокус с базовых концепций на суровый инжиниринг. Прикрутить API к Java-бэкенду легко, а вот сделать систему масштабируемой, со строгим контролем затрат и интеграцией в Enterprise без галлюцинаций — задача со звёздочкой.

В программе:

— стабильные интеграции по протоколу MCP и мультиагентные паттерны;
— оркестрация в LangGraph: human-in-the-loop и runbook для диагностики;
— продвинутый RAG для промышленной эксплуатации;
— контроль экономики агентов: детальное управление ресурсами и маршрутизация;
— развёртывание опенсорс-моделей в закрытых контурах по 152-ФЗ.

В честь старта продаж действует спецпредложение: 3 курса по цене 1 (два дополнительных курса в подарок).

Доступ к материалам для предварительной подготовки откроется сразу после оплаты.

По промокоду Agent забирайте скидку 10 000 ₽ (89 000 ₽ вместо 99 000 ₽). Успейте занять место до 28 февраля!

👉 Присоединиться к четвёртому потоку и внедрить агентов в Enterprise
Post #7438 2.75K
🔧 Caffeine: локальный кеш когда Redis избыточен

Для кеширования результатов тяжёлых вычислений или редко меняющихся справочников Redis часто избыточен. Caffeine — высокопроизводительный in-process кеш с Window TinyLFU eviction policy.

🔹 Решение

▪️ Зависимость

<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>


▪️ Конфигурация через Spring Cache

spring:
cache:
type: caffeine
caffeine:
spec: maximumSize=1000,expireAfterWrite=10m
cache:
cache-names:
- currencies
- countryDictionary
- featureFlags


▪️ Программная конфигурация с разными политиками

@Configuration
@EnableCaching
public class CaffeineConfig {

@Bean
public CacheManager cacheManager() {
CaffeineCacheManager manager = new CaffeineCacheManager();
manager.setCacheLoader(caffeineCacheLoader());

// разные настройки для разных кешей
manager.registerCustomCache("currencies",
buildCache(500, Duration.ofHours(1)));

manager.registerCustomCache("featureFlags",
buildCache(100, Duration.ofMinutes(5)));

// дефолт для остальных
manager.setCaffeine(Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.recordStats()); // метрики

return manager;
}

private Cache<Object, Object> buildCache(int size, Duration ttl) {
return Caffeine.newBuilder()
.maximumSize(size)
.expireAfterWrite(ttl)
.refreshAfterWrite(ttl.dividedBy(2)) // фоновое обновление
.recordStats()
.build();
}
}


▪️ Метрики через Micrometer

@Bean
public CacheMetricsRegistrar cacheMetricsRegistrar(MeterRegistry registry,
CacheManager cacheManager) {
return new CacheMetricsRegistrar(registry, List.of(cacheManager));
}


Даёт метрики: cache.gets, cache.puts, cache.evictions, cache.hits, hit rate.

refreshAfterWrite — ключевая настройка для production. В отличие от expireAfterWrite, он не удаляет запись по истечении TTL, а помечает её для фонового обновления. Следующий запрос получит устаревшее значение мгновенно, пока фоновый поток обновляет кеш. Латентность не растёт при рефреше.

⚠️ Caffeine — in-process кеш. При горизонтальном масштабировании каждый инстанс имеет свой кеш. Инвалидация при изменении данных требует либо короткого TTL, либо внешнего механизма (Redis pub/sub, Kafka event). Для справочников с редкими изменениями короткого TTL обычно достаточно.

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

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

#Enterprise
  • 👍 5
  • ❤ 3
  • 🔥 1
Post #7435 2.43K
📌 False Sharing и Object Memory Layout в Java

Если производительность внезапно падает, хотя два потока работают с разными частями одного объекта, скорее всего, проблема в так называемом false sharing.

Вот как это работает: процессор не берет данные из памяти побайтово. Он работает с cache line, обычно это 64 байта. Если два ядра процессора держат одну и ту же cache line в своих кэшах первого уровня (L1) и хотя бы одно ядро в нее пишет, запускается протокол когерентности (MESI). Этот протокол делает копию cache line недействительной в другом ядре. И это происходит, даже если ядра пишут в разные поля объекта, но эти поля физически находятся в одной cache line.

🔹 Как объекты лежат в памяти

JVM (HotSpot) раскладывает объект примерно так:
[mark word — 8 байт]
[klass pointer — 4/8 байт]
[поля по убыванию размера: long/double → int/float → short/char → byte/boolean → ссылки]
[padding до выравнивания на 8 байт]


Порядок полей в исходнике не соответствует порядку в памяти. JVM перегруппировывает их для минимизации выравнивания. Проверить реальный layout можно через JOL:

javaSystem.out.println(ClassLayout.parseClass(MyClass.class).toPrintable());

Для класса с `long`, `int` и `boolean` JOL покажет что-то вроде:

OFFSET SIZE TYPE
0 4 (object header: mark)
4 4 (object header: mark)
8 4 (object header: class)
12 4 int value
16 8 long counter
24 1 boolean flag
25 7 (alignment/padding)


Итого 32 байта — меньше cache line. Два таких объекта рядом в массиве легко попадут в одну линию.

🔹 Классический сценарий

class Counters {
volatile long a;
volatile long b;
}


Поля a и b занимают по 8 байт. Заголовок объекта – примерно 12-16 байт. Вероятно, оба поля попадают в одну 64-байтную строку кэша. Если два потока независимо увеличивают a и b, они постоянно будут делать строку недействительной друг для друга. На многоядерной системе это легко может привести к падению производительности в 5-10 раз по сравнению с независимыми счетчиками.

Решение – добавление отступов (padding). До Java 8 это делали руками, добавляя фиктивные поля:

class PaddedCounter {
volatile long value;
long p1, p2, p3, p4, p5, p6, p7; // 56 байт padding
}


Уродливо и ненадёжно. Плюс JIT мог выкинуть «мёртвые» поля.

С Java 8 появилась аннотация @Contended (пакет jdk.internal.vm.annotation):

@Contended
volatile long a;
@Contended
volatile long b;


В JVM автоматически добавляется padding в 128 байт вокруг помеченного поля. Это сделано для защиты от prefetcher'а (размер двойной cache line). Чтобы это работало вне классов JDK, нужно использовать опцию -XX:-RestrictContended.

Аннотация @Contended стоит на полях внутри Thread (например, для thread-local random), ForkJoinPool и Striped64, который является базовым классом для LongAdder и LongAccumulator.

🔹 LongAdder как способ решения проблемы

LongAdder показывает, как можно решить проблему не просто добавлением отступов, а с помощью особого подхода к проектированию. Вместо одного volatile long используется массив Cell[], где у каждой ячейки стоит @Contended. Каждый поток пишет в свою ячейку, итоговое значение получается через sum(). При большой нагрузке это работает намного быстрее, чем AtomicLong.

За это приходится платить: sum() не является атомарной операцией. Если нужна полная точность в момент чтения, LongAdder не подойдет.

🔹 О массивах примитивных типов

Тут есть свои нюансы с long[]. Элементы располагаются один за другим, без каких-либо дополнительных данных между ними. Восемь элементов long занимают ровно одну cache line. Если несколько потоков одновременно пишут в соседние ячейки, false sharing практически гарантирован. Именно поэтому в Striped64 используется массив объектов Cell[], а не long[]: каждый объект с @Contended изолирован от других.

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

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

#CoreJava
  • 👍 12
  • 🔥 7
  • ❤ 4
  • 👏 1
Post #7434 2.63K
🖥 Параллельная обработка Kafka сообщений с гарантией at-least-once в условиях медленного внешнего сервиса

Если один запрос зависает, batch встаёт колом. Простое увеличение max.poll.records не решит проблему, если медленные ответы — это постоянное явление.

В статье описывается, как заменить пакетную синхронизацию на трёхэтапную модель: подготовка, обработка, постобработка – через специальный wait-топик. Коммит офсета происходит сразу, сохраняя гарантию at-least-once, и даже 8-часовая недоступность Postgres не сорвёт обработку.

В итоге получили 46 TPS против 170 TPS с теми же настройками. Код, конфиги Kafka Connect и результаты тестов прилагаются.

🔗 Подробнее в статье

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

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

#Enterprise
  • 👍 7
  • ❤ 2
  • 🔥 1
Post #7433 2.74K
🐳 Магия Docker CLI

Забываете, сколько места занимают контейнеры, образы и тома Docker? Не хотите каждый раз лезть в df или гадать? Просто запустите docker system df, и у вас будет вся картина перед глазами.

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

— Показывает сколько места занимают образы, контейнеры, тома и билд-кэш.
— Видно реальный размер и сколько можно освободить прямо сейчас.
— Незаменимо, когда диск неожиданно кончается на проде или CI/CD агенте.

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

— Краткая сводка: docker system df
— Подробный разбор по каждому объекту: docker system df -v
— Можно комбинировать с docker system prune, чтобы сразу почистить лишнее.
— Удобно добавить в cron или мониторинг, чтобы отслеживать рост потребления.

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

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

#Enterprise
  • 👍 6
  • 🔥 2
  • ❤ 1
Post #7431 3.11K
🖥 Визуализатор кода

Прямо в браузере можно посмотреть, что происходит под капотом кода: как создаются и взаимодействуют объекты, как данные перемещаются в памяти, и как работает стек вызовов. Всё это можно увидеть пошагово.

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

⛓ Ссылка на сервис

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

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

#Enterprise
  • 👍 6
  • 🔥 3
  • ❤ 1
Post #7430 2.75K
☕ AI-агенты в Enterprise: от прототипов на коленке к надёжным JVM-системам

Для Java-разработчика важна стабильность и предсказуемость. В новом курсе по AI-агентам мы учим строить системы, которые не «галлюцинируют» в продакшене, а работают по строгим инженерным метрикам и соблюдают законы РФ.

📚 В обновлённой программе:

— управляемый инжиниринг: трассировка ошибок, логирование и контроль производительности;
— правовое поле: развёртывание AI-решений с учётом 152-ФЗ и всей документации;
— LangGraph промышленного уровня: интеграция human-in-the-loop в бизнес-процессы;
— продвинутый RAG: работа с корпоративными данными, таблицами и сканами.

Доступ к вводным материалам открывается сразу после покупки — начните погружение в архитектуру агентов 2026 года.

⏳ Специальные условия до 28 февраля:

— введите промокод Agent для получения скидки 10 000 рублей**;
— участвуйте в **акции «3 курса по цене 1» — выберите два любых курса в дополнение к основному.

👉 Получить доступ к курсу и подаркам
Post #7429 2.45K

Forwarded from Библиотека собеса по Java | вопросы с собеседований

✔️ SQL-тест: Топ категорий Google Search

Задача из реального интервью в Google 👇

📦 Задание

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

Таблицы на картинке. Напишите запрос для подсчета общего количества поисков в каждой категории по месяцам за 2024 год.

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

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

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

#practise
  • 🔥 15
  • ❤ 1
  • 👍 1
Post #7428 2.58K
💥 Разрушим частый миф

Мне НЕ нужно разбираться с управлением памятью в Java, так как за меня всё делает GC


Garbage Collector (GC) действительно очищает неиспользуемые объекты, которые больше не имеют активных ссылок. Благодаря этому в Java не нужно вручную освобождать память, как в C++.

Несколько предпосылок к исходному тезису:

🔹 В других языках, где ручное управление памятью (C, C++), утечки очевидны — если забыл free(), память навсегда потеряна.
🔹 В Java GC работает автоматически, поэтому кажется, что он решает все проблемы сам.
🔹 "Ну раз GC есть, значит, про память можно не думать!" — типичная ошибка.

GC удаляет только те объекты, которые больше не имеют активных ссылок. Если же объект остаётся доступным, но фактически не используется, он будет занимать память до завершения работы приложения.

🔽 Несколько случаев утечек памяти

1️⃣ Статические коллекции (заполняем, но не чистим)

Если создать static List и постоянно добавлять в него объекты, они никогда не будут удалены GC, потому что статические поля живут весь срок жизни приложения.

public class MemoryLeak {
private static final List<byte[]> cache = new ArrayList<>();

public static void main(String[] args) {
while (true) {
cache.add(new byte[10 * 1024 * 1024]);
System.out.println("Добавили 10MB в кеш. Используемая память: " +
(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024 * 1024) + "MB");
}
}
}


Через пару минут — OutOfMemoryError

2️⃣ Потоковые переменные (ThreadLocal)

Объекты, хранящиеся в ThreadLocal, привязываются к потоку, а в пуле потоков они могут жить дольше, чем нужно.

public class ThreadLocalLeak {
private static final ThreadLocal<byte[]> threadLocalData = new ThreadLocal<>();

public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);

for (int i = 0; i < 10; i++) {
executor.execute(() -> {
threadLocalData.set(new byte[10 * 1024 * 1024]); // 10MB на поток
System.out.println("Память занята потоком!");
});
}

executor.shutdown();
}
}


Поток завершится, а память останется занята, потому что ThreadLocal не очищается автоматически.

3️⃣ Внутренние классы и "утекшие" ссылки

Если анонимный класс или лямбда-ссылка ссылается на внешний объект, она может мешать GC очистить его.

public class InnerClassLeak {
private String data = "Очень важные данные";

public void createAnonymousClass() {
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("Используем: " + data);
}
};
new Thread(task).start();
}
}


task ссылается на data, даже если InnerClassLeak больше не используется → GC не очистит объект.

👎 Миф разрушен. GC не всемогущий и даже с ним придётся изучать, как работать с памятью в Java.

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

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

#CoreJava
  • 👍 17
  • ❤ 5
  • 🔥 3
Post #7427 2.78K
ИИ-агенты в Enterprise: как подружить LLM и суровый продакшн

Интегрировать нейросеть в корпоративный бэкенд на Java — это не просто дёрнуть API. Бизнес требует предсказуемости, отказоустойчивости и строгих лимитов. Агенты не должны ложиться от кривых данных или сливать бюджет на бесконечные ретраи.

Мы полностью обновили курс по разработке AI-агентов. Теперь фокус на enterprise-подходе: логирование, трассировка, изоляция доменов и предсказуемое поведение систем.

Что в инженерном фокусе нового потока:

— промышленный RAG: извлечение данных из legacy-баз, сканов и сложных таблиц;
— архитектура агентов: внедрение human-in-the-loop для аппрува критичных транзакций;
— ресурсоёмкость: жёсткий контроль кэша, токенов и переключение режимов обработки;
— интеграции: учим агентов взаимодействовать с легаси-интерфейсами и браузерами;
— комплаенс: развёртывание решений с полным соблюдением 152-ФЗ.

Используйте промокод Agent до 28 февраля, чтобы получить скидку 10 000 рублей.

Выбирайте акцию «3 курса по цене 1», чтобы забрать два дополнительных курса в подарок.

Внедрить AI в Enterprise
Older posts →
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →