Java Portal | Программирование
13.2K subscribers
1.17K photos
97 videos
37 files
1.09K links
Присоединяйтесь к нашему каналу и погрузитесь в мир для Java-разработчика

Связь: @devmangx

РКН: https://clck.ru/3H4WUg
Download Telegram
Совет по Java: используйте default-методы в интерфейсах для поддержки обратной совместимости (начиная с Java 8).

Допустим, у вас есть интерфейс Shape:

interface Shape {
double area();
}


Типичная реализация выглядит так:

class Circle implements Shape {
private double radius;

Circle(double radius) {
this.radius = radius;
}

@Override
public double area() {
return Math.PI * radius * radius;
}
}


Теперь нужно добавить метод perimeter(). Если добавить обычный метод в интерфейс, все существующие реализации Shape сломаются. Вместо этого можно добавить default-метод.

interface Shape {
double area();

// Новый метод
default double perimeter() {
...
}
}


Класс Circle автоматически получает реализацию default-метода. При необходимости его можно переопределить.

пример : https://gist.github.com/mcasari/b0ee1d94046793ba20f02538ef916f48

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍116
Нужно загрузить ресурсы в Spring? Вот несколько рекомендуемых подходов:

@Value + Resource для classpath, файловой системы и URL
ResourceLoader для путей, которые определяются во время выполнения
ResourcePatternResolver для загрузки по шаблонам

У каждого варианта своя зона применения.

Разбираем, когда и что использовать

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍3
WebFlux часто вызывает путаницу, потому что его обычно объясняют со стороны инструмента, а не со стороны проблемы.

Поэтому начнем с базы.

Сначала: блокирующий vs неблокирующий код

-» Блокирующий код:
Поток встает и ждет, пока операция не завершится.
Пока ждет, он ничего больше делать не может.

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

Это не быстрее.
Это просто более эффективное использование потоков.

Еще одно ключевое понятие перед тем как идти дальше:

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

Представь так:
потребитель может сказать продюсеру: «присылай только столько, сколько я успеваю обработать».

Теперь к WebFlux.

WebFlux позволяет строить полностью неблокирующие приложения end-to-end:

-»Неблокирующий сервер (Netty)
-»Неблокирующие API
-»Проброс backpressure без перегрузки потребителя
-»Реактивная модель (Mono, Flux)

Ключевое слово здесь end-to-end.
Если хотя бы одна часть блокирует, весь выигрыш размывается.

Самая частая ошибка при работе с WebFlux

Использовать WebFlux, а потом:

-»Дернуть блокирующую базу данных
-»Использовать SDK без реактивной поддержки
-»Делать .block(), потому что так проще

В итоге:

-»Все равно все становится блокирующим
-»Ты просто добавил сложности
-»Ничего не выиграл

WebFlux не превращает блокирующий код в неблокирующий.

А Virtual Threads разве это не меняют?

Меняют, и это важно.

С Virtual Threads:

-»Блокирующая модель снова становится жизнеспособной
-»Цена ожидания резко падает
-»Необходимость в WebFlux уменьшается

Но есть нюанс.

Virtual Threads не дают:

-»Backpressure
-»Контроль потока
-»Композицию стримов
-»Настоящий стриминг данных

Они меняют tradeoff, но не убирают саму проблему.

Когда WebFlux все еще имеет смысл

WebFlux оправдан, когда:

-»Нужен реальный backpressure
-»Ты работаешь с непрерывными стримами (SSE, WebSockets)
-»Есть fan-out из множества внешних вызовов
-»Нужно явно контролировать поток данных

По необходимости, а не потому что модно.

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

-»Классический CRUD
-»Сложная бизнес-логика
-»Команды без реактивного опыта
-»Проекты, где читаемость важнее конкурентности
-»Когда Virtual Threads решают задачу с меньшей когнитивной стоимостью

WebFlux это другая модель исполнения.

