Java for Beginner
780 subscribers
771 photos
220 videos
12 files
1.29K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Паттерны использования

Безопасное удаление во время итерации
Iterator<Item> iterator = collection.iterator();
while (iterator.hasNext()) {
Item item = iterator.next();
if (shouldRemove(item)) {
iterator.remove(); // Безопасное удаление
}
}


Фильтрация коллекции
public static <T> void filter(Collection<T> collection, Predicate<T> predicate) {
Iterator<T> iterator = collection.iterator();
while (iterator.hasNext()) {
if (!predicate.test(iterator.next())) {
iterator.remove();
}
}
}



Очистка коллекции по условию

// Удалить все null элементы
iterator = list.iterator();
while (iterator.hasNext()) {
if (iterator.next() == null) {
iterator.remove();
}
}



Сравнение с альтернативными подходами

Iterator.remove() vs Collection.remove()

Преимущества Iterator.remove():
Безопасность во время итерации
Корректное обновление состояния итератора
Оптимизированная реализация для конкретной структуры данных


Недостатки Collection.remove():
Риск ConcurrentModificationException
Необходимость повторного поиска элемента
Потенциальная неэффективность


Iterator.remove() vs Copy-and-Filter

Копирование с фильтрацией:

List<Item> filtered = new ArrayList<>();
for (Item item : original) {
if (!shouldRemove(item)) {
filtered.add(item);
}
}
original = filtered;


Сравнение:

Копирование: Проще, но требует дополнительной памяти
Итератор: Эффективнее по памяти, но требует осторожности
Интеграция трех методов: Полный цикл итерации
Согласованная работа hasNext(), next() и remove()


Эти три метода образуют единую систему, где каждый играет свою роль в процессе итерации:
public class SafeIterationExample {
public static void processAndRemove(Collection<Data> collection) {
Iterator<Data> iterator = collection.iterator();

// Фаза 1: Подготовка и проверка
while (iterator.hasNext()) {

// Фаза 2: Получение элемента
Data data = iterator.next();

// Фаза 3: Обработка и возможное удаление
if (data.isProcessed()) {
iterator.remove(); // Безопасное удаление
} else {
data.process();
}
}
}
}



#Java #для_новичков #beginner #Iterator
👍1
Паттерны управления состоянием

Управление состоянием lastReturned


Правильное управление полем lastReturned критически важно для корректной работы remove():
Установка: В next() после успешного получения элемента
Сброс: В remove() после успешного удаления
Проверка: Перед вызовом remove() для валидации состояния


Синхронизация счетчиков модификаций

Для fail-fast итераторов необходимо поддерживать синхронизацию:
expectedModCount: Сохраняется при создании итератора
modCount: Обновляется при структурных изменениях коллекции
Сравнение: При каждой операции проверяется равенство счетчиков


Расширенные сценарии использования

Итерация с пропуском элементов
public static <T> void skipAndProcess(Iterator<T> iterator, int skipCount) {
// Пропуск первых N элементов
for (int i = 0; i < skipCount && iterator.hasNext(); i++) {
iterator.next();
}

// Обработка оставшихся с возможным удалением
while (iterator.hasNext()) {
T element = iterator.next();
if (shouldProcess(element)) {
processElement(element);
} else {
iterator.remove();
}
}
}


Пакетная обработка с удалением
public static <T> List<T> batchProcessAndRemove(
Collection<T> collection,
Predicate<T> removalCondition,
int batchSize) {

List<T> removed = new ArrayList<>();
Iterator<T> iterator = collection.iterator();
int processed = 0;

while (iterator.hasNext() && processed < batchSize) {
T element = iterator.next();
if (removalCondition.test(element)) {
removed.add(element);
iterator.remove();
}
processed++;
}

return removed;
}



Best Practices и рекомендации


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

Используйте enhanced for-loop когда возможно:
for (Element element : collection) {
// Автоматическое управление итератором
}


Избегайте ненужных вызовов hasNext():
// Неоптимально:
while (iterator.hasNext()) {
if (condition) {
process(iterator.next());
}
}

// Оптимально:
while (iterator.hasNext()) {
Element element = iterator.next();
if (condition) {
process(element);
}
}


Безопасность в многопоточных сценариях

Синхронизируйте доступ к итераторам:

synchronized(collection) {
Iterator<E> iterator = collection.iterator();
while (iterator.hasNext()) {
// Обработка
}
}


