Media is too big
VIEW IN TELEGRAM
В этом видео автор подробно разбирает, как создавать и использовать функции (методы) в Java.
Пошагово объясняется, как объявлять методы, передавать параметры, возвращать значения и вызывать их из других частей программы.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
Java 8 позволяет добавлять неабстрактные реализации методов в интерфейс, используя ключевое слово
default:
interface Example {
int process(int a);
default void show() {
System.out.println("default show()");
}
}
java.lang.Object.Вызывать
default метод интерфейса в реализующем этот интерфейс классе можно используя ключевое слово super вместе с именем интерфейса:
interface Paper {
default void show() {
System.out.println("default show()");
}
}
class Licence implements Paper {
public void show() {
Paper.super.show();
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍6
Java является строго типизированным языком программирования, а это означает, то что каждое выражение и каждая переменная имеет строго определенный тип уже на момент компиляции. Однако определен механизм приведения типов (casting) - способ преобразования значения переменной одного типа в значение другого типа.
В Java существуют несколько разновидностей приведения:
✔️ Тождественное (identity). Преобразование выражения любого типа к точно такому же типу всегда допустимо и происходит автоматически.
✔️ Расширение (повышение, upcasting) примитивного типа (widening primitive). Означает, что осуществляется переход от менее емкого типа к более ёмкому. Например, от типа
byte (длина 1 байт) к типу int (длина 4 байта). Такие преобразование безопасны в том смысле, что новый тип всегда гарантировано вмещает в себя все данные, которые хранились в старом типе и таким образом не происходит потери данных. Этот тип приведения всегда допустим и происходит автоматически.✔️ Сужение (понижение, downcasting) примитивного типа (narrowing primitive). Означает, что переход осуществляется от более емкого типа к менее емкому. При таком преобразовании есть риск потерять данные. Например, если число типа
int было больше 127, то при приведении его к byte значения битов старше восьмого будут потеряны. В Java такое преобразование должно совершаться явным образом, при этом все старшие биты, не умещающиеся в новом типе, просто отбрасываются - никакого округления или других действий для получения более корректного результата не производится.✔️ Расширение объектного типа (widening reference). Означает неявное восходящее приведение типов или переход от более конкретного типа к менее конкретному, т.е. переход от потомка к предку. Разрешено всегда и происходит автоматически.
✔️ Сужение объектного типа (narrowing reference). Означает нисходящее приведение, то есть приведение от предка к потомку (подтипу). Возможно только если исходная переменная является подтипом приводимого типа. При несоответствии типов в момент выполнения выбрасывается исключение
ClassCastException. Требует явного указания типа.✔️ Преобразование к строке (to String). Любой тип может быть приведен к строке, т.е. к экземпляру класса
String.✔️ Запрещенные преобразования (forbidden). Не все приведения между произвольными типами допустимы. Например, к запрещенным преобразованиям относятся приведения от любого ссылочного типа к примитивному и наоборот (кроме преобразования к строке). Кроме того, невозможно привести друг к другу классы, находящиеся на разных ветвях дерева наследования и т.п.
При приведении ссылочных типов с самим объектом ничего не происходит, - меняется лишь тип ссылки, через которую происходит обращение к объекту.
Для проверки возможности приведения нужно воспользоваться оператором
instanceof:
Parent parent = new Child();
if (parent instanceof Child) {
Child child = (Child) parent;
}
#java #casting #upcasting #downcasting
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤3🔥2
Java всегда славилась многопоточкой, но классические треды прожорливы: на каждый создаётся отдельный системный поток. Loom это меняет — теперь у нас есть виртуальные потоки (virtual threads).
🧵 Старый способ — тяжёлые треды
Thread thread = new Thread(() -> {
System.out.println("Hello from thread!");
});
thread.start();
Thread vThread = Thread.ofVirtual().start(() -> {
System.out.println("Hello from virtual thread!");
});
for (int i = 0; i < 1_000_000; i++) {
Thread.ofVirtual().start(() -> work());
}
OutOfMemoryError. На обычных тредах — краш.
Thread.ofVirtual().start(() -> {
Thread.sleep(1000); // блокируемся
System.out.println("Done!");
});
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> fetchFromApi());
executor.submit(() -> readFromDb());
}
Files.lines(Path.of("data.txt"))
.forEach(line -> System.out.println(line));
🟢 сетевые сервисы (много коннектов);🟢 микросервисы;🟢 парсеры/скрейперы;🟢 всё, где тысячи одновременных задач.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤5🔥1
Integer a = 128;
Integer b = 128;
System.out.println(a == b); // false
Integer x = 1;
Integer y = 1;
System.out.println(x == y); // true
Это не просто волшебство! Это связано с целочисленным кэшированием в Java.
Для значений в диапазоне от -128 до 127 объекты типа
Integer кэшируются, и ссылки на них одинаковы, а для значений за пределами этого диапазона создаются новые объекты, и ссылки на них будут разными. Так как используется оператор ==, то сравниваются ссылки на объекты.#java #magic
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18❤2
Media is too big
VIEW IN TELEGRAM
В этом видео автор объясняет, что такое конструкторы в Java и зачем они нужны.
Пошагово показывается, как создавать конструкторы, передавать параметры при инициализации объектов и использовать перегрузку для разных вариантов создания экземпляров класса.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍2🔥2
Spring Boot 3 принёс AOT (Ahead-Of-Time) компиляцию. Теперь можно собирать приложение в нативный бинарь через GraalVM и запускать его почти мгновенно. Это не просто оптимизация — это новый уровень для микросервисов.
Разберём, что это даёт в реальных проектах
./mvnw -Pnative native:compile
./target/demo
FROM ghcr.io/graalvm/native-image:latest as builder
WORKDIR /app
COPY . .
RUN ./mvnw -Pnative native:compile
FROM ubuntu:22.04
COPY --from=builder /app/target/demo /demo
CMD ["/demo"]
JVM-приложение: 150–200 MB RAM
AOT-приложение: 20–40 MB RAM
@SpringBootApplication
public class DemoApp {
public static void main(String[] args) {
SpringApplication.run(DemoApp.class, args);
}
}
reflect-config.json), чтобы GraalVM понял, какие классы реально нужны.@TypeHint(types = {MyEntity.class}, access = AccessBits.ALL)
public class MyHints implements NativeConfiguration {}Class.forName(), нужно явно подсказать GraalVM. Иначе приложение упадёт.@GetMapping("/hello")
public Mono<String> hello() {
return Mono.just("Привет из AOT!");
}🟢 JIT (обычная JVM) быстрее на долгих задачах, потому что оптимизирует байткод во время работы.🟢 AOT быстрее стартует и экономнее в памяти, но чуть медленнее на горячем коде.
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: ./mvnw -Pnative native:compile
- uses: actions/upload-artifact@v3
with:
name: demo-binary
path: target/demo
👍 Долгая компиляция (в разы медленнее, чем обычный mvn package).👍 Динамические фреймворки (Hibernate proxies, некоторые reflection API) требуют ручных hint'ов.👍 Не всегда легко дебажить — стек-трейсы короче.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍5🔥3
🌊 JEP 458: Stream Gatherers в Java 22
Java 22 добавила Gatherers — расширение Stream API, которое позволяет собирать элементы не только привычными map/filter/reduce, а своими кастомными способами. Теперь поток можно «собирать» прямо в процессе, без костылей.
📦 Простая идея Gatherer
➡️ Элементы пакуются по 2 штуки.
Вывод:
🔄 Sliding Window — скользящее окно
➡️ Формируются окна по 3 элемента с шагом в 1.
Вывод:
🗂 Группировка по условию
➡️ Группа закрывается, когда встречается "x".
Вывод:
⚙️ Свой Gatherer
➡️ Числа суммируются, пока не превысят 10, потом сбрасываются.
📊 Gatherers.fold — промежуточная агрегация
➡️ Видим все промежуточные суммы.
Вывод:
⚖️ Gatherers 🆚 Collectors
➡️ Это позволяет писать stateful-преобразования, которые раньше выглядели громоздко.
🚀 Реальные применения
➡️ Всё это теперь можно писать нативно в Stream API.
🔗 Комбинации
➡️ Сначала разбиваем на пары, потом суммируем каждую.
Вывод:
🗣️ Запомни: Gatherers в Java 22 выводят стримы на новый уровень. Они позволяют обрабатывать данные «на лету» и строить stateful-пайплайны, которые раньше были невозможны без костылей.
Java 22 добавила Gatherers — расширение Stream API, которое позволяет собирать элементы не только привычными map/filter/reduce, а своими кастомными способами. Теперь поток можно «собирать» прямо в процессе, без костылей.
Stream.of(1, 2, 3, 4, 5)
.gather(Gatherers.windowFixed(2))
.forEach(System.out::println);
Вывод:
[1, 2]
[3, 4]
[5]
Stream.of(1, 2, 3, 4, 5)
.gather(Gatherers.windowSliding(3))
.forEach(System.out::println);
Вывод:
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
Stream.of("a", "b", "x", "y", "c", "d")
.gather(Gatherers.groupUntil(s -> s.equals("x")))
.forEach(System.out::println);
Вывод:
[a, b, x]
[y, c, d]
Gatherer<Integer, ?, Integer> sumPairs =
Gatherer.of(
() -> new int[1], // state
(state, e, down) -> {
state[0] += e;
if (state[0] >= 10) {
down.push(state[0]);
state[0] = 0;
}
return true;
},
(state, down) -> {}
);
Stream.of(3, 4, 5, 6, 7)
.gather(sumPairs)
.forEach(System.out::println);
12
13
Stream.of(1, 2, 3, 4, 5)
.gather(Gatherers.fold(0, Integer::sum))
.forEach(System.out::println);
Вывод:
1
3
6
10
15
Collectors работают в конце стрима (collect(...)).
Gatherers же действуют прямо в процессе, шаг за шагом.
1. Буферизация при работе с большими потоками.
2. Batch-загрузки в БД.
3. Скользящие средние и метрики.
4. Stateful-пайплайны без ручного кода.
Stream.of(1, 2, 3, 4, 5, 6)
.gather(Gatherers.windowFixed(2))
.map(list -> list.stream().mapToInt(i -> i).sum())
.forEach(System.out::println);
Вывод:
3
7
11
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤9🔥6✍2
Media is too big
VIEW IN TELEGRAM
В этом видео автор показывает, как в Java создавать собственные классы и объекты.
Разбирается структура класса, определение полей и методов, а также процесс создания экземпляров через ключевое слово new. Это практическая основа объектно-ориентированного программирования, без которой невозможно двигаться дальше.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5💊5❤3🔥1
Когда нужен прямой доступ к памяти без
Unsafe и JNI — помогает Panama API.MemorySegment и MemorySession позволяют работать с off-heap памятью безопасно, быстро и управляемо.try (var session = MemorySession.openConfined()) {
MemorySegment segment = MemorySegment.allocateNative(100, session);
System.out.println("Размер: " + segment.byteSize());
}try (var session = MemorySession.openConfined()) {
MemorySegment seg = MemorySegment.allocateNative(8, session);
seg.set(ValueLayout.JAVA_INT, 0, 42);
int x = seg.get(ValueLayout.JAVA_INT, 0);
System.out.println(x);
}try (var session = MemorySession.openConfined()) {
MemorySegment seg = MemorySegment.allocateNative(4 * 5, session);
for (int i = 0; i < 5; i++) {
seg.setAtIndex(ValueLayout.JAVA_INT, i, i * 10);
}
System.out.println(seg.getAtIndex(ValueLayout.JAVA_INT, 3));
}setAtIndex и getAtIndex делают сегмент похожим на массив.🌀 MemorySession = контроль жизни
MemorySegment seg;
try (var session = MemorySession.openConfined()) {
seg = MemorySegment.allocateNative(16, session);
}
System.out.println(seg.isAlive()); // false
📑 Slice и view памяти
try (var session = MemorySession.openConfined()) {
MemorySegment seg = MemorySegment.allocateNative(16, session);
MemorySegment part = seg.asSlice(4, 8);
System.out.println("Slice: " + part.byteSize());
}try (var session = MemorySession.openConfined()) {
MemorySegment seg = MemorySegment.allocateNative(4, session);
seg.set(ValueLayout.JAVA_INT, 0, 123);
long addr = seg.address();
System.out.println("Адрес: " + addr);
}Please open Telegram to view this post
VIEW IN TELEGRAM
1👍5❤4🤔1
Обычно JFR включают флагами JVM, но начиная с Java 14 появился Events API. Теперь можно описывать и логировать свои события прямо из кода, без тяжёлых логгеров.
import jdk.jfr.Event;
import jdk.jfr.Label;
class LoginEvent extends Event {
@Label("User")
String username;
@Label("Success")
boolean success;
}
Event и описываем поля — это как лог-сообщение, но встроенное в JVM.LoginEvent e = new LoginEvent();
e.username = "ivan";
e.success = true;
e.commit();
java -XX:StartFlightRecording=filename=recording.jfr,duration=20s -jar app.jar
import jdk.jfr.Category;
@Category("Auth")
class LoginEvent extends Event {
String username;
}
class CalcEvent extends Event {}
CalcEvent e = new CalcEvent();
e.begin();
// тяжёлая операция
e.end();
e.commit();LoginEvent e = new LoginEvent();
e.username = "alex";
e.success = false;
e.commit();
System.out.println("Login attempt failed: alex");
class TaskEvent extends Event {
String thread;
}
TaskEvent e = new TaskEvent();
e.thread = Thread.currentThread().getName();
e.commit();📑 Анализ в Mission Control
Открываешь recording.jfr — и видишь всё: GC, потоки, задержки и свои кастомные события.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤1
Record убирает шаблонный код и делает объект иммутабельным.
Sealed Class закрывает иерархию и контролирует наследование.
Разные задачи, но вместе решают боль старой Java.
public record User(String name, int age) {}
class User {
private final String name;
private final int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String name() { return name; }
public int age() { return age; }
}
public record Product(String name, int price) {
public Product {
if (price < 0) throw new IllegalArgumentException("price < 0");
}
}
public sealed class Shape permits Circle, Rectangle {}
public final class Circle extends Shape {}
public final class Rectangle extends Shape {}
public sealed interface Payment
permits CardPayment, CashPayment {}
public record CardPayment(String number) implements Payment {}
public record CashPayment(double amount) implements Payment {}
Payment p = new CardPayment("1234");
switch (p) {
case CardPayment c -> System.out.println("💳 " + c.number());
case CashPayment c -> System.out.println("💵 " + c.amount());
}
📐 Sealed vs enum
sealed interface Command permits Reboot, Shutdown {}
record Reboot() implements Command {}
record Shutdown() implements Command {}
sealed interface Event permits Login, Logout {}
record Login(String user) implements Event {}
record Logout(String user) implements Event {}
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍5🔥3
Text Blocks (
""") позволяют удобно работать с многострочными строками без необходимости экранировать кавычки.• Улучшают читаемость кода.• Поддерживают форматирование и перенос строк.• Упрощают работу с JSON, SQL и HTML.
String json = """
{
"name": "Alice",
"age": 30
}
""";
System.out.println(json);
#java #textblocks #java15
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤3🔥3
С появлением виртуальных потоков (
Project Loom) старый ThreadLocal стал проблемой. Он держит данные на весь поток, что для миллионов lightweight-тредов дорого. Решение — ScopedValue.import java.lang.ScopedValue;
public class Demo {
static final ScopedValue<String> USER = ScopedValue.newInstance();
}
🧩 Передача значения в scope
ScopedValue.where(USER, "Alice").run(() -> {
System.out.println("Hello, " + USER.get());
});run(). Вышел из scope — данных нет.⚡️ Виртуальные потоки + ScopedValue
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() ->
ScopedValue.where(USER, "Bob").run(() -> {
System.out.println("User in virtual thread: " + USER.get());
})
);
}ThreadLocal<String> local = new ThreadLocal<>();
local.set("Alice");
ScopedValue.where(USER, "Charlie").run(() -> {
// USER.set("Dave"); ❌ нельзя
System.out.println(USER.get());
});ScopedValue.where(USER, "Admin").run(() -> {
new Thread(() -> System.out.println(USER.get())).start();
});🟢 Контекст запроса (user id, trace id).🟢 Логирование.🟢 Передача настроек без проброса аргументов.
🔴 Только read-only.🔴 Живёт только в рамках scope.🔴 Работает начиная с Java 20+.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤3
Раньше, чтобы вызвать C-код из Java, приходилось страдать с JNI — заголовки,
javah, System.loadLibrary(), краши и боль.Теперь всё проще: Foreign Function & Memory API (FFM) — новый стандарт для прямого доступа к нативным функциям и памяти.FFM API позволяет вызывать функции из
libc без JNI.import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
try (Arena arena = Arena.openConfined()) {
Linker linker = Linker.nativeLinker();
SymbolLookup libc = linker.defaultLookup();
MethodHandle printf = linker.downcallHandle(
libc.find("printf").get(),
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS)
);
MemorySegment msg = arena.allocateUtf8String("Hello from C!\n");
printf.invoke(msg);
}
jni.h, с полным контролем памяти.try (Arena arena = Arena.openConfined()) {
MemorySegment segment = arena.allocate(4);
segment.set(ValueLayout.JAVA_INT, 0, 1337);
System.out.println(segment.get(ValueLayout.JAVA_INT, 0));
}Unsafe и без GC-хаоса.Хочешь вызвать
strlen или qsort? Пожалуйста.MethodHandle strlen = linker.downcallHandle(
libc.find("strlen").get(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
);
long len = (long) strlen.invoke(arena.allocateUtf8String("PyLinux"));
System.out.println(len);
🧰 Безопасность и контроль
FFM API чётко ограничивает область памяти (через Arena), предотвращая утечки и use-after-free.
Можно использовать “scoped memory” — живёт ровно столько, сколько нужно.
JNI требует перехода между Java и C стэками → дорого.
FFM API компилируется JIT’ом и оптимизируется HotSpot → почти нативная скорость.
jextract генерирует Java-обёртки по .h-файлам.jextract -t com.libc /usr/include/stdio.h
🧱 Будущее без JNI
С FFM API Java получает настоящий доступ к низкоуровневому миру:
🟢 без C-кода,🟢 без падений JVM,🟢 без плясок с System.load().
Скорость и контроль — без риска.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍7🔥6👎1🥰1
Kafka Streams и ksqlDB + Java: живые системы на событиях
Сервисы больше не должны “ждать запрос”. Современные системы реагируют на события — в реальном времени. Kafka Streams и ksqlDB превращают поток данных в ядро приложения, а не просто транспорт сообщений.
⚡️ 1. Архитектура event-driven
Kafka — это не просто очередь. Это commit-log всех событий в компании:
🟠 пользователь оплатил заказ,🟠 склад обновил остатки,🟠 ML-модель прислала прогноз.
🧩 2. Kafka Streams — логика прямо в потоке
Пример: считаем количество заказов в минуту по пользователю
StreamsBuilder builder = new StreamsBuilder();
KStream<String, String> orders = builder.stream("orders");
orders.groupByKey()
.windowedBy(TimeWindows.of(Duration.ofMinutes(1)))
.count()
.toStream()
.to("orders_per_minute");
KafkaStreams streams = new KafkaStreams(builder.build(), props);
streams.start();
📊 3. KTable и состояние
Kafka Streams позволяет хранить состояние между событиями:
KTable<String, Long> orderCounts = orders
.groupByKey()
.count(Materialized.as("orders-store"));
🧠 4. ksqlDB — SQL для стримов
Если не хочется писать Java-код, можно просто запросить поток SQL-ом:
CREATE STREAM orders_stream (user_id VARCHAR, price DOUBLE)
WITH (KAFKA_TOPIC='orders', VALUE_FORMAT='JSON');
CREATE TABLE user_revenue AS
SELECT user_id, SUM(price) AS total
FROM orders_stream
WINDOW TUMBLING (SIZE 1 HOUR)
GROUP BY user_id;
🔄 5. Реактивные микросервисы
Сервисы больше не ходят в БД, чтобы “узнать новое”.
Kafka — это их канал событий.
@KafkaListener(topics = "user_revenue")
public void handleRevenue(String msg) {
System.out.println("💰 " + msg);
}
[orders] → [Kafka Streams: агрегация] → [ksqlDB: фильтрация] → [billing-service]
Kafka Streams хранит state в RocksDB и реплицирует его между инстансами.
Перезапуск — и поток сам продолжает с последнего offset.
@EnableKafkaStreams
@Configuration
public class StreamConfig {
@Bean
public KStream<String, String> kStream(StreamsBuilder builder) {
return builder.stream("orders");
}
}
Kafka связывает микросервисы, ML, ETL и аналитические пайплайны.
Хранилище, транспорт и очередь — всё в одном.
| Сценарий | Что выбрать |
| ----------------------------- | ---------------- |
| Быстрый прототип, SQL-запросы | ksqlDB |
| Сложная логика, типизация | Kafka Streams |
| Обработка JSON и Avro | оба поддерживают |
| Глубокая интеграция с Java | Streams |
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤3🔥3👏1
Мониторинг, трассировка и метрики, которые спасают прод
Observability — это не просто графики и алерты. Это способ понять, что реально происходит внутри системы.
В отличие от обычного мониторинга, observability показывает почему всё пошло не так.
Разберём, как инженеры держат прод под контролем
📡 Metrics — сердце наблюдаемости
Prometheus + Grafana — золотой стандарт.
# prometheus.yml
scrape_configs:
- job_name: 'app'
static_configs:
- targets: ['app:8080']
Grafana визуализирует, Prometheus хранит и алертит.
Elastic Stack или Loki от Grafana.
docker run -d -p 9200:9200 elasticsearch
docker run -d -p 5601:5601 kibana
Хочешь знать, почему? Иди в логи.
🚦 Traces — кто виноват в задержках
OpenTelemetry + Jaeger показывают путь запроса через микросервисы.
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("db_query"):
query_database()
С его помощью видно, где реально теряется время.
Собирает метрики, логи и трассировки из любого кода.
pip install opentelemetry-sdk opentelemetry-exporter-otlp
Prometheus + Alertmanager → уведомления при SLA-фейле.
- alert: HighErrorRate
expr: rate(http_requests_total{status="500"}[5m]) > 0.05
for: 2m
labels:
severity: critical
🧱 Distributed Context — связать всё вместе
Код → метрики → логи → трассы — одно пространство событий.
Zipkin, Tempo, Jaeger — строят полную цепочку от запроса до ответа.
Библиотеки сами шлют метрики: Spring Boot Actuator, FastAPI middleware, Django signals.
AWS CloudWatch, GCP Operations Suite, Datadog, New Relic.
Для Kubernetes — Lens, K9s, Grafana Tempo.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3🔥3👍2
Contract Testing в Java — контроль реальности между продами
Когда микросервисы множатся, интеграционные тесты превращаются в хаос.
Сервис А ждёт одно, сервис B отдаёт другое — и привет, баг в проде.
Contract Testing решает это через договор: что именно API обещает и возвращает.
Потребитель (Consumer) описывает, что он ждёт от поставщика (Provider).
Provider подтверждает: «Да, я действительно так отвечаю».
Pact позволяет Consumer’у описывать ожидаемые запросы и ответы:
@Pact(consumer = "OrderService")
public RequestResponsePact createPact(PactDslWithProvider builder) {
return builder
.given("User exists")
.uponReceiving("a request for user details")
.path("/users/123")
.method("GET")
.willRespondWith()
.status(200)
.body("{\"id\":123,\"name\":\"Alex\"}")
.toPact();
}
@ExtendWith(PactConsumerTestExt.class)
@PactTestFor(providerName = "UserService", port = "8080")
public void testUserService(MockServer server) {
Response response = get(server.getUrl() + "/users/123");
assertEquals(200, response.getStatusCode());
}
Поставщик поднимает mock и проверяет:
«Я реально возвращаю то, что обещал в контракте».
@Provider("UserService")
@PactFolder("pacts")
class UserServicePactTest {
@TestTarget
final Target target = new HttpTarget(8080);
@State("User exists")
public void userExists() {
// фикстуры для теста
}
}Все контракты хранятся в брокере.
Каждый сервис знает, с кем и что согласовано.
docker run -d -p 9292:9292 pactfoundation/pact-broker
🧩 Contract Testing ≠ интеграция
Интеграционные тесты требуют запущенные сервисы.
Контрактные тесты — только спецификации.
Работает прямо в Spring Boot.
contract {
request {
method 'GET'
url '/users/123'
}
response {
status 200
body(id: 123, name: "Alex")
}
}➕ Нет неожиданностей при релизах.➕ CI ломается до продакшена, если кто-то нарушил контракт.➕ Каждый сервис тестируется изолированно.
➖ Нужно поддерживать актуальность контрактов.➖ При множестве сервисов — много файлов.➖ Не ловит ошибки логики (только интерфейсов).
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤4🔥3
Media is too big
VIEW IN TELEGRAM
В этом видео автор подробно объясняет концепцию наследования в Java: как один класс может наследовать свойства и методы другого, когда применять `extends`, как переопределять методы и использовать ключевое слово `super`.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4🔥3
Media is too big
VIEW IN TELEGRAM
В этом видео автор подробно объясняет концепцию полиморфизма в объектно-ориентированном программировании на примере Java.
Разбирается, как один объект может иметь разные формы в зависимости от контекста, как работает динамическое связывание, как переопределять методы в наследниках и использовать ссылки на родительские типы для управления объектами.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3❤🔥2