Если твоя проблема не в масштабной I/O-конкурентности,
WebFlux тебе не поможет.

А сегодня, с Virtual Threads, многим он уже просто не нужен.

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍6
This media is not supported in your browser
VIEW IN TELEGRAM
Тема Islands теперь используется по умолчанию во всех IDE от JetBrains.

Более мягкий и сбалансированный внешний вид, рассчитанный на комфорт и концентрацию во время работы.

Посмотреть подробнее 👉 https://jb.gg/discover-the-islands

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11
Реальная история:

Дизайнерское агентство разработало интерфейс приложения в сервисе Figma, но из-за того, что карты Visa и Mastercard оказались заблокированы, не смогли выгрузить проект.

Осложнялось всё тем, что они могли олатить сервис только с корпоративной карты.

Читпей организовали оплату в течение 10 минут. Ребята оплатили подписку, и выгрузили проект. Интерфейс приложения был спасён!

Что делает CheatPay:

🔘Помогает оплатить зарубежные сервисы российскими картами, в том числе Visa и Mastercard.
🔘Восстанавливают доступ к аккаунту
🔘Работают прозрачно с оплатой на ИП и всеми закрывающими документами

Например:

Потеряли доступ к Zoom, Airtable, Atlassian, Canva и другим программам для управления IT-проектами? Простаивают проекты в Adobe Photoshop, Premiere Pro и After Effects? Другие сервисы? Помогут.

Обращаться в https://t.iss.one/cheatpay_ru
🤣41🔥1😁1
Случайные UUID убивают производительность базы данных.

Ты перешел с целочисленных ID (1, 2, 3…) на UUID (a1b2-3c4d-…) ради безопасности или распределенной генерации.

И вдруг записи в БД стали медленнее. Иногда намного.

Вот почему.

Фрагментация индексов.

Большинство индексов в БД это B-Tree (сбалансированные отсортированные деревья). Физическое расположение данных имеет значение.

1. Последовательные ID

Когда ты вставляешь последовательные числа (1, 2, 3), новые записи всегда попадают в самый правый лист индекса.

Записи предсказуемые и последовательные.
Максимальные cache hit’ы.
Страницы индекса остаются заполненными на 100%.

Это максимальная скорость, на которую способна твоя база.

2. Случайные UUIDv4

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

Из-за этого:

-» База постоянно подгружает случайные страницы с диска в память (random I/O).

-» Page split. Если целевая страница заполнена, БД вынуждена делить ее пополам, в итоге получаются две полупустые страницы.

-» Эффект швейцарского сыра. Индекс раздувается и заполняется дырками, зря тратя RAM и место на диске.

-» Когда размер индекса превышает объем доступной памяти, пропускная способность на запись может просесть на 20–90%.

3. UUIDv7

Перестань использовать UUIDv4 в качестве primary key. Используй UUIDv7 (стандартизирован в RFC 9562).

UUIDv7 содержит таймстемп в начале ID, поэтому он сортируемый.

В итоге ты получаешь лучшее из двух миров:

-» Распределенная генерация, без центрального счетчика.

-» Монотонные вставки. В B-Tree они ведут себя почти как последовательные числа, без фрагментации.

-» Безопасность. Нельзя просто угадать ID (атакующий не узнает, что пользователь 101 идет сразу после 100), хотя стоит учитывать, что время создания записи становится видно.

Итог простой: ты сохраняешь удобство UUID и избавляешься от перфоманс-потерь.

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14👍5
Совет по Spring Boot: используйте TaskScheduler для реализации лёгких динамических задач. Для более сложных сценариев лучше смотреть в сторону Quartz.

Инжектим и используем:

@Autowired
TaskScheduler scheduler;

scheduler.schedule(
() -> System.out.println("Hello!"),
new CronTrigger("0 */5 * * * *") // каждые 5 минут
);


