Spring Boot: для новых приложений выбирайте WebClient вместо RestTemplate
~ Реактивный и неблокирующий
~ Работает на event-loop, а не на модели один поток на запрос
~ Отлично подходит для микросервисной архитектуры
👉 Java Portal
~ Реактивный и неблокирующий
~ Работает на 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);
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤5
This media is not supported in your browser
VIEW IN TELEGRAM
Если у тебя Spring Boot приложение подключено как вложенный модуль и оно использует поддержку Docker Compose, то в IntelliJ IDEA нужно задать MODULE_WORKING_DIRECTORY, чтобы среда смогла корректно определить, где лежит файл compose.yaml.
👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤2
Spring Boot: можно добавить глобальные метаданные OpenAPI с помощью аннотации
Размести её на главном классе приложения Spring Boot или в отдельном конфигурационном классе.
@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 = "[email protected]",
url = "https://example.com/support"
),
license = @License(
name = "Apache 2.0",
url = "https://www.apache.org/licenses/LICENSE-2.0"
)
)
)
public class ApiApplication {
}
👍4
Spring Boot: аккуратно задавайте границы
Предположим, вы пишете что-то вроде
- Увеличивается время сканирования classpath
- Замедляется старт приложения
- Подтягиваются классы, которые вообще не должны регистрироваться как Spring-бины
Лучшие практики:
Полагайтесь на дефолты:
По умолчанию Spring сканирует только подпакеты пакета, в котором лежит MyApplication.
Сканируйте конкретные подпакеты:
👉 Java Portal
@ComponentScan, чтобы случайно не просканировать целиком весь пакет.Предположим, вы пишете что-то вроде
@ComponentScan("com.mycompany"), хотя на самом деле нужно сканировать только часть подпакетов com.mycompany:- Увеличивается время сканирования classpath
- Замедляется старт приложения
- Подтягиваются классы, которые вообще не должны регистрироваться как Spring-бины
Лучшие практики:
Полагайтесь на дефолты:
@SpringBootApplication
public class MyApplication { }
По умолчанию Spring сканирует только подпакеты пакета, в котором лежит MyApplication.
Сканируйте конкретные подпакеты:
@ComponentScan({
"com.mycompany.myapp.product",
"com.mycompany.myapp.order"
})Please open Telegram to view this post
VIEW IN TELEGRAM
❤5
Java совет: по возможности не возвращай из методов изменяемые внутренние коллекции. Это дает плюсы:
1. Не светишь внутреннее состояние, меньше рисков и утечек
2. Можно спокойно менять реализацию под капотом, не ломая остальной код
Если всё же нужно возвращать коллекции напрямую, можно сделать так:
- Вернуть глубокие копии коллекций (сложнее в реализации)
- Вернуть только read-only представление, например:
👉 Java Portal
1. Не светишь внутреннее состояние, меньше рисков и утечек
2. Можно спокойно менять реализацию под капотом, не ломая остальной код
Если всё же нужно возвращать коллекции напрямую, можно сделать так:
- Вернуть глубокие копии коллекций (сложнее в реализации)
- Вернуть только read-only представление, например:
Collections.unmodifiableList(items)
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Может пригодится: самохостящаяся система uptime-мониторинга, которую можно развернуть у себя и следить за тем, работают ли твои HTTP/S сервисы. Приложение показывает статус, считает аптайм, умеет проверять SSL, есть публичные статус-страницы, уведомления и прочее.
Забираем здесь
👉 Java Portal
Забираем здесь
Please open Telegram to view this post
VIEW IN TELEGRAM
Java-лайфхак: record — это не только про данные. В них можно добавить свои конструкторы или методы для валидации.
👉 Java Portal
// Конструкторы, статические методы и методы экземпляра:
public record Email(String address) {
// Конструктор с валидацией
public Email {
if (address == null || !address.matches("^[\\w-.]+@([\\w-]+\\.)+[\\w-]{2,4}$")) {
throw new IllegalArgumentException("Некорректный email: " + address);
}
}
// Метод экземпляра
public String domain() {
return address.substring(address.indexOf('@') + 1);
}
// Статический метод
public static Email from(String raw) {
return new Email(raw.trim().toLowerCase());
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥2
Понимание Git worktree. Отличная фича Git, особенно актуальная в эпоху агентных ИИ.
Подробнее
👉 Java Portal
Подробнее
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Git worktree — отличный способ поднять песочницу, куда можно запустить агентов искать решение, пока ты спокойно продолжаешь работу на master или другой ветке. На фото баш-скрипт, который : создаёт новый worktree/ветку под задачку с префиксом ga fix (типа fizzy--fix), а потом gd можно снести всё к чертям, когда закончил.
Если хочешь потыкать у себя — есть удобный gist для копипасты.
👉 Java Portal
Если хочешь потыкать у себя — есть удобный gist для копипасты.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Hidden классы в Java. Что скрывают Lambda выражения
С переходом Java на более безопасные и стандартизированные подходы к динамической генерации классов, скрытые (hidden) классы стали ключевым механизмом замены устаревшего
Они решают проблемы доступности, управления жизненным циклом и контроля доступа, особенно актуальные для разработчиков фреймворков и языков на JVM. Хотя скрытые классы пока не полностью заменяют функциональность Unsafe, они лежат в основе ряда важных механизмов, такие как, например, реализация лямбд в JDK.
Подробнее
👉 Java Portal
С переходом Java на более безопасные и стандартизированные подходы к динамической генерации классов, скрытые (hidden) классы стали ключевым механизмом замены устаревшего
Unsafe::defineAnonymousClassОни решают проблемы доступности, управления жизненным циклом и контроля доступа, особенно актуальные для разработчиков фреймворков и языков на JVM. Хотя скрытые классы пока не полностью заменяют функциональность Unsafe, они лежат в основе ряда важных механизмов, такие как, например, реализация лямбд в JDK.
Подробнее
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1
Spring Boot совет
В тестах на Spring Boot можно без лишних настроек использовать
Для TestRestTemplate есть аналогичная аннотация —
👉 Java Portal
В тестах на Spring Boot можно без лишних настроек использовать
RestTestClient через аннотацию @AutoConfigureRestTestClientДля TestRestTemplate есть аналогичная аннотация —
@AutoConfigureTestRestTemplate@SpringBootTest
@AutoConfigureRestTestClient
public class PersonControllerTests {
private static final String API_PATH = "/persons";
@Test
void add(@Autowired RestTestClient restTestClient) {
restTestClient.post().uri(API_PATH)
.body(Instancio.create(Person.class))
.exchange()
.expectStatus().is2xxSuccessful()
.expectBody(Person.class)
.value(person -> assertNotNull(person.getId()));
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5
LATENCY VS THROUGHPUT
Когда пользователи говорят что приложение «тормозит», причина может быть в двух плоскостях:
* проблема latency (каждый запрос обрабатывается медленно)
* проблема throughput (система забита и не тянет нагрузку)
Иногда это вообще смесь обоих.
Latency — время, которое сервер тратит на выполнение одного запроса от начала до ответа.
Throughput — сколько запросов сервер способен обработать за единицу времени.
Представим, что ты инженер в сервисе доставки пиццы.
Сценарий 1:
Пользователь нажимает «Отправить заказ» и ответ долго крутится — значит высокий latency. Для пользователя это воспринимается как «всё лагает».
Низкий latency = быстрый отклик для каждого конкретного запроса.
Сценарий 2:
Ответы идут примерно по 100 мс — вроде норм. Но если система способна держать только 10 запросов в секунду, а одновременно приходит тысяча, всё разваливается. Это низкий throughput.
Высокий throughput = возможность обслуживать много пользователей одновременно.
Обе метрики критичны, особенно когда система растёт по нагрузке.
* Ответы быстрые, но при всплеске трафика сыпятся таймауты — проблема в throughput.
* Каждый запрос тормозит даже при маленьком трафике — проблема в latency.
» Как чинят проблемы с latency (ускоряем отдельные запросы)
Причины: медленные SQL-запросы, тяжёлые внешние API, прожорливая логика, геолокация сервера и так далее.
Обычно помогают индексы в БД, кэширование, оптимизация кода, CDN, профилирование узких мест.
» Как фиксят проблемы с throughput (увеличиваем пропускную способность)
Обычно упирается в ресурсы. Масштабирование: больше серверов, балансировка нагрузки, кэширование, горизонтальное расширение и т. п.
P.S. Для пользовательских API чаще гонятся за низким latency, а для фоновых задач типа batch-процессинга чаще важнее throughput, чем скорость каждого отдельного задания.
Всё упирается в то, под что именно ты оптимизируешься.
👉 Java Portal
Когда пользователи говорят что приложение «тормозит», причина может быть в двух плоскостях:
* проблема latency (каждый запрос обрабатывается медленно)
* проблема throughput (система забита и не тянет нагрузку)
Иногда это вообще смесь обоих.
Latency — время, которое сервер тратит на выполнение одного запроса от начала до ответа.
Throughput — сколько запросов сервер способен обработать за единицу времени.
Представим, что ты инженер в сервисе доставки пиццы.
Сценарий 1:
Пользователь нажимает «Отправить заказ» и ответ долго крутится — значит высокий latency. Для пользователя это воспринимается как «всё лагает».
Низкий latency = быстрый отклик для каждого конкретного запроса.
Сценарий 2:
Ответы идут примерно по 100 мс — вроде норм. Но если система способна держать только 10 запросов в секунду, а одновременно приходит тысяча, всё разваливается. Это низкий throughput.
Высокий throughput = возможность обслуживать много пользователей одновременно.
Обе метрики критичны, особенно когда система растёт по нагрузке.
* Ответы быстрые, но при всплеске трафика сыпятся таймауты — проблема в throughput.
* Каждый запрос тормозит даже при маленьком трафике — проблема в latency.
» Как чинят проблемы с latency (ускоряем отдельные запросы)
Причины: медленные SQL-запросы, тяжёлые внешние API, прожорливая логика, геолокация сервера и так далее.
Обычно помогают индексы в БД, кэширование, оптимизация кода, CDN, профилирование узких мест.
» Как фиксят проблемы с throughput (увеличиваем пропускную способность)
Обычно упирается в ресурсы. Масштабирование: больше серверов, балансировка нагрузки, кэширование, горизонтальное расширение и т. п.
P.S. Для пользовательских API чаще гонятся за низким latency, а для фоновых задач типа batch-процессинга чаще важнее throughput, чем скорость каждого отдельного задания.
Всё упирается в то, под что именно ты оптимизируешься.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Подсказка по Spring Boot: если нужно, чтобы DTO спокойно игнорировали лишние поля в JSON от клиента и не ломали API, можно повесить
Дано DTO:
Если с клиента прилетает такой JSON:
Получим ошибку:
Эту ситуацию решаем через аннотацию
👉 Java Portal
@JsonIgnoreProperties(ignoreUnknown = true)Дано DTO:
public class UserDTO {
private String name;
private int age;
// геттеры и сеттеры
}Если с клиента прилетает такой JSON:
{
"name": "Alice",
"age": 25,
"extraField": "not expected"
}Получим ошибку:
UnrecognizedPropertyException: Unrecognized field "extraField"
Эту ситуацию решаем через аннотацию
@JsonIgnoreProperties:@JsonIgnoreProperties(ignoreUnknown = true)
public class UserDTO {
...
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤3
Spring Boot: дорогие по времени API-вызовы можно кешировать через
Первый запрос с конкретным ключом идет в реальный API.
Все повторные — из Redis за миллисекунды.
Вот ссылка на демо-проект на Spring Boot (для работы нужен запущенный Redis на порту 6379)
👉 Java Portal
@Cacheable и Redis.Первый запрос с конкретным ключом идет в реальный API.
Все повторные — из Redis за миллисекунды.
<!-- Добавляем зависимости -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-b
# Базовая настройка в application.yml
spring:
redis:
host: localhost
port: 6379
cache:
type: redis
server:
port: 8080
// Конфигурация Redis (TTL 10 минут)
@Bean
public RedisCacheConfiguration cacheConfig() {
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10))
.disableCachingNullValues();
}
// Кэшируем метод сервиса
@Cacheable("users")
public User getUserById(Long id) {
return userService.fetchUserFromRemoteApi(id);
}
Вот ссылка на демо-проект на Spring Boot (для работы нужен запущенный Redis на порту 6379)
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍5🔥5
Spring Boot Istio 1.2.0 вышел
Что подтянули в обновлении:
🍃авто-генерация fault-ов
🍃 авто-создание и инжект gateway
🍃 авто-генерация HTTP matches
Детали тут : https://github.com/piomin/spring-boot-istio
👉 Java Portal
Что подтянули в обновлении:
🍃авто-генерация fault-ов
🍃 авто-создание и инжект gateway
🍃 авто-генерация HTTP matches
Детали тут : https://github.com/piomin/spring-boot-istio
Please open Telegram to view this post
VIEW IN TELEGRAM
Принцип открытости/закрытости (OCP): это принцип из SOLID, который говорит, что классы должны быть открыты для расширения, но закрыты для модификации.
Вместо правки существующей реализации стоит добавлять новое поведение через интерфейсы и наследование в Java, не трогая уже написанный код.
👉 Java Portal
Вместо правки существующей реализации стоит добавлять новое поведение через интерфейсы и наследование в Java, не трогая уже написанный код.
// Плохой способ реализовать генератор отчётов
class ReportGenerator {
public void generateReport(String type) {
if (type.equals("PDF")) {
System.out.println("Generating PDF Report...");
} else if (type.equals("CSV")) {
System.out.println("Generating CSV Report...");
} else if (type.equals("EXCEL")) {
System.out.println("Generating Excel Report...");
}
}
}
// Более правильный вариант
// Определяем интерфейс
interface Report {
void generate();
}
// Реализации
class PdfReport implements Report {
public void generate() {
System.out.println("Generating PDF Report...");
}
}
class CsvReport implements Report {
public void generate() {
System.out.println("Generating CSV Report...");
}
}
// Используем конкретную реализацию
public class ReportApp {
public static void main(String[] args) {
Report report = new PdfReport();
report.generate();
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍8❤3🤔2
У платформенных потоков в Java есть 6 состояний. У виртуальных потоков их около 20, но они скрыты от нас.
Держите свежий выпуск, где автор показывает, как с помощью глубокой рефлексии можно разобраться, как это вообще устроено:
Узнаем здесь!🤭
👉 Java Portal
Держите свежий выпуск, где автор показывает, как с помощью глубокой рефлексии можно разобраться, как это вообще устроено:
Узнаем здесь!
Please open Telegram to view this post
VIEW IN TELEGRAM
www.javaspecialists.eu
[JavaSpecialists 331] - Virtual Thread States
No major feature has been adopted as quickly into production systems as virtual threads. Not generics. Not streams. But there are some catches. Original Java threads have six states. Virtual threads have 20 states, which Java maps onto the original six states.…
👍7
Java-совет : избегай преждевременной оптимизации.
Сначала сосредоточься на чистом, рабочем коде.
Пиши код, который корректно работает, и в первую очередь закрывает функциональные требования.
👉 Java Portal
Сначала сосредоточься на чистом, рабочем коде.
Пиши код, который корректно работает, и в первую очередь закрывает функциональные требования.
// Простейший пример
String helloThere = "Hello " + "there" + "!";
System.out.println(helloThere);
// Эта строка кода читаемая и работает как задумано
// Позже можно подумать об оптимизации через StringBuilder
StringBuilder sb = new StringBuilder();
sb.append("Hello ");
sb.append("there");
sb.append("!");
System.out.println(sb.toString());
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🤔4❤2
Arconia для Spring Boot Dev Services и Observability
В этом посте объясняется, как использовать фреймворк Arconia для улучшения опыта разработки со Spring Boot. Проект совсем свежий и активно развивается. Меня он зацепил одной фичей, которую я люблю в Quarkus, но которой не хватает в Spring Boot. Речь про Dev Services. Если вы работали с Quarkus, то наверняка знаете, о чём речь. Dev Services позволяют автоматически поднимать не сконфигуренные сервисы в dev-режиме и при тестировании. Как и в Quarkus, Arconia базируется на Testcontainers и использует поддержку Spring Boot Testcontainers.
Чтобы разобраться, как Spring Boot работает с Testcontainers, посмотрите статью на эту тему. Если интересуют Dev Services в Quarkus, то вот пост, который фокусируется на автоматизации тестирования в Quarkus.
Можете использовать этот исходный код, если хотите попробовать. Для этого просто склонируйте пример из GitHub и следуйте инструкциям.
👉 Java Portal
В этом посте объясняется, как использовать фреймворк Arconia для улучшения опыта разработки со Spring Boot. Проект совсем свежий и активно развивается. Меня он зацепил одной фичей, которую я люблю в Quarkus, но которой не хватает в Spring Boot. Речь про Dev Services. Если вы работали с Quarkus, то наверняка знаете, о чём речь. Dev Services позволяют автоматически поднимать не сконфигуренные сервисы в dev-режиме и при тестировании. Как и в Quarkus, Arconia базируется на Testcontainers и использует поддержку Spring Boot Testcontainers.
Чтобы разобраться, как Spring Boot работает с Testcontainers, посмотрите статью на эту тему. Если интересуют Dev Services в Quarkus, то вот пост, который фокусируется на автоматизации тестирования в Quarkus.
Можете использовать этот исходный код, если хотите попробовать. Для этого просто склонируйте пример из GitHub и следуйте инструкциям.
Please open Telegram to view this post
VIEW IN TELEGRAM