TGViewer
Channel Public Channel
Java Portal | Программирование

Java Portal | Программирование

@java_iibrary

Присоединяйтесь к нашему каналу и погрузитесь в мир для Java-разработчика

Связь: @devmangx

РКН: https://clck.ru/3H4WUg
Subscribers
11.6K
Photos
1.6K
Videos
118
Links
1.6K

Showing posts older than #1959 · Back to latest

Older Posts 20 shown
Post #1957 2.83K
Совет по Spring Boot

RestTestClient для тестирования API

RestTestClient это единый инструмент для тестирования REST API, который можно использовать вместо WebTestClient, TestRestTemplate (удалён) или MockMVC.

1. Определи объект RestTestClient в тесте
2. Привяжи его к конкретному компоненту. Это может быть controller, MockMVC, server или Spring context
3. Потом используй RestTestClient в тесте

@SpringBootTest
public class PersonControllerWithHeadersTests {

private WebApplicationContext context;
private RestTestClient restTestClient;

@BeforeEach
public void setup(WebApplicationContext context) {
restTestClient = RestTestClient
.bindToApplicationContext(context)
.build();
}

@Test
void addPersonTest() {
restTestClient.post()
.uri("/persons")
.body(Instancio.create(Person.class))
.exchange()
.expectStatus().is2xxSuccessful()
.expectBody(Person.class)
.value(p -> assertNotNull(p.getId()));
}
}


👉 Java Portal
  • 👍 10
  • 🔥 3
Post #1956 2.6K
Всегда есть шанс, что один и тот же запрос прилетит в ваш API или сервер несколько раз.

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

Это решается идемпотентностью.

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

Например, в платежных системах это обязательное свойство.

Это разница между стабильной системой и системой, которая может уронить бизнес.

Представим эндпоинт /payment/charge.

Если пользователь дважды жмёт кнопку оплатить, приложение не должно интерпретировать это как две транзакции.

По сути решение простое:

клиент отправляет idempotency-key, а сервер гарантирует, что операция будет выполнена только один раз.
Но правильная реализация важна.

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

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

Без идемпотентности API ведет себя непредсказуемо.

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

👉 Java Portal
  • 👍 12
  • ❤ 4
Post #1955 3K
Java 21. Виртуальные потоки + структурированная конкуренция в 15 строках:

Запускай 100000+ конкурентных задач почти без оверхеда с виртуальными потоками.

Структурированная конкуренция делает async-код читаемым как синхронный, без утечек и callback-адского.
В реальных проектах даёт 10-кратный рост пропускной способности при гораздо более простом коде.

👉 Java Portal
  • 👍 8
  • ❤ 5
  • 😁 1
Post #1954 2.88K
Spring Boot: Spring Data JPA имеет встроенную поддержку пагинации через Pageable.

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

Вместо такого репозитория:

public interface BookRepository extends JpaRepository<Book, Long> {
List<Book> findAll();
}


Лучше так:

public interface BookRepository extends JpaRepository<Book, Long> {
Page<Book> findAll(Pageable pageable);
}


И дальше в сервисном слое:

public Page<Book> getBooks(int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
return bookRepository.findAll(pageable);
}


👉 Java Portal
  • 👍 15
Post #1953 2.75K
Есть забавная вещь, которая происходит со многими Java-разработчиками. Oни пишут современный код с мышлением старой Java.

И, если честно, их сложно за это винить.

Java менялась куда быстрее, чем комьюнити успевало к этим изменениям адаптироваться.

Годами Java ассоциировалась с огромными классами, кучей геттеров и сеттеров, громоздкими фабриками, бесконечными try/catch блоками. Со стороны казалось, что язык боится любого изменения.

Java была про структуры и правила.

Это был язык принципа = делай явно или не делай вообще.

Но потом подъехали серьезные обновления: лямбды, records, pattern matching, sealed-классы, streams, более выразительный switch, выведение типов, более декларативные API, и стиль, который стал ближе к функциональному.

У всего этого есть плюсы и минусы, понятно.

И внезапно Java перестала ощущаться языком начала двухтысячных и стала языком под 2025 год (ну или под 2020, кому как). Хотя многие до сих пор пишут так, будто на календаре 2010.

Но важно понимать! часто дело не в техническом выборе. Это может быть привычка, вкусы или просто невозможность перейти на новую версию.

И главный момент тут в том, что современная Java не заменяет классическую = они существуют параллельно. Хотя со временем, конечно, и "новая Java" тоже станет предметом споров и хейта, как старая.

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

Java сегодня это умение жить сразу в двух эпохах.