Используйте потокобезопасные альтернативы:
// CopyOnWriteArrayList для read-heavy workloads
// ConcurrentHashMap для concurrent модификаций
// Collections.synchronized для оберток


Избегайте структурных изменений во время итерации:
// Собирайте элементы для удаления отдельно
List<Element> toRemove = new ArrayList<>();
for (Element element : collection) {
if (shouldRemove(element)) {
toRemove.add(element);
}
}
collection.removeAll(toRemove);



#Java #для_новичков #beginner #Iterator
👍1
Predicates (условия маршрутизации)

Predicates в Spring Cloud Gatewayэто функции, которые принимают ServerWebExchange и возвращают boolean, определяя, попадает ли входящий запрос под конкретный Route. Predicates — фундамент маршрутизации: маршрут считается подходящим только если все его предикаты возвращают true (логическое AND между перечисленными предикатами одного маршрута). Понимание механизмов работы предикатов критично для корректного и производительного построения маршрутов.


Ключевые принципы — механика и порядок


Маршрут выбран, если все его предикаты истинны.
В конфигурации YAML или Java DSL, когда у Route указано несколько предикатов, они объединяются логически через AND.

Порядок маршрутов важен.
Gateway перебирает доступные маршруты в порядке, определённом компонентом, который предоставляет Flux<Route> (обычно RouteDefinitionLocator → RouteDefinitionRouteLocator). Конкретный порядок определяется полем order (если задано) или порядком получения/создания маршрутов. Первый подходящий маршрут (в смысле совпадения предикатов) берётся в обработку. Следовательно, дешёвые «фильтрующие» предикаты (например, Host, Method) следует располагать раньше в конфигурации маршрутов по логике — не путать с порядком предикатов в одном маршруте: в одном маршруте порядок предикатов обычно не меняет семантику, но влияет на порядок выполнения (см. рекомендации по эффективности ниже).

Оценка предикатов — последовательная и краткая:
Для одного маршрута предикаты обычно вычисляются последовательно и, при первом false, дальнейшая проверка предикатов для этого маршрута останавливается (short-circuit). Тем не менее, поскольку маршрутов может быть много, Gateway продолжит проверять следующие маршруты до нахождения первого подходящего.

Рекомендации по производительности:
Помещайте дешёвые и часто отсекающие проверки (например, Host, Method, Path) до дорогих вычислений (например, проверки содержимого тела, обращения в внешние сервисы или сложных регулярных выражений).

Избегайте предикатов, которые делают блокирующие операции — predicated должны быть чисто вычислительными и быстрыми. При необходимости используйте Java DSL и инкапсулируйте асинхронную работу в фильтрах, а не в предикатах.

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

Композиция логики (AND/OR):
AND — поведение по умолчанию: несколько предикатов в одном Route объединяются через AND.
OR — декларативно не представлен как оператор на уровне одной записи YAML; для выражения OR чаще используют либо несколько маршрутов с разными наборами предикатов, либо пишут кастомный предикат, который внутри реализует логическое OR. В Java DSL также проще объявить несколько маршрутов или написать композиционный предикат в коде.


Встроенные предикаты — семантика, YAML-примеры, Java DSL-примеры

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

1) Path
Проверяет соответствие URL-пути. Поддерживает шаблоны в стиле Ant (/users/**, /api/*/items) и также поддерживает регулярные группы при использовании Rewrite и фильтров.