Инжектируемый бин — это ThreadPoolTaskScheduler, встроенная реализация сразу TaskScheduler и ScheduledExecutorService

При необходимости конфигурацию можно кастомизировать:

@Configuration
@EnableScheduling
public class SchedulerConfig {

@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(5);
scheduler.setThreadNamePrefix("my-scheduler-");
scheduler.initialize();
return scheduler;
}
}


👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31
В JDK 26 появилась новая фича: HttpClient, который входит в Java SE ещё со времён JDK 11, теперь поддерживает HTTP/3 😱

HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build(); // создать экземпляр HttpClient с HTTP/3 в качестве предпочтительной версии

URI reqURI = new URI("https://www.google.com/");
HttpRequest req = HttpRequest.newBuilder()
.uri(reqURI)
.build(); // создать экземпляр запроса

final HttpResponse.BodyHandler<String> bodyHandler =
BodyHandlers.ofString(StandardCharsets.UTF_8);

HttpResponse<String> resp = client.send(req, bodyHandler);
// отправить запрос и получить ответ в виде строки

System.out.println(
"status code: " + resp.statusCode() +
" HTTP protocol version: " + resp.version()
); // вывести код ответа и используемую версию HTTP


Подробнее

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12
Сегодня в рубрике crazy Java: этот код работает (на Java 25):

<T extends Integer> void test(T t) {
t++;
// t += 1;
IO.println(t);
}

void main() {
test(10);
}


Но если заменить t++ на t += 1, всё ломается.

Ошибка:

Main.java:3: error: incompatible types: int cannot be converted to T


👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
2🤯143👍2😁2🔥1
This media is not supported in your browser
VIEW IN TELEGRAM
Spring Boot 4 теперь полностью безопасен по работе с null — большой плюс для Kotlin-разработчиков.

С поддержкой JSpecify в Kotlin 2.2 наконец-то можно избавиться от платформенных типов и получить корректную работу с null даже в обобщённых типах.

Подробнее и попробовать Spring с Kotlin ☺️

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
Этот проект показывает систему отслеживания местоположения курьера в реальном времени, похожую на то, как это реализовано в Zomato или Swiggy, и построенную на Spring Boot и Apache Kafka.

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
🤣4🌚32💊1
Когда всё живёт в одной базе данных, с транзакциями всё просто.

BEGIN → COMMIT → ROLLBACK.

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

В чём реальная проблема ???

В распределённых системах нельзя сделать одну транзакцию на всё сразу по нескольким причинам:

- несколько сервисов
- несколько баз данных
- множество возможных отказов

Классического rollback больше не существует.

И тут появляется паттерн Saga.

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

Каждый шаг:

- выполняет своё изменение
- фиксируется
- и определяет, как себя компенсировать, если дальше что-то пойдёт не так

Глобальной транзакции нет.
Есть eventual consistency.

Простой пример » Создание заказа может включать:

1. Создать заказ

2. Зарезервировать товар

3. Провести оплату

Если на шаге 3 происходит сбой, глобального rollback нет.
Выполняются компенсирующие действия:

- освободить резерв
- пометить заказ как отменённый

Это и есть Saga.

Два способа реализации Saga -

Хореография:

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

Оркестрация:

- есть компонент-координатор
- поток шагов явно описан
- проще рассуждать о логике
- выше связность

Ни один подход не является универсально лучшим.
Всё зависит от системы и контекста.


Частая ошибка » Считать, что Saga — это замена ACID-транзакциям.

Это не так.

Saga меняет сам контракт:

- допускаются промежуточные состояния
- компенсации проектируются явно
- принимается факт, что система может упасть на середине процесса

Когда Saga действительно имеет смысл

- долгоживущие процессы
- несколько сервисов
- реальные побочные эффекты: платежи, доставки, резервы
- ситуации, где технического rollback не существует

Если всё находится в одной базе данных, Saga не нужна. Здесь легко уйти в оверинжиниринг.

