Тут мы включаем асинхронное выполнение событий. Операции выполняются в разных транзакциях и в разных потоках.
Все варианты имеют право на жизнь, главное не забывать как работает Spring Events. Ну и если у нас есть "дешевые" транзации на БД (дешевые по сравнению с распределенными транзакциями), то имеет смысл использовать их по максимуму.
P.S. Что интересно, статья с baeldung.com по запросу Spring Events ранжируется выше официальной документации.
#spring #transactions #event_driven
Все варианты имеют право на жизнь, главное не забывать как работает Spring Events. Ну и если у нас есть "дешевые" транзации на БД (дешевые по сравнению с распределенными транзакциями), то имеет смысл использовать их по максимуму.
P.S. Что интересно, статья с baeldung.com по запросу Spring Events ранжируется выше официальной документации.
#spring #transactions #event_driven
Baeldung on Kotlin
Spring Events | Baeldung
The Basics of Events in Spring - create a simple, custom Event, publish it and handle it in a listener.
И снова новости AI
В Spring AI появилась возможность работы с embeddings - https://www.baeldung.com/spring-ai-embeddings-model-api
Напомню, embeddings - векторное представление привычных нам текстовых, графических или аудио данных. Для чего нужно работать с embeddings - ведь мы можем общаться с моделью текстом, а все остальное она сделает сама?
Детали тут - https://habr.com/ru/companies/otus/articles/787116/
А если вкратце - например, с их помощью мы можем тренировать свою локальную модель. Или перейти от "программирования на русском языке" к более низкоуровневым операциям, теперь и на Java. Примеры таких действия: найти похожие слова, подставить недостающее слово.
#ai #spring #java
В Spring AI появилась возможность работы с embeddings - https://www.baeldung.com/spring-ai-embeddings-model-api
Напомню, embeddings - векторное представление привычных нам текстовых, графических или аудио данных. Для чего нужно работать с embeddings - ведь мы можем общаться с моделью текстом, а все остальное она сделает сама?
Детали тут - https://habr.com/ru/companies/otus/articles/787116/
А если вкратце - например, с их помощью мы можем тренировать свою локальную модель. Или перейти от "программирования на русском языке" к более низкоуровневым операциям, теперь и на Java. Примеры таких действия: найти похожие слова, подставить недостающее слово.
#ai #spring #java
Baeldung on Kotlin
A Guide to Embeddings Model API in Spring AI | Baeldung
The embeddings model API in Spring AI provides the abstraction layer and support for model providers like OpenAI, enabling us to incorporate it into our Java applications.
Зачистка пропертей
Не люблю фразу "как я уже говорил". Ладно, кого я обманываю)
Но как я уже говорил - рефакторинг и чистка нужна не только коду, но и настройкам. https://t.me/javaKotlinDevOps/328
Проблема в том, что до настроек часто не доходят руки. По понятным причинам - код важнее.
Вот если бы проверку автоматизировать. Например, встроить в процесс сборки.
А пожалуйста https://www.baeldung.com/spring-properties-cleaner
Плагин работает со Spring Properties.
Умеет:
1) находить дубли
2) группировать по объекту настройки (по префиксу ключа настройки по сути)
3) выносить повторяющиеся настройки разных профилей в общий файл properties
4) повторяющиеся части - в отдельные настройки
5) форматировать и удалять лишние пробелы
В целом - рекомендую.
P.S. Искать неиспользуемые настройки не умеет. Но не все сразу)
#spring #configuration
Не люблю фразу "как я уже говорил". Ладно, кого я обманываю)
Но как я уже говорил - рефакторинг и чистка нужна не только коду, но и настройкам. https://t.me/javaKotlinDevOps/328
Проблема в том, что до настроек часто не доходят руки. По понятным причинам - код важнее.
Вот если бы проверку автоматизировать. Например, встроить в процесс сборки.
А пожалуйста https://www.baeldung.com/spring-properties-cleaner
Плагин работает со Spring Properties.
Умеет:
1) находить дубли
2) группировать по объекту настройки (по префиксу ключа настройки по сути)
3) выносить повторяющиеся настройки разных профилей в общий файл properties
4) повторяющиеся части - в отдельные настройки
5) форматировать и удалять лишние пробелы
В целом - рекомендую.
P.S. Искать неиспользуемые настройки не умеет. Но не все сразу)
#spring #configuration
Telegram
(java || kotlin) && devOps
Всем привет!
Я часто вижу в проектах лишние настройки. Как правило, они попадают в проект следующими путями:
1) скопировали из каркаса\образца\соседнего сервиса не задумываясь - нужны эти настройки или нет. Да, принцип "работает - не трогай" встречается…
Я часто вижу в проектах лишние настройки. Как правило, они попадают в проект следующими путями:
1) скопировали из каркаса\образца\соседнего сервиса не задумываясь - нужны эти настройки или нет. Да, принцип "работает - не трогай" встречается…
AI на практике или учимся читать с помощью AI)
Вот есть неплохая статья - введение в тему работы с ElasticSearch и JPA на Java+Spring https://habr.com/ru/companies/rostelecom/articles/851658/
Всем она хороша, кроме одного - 1700 строк, 120 кб текста, время для чтения - 41 минута. И как нетрудно догадаться - статья покрывает все основные темы по поиску с помощью Elasticsearch, но там прям много воды. Может автору за символы платят, хз)
Но повторюсь по сути все ок.
И тут казалось бы - вот звездный час AI. Тем более они теперь с интернетом дружат.
Скормил статью разным AI чатам, попросил сократить, сохранив код, основные классификации и описания атрибутов.
Итоги такие:
0) вне конкурса - пересказ в браузере Яндекс. Сокращает - отлично, но очень тезисно получается, ничего не понятно. Незачет
1) YaGPT - сказал, что не умеет, отправил на внешние сайты. Незачет
2) DeepSeek - полное фиаско. Во-первых забавный факт - когда я забыл отжать галочку: "искать в вебе" - модель стала пересказывать какую-то левую статью про работу с LLM. Включил галочку - модель увидела в ссылке слово rostelecom и стала пересказывать тарифы оператора. Ок, включаю режим рассуждений. Снова мимо, причем с дико странной формулировкой: "Мы не можем напрямую загрузить и обработать веб-страницу, но я могу вспомнить или найти ключевые моменты статьи, основываясь на ее содержании, если я с ней знаком." И далее снова левая статья и ее пересказ. В общем No comments, не пересказ - не конек DeepSeek
3) GigaChat - пересказал всю статью, сильно лучше Яндекс браузера, но потом пошли глюки. В первой версии пересказа был только код, почти без текста. Непонятно. Попросил добавить текста - исчез весь код. Попросил совместить - начал придумывать какие-то левые классы, т.е. потерял контекст. Еще работает медленно. Незачет
4) Perplexity - в целом неплохо пересказал с первого раза. Но - потерял последнюю треть документа - похоже на оптимизацию. Добавил недостающее после указания конкретных глав. Если просишь добавить без конкретики какие главы пропущены - все равно пропускает. Причем чем больше просишь - тем компактнее становится итоговый текст, т.е. видно, что модель экономит контекст. Еще минусы:
а) переставляет местами главы, причем не релевантно смыслу.
б) оставляет мало текста, приходится просить добавлять текстовые описания для атрибутов и вариантов реализации
5) Mistral - примерно все тоже самое, только в первой версии пересказа вообще практически не было текста, только код. Хотя просил я другое. После просьбы добавить текста - добавил. В остальном работает также, как Perplexity, с теми же минусами
Вывод: похоже с первого раза выдать нормальный пересказ большой статьи современные LLM не могут. И это даже не книга. Причина в оптимизации из-за ограниченного контекста. Но в режиме переписки работать можно.
P.S. И статья на 120 кб - это конечно перебор) Я люблю читать - но все равно перебор)
#ai #llm #elasticsearch #java #spring
Вот есть неплохая статья - введение в тему работы с ElasticSearch и JPA на Java+Spring https://habr.com/ru/companies/rostelecom/articles/851658/
Всем она хороша, кроме одного - 1700 строк, 120 кб текста, время для чтения - 41 минута. И как нетрудно догадаться - статья покрывает все основные темы по поиску с помощью Elasticsearch, но там прям много воды. Может автору за символы платят, хз)
Но повторюсь по сути все ок.
И тут казалось бы - вот звездный час AI. Тем более они теперь с интернетом дружат.
Скормил статью разным AI чатам, попросил сократить, сохранив код, основные классификации и описания атрибутов.
Итоги такие:
0) вне конкурса - пересказ в браузере Яндекс. Сокращает - отлично, но очень тезисно получается, ничего не понятно. Незачет
1) YaGPT - сказал, что не умеет, отправил на внешние сайты. Незачет
2) DeepSeek - полное фиаско. Во-первых забавный факт - когда я забыл отжать галочку: "искать в вебе" - модель стала пересказывать какую-то левую статью про работу с LLM. Включил галочку - модель увидела в ссылке слово rostelecom и стала пересказывать тарифы оператора. Ок, включаю режим рассуждений. Снова мимо, причем с дико странной формулировкой: "Мы не можем напрямую загрузить и обработать веб-страницу, но я могу вспомнить или найти ключевые моменты статьи, основываясь на ее содержании, если я с ней знаком." И далее снова левая статья и ее пересказ. В общем No comments, не пересказ - не конек DeepSeek
3) GigaChat - пересказал всю статью, сильно лучше Яндекс браузера, но потом пошли глюки. В первой версии пересказа был только код, почти без текста. Непонятно. Попросил добавить текста - исчез весь код. Попросил совместить - начал придумывать какие-то левые классы, т.е. потерял контекст. Еще работает медленно. Незачет
4) Perplexity - в целом неплохо пересказал с первого раза. Но - потерял последнюю треть документа - похоже на оптимизацию. Добавил недостающее после указания конкретных глав. Если просишь добавить без конкретики какие главы пропущены - все равно пропускает. Причем чем больше просишь - тем компактнее становится итоговый текст, т.е. видно, что модель экономит контекст. Еще минусы:
а) переставляет местами главы, причем не релевантно смыслу.
б) оставляет мало текста, приходится просить добавлять текстовые описания для атрибутов и вариантов реализации
5) Mistral - примерно все тоже самое, только в первой версии пересказа вообще практически не было текста, только код. Хотя просил я другое. После просьбы добавить текста - добавил. В остальном работает также, как Perplexity, с теми же минусами
Вывод: похоже с первого раза выдать нормальный пересказ большой статьи современные LLM не могут. И это даже не книга. Причина в оптимизации из-за ограниченного контекста. Но в режиме переписки работать можно.
P.S. И статья на 120 кб - это конечно перебор) Я люблю читать - но все равно перебор)
#ai #llm #elasticsearch #java #spring
Хабр
Полнотекстовый поиск в java приложениях с помощью Elasticsearch
Введение В современном мире объёмы данных растут экспоненциально, и эффективное управление информацией становится критически важным для успеха любого приложения. Полнотекстовый поиск играет ключевую...
И снова AI агенты...
AI агент по определению должен делать что-то полезное, делать это с использованием AI, автономно и недетерминировано.
Сейчас я хочу рассмотреть свойство полезности.
AI агент в чем-то похож на умный proxy. Ум обеспечивает LLM (или не обеспечивает, тут идут споры))) ). А далее агент вызывает некую существующую функцию. Или несколько функций.
В терминологии AI это tool:
1) https://python.langchain.com/docs/concepts/tools/
2) https://docs.spring.io/spring-ai/reference/api/tools.html
tool - вообще говоря это просто метод Java, Python или любого другого языка, аннотированый соответствующим образом.
Как агент понимает, что умеет tool? Аннотации с описанием назначения тула, входных и выходных параметров.
Но если подумать - мы же живем в REST мире, в нем победил OpenAPI, а там вся необходимая информация есть. И текстовые описания, и граничные значения, и примеры. Даже адреса серверов на разных средах можно в спеке указать.
Нельзя ли это как-то переиспользовать? DRY все таки!
Можно. https://python.langchain.com/docs/integrations/tools/openapi/ на примере Python
Загружаем спеку, преобразуем в формат, понятный AI и создаем агента:
with open("spotify_openapi.yaml") as f:
raw_spotify_api_spec = yaml.load(f, Loader=yaml.Loader)
spotify_api_spec = reduce_openapi_spec(raw_spotify_api_spec)
...
spotify_agent = planner.create_openapi_agent(
spotify_api_spec,
requests_wrapper,
llm,
allow_dangerous_requests=ALLOW_DANGEROUS_REQUEST,
)
Почему не Java?
https://github.com/langchain4j/langchain4j/issues/1307
Ждем-с.
Что-то делается и для Spring AI, но пока сторонними разработчиками https://readmedium.com/connect-existing-openapis-to-llms-with-spring-ai-039ccabde406
Это самый простой способ вызвать существующий функционал.
Если он не подходит по одной следующих причин:
1) нет готового адаптера OpenAPI
2) нет OpenAPI спецификации, или она сделана криво, а доработка ее другой командой требует времени
3) хочется объединить несколько запросов в один tool или обогатить ответ tool-а локальной информацией
4) нужно убрать лишнее из ответа
то можно вернуться к исходному варианту - написать свой кастомный tool, возвращающий только то, что нужно и документированный так, как нужно.
Ну и третий вариант - отдельный MCP сервер https://t.me/javaKotlinDevOps/376.
У него два плюса:
1) MCP API - это специализированное API, адаптированное для использования LLM
2) tool-ом в виде MCP сервера может в теории воспользоваться любой AI агент
#ai #llm #spring #python
AI агент по определению должен делать что-то полезное, делать это с использованием AI, автономно и недетерминировано.
Сейчас я хочу рассмотреть свойство полезности.
AI агент в чем-то похож на умный proxy. Ум обеспечивает LLM (или не обеспечивает, тут идут споры))) ). А далее агент вызывает некую существующую функцию. Или несколько функций.
В терминологии AI это tool:
1) https://python.langchain.com/docs/concepts/tools/
2) https://docs.spring.io/spring-ai/reference/api/tools.html
tool - вообще говоря это просто метод Java, Python или любого другого языка, аннотированый соответствующим образом.
Как агент понимает, что умеет tool? Аннотации с описанием назначения тула, входных и выходных параметров.
Но если подумать - мы же живем в REST мире, в нем победил OpenAPI, а там вся необходимая информация есть. И текстовые описания, и граничные значения, и примеры. Даже адреса серверов на разных средах можно в спеке указать.
Нельзя ли это как-то переиспользовать? DRY все таки!
Можно. https://python.langchain.com/docs/integrations/tools/openapi/ на примере Python
Загружаем спеку, преобразуем в формат, понятный AI и создаем агента:
with open("spotify_openapi.yaml") as f:
raw_spotify_api_spec = yaml.load(f, Loader=yaml.Loader)
spotify_api_spec = reduce_openapi_spec(raw_spotify_api_spec)
...
spotify_agent = planner.create_openapi_agent(
spotify_api_spec,
requests_wrapper,
llm,
allow_dangerous_requests=ALLOW_DANGEROUS_REQUEST,
)
Почему не Java?
https://github.com/langchain4j/langchain4j/issues/1307
Ждем-с.
Что-то делается и для Spring AI, но пока сторонними разработчиками https://readmedium.com/connect-existing-openapis-to-llms-with-spring-ai-039ccabde406
Это самый простой способ вызвать существующий функционал.
Если он не подходит по одной следующих причин:
1) нет готового адаптера OpenAPI
2) нет OpenAPI спецификации, или она сделана криво, а доработка ее другой командой требует времени
3) хочется объединить несколько запросов в один tool или обогатить ответ tool-а локальной информацией
4) нужно убрать лишнее из ответа
то можно вернуться к исходному варианту - написать свой кастомный tool, возвращающий только то, что нужно и документированный так, как нужно.
Ну и третий вариант - отдельный MCP сервер https://t.me/javaKotlinDevOps/376.
У него два плюса:
1) MCP API - это специализированное API, адаптированное для использования LLM
2) tool-ом в виде MCP сервера может в теории воспользоваться любой AI агент
#ai #llm #spring #python
Langchain
Tools | 🦜️🔗 LangChain
- Chat models
👍1
RestTemplate is dead, baby)))
Spring наконец-то решили задепрекейтить RestTemplate.
Пруф: https://spring.io/blog/2025/09/30/the-state-of-http-clients-in-spring
Его замены в fluent стиле: RestClient для синхронного и WebCLient для асинхронного взаимодействия.
Видимо, команда Spring таки выпилила его из компонентов фреймворка и теперь предлагает это сделать всем остальным)
На самом деле я немного добавил сенсационности в пост.
А реальная хронология событий планируется такая:
- в ноябре этого года (Spring 7.0) будет объявлено о том, что компонент deprecated
- формально deprecated он станет в ноябре 2026 года (Spring 7.1)
- выпилят в Spring 8.0 где-то в 27 году.
Это мир Java == мир обратной совместимости)
#spring #web
Spring наконец-то решили задепрекейтить RestTemplate.
Пруф: https://spring.io/blog/2025/09/30/the-state-of-http-clients-in-spring
Его замены в fluent стиле: RestClient для синхронного и WebCLient для асинхронного взаимодействия.
Видимо, команда Spring таки выпилила его из компонентов фреймворка и теперь предлагает это сделать всем остальным)
На самом деле я немного добавил сенсационности в пост.
А реальная хронология событий планируется такая:
- в ноябре этого года (Spring 7.0) будет объявлено о том, что компонент deprecated
- формально deprecated он станет в ноябре 2026 года (Spring 7.1)
- выпилят в Spring 8.0 где-то в 27 году.
Это мир Java == мир обратной совместимости)
#spring #web
The state of HTTP clients in Spring
Level up your Java code and explore what Spring can do for you.
👍2
Версионирование для REST в Java - оно как бы есть, и его как бы нет)
Основная проблема с версионированием - которую многие, в т.ч. и я, не замечают - следующая.
Версионирование нужно и его почти везде используют, но при этом Spring, который также везде используют, не делает ничего для его поддержки.
Точнее не делал.
Начиная со Spring 7, который уже вышел, данный функционал наконец таки появился: https://habr.com/ru/companies/spring_aio/articles/967454/
Что добавили:
1) определение способа передачи версии:
2) указание версию по умолчанию:
3) указание списка поддерживаемых версий и, соответственно, их валидацию:
или
Естественно, все это можно сделать через код.
4) Стандартный парсер версий - по стандарту семантического версионирования
5) Само собой есть возможность определить нестандартный механизм передачи и формата версий через создание ApiVersionResolver и ApiVersionParser
6) Есть даже ApiVersionDeprecationHandler - стандартизация уведомления клиента об устаревших версиях и автоматическая 400-ка для неподдерживаемых
7) И конечно механизм маршрутизации по версиям, который автоматически разрешает вот такую конструкцию:
8) аналогично для endpoint в функциональном стиле:
9) плюс все поддерживается для reactive stack
10) и на клиенте (для тестовых клиентов тоже):
Еще одна важная фича, которая должна была появиться раньше.
#spring #versioning
Основная проблема с версионированием - которую многие, в т.ч. и я, не замечают - следующая.
Версионирование нужно и его почти везде используют, но при этом Spring, который также везде используют, не делает ничего для его поддержки.
Точнее не делал.
Начиная со Spring 7, который уже вышел, данный функционал наконец таки появился: https://habr.com/ru/companies/spring_aio/articles/967454/
Что добавили:
1) определение способа передачи версии:
# Path segment versioning (e.g., /api/v1/users)
spring.mvc.apiversion.use.path-segment=1
# Request header versioning (e.g., X-API-Version: 1.0)
spring.mvc.apiversion.use.header=X-API-Version
# Query parameter versioning (e.g., ?version=1.0)
spring.mvc.apiversion.use.query-parameter=version
# Media type parameter versioning (e.g., Accept: application/json;version=1.0)
spring.mvc.apiversion.use.media-type-parameter[application/json]=version
2) указание версию по умолчанию:
# Basic versioning configuration
spring.mvc.apiversion.default=1.0
3) указание списка поддерживаемых версий и, соответственно, их валидацию:
spring.mvc.apiversion.supported=1.0,2.0
или
# автоматическое детектирование по содержимому контроллеров
spring.mvc.apiversion.detect-supported = true
Естественно, все это можно сделать через код.
4) Стандартный парсер версий - по стандарту семантического версионирования
5) Само собой есть возможность определить нестандартный механизм передачи и формата версий через создание ApiVersionResolver и ApiVersionParser
6) Есть даже ApiVersionDeprecationHandler - стандартизация уведомления клиента об устаревших версиях и автоматическая 400-ка для неподдерживаемых
7) И конечно механизм маршрутизации по версиям, который автоматически разрешает вот такую конструкцию:
public class AccountController {
@GetMapping
public Account getAccount() {
}
@GetMapping(version = "1.1")
public Account getAccount1_1() {
}
@GetMapping(version = "1.2+")
public Account getAccount1_2() {
}
@GetMapping(version = "1.5")
public Account getAccount1_5() {
}
}8) аналогично для endpoint в функциональном стиле:
RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/hello-world", version("1.2"),
request -> ServerResponse.ok().body("Hello World")).build();
9) плюс все поддерживается для reactive stack
10) и на клиенте (для тестовых клиентов тоже):
RestClient client = RestClient.builder()
.baseUrl("http://localhost:8080")
.apiVersionInserter(ApiVersionInserter.useHeader("API-Version"))
.build();
...
Account account = client.get().uri("/accounts/1")
.apiVersion(1.1)
.retrieve()
.body(Account.class);
Еще одна важная фича, которая должна была появиться раньше.
#spring #versioning
Хабр
Нативный API Versioning в Spring 7: долгожданная официальная поддержка
Команда Spring АйО подготовила перевод статьи о том, как Spring Framework 7 приносит нативную поддержку API-версионирования — темы, которая годами оставалась на разработчиках и собирала тонны...