12073
Присоединяйтесь к нашему каналу и погрузитесь в мир для Java-разработчика Связь: @devmangx РКН: https://clck.ru/3H4WUg
Какие фичи C++ реально стоят того, чтобы ими пользоваться каждый день — а какие живут только в докладах и спорах на форумах?
Во втором выпуске «АйТир Листа» встретились два практикующих разработчика —
Данил Черепанов (МойОфис) и Антон Полухин (Яндекс) — и разобрали любимые и спорные возможности C++. Где-то всё было однозначно, а местами мнения разошлись кардинально 🙂
👉 Выпуск ЗДЕСЬ
Реклама
ООО "НОВЫЕ ОБЛАЧНЫЕ ТЕХНОЛОГИИ"
ИНН: 7703807270
erid: 2W5zFJaQXLt
Типы Серверов в Современных Системах
👉 Java Portal
Spring Boot: можно добавить глобальные метаданные OpenAPI с помощью аннотации @OpenAPIDefinition.
Размести её на главном классе приложения Spring Boot или в отдельном конфигурационном классе.
@SpringBootApplicationЧитать полностью…
@OpenAPIDefinition(
info = @Info(
title = "Product Management API",
version = "v1.0",
description = "Product API Example",
termsOfService = "https://www.example.com/terms",
contact = @Contact(
name = "API Support",
email = "support@example.com",
url = "https://example.com/support"
),
license = @License(
name = "Apache 2.0",
url = "https://www.apache.org/licenses/LICENSE-2.0"
)
)
)
public class ApiApplication {
}
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-совет: избегайте глубоко вложенных 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("Заказ обработан");
Spring Boot наконец получил нативную поддержку gRPC
Забудьте о сторонних стартерах и костылях — Spring gRPC 1.0 GA уже здесь. Теперь можно строить высокопроизводительные RPC-сервисы с Protocol Buffers прямо из коробки, без плясок с бубном.
👉 Java Portal
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
}public record Book(String title, int price) {}
Этот проект показывает систему отслеживания местоположения курьера в реальном времени, похожую на то, как это реализовано в Zomato или Swiggy, и построенную на Spring Boot и Apache Kafka.
👉 Java Portal
Сегодня в рубрике crazy Java: этот код работает (на Java 25):
<T extends Integer> void test(T t) {
t++;
// t += 1;
IO.println(t);
}
void main() {
test(10);
}Main.java:3: error: incompatible types: int cannot be converted to T
Совет по 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;
}
}
Реальная история:
Дизайнерское агентство разработало интерфейс приложения в сервисе Figma, но из-за того, что карты Visa и Mastercard оказались заблокированы, не смогли выгрузить проект.
Осложнялось всё тем, что они могли олатить сервис только с корпоративной карты.
Читпей организовали оплату в течение 10 минут. Ребята оплатили подписку, и выгрузили проект. Интерфейс приложения был спасён!
Что делает CheatPay:
🔘Помогает оплатить зарубежные сервисы российскими картами, в том числе Visa и Mastercard.
🔘Восстанавливают доступ к аккаунту
🔘Работают прозрачно с оплатой на ИП и всеми закрывающими документами
Например:
Потеряли доступ к Zoom, Airtable, Atlassian, Canva и другим программам для управления IT-проектами? Простаивают проекты в Adobe Photoshop, Premiere Pro и After Effects? Другие сервисы? Помогут.
Обращаться в /channel/cheatpay_ru
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
🔍 Завтра тестовое собеседование с Java-разработчиком
17 декабря(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика.
Как это будет:
📂 Сергей Чамкин, старший разработчик из Uzum, ex-WildBerries, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Cергей будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Сергею
Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.
Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_sh_bot
Реклама.
О рекламодателе.
Postgres 18 получил поддержку виртуальных вычисляемых колонок. Вычисляемые STORED-колонки в Postgres уже были несколько версий подряд.
Вычисляемые колонки позволяют:
• создавать колонку на основе других данных
• ссылаться на значения из других колонок
• заранее считать колляции или любые вычисления в базе, а не в приложении
Синтаксис GENERATED ALWAYS AS открывает выражение, а в конце указывается режим VIRTUAL или STORED.
Виртуальные вычисляемые колонки пересчитываются при каждом чтении, поэтому не подходят для тяжёлых вычислений. Для таких случаев лучше использовать STORED-колонку или даже expression index. Но они удобны, когда значение нужно редко и его логично вычислять на лету.
Пример:
CREATE TABLE products (
id serial PRIMARY KEY,
price numeric,
tax_rate numeric DEFAULT 0.05,
total_price numeric GENERATED ALWAYS AS (price * (1 + tax_rate)) VIRTUAL
);
Java tip : можно использовать List.of() и Set.of(), чтобы создавать неизменяемые (immutable) коллекции.
Это быстрый способ получить немодифицируемые списки и множества без Collections.unmodifiableList().
Пример:
List<String> letters = List.of("A", "B", "C");letters.add("D"); // исключение во время выполнения
Spring Boot: аккуратно задавайте границы @ComponentScan, чтобы случайно не просканировать целиком весь пакет.
Предположим, вы пишете что-то вроде @ComponentScan("com.mycompany"), хотя на самом деле нужно сканировать только часть подпакетов com.mycompany:
- Увеличивается время сканирования classpath
- Замедляется старт приложения
- Подтягиваются классы, которые вообще не должны регистрироваться как Spring-бины
Лучшие практики:
Полагайтесь на дефолты:
@SpringBootApplication
public class MyApplication { }
@ComponentScan({
"com.mycompany.myapp.product",
"com.mycompany.myapp.order"
})
⭐️ Road To Highload: Крупноблочная архитектура
В видеопроекте Road to Highload Сергей Трегуб, руководитель бэкенд‑разработки продуктовых сервисов Яндекс 360, рассказывает, как крупноблочная архитектура упрощает коммуникацию между разными командами разработки и помогает быстро принимать решения: в каком компоненте разместить новую бизнес‑логику и какие дополнительные блоки потребуются для её поддержки.
Road to Highload — это цикл видеоматериалов от Яндекс 360 о том, как строятся системы, которыми ежедневно пользуются миллионы людей и тысячи компаний. Здесь обсуждаются high‑load и отказоустойчивость не по учебникам, а на основе многолетнего практического опыта.
Если у тебя Spring Boot приложение подключено как вложенный модуль и оно использует поддержку Docker Compose, то в IntelliJ IDEA нужно задать MODULE_WORKING_DIRECTORY, чтобы среда смогла корректно определить, где лежит файл compose.yaml.
👉 Java Portal
Создаём первый Minecraft мод и подробно разбираем Mixin. Просто и понятно
Эта статья была написана из-за отсутствия адекватных русскоязычных руководств по моддингу, особенно для новых версий Minecraft. То, что описано в статье, будет применимо для таких загрузчиков модов, как Fabric и Forge, а также для реализации различных функций.
👉 Java Portal
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.metadata.name}}"
patchStrategicMerge:
spec:
containers:
- (name): sample-app-on-kubernetes
resources:
limits:
cpu: 0.5
Когда ты уже не джун и даже не мидл, на собеседованиях по 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);
}
private InventoryResponse inventoryFallback(String productId, Throwable ex) {
return cacheService.getInventory(productId);
}@RestControllerAdvicepublic class ProductNotFoundException extends RuntimeException {
public ProductNotFoundException(String id) {
super("Product not found: " + id);
}
}@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"
}
Когда всё живёт в одной базе данных, с транзакциями всё просто.
BEGIN → COMMIT → ROLLBACK.
Но в распределённых системах всё резко усложняется.
В чём реальная проблема ???
В распределённых системах нельзя сделать одну транзакцию на всё сразу по нескольким причинам:
- несколько сервисов
- несколько баз данных
- множество возможных отказов
Классического rollback больше не существует.
И тут появляется паттерн Saga.
Saga это последовательность локальных транзакций, которые координируются между собой.
Каждый шаг:
- выполняет своё изменение
- фиксируется
- и определяет, как себя компенсировать, если дальше что-то пойдёт не так
Глобальной транзакции нет.
Есть eventual consistency.
Простой пример » Создание заказа может включать:
1. Создать заказ
2. Зарезервировать товар
3. Провести оплату
Если на шаге 3 происходит сбой, глобального rollback нет.
Выполняются компенсирующие действия:
- освободить резерв
- пометить заказ как отменённый
Это и есть Saga.
Два способа реализации Saga -
Хореография:
- сервисы реагируют на события
- центрального координатора нет
- слабая связность
- сложнее проследить общий поток
Оркестрация:
- есть компонент-координатор
- поток шагов явно описан
- проще рассуждать о логике
- выше связность
Ни один подход не является универсально лучшим.
Всё зависит от системы и контекста.
Saga не убирает сложность.
Она делает её явной.
Spring Boot 4 теперь полностью безопасен по работе с null — большой плюс для Kotlin-разработчиков.
С поддержкой JSpecify в Kotlin 2.2 наконец-то можно избавиться от платформенных типов и получить корректную работу с null даже в обобщённых типах.
Подробнее и попробовать Spring с Kotlin ☺️
👉 Java Portal
В 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
Случайные 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
Тема Islands теперь используется по умолчанию во всех IDE от JetBrains.
Более мягкий и сбалансированный внешний вид, рассчитанный на комфорт и концентрацию во время работы.
Посмотреть подробнее 👉 https://jb.gg/discover-the-islands
👉 Java Portal
Нужно загрузить ресурсы в Spring? Вот несколько рекомендуемых подходов:
-» @Value + Resource для classpath, файловой системы и URL
-» ResourceLoader для путей, которые определяются во время выполнения
-» ResourcePatternResolver для загрузки по шаблонам
У каждого варианта своя зона применения.
Разбираем, когда и что использовать
👉 Java Portal
Совет по 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;
}
}interface Shape {
double area();
// Новый метод
default double perimeter() {
...
}
}
Представь, что тебя связали, кинули в багажник и увезли в ангар за городом
Похититель наклоняется к твоему уху и шепчет: "Или ты правильно отвечаешь на 3 вопроса, или пуля летит тебе прямиком в висок:
💚Почему @Transactional в Spring не работает, если вызвать метод внутри того же класса?
💚Почему Kafka с exactly-once семантикой в 3 раза медленнее обычной и когда можно на это забить?
💚2 запроса одновременно читают баланс 1000₽, вычитают по 600₽ и сохраняют - что окажется в бд?
"
Ну как? Выживешь в такой ситуации?
А ведь именно так сейчас выглядит рынок найма - эти вопросы взяты с реальных собесов 😯
Рынок усложнился, и на вакансию джуна спрашивают как на синьора 3 года назад. Нужно реально шарить, и шарить глубоко, только на одних нейронках далеко не уедешь
Чтобы разобраться раз и навсегда со Spring, Kafka, Redis Паша Сорокин 18 декабря в 19:00 по МСК проведёт живой открытый урок для Java-разработчиков и тех, кто хочет им стать:
💚 За какие темы надо шарить, чтобы получить оффер на миддла
💚 В каком порядке и до какой глубины их изучать
💚 По каждой технологии (Spring Boot, HTTP, REST, Postgresql, Hibernate, микросервисы, Kafka, Redis) разберут как делать не надо и как делать надо
🟢 Ссылка на урок будет в закрытом канале урока
В этом же канале вас ждёт подарок - гайд "Roadmap из 11 шагов до ЗП в 200.000+"
P.S. Паша - ex Senior Dev в ВТБ с 6 годами коммерческого опыта, так что фигни не посоветует))
Больше десяти лет RestTemplate был главным вариантом для HTTP-запросов в Spring.
Прямолинейный. Синхронный. Простой.
Он больше не развивается: новых фич и улучшений не будет, плюс есть ограничения по ошибкам и расширяемости.
RestTemplateRestTemplate rest = new RestTemplate();
ResponseEntity<User> response =
rest.exchange(url, HttpMethod.GET, request, User.class);
User user = client.get()
.uri(url)
.header("Authorization", token)
.retrieve()
.body(User.class);
❓RestTemplate “мертв”
RestTemplate oldRestTemplate;
var restClient = RestClient.create(oldRestTemplate);