Ключевая мысль:

Saga не убирает сложность.
Она делает её явной.


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

В распределённых системах сбои — это не исключение, а часть нормального потока.

Паттерн Saga не избавляет от всех проблем,
но помогает избежать беспорядка.

А в продакшене это уже большая разница. ☃️

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍51
Java Tip: Начиная с Java 14 можно использовать record для создания компактных неизменяемых объектов, которые просто переносят данные.

» Они короче обычных POJO
» У них из коробки есть equals(), hashCode() и toString()
» По умолчанию они неизменяемые

Классический POJO:

public class Book {
private final String title;
private final int price;

public Book(String title, int price) {
this.title = title;
this.price = price;
}

// геттеры, toString, equals и hashCode
}


Вместо этого можно создать record:

public record Book(String title, int price) {}


Смысл ровно тот же, но без бойлерплейта.

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17
Когда ты уже не джун и даже не мидл, на собеседованиях по Java почти не задают вопросы в стиле «что такое HashMap». В ход идут сценарии из реальной жизни.

Разберём типичный кейс.

Твой сервис — оркестратор. Он дергает несколько downstream-сервисов, чтобы обработать запрос.
Один из них проблемный: медленный и периодически отвечает 503 Service Unavailable.

1. Как сделать сервис устойчивым к ненадёжному downstream

Первое, что здесь просится » Circuit Breaker.

Идея простая: если зависимый сервис начинает фейлиться, мы перестаём его дёргать, чтобы:

- не тратить ресурсы,
- не увеличивать латентность,
- не убивать весь сервис каскадными таймаутами.

Реализация с Resilience4j:

Подключаем зависимость и оборачиваем вызов проблемного сервиса в CircuitBreaker.

Ключевые состояния:

Всё нормально. Запросы проходят. Ошибки считаются.

Порог ошибок превышен. Все вызовы сразу фейлятся, downstream даже не вызывается.

Пробный режим. Ограниченное число запросов, чтобы проверить — ожил сервис или нет.

Пример:

@CircuitBreaker(name = "inventoryService", fallbackMethod = "inventoryFallback")
public InventoryResponse getInventory(String productId) {
return inventoryClient.getInventory(productId);
}


Конфиг обычно задаётся через application.yml:

- процент ошибок
- sliding window
- waitDurationInOpenState
- permittedNumberOfCallsInHalfOpenState

2. Что делать, когда circuit открыт (fallback)

Тут нет универсального ответа » зависит от бизнеса.

Типовые варианты:

🔹Кэш

Если данные не критичны к свежести:

- Redis
- локальный cache
- stale-данные лучше, чем 503

private InventoryResponse inventoryFallback(String productId, Throwable ex) {
return cacheService.getInventory(productId);
}


🔹Дефолтный ответ

Если можно вернуть «безопасное» значение:

- пустой список
- available = false
- статус UNKNOWN

🔹Очередь

Если запрос важен, но не срочный:

- кладём событие в Kafka / Rabbit
- обрабатываем асинхронно
- отвечаем клиенту 202 Accepted

На практике часто комбинируют:
кэш + деградация функциональности.

3. Глобальная обработка ошибок через @RestControllerAdvice

Чтобы не размазывать try/catch по всему коду, делаем централизованный обработчик.

Пример бизнес-исключения

public class ProductNotFoundException extends RuntimeException {
public ProductNotFoundException(String id) {
super("Product not found: " + id);
}
}


Глобальный handler

@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(ProductNotFoundException.class)
public ResponseEntity<ErrorResponse> handleProductNotFound(ProductNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
"PRODUCT_NOT_FOUND",
ex.getMessage()
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
}

JSON-ответ
{
"code": "PRODUCT_NOT_FOUND",
"message": "Product not found: 123"
}


Плюсы подхода:

- чистые контроллеры
- единый формат ошибок
- нормальная мапа бизнес-ошибок на HTTP-статусы