Если ты понимаешь эту двойственность, тогда можешь использовать язык по максимуму.

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

👉 Java Portal
  • ❤ 11
Post #1952 2.54K
Java Portal | Программирование Учись алгоритмам программирования с этим ресурсом - код, пошаговое выполнение и наглядное представление. Более 70 алгоритмов на JavaScript, Java и C++ - идеально для практики и понимания логики. → http://algorithm-visualizer.org 👉 Java Portal
Очень в тему перечитать этот классический материал от Майка Бостока. Отлично ложится как продолжение поста.

https://bost.ocks.org/mike/algorithms/

👉 Java Portal
  • ❤ 6
  • 👍 2
Post #1951 2.6K
В начале карьеры я удалил 5GB лог-файл на продовом сервере, который уже почти забился.

Я запустил df -h, ожидая, что использование диска упадёт. Не упало.

Всё ещё показывало 100% занято.

Ни ошибок, ни предупреждений. Просто те же цифры, будто я вообще ничего не удалял.

И вот тогда я понял, что удаление файла не всегда сразу освобождает место.

В Linux то, что мы называем "файлом", на самом деле состоит из двух частей: имени файла (по сути указателя) и inode (где лежат данные и метаданные). Когда ты удаляешь имя файла, ты просто убираешь указатель. Но inode с данными остаётся на диске, пока какой-то процесс держит файл открытым.

В моём случае веб-сервер всё ещё писал в тот самый лог. И хотя я удалил имя файла, процесс продолжал держать открытый файловый дескриптор. Inode оставался живым — его не видно обычными командами, но место он продолжал занимать.

Место освободилось только после перезапуска веб-сервера, когда все дескрипторы закрылись.

Поэтому нужны разные команды, чтобы увидеть реальную картину:

Проверить использование файловой системы:

df -h


Посмотреть реальные размеры директорий:

du -sh /var/log/*


Найти удалённые файлы, которые всё ещё держатся процессами:

lsof +L1


du показывает, что в директориях реально занимает место, а df сколько занято на уровне файловой системы.

Если они не совпадают, почти всегда причина = удалённые файлы, которые всё ещё открыты процессами.

По этой же причине нормальный log rotation не просто удаляет файлы. Такие инструменты, как logrotate, сначала переименовывают файл и отправляют сигнал процессу, чтобы он корректно закрыл старый дескриптор и открыл новый.

Три важных вывода:

Имя файла это всего лишь указатель на inode

Удаление происходит только тогда, когда inode больше никем не используется

При разборе проблем с диском всегда проверяй и df, и du


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

👉 Java Portal
  • 👍 23
  • ❤ 6
  • 🔥 4
  • 😁 1
Post #1950 2.56K
Путаешь Dockerfile и Docker Compose?

Тогда вот короткое пояснение.

Они дополняют друг друга, но выполняют разные задачи в контейнерной инфраструктуре.

Dockerfile используется для создания и сборки Docker-образов.

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

Примечание. Начиная с версии 1.28.6, Docker Compose по умолчанию ищет compose.yaml или compose.yml, но сохраняет обратную совместимость с docker-compose.yaml/yml. Если в проекте есть оба варианта, приоритет у compose.yaml.

Прикрепил простую визуализацию, чтобы было легче разобраться.

👉 Java Portal
  • 👍 12
  • 🔥 4
  • ❤ 3
Post #1949 2.63K
Java хотела добавить лямбды, но при этом не поломать JVM и не переписывать двадцатилетние API.

Решение? Сделать вид, что в Java есть функции первого класса, используя то, что уже было: интерфейсы с единственным абстрактным методом.

Так появились функциональные интерфейсы.

@FunctionalInterface
interface Operation {
int apply(int x, int y);
}

Operation add = (a, b) -> a + b;

System.out.println(add.apply(3, 4)); // 7


Кажется, что ты передаешь функцию... но нет.

Компилятор создаёт экземпляр Operation.

В Java нет "свободных" функций, как в JavaScript или Kotlin.

Сейчас это используется даже со старыми классами языка.

Например, Runnable существует ещё с Java 1.0 и содержит всего один метод run.

Когда в Java 8 завезли лямбды, стало можно писать так:

new Thread(() -> System.out.println("Hello from a thread")).start();


И старый конструктор Thread(Runnable r) продолжил работать без изменения ни одной строки оригинального API.

Именно это позволило не сломать экосистему и сохранить стабильность, за которую Java так ценят.

👉 Java Portal
  • ❤ 14
  • 👍 3
  • 🔥 2
Post #1948 2.7K
В Java такое видел почти каждый:

var user = new User("Jordy", "Colombia", 28, true, "admin");

Код компилируется, но никто не помнит, что означает каждый параметр (если только IDE не подсказывает).

Порядок важен, значения путаются, а любое изменение превращается в клоунаду.

Классический конструктор начинает убивать читаемость.

От этого и спасает Builder:

var user = User.builder()
.name("Jordy")
.country("Colombia")
.age(28)
.active(true)
.role("admin")
.build();


Теперь код читается как нормальное предложение.

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

Причем сам JDK в новых API давно использует этот подход, пример с HttpRequest:

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("url"))
.GET()
.build();


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

Builder это не просто "красивый паттерн".

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

Создание объекта не должно выглядеть как угадывание параметров.

👉 Java Portal
  • 👍 22
  • 🔥 6
  • ❤ 3
  • 💊 1
Post #1946 2.53K
Хочешь избежать проблем, когда Cloudflare падает?

Или когда La Liga блокирует твои страницы?

Разработчик сделал open-source CLI, чтобы можно было легко отключать этот сервис в проектах за пару секунд.

$ npx disable-cloudflare@latest


👉 Java Portal
  • 👍 5
  • 🔥 3
Post #1945 2.53K
Почему Redis однопоточный (и почему из-за этого он быстрый)

У тебя сервер на 32 ядра, а Redis грузит только одно.

На вид неэффективно. Но при этом Redis остается одной из самых быстрых in-memory баз.
Почему так?

Основной bottleneck обычно не CPU

Для типичных операций Redis вроде GET, SET, INCR CPU почти не тратится.

В реальных условиях упираешься в другое:

- пропускная способность сети (ответы клиентам)
- пропускная способность памяти (RAM <--> CPU cache)
- системные вызовы, TCP, epoll и другая возня ядра

В таких условиях добавление потоков не дает прироста, потому что ограничение в другом.

Почему мультипоточность сделала бы Redis медленнее?

Если бы несколько потоков трогали одну область данных, понадобились бы:

Локи
Чтобы безопасно читать или писать ключи. Ожидание блокировок добавляет задержки.

Context Switching
Переключение между 32 потоками сжигает ресурсы и ломает кэш-локальность.

Cache Coherence
Когда один поток обновляет данные, которые кэшировал другой, аппаратная часть должна инвалидировать кэши по ядрам.


Это создает "cache thrashing" и ест производительность.

Во многих сценариях эти накладные расходы больше, чем сама работа Redis-команды.

Секрет - однопоточный event loop


Redis работает на неблокирующем event loop (epoll/kqueue).
Он умеет держать тысячи соединений и обрабатывать только те сокеты, у которых есть данные.

Плюсы:

- без блокировок
- предсказуемые задержки
- строгий порядок операций
- минимальный overhead

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

Но небольшая поправка = Redis не полностью однопоточный

Redis однопоточен только в части, которая работает с данными.

Остальное вынесено в фоновые потоки.

✔️Главный поток

Выполняет команды и трогает dataset.

✔️BIO-потоки

Для долгих задач:

lazy delete (UNLINK)
синхронизация AOF
фоновые задачи модулей

✔️I/O threads (с версии Redis 6+)

Для чтения запросов и отправки ответов по сети.
При этом сами данные не трогаются, значит = без локов.
Так Redis получает выгоду от распараллеливания, не ломая простоту и предсказуемость.

Redis специально остается однопоточным в части доступа к данным, чтобы избежать локов, гонок и непредсказуемости.
При этом дополнительные потоки используются там, где они реально помогают.
В результате Redis часто обходится быстрее сложных многопоточных систем.

👉 Java Portal
  • ❤ 4
  • 👍 4
  • 👀 4
  • 🔥 1
Post #1943 2.42K
Одна простая привычка, которая сильно упрощает поддержку Dockerfile, это сортировка многострочных списков пакетов по алфавиту.

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

Когда ставишь пакеты в многострочном apt-get install или apk add блоке, легко случайно добавить дубликат или пропустить что-то, что затерялось в середине списка.

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

Плюс уменьшаются шумные диффы в код-ревью.

Когда пакеты отсортированы, добавление или удаление одного пункта выглядит как аккуратное и предсказуемое изменение.

Без сортировки маленькое обновление часто превращается в огромный diff, и ревью становится сложнее, чем должно быть.

Этот подход особенно полезен, когда команда растёт.

Несколько человек, трогающих один Dockerfile, не начинают перетасовывать строки под свой вкус или форматировать по-разному.

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

👉 Java Portal
  • 🔥 13
  • 👍 4
  • ❤ 2
Post #1942 2.48K
Только что выкатили демо, показывающее, почему Spring Data AOT реально важен для производительности.

Перенос парсинга запросов из рантайма в стадию компиляции дает:

- запуск приложения быстрее на 50–70%
- ошибки в запросах ловятся до деплоя
- SQL генерируется заранее на этапе сборки
- всё готово для GraalVM Native

Репозиторий:
https://github.com/danvega/spring-data-aot

👉 Java Portal
GitHub GitHub - danvega/spring-data-aot Contribute to danvega/spring-data-aot development by creating an account on GitHub.
  • 👍 9
  • ❤ 3
  • 😁 1
Post #1941 2.61K
12 рекомендаций по дизайну API для бэкенд-разработчиков

(Их часто спрашивают на собеседованиях)

1. Понятные имена ресурсов

❌`GET /get-all-orders`  
✅`GET /orders`


2. Корректное использование HTTP-методов

- POST /users — создать пользователя
- GET /users/123 — получить пользователя
- PUT /users/123 — заменить полностью
- DELETE /users/123 — удалить

3. Идемпотентность

Клиент отправляет:

POST /payments
Idempotency-Key: abc-123


Если запрос повторится, сервер должен вернуть тот же результат, не создавая операцию заново.

4. Версионирование API

Рекомендуемый вариант — в URL:

GET /v1/products/42
GET /v2/products/42


5. Правильные статус-коды

Если пользователь не найден:

❌ `200 OK { "error": "user not found" }`  
✅ `404 Not Found`


6. Пагинация

Пример:

GET /articles?page=2&limit=50


Ответ должен содержать элементы 51–100.

7. Фильтрация и сортировка

Пример:

GET /orders?status=shipped&sort=-created_at


8. Безопасность

Использование JWT в заголовках:

Authorization: Bearer <token>


9. Rate limiting

Например: 100 запросов в минуту.
После превышения — вернуть:

429 Too Many Requests


10. Кэширование

Запрос:

GET /blog/posts/123


Ответ содержит:

ETag: "abc"


Повторный запрос:

If-None-Match: "abc"


Если не изменилось — 304 Not Modified.

11. Документация

Используй:

- Swagger UI
- OpenAPI

Разработчики должны видеть схемы, параметры и иметь возможность тестировать запросы.

12. Быть прагматичным

Иногда лучше так:

POST /auth/login


чем строго REST-подход:

POST /sessions


Хороший API читается как логичная, предсказуемая система и экономит время всем, кто с ним работает.

👉 Java Portal
  • 👍 17
  • ❤ 6
  • 🔥 6
Post #1940 2.67K
25 золотых правил системного дизайна

1. Система с упором на чтение
> Используй кэш (Redis/Memcached) для частых запросов вроде профилей пользователей

2. Низкая задержка
> Используй кэш и CDN (Cloudflare), чтобы раздавать статику ближе к пользователю

3. Система с упором на запись
> Используй Message Queue (Kafka) для буферизации большого объема записей (логи, аналитика)

4. ACID-требования
> Используй SQL (PostgreSQL) для строгих транзакций вроде банковских операций

5. Неструктурированные данные
> Используй NoSQL (MongoDB) для гибких схем, например каталогов товаров

6. Сложные медиа-ресурсы
> Используй Blob Storage (AWS S3) для видео, изображений и больших файлов

7. Сложные предварительные расчёты
> Используй Message Queue + Cache для асинхронной генерации контента (например ленты новостей)

8. Поиск при больших объемах данных
> Используй Elasticsearch для полнотекстового поиска и автокомплита

9. Масштабирование SQL
> Используй шардирование, чтобы разделить большие таблицы на несколько инстансов

10. Высокая доступность
> Используй Load Balancer (NGINX) чтобы распределять трафик и избегать перегрузки

11. Глобальная доставка данных
> Используй CDN для стабильного стриминга и раздачи контента по всему миру

12. Графовые данные
> Используй Graph DB (Neo4j) для соцсетей, рекомендаций и связей между сущностями

13. Масштабирование компонентов
> Используй горизонтальное масштабирование, а не просто апгрейд железа

14. Быстрые запросы к базе
> Используй индексы на ключевых колонках вроде email или user_id

15. Пакетные задачи
> Используй Batch Processing для отчётов, расчётов или периодических задач

16. Защита от злоупотреблений
> Используй Rate Limiter, чтобы предотвращать DDoS и спам запросов к API

17. Доступ к микросервисам
> Используй API Gateway для авторизации, маршрутизации и SSL-терминации

18. Единая точка отказа
> Добавляй Redundancy (Active-Passive), чтобы сервис продолжал работать при сбоях

19. Отказоустойчивость данных
> Используй репликацию (Master-Slave), чтобы данные не терялись при падении узлов

20. Реальное время
> Используй WebSockets для чатов, лайв-обновлений, лайв-результатов

21. Обнаружение сбоев
> Используй Heartbeat-пинг, чтобы проверять статус сервисов каждые несколько секунд

22. Целостность данных
> Используй Checksums (MD5/SHA) чтобы проверить, что загруженные файлы не повреждены

23. Децентрализованное состояние
> Используй Gossip Protocol, чтобы ноды обменивались статусами без центрального сервера

24. Эффективное кеширование
> Используй Consistent Hashing, чтобы добавлять или убирать кэш-ноды без полного пересчёта ключей

25. Работа с геоданными
> Используй Quadtree или Geohash для быстрых запросов вроде поиска ближайших водителей


👉 Java Portal
  • 👍 9
  • ❤ 4
  • 🔥 2
Post #1938 3.06K
Spring Boot: на этапе разработки можно включить spring.main.lazy-initialization=true, чтобы ускорить запуск приложения.

По умолчанию Spring Boot инициализирует все бины при старте. В среде разработки это приводит к:

Более долгому старту приложения, особенно в крупных проектах

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

Чтобы избежать этого, в application.properties укажите:

spring.main.lazy-initialization=true


Важно сохранить поведение по умолчанию в проде, потому что:

Ошибки на старте выявляются раньше

Все компоненты готовы принимать запросы сразу

👉 Java Portal
  • 🔥 8
  • 👍 5
Post #1937 2.69K
API VERSIONING позволяет разработчикам поддерживать несколько версий эндпоинтов веб-сервиса (например, /users или /products), не ломая интеграции у клиентов при внесении изменений.

В новом Spring Boot 4 появилась встроенная поддержка версионирования API, и настраивается она очень просто.

→ Добавь строки в конфиг (application.properties) под твой сценарий. Можно использовать оба варианта одновременно — заголовки и query-параметры.

1 способ: версионирование через заголовок
(Клиент передает версию в header, например: api-version: 1 — название заголовка можно выбрать любое)

spring.mvc.apiversion.use.header = api-version


2 способ: версионирование через query-параметр
(Клиент передает версию в запросе, например: ?version=1)

spring.mvc.apiversion.use.query-parameter = version


→ Установка версии по умолчанию
(Используется версия 1.0, если клиент ничего не указал)

spring.mvc.apiversion.default = 1.0


Примечание: если версия указана и в заголовке, и в query-параметре, то приоритет у более высокой версии. Например, header = 2, query = 1 → в итоге будет 2.

Готово.

a) Без множества контроллеров
b) Без /v1/, /v2/ в URL
c) Код чище и проще поддерживать

👉 Java Portal
  • 🔥 8
  • 👍 7
  • ❤ 2
Post #1936 2.9K
Совет по Java: можно использовать Collectors.joining(", ") чтобы собрать Stream в строку с разделителем запятая (или любым другим, если нужно).

Обычный способ:

List<String> items = List.of("a", "b", "c");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < items.size(); i++) {
sb.append(items.get(i));
if (i < items.size() - 1) {
sb.append(", ");
}
}
System.out.println(sb.toString());


Через joining:

List<String> items = List.of("a", "b", "c");
String result = items.stream().collect(Collectors.joining(", "));
System.out.println(result.toString());


👉 Java Portal
  • ❤ 15
  • 👍 3
Post #1935 2.93K
Многие ненавидят Java не потому что язык плохой, а потому что первое знакомство с ним вышло травматичным.

В универе его чаще всего преподавали на примерах из прошлого века, а преподаватели были больше заняты тем, чтобы сто раз написать public static void main, чем объяснить, почему всё устроено именно так. А ещё эта многословность, ошибки компиляции, необходимость сходу понимать классы, объекты, наследование… неудивительно, что после такой встречи у людей остаётся отвращение.

Плюс порог входа у Java выше, чем у более дружелюбных языков вроде Python или JavaScript. И хотя это уже давно не тот тормозной тяжёлый язык из 2000-х, многие до сих пор думают о нем именно так, не замечая, что сегодня он работает почти во всех крупных системах.

И тут самое забавное: пока в интернете его поливают грязью, компании всё так же активно ищут Java-разработчиков и платят им очень хорошие деньги. Потому что в реальном мире остаётся то, что работает и масштабируется.

👉 Java Portal
  • ❤ 29
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 →