YAML:
predicates:
- Path=/api/users/**


Java DSL:
.route("users", r -> r.path("/api/users/**")
.uri("lb://user-service"))


Замечание: Path — обычно самый дешёвый и самый часто используемый предикат; ставьте его в начале списка логических проверок маршрутов.

2) Host
Проверяет заголовок Host (например, api.example.com). Поддерживает подстановки (*.example.com).

YAML:
predicates:
- Host=api.example.com, *.internal.example.org


Java DSL:
.route("host-route", r -> r.host("api.example.com", "*.internal.example.org")
.uri("http://localhost:8080"))


Заметка: Host — особенно полезен при мульти-тенантной конфигурации.


#Java #middle #Spring_Cloud_Gateway
👍1
3) Method
Сравнивает HTTP-метод (GET/POST/PUT/...).

YAML:
predicates:
- Method=GET,POST


Java DSL:
.route("read-write", r -> r.method(HttpMethod.GET, HttpMethod.POST)
.uri("lb://some-service"))


4) Header

Проверяет наличие и (при задании) значение заголовка. Поддерживает регулярные выражения на значение.

YAML:
predicates:
- Header=X-Client, ^mobile-.*


Java DSL:
.route("header-route", r -> r.header("X-Client", "^mobile-.*")
.uri("http://mobile-backend"))


Если значение не указано — проверяется только наличие заголовка.

5) Query
Проверяет наличие параметра query и, при наличии второго аргумента, — значение (регулярное выражение).

YAML:
predicates:
- Query=version, ^v[0-9]+$


Java DSL:
.route("versioned", r -> r.query("version", "^v[0-9]+$")
.uri("lb://versioned-service"))


6) RemoteAddr
Проверяет IP-адрес клиента — поддерживает одиночные адреса и CIDR. Важно: в случае проксирования через балансировщики/Gateway заранее убедитесь, какие IP попадают в RemoteAddr (реальный клиент или IP reverse-proxy). Иногда требуется проверка X-Forwarded-For.

YAML:
predicates:
- RemoteAddr=192.168.1.0/24, 10.0.0.10


Java DSL:
.route("internal-only", r -> r.remoteAddr("192.168.0.0/16", "10.0.0.0/8")
.uri("lb://internal-service"))


7) Weight / Load Balancer predicates (логика распределения) — концепция и применение
Weight — предикат, встречающийся в экосистеме Spring Cloud Gateway в контексте динамических маршрутов и Discovery клиента. Его цель — поддержка взвешенной маршрутизации: когда один и тот же serviceId представлен несколькими маршрутами с разными весами (weight), Gateway на этапе выбора маршрута учитывает веса (для канареечных релизов, A/B, geo-aware routing и т.п.).

Практическая схема использования:
Discovery-driven маршрутизация (DiscoveryClientRouteDefinitionLocator) может генерировать маршруты для каждого экземпляра/группы с метаданными веса.
Weight-предикат в составе маршрута проверяет — подходит ли текущий запрос под данный «весовой» маршрут (обычно реализуется через случайное/round-robin решение, зависящее от weight).

Примечание по реализации и совместимости: синтаксис и встроенные реализации могут различаться между версиями Spring Cloud.

Если требуется взвешенная маршрутизация в production, часто применяют:
Spring Cloud LoadBalancer для распределения трафика по инстансам;
Weight-подход в комбинировании с DiscoveryClientRouteDefinitionLocator или кастомным RouteDefinitionRepository.
Если нужен пример конфигурации — лучше реализовать weight механизмы через Discovery + metadata на стороне сервисов или через кастомный RoutePredicateFactory (пример ниже покажет, как писать свой предикат).


Сложные комбинации — AND / OR / NOT

AND: стандартная комбинация — несколько предикатов в одном Route → все должны быть true. Шаблон конфигурирования одинаков как в YAML, так и в DSL.


OR: декларативного OR в одном Route нет.

Для выражения OR есть два варианта:
Несколько маршрутов: создайте два (или больше) маршрута, каждый с различным предикатом; порядок важен, если маршруты пересекаются.
Кастомный предикат (composite): напишите RoutePredicateFactory, который внутри реализует логику a || b, и используйте его как обычный предикат. Это предпочтительно, если хотите избежать дублирования конфигурации и централизовать логику.

NOT / Negation: также не представлен в YAML как отдельный оператор; реализуется кастомным предикатом или путём композиции (например, создание предиката NotHeader).


#Java #middle #Spring_Cloud_Gateway
👍1
Написание собственного Predicate — RoutePredicateFactory

Когда встроенных предикатов недостаточно по логике или производительности, пишут собственные RoutePredicateFactory. Ниже — полный пример: реализуем предикат, который проверяет наличие параметра X-Feature и сопоставляет его по списку допустимых значений, при этом конфигурируемый через YAML.


1) Структура — класс предиката

import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.web.server.ServerWebExchange;
import java.util.function.Predicate;
import java.util.List;

public class XFeatureRoutePredicateFactory extends AbstractRoutePredicateFactory<XFeatureRoutePredicateFactory.Config> {

public XFeatureRoutePredicateFactory() {
super(Config.class);
}

@Override
public Predicate<ServerWebExchange> apply(Config config) {
List<String> allowed = config.getAllowedValues();
boolean ignoreCase = config.isIgnoreCase();

return exchange -> {
List<String> headerValues = exchange.getRequest().getHeaders().get("X-Feature");
if (headerValues == null || headerValues.isEmpty()) {
return false;
}
for (String hv : headerValues) {
for (String allowedVal : allowed) {
if (ignoreCase) {
if (hv.equalsIgnoreCase(allowedVal)) return true;
} else {
if (hv.equals(allowedVal)) return true;
}
}
}
return false;
};
}

public static class Config {
private List<String> allowedValues;
private boolean ignoreCase = true;

public List<String> getAllowedValues() { return allowedValues; }
public void setAllowedValues(List<String> allowedValues) { this.allowedValues = allowedValues; }
public boolean isIgnoreCase() { return ignoreCase; }
public void setIgnoreCase(boolean ignoreCase) { this.ignoreCase = ignoreCase; }
}
}


Ключевые моменты:
Наследуемся от AbstractRoutePredicateFactory<Config>. Это даёт поддержку автоконфигурируемой десериализации конфигурации из YAML в Config.
apply(Config) возвращает Predicate<ServerWebExchange>, который будет выполняться для каждого запроса.
Config — POJO с геттерами/сеттерами: Spring автоматически маппит значения из YAML (через Binder).

2) Регистрация (component или bean)
import org.springframework.stereotype.Component;

@Component
public class XFeatureRoutePredicateFactory extends AbstractRoutePredicateFactory<XFeatureRoutePredicateFactory.Config> {
// ... код как выше
}


Если не использовать @Component, можно зарегистрировать как bean через конфигурацию.

3) Использование в YAML
spring:
cloud:
gateway:
routes:
- id: feature-route
uri: http://feature-service
predicates:
- XFeature=allowed1,allowed2


В AbstractRoutePredicateFactory стандартная разбивка аргументов (если конфиг простой) позволит подставить список значений. Для более сложной настройки (ключ:значение) необходимо переопределить shortcutFieldOrder() или использовать вложенный Config с именованными полями.

4) Использование в Java DSL
Если хотите inline-предикат в коде (без фабрики), Java DSL позволяет:
.route("xfeature-inline", r -> r.p(exchange -> {
List<String> values = exchange.getRequest().getHeaders().get("X-Feature");
return values != null && values.stream().anyMatch(v -> v.equalsIgnoreCase("allowed1"));
}).uri("http://feature-service"))


Однако такой inline-предикат теряет преимущества конфигурируемости через YAML и переиспользуемости.


#Java #middle #Spring_Cloud_Gateway
👍1
Работа с конфигурационными объектами и AbstractRoutePredicateFactory — тонкости

Shortcut configuration vs. full Config binding

Если RoutePredicateFactory использует короткий синтаксис (например: MyPred=val1,val2), то AbstractRoutePredicateFactory поддерживает маппинг по shortcutFieldOrder() — список полей Config, которые будут заполнены по порядку.
Для явной структуры лучше использовать именованные свойства в YAML и обычный биндинг в Config.

Валидация конфигурации

Валидируйте конфиг в конструкторе предиката или в apply() — например, проверить, что список не пуст. Ошибки конфигурации лучше бросать на старте приложения, а не при первом запросе.
Сериализация/десериализация сложных типов
Для сложных типов (например, Duration, Pattern, InetAddress[]) используйте соответствующие конвертеры или храните строковые представления и парсите в Config.

Потокобезопасность

Predicate возвращаемый apply() должен быть потокобезопасным и не содержать mutable state, зависящего от запроса; храните precomputed структуры (например Pattern), а не парсьте каждый раз.

Логирование и мониторинг

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


Примеры: несколько реальных сценариев и лучшие практики

Пример 1 — комбинация Host + Path + Method (YAML)
routes:
- id: users-route
uri: lb://user-service
predicates:
- Host=api.example.com
- Path=/users/**
- Method=GET

Пояснение: типичный маршрут, дешёвые проверки (Host/Method/Path) — быстрый short-circuit.


Пример 2 — OR-логика через два маршрута (YAML)
routes:
- id: mobile-route
uri: lb://mobile-backend
predicates:
- Header=X-Client, ^mobile-.*
- Path=/api/**
- id: fallback-route
uri: lb://web-backend
predicates:
- Path=/api/**

Пояснение: первый маршрут отведёт мобильный трафик на mobile-backend; второй поймает остальные запросы по тому же Path — это простой способ построить OR-поведение без кастомных предикатов.


Пример 3 — кастомный предикат с конфигом в YAML
predicates:
- XFeature=allowedA,allowedB

(см. реализацию XFeatureRoutePredicateFactory выше).


#Java #middle #Spring_Cloud_Gateway
👍1