Итог

На таком вопросе проверяют не знание аннотаций, а мышление:

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

Это уже разговор не про «Java», а про архитектуру продакшн-сервисов

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍104
Spring Boot наконец получил нативную поддержку gRPC

Забудьте о сторонних стартерах и костылях — Spring gRPC 1.0 GA уже здесь. Теперь можно строить высокопроизводительные RPC-сервисы с Protocol Buffers прямо из коробки, без плясок с бубном.

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
9
Kubernetes-совет:

Java-приложения обычно требуют больше CPU на старте, чем во время обычной работы ☕️

Эта политика Kyverno:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: resize-pod-policy
spec:
mutateExistingOnPolicyUpdate: false
rules:
- name: resize-pod-policy
match:
any:
- resources:
kinds:
- Pod/status
- Pod
preconditions:
all:
- key: "{{request.object.status.containerStatuses[0].ready}}"
operator: Equals
value: true
mutate:
targets:
- apiVersion: v1
kind: Pod.resize
name: "{{request.object.iss.onetadata.name}}"
patchStrategicMerge:
spec:
containers:
- (name): sample-app-on-kubernetes
resources:
limits:
cpu: 0.5


- отслеживает момент завершения старта pod’а
- обновляет ресурсы pod’а прямо на месте
- снижает CPU-лимиты после старта

Итог: более быстрый запуск и меньшие затраты

Вот моя статья об этом

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔61👍1
Java-совет: избегайте глубоко вложенных if/else.

Используйте guard clauses (ранние выходы из метода) вместо этого.

 Вложенные if-else:

public void processOrder(Order order) {
if (order != null) {
if (order.isPaid()) {
if (order.getItems().size() > 0) {
// Обработка заказа
System.out.println("Заказ обработан");
} else {
System.out.println("В заказе нет позиций");
}
} else {
System.out.println("Заказ не оплачен");
}
} else {
System.out.println("Заказ равен null");
}
}

Использование guard clauses (читается проще):

public void processOrder(Order order) {
if (order == null) {
System.out.println("Заказ равен null");
return;
}

if (!order.isPaid()) {
System.out.println("Заказ не оплачен");
return;
}

if (order.getItems().isEmpty()) {
System.out.println("В заказе нет позиций");
return;
}

// Обработка заказа
System.out.println("Заказ обработан");


👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍94
🔴 Завтра тестовое собеседование с Java-разработчиком

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

Как это будет:
📂 Сергей Чамкин, старший разработчик из Uzum, ex-WildBerries, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Cергей будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Сергею

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

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

Реклама.
О рекламодателе.
Please open Telegram to view this post
VIEW IN TELEGRAM
1
Создаём первый Minecraft мод и подробно разбираем Mixin. Просто и понятно

Эта статья была написана из-за отсутствия адекватных русскоязычных руководств по моддингу, особенно для новых версий Minecraft. То, что описано в статье, будет применимо для таких загрузчиков модов, как Fabric и Forge, а также для реализации различных функций.

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
13👍3🔥1
Spring Boot: для новых приложений выбирайте WebClient вместо RestTemplate

~ Реактивный и неблокирующий
~ Работает на event-loop, а не на модели один поток на запрос
~ Отлично подходит для микросервисной архитектуры

 RestTemplate (blocking)

@Service
public class ItemService {

private final RestTemplate restTemplate;

public ItemService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}

public Item getItem(Long id) {
return restTemplate.getForObject(
"https://api.example.com/items/{id}",
Item.class,
id
);
}
}

WebClient (non-blocking)

@Service
public class ItemService {

private final WebClient webClient;

public ItemService(WebClient webClient) {
this.webClient = webClient;
}

public Mono<Item> getItem(Long id) {
return webClient.get()
.uri("/items/{id}", id)
.retrieve()
.bodyToMono(Item.class);
}
}


👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍53