Вышел Spring Boot 3.3.0. В обновлении представлены следующие нововведения:
🚀 Поддержка CDS для ускорения времени запуска.
🔍 Улучшение Observability.
🔐 Новые возможности Spring Security.
🧵 Поддержка виртуальных потоков для вебсокетов.
⚡ Обновление зависимостей и многое другое.
#Spring #SpringBoot
🔗 https://spring.io/blog/2024/05/23/spring-boot-3-3-0-available-now
Подписывайтесь:
😌 @spring_aio
🚀 Поддержка CDS для ускорения времени запуска.
🔍 Улучшение Observability.
🔐 Новые возможности Spring Security.
🧵 Поддержка виртуальных потоков для вебсокетов.
⚡ Обновление зависимостей и многое другое.
#Spring #SpringBoot
🔗 https://spring.io/blog/2024/05/23/spring-boot-3-3-0-available-now
Подписывайтесь:
Please open Telegram to view this post
VIEW IN TELEGRAM
Иногда полезно освежить в памяти базовые принципы фреймворка. В Spring Framework центральное место занимает Bean. В своем видео Джош Лонг рассказывает об истории Bean и его жизненном цикле.
#SpringTips #SpringBoot
📱 https://www.youtube.com/watch?v=Z5hxolai4Tk
Подписывайтесь:
😌 @spring_aio
#SpringTips #SpringBoot
Подписывайтесь:
Please open Telegram to view this post
VIEW IN TELEGRAM
Spring Стартер Недели
Как сделать CRUD за 5 минут? Создаем модель данных, репозиторий, контроллер с 5-8 методами и, возможно, несколько dto. Если печатать со скоростью 600 символов в минуту, то можно успеть 🙈
Или берем Spring Data Rest, и получаем круды для своих репозиториев из коробки. И не только круды. Можно выставить наружу любой метод доступа к данным, прикрутить пагинацию и сортировку.
Однако, быстрый старт в начале может обернуться болью в процессе кастомизации или добавлении сложной (и не очень) логики. Тем не менее, выглядит как вполне себе хорошее решение для небольших REST сервисов, почти что Low Code 🙃.
Больше информации о данном стартере можно получить из доклада Рустама Курамшина на JPoint 2023.
📱 https://www.youtube.com/watch?v=roaGUHaWPxw
🔗 https://spring.io/projects/spring-data-rest
#SpringStarter #SpringBoot
Подписывайтесь:
😌 @spring_aio
Как сделать CRUD за 5 минут? Создаем модель данных, репозиторий, контроллер с 5-8 методами и, возможно, несколько dto. Если печатать со скоростью 600 символов в минуту, то можно успеть 🙈
Или берем Spring Data Rest, и получаем круды для своих репозиториев из коробки. И не только круды. Можно выставить наружу любой метод доступа к данным, прикрутить пагинацию и сортировку.
Однако, быстрый старт в начале может обернуться болью в процессе кастомизации или добавлении сложной (и не очень) логики. Тем не менее, выглядит как вполне себе хорошее решение для небольших REST сервисов, почти что Low Code 🙃.
Больше информации о данном стартере можно получить из доклада Рустама Курамшина на JPoint 2023.
🔗 https://spring.io/projects/spring-data-rest
#SpringStarter #SpringBoot
Подписывайтесь:
Please open Telegram to view this post
VIEW IN TELEGRAM
🕐 Тонкое управление Scheduled задачами в Spring
Какими способами можно настроить время и условие запуска Scheduled задач в Spring?
Первый способ - через
Но в таком варианте, мы управляем только условием запуска, но не можем настроить переодичность.
Другой вариант, это объявление cron expression в пропертях:
Но как тогда в таком случае отключить джобу? В качестве cron expression можно использовать дефис(-), что означает, что джобу запускать не нужно вовсе. И тогда мы обходимся без явного перечисления профилей в
Такой подход особо полезен при использовании spring-cloud-config, тогда нет необходимости делать передеплой приложения, чтобы выключить, или донастроить джобу. Нужно только не забыть повесить
#SpringBoot #SpringTips
Какими способами можно настроить время и условие запуска Scheduled задач в Spring?
Первый способ - через
@Profile
над сервисом, в котором объявлена задача, чтобы указать профили, в которых она должна (или не должна) запускаться.
java
@Profile("test,!prod")
class Teapot {
@Scheduled(cron = "0 15 10 15 * ?")
void makeTea() {
log.info("I'am a teapot.");
}
}
Но в таком варианте, мы управляем только условием запуска, но не можем настроить переодичность.
Другой вариант, это объявление cron expression в пропертях:
#application-test.properties
teapot.make_tea.cron=0 15 10 15 * ?
@Scheduled(cron = "${teapot.make_tea.cron}")
void makeTea() {
log.info("I'am a teapot.");
}
Но как тогда в таком случае отключить джобу? В качестве cron expression можно использовать дефис(-), что означает, что джобу запускать не нужно вовсе. И тогда мы обходимся без явного перечисления профилей в
@Profile
.
#application-prod.properties
teapot.make_tea.cron=-
Такой подход особо полезен при использовании spring-cloud-config, тогда нет необходимости делать передеплой приложения, чтобы выключить, или донастроить джобу. Нужно только не забыть повесить
@RefreshScope
на класс.#SpringBoot #SpringTips
🎉 Встречайте нашу дебютную статью на Habr!
👩💻 Блеск и нищета нового Scrolling API в Spring Data
В статье мы разобрали новое Scrolling API, которое предназначено для эффективной пагинации. В частности, Scrolling API приносит KeySet пагинацию в Spring Data, которая в теории может быть сильно быстрее стандратной и всем известной offset пагинации.
Однако, в процессе исследования выяснилось, что с PostgreSQL новое API работает не совсем так, как ожидается. А ведь это одна из самых популярных СУБД в мире!
Заинтриговали? Переходите по ссылке, оставляйте комментарии, и, конечно, ждем ваших лайков)
🔗 https://habr.com/ru/companies/spring_aio/articles/819193/
#SpringBoot #SpringData
Подписывайтесь:
😌 @spring_aio
В статье мы разобрали новое Scrolling API, которое предназначено для эффективной пагинации. В частности, Scrolling API приносит KeySet пагинацию в Spring Data, которая в теории может быть сильно быстрее стандратной и всем известной offset пагинации.
Однако, в процессе исследования выяснилось, что с PostgreSQL новое API работает не совсем так, как ожидается. А ведь это одна из самых популярных СУБД в мире!
Заинтриговали? Переходите по ссылке, оставляйте комментарии, и, конечно, ждем ваших лайков)
🔗 https://habr.com/ru/companies/spring_aio/articles/819193/
#SpringBoot #SpringData
Подписывайтесь:
Please open Telegram to view this post
VIEW IN TELEGRAM
🌟 Статическая и динамическая конфигурация в Spring Boot: что выбрать?
В мире облачных сервисов правильная настройка конфигурации играет ключевую роль. В этой статье рассматриваются два подхода: статическая и динамическая конфигурация.
🔧 Статическая конфигурация удобна предсказуемостью и простотой развертывания. Пример на Spring Boot:
🔄 Динамическая конфигурация обеспечивает гибкость и адаптивность. Например, для изменения конфигураций во время выполнения приложения можно использовать Spring Cloud:
Spring Cloud предоставляет возможность создания config сервера, который может подгружать новые конфигурации из Git. Ознакомьтесь с полной статьей для более детального погружения и примеров кода!
📚 Читать далее
#SpringBoot #SpringCloud
В мире облачных сервисов правильная настройка конфигурации играет ключевую роль. В этой статье рассматриваются два подхода: статическая и динамическая конфигурация.
🔧 Статическая конфигурация удобна предсказуемостью и простотой развертывания. Пример на Spring Boot:
@Value("${myapp.staticValue}")
private String staticValue;
public void printStaticValue() {
System.out.println(staticValue);
}
🔄 Динамическая конфигурация обеспечивает гибкость и адаптивность. Например, для изменения конфигураций во время выполнения приложения можно использовать Spring Cloud:
@RefreshScope
@RestController
public class MessageRestController {
@Value("${message:Hello default}")
private String message;
@GetMapping("/message")
public String getMessage() {
return this.message;
}
}
Spring Cloud предоставляет возможность создания config сервера, который может подгружать новые конфигурации из Git. Ознакомьтесь с полной статьей для более детального погружения и примеров кода!
📚 Читать далее
#SpringBoot #SpringCloud
На первый взгляд, выбор правильного Java-рантайма для вашего проекта на Spring Boot может показаться тривиальным. В конце концов, все популярные рантаймы основываются на коде OpenJDK и предлагают одинаковые программные интерфейсы.
Но не все рантаймы реализованы одинаково. В этой статье мы обсудим различные показатели, которые могут повлиять на ваше решение выбрать определенный дистрибутив Java для Spring Boot приложения.
🔗 https://habr.com/ru/companies/spring_aio/articles/819899/
#JVM #Java #SpringBoot
Please open Telegram to view this post
VIEW IN TELEGRAM
🐢 Неожиданное падение производительности при переходе на Spring Boot 3.3.0 👩💻
Hibernate подложил небольшую свинью, конвертируя JPQL в SQL. Внезапно, запрос поиска записей по списку id с передачей пустого списка деградирует в table full scan.
Однако, Hibernate пофиксил баг в течении 10 часов после поста в запрещенную в РФ соцсеть. Осталось дождаться новой версии Spring Boot, которая включит себе этот фикс.
🔗Подробнее читайте на Хабре
#SpringBoot #Hibernate #BreakingNews
Hibernate подложил небольшую свинью, конвертируя JPQL в SQL. Внезапно, запрос поиска записей по списку id с передачей пустого списка деградирует в table full scan.
Однако, Hibernate пофиксил баг в течении 10 часов после поста в запрещенную в РФ соцсеть. Осталось дождаться новой версии Spring Boot, которая включит себе этот фикс.
🔗Подробнее читайте на Хабре
#SpringBoot #Hibernate #BreakingNews
Please open Telegram to view this post
VIEW IN TELEGRAM
Одной из главных причин популярности Spring Boot является его способность автоматически конфигурировать (auto-configuration) множество компонентов, существенно упрощая жизнь разработчикам. Однако иногда возникает необходимость слегка подправить настройки этих компонентов без отказа от всех преимуществ автоматической конфигурации.
Предположим, вам нужно кастомизировать настройки кэша в вашем приложении. Вместо того чтобы полностью переопределять CacheManager, вы можете использовать Customizer интерфейс, чтобы внести необходимые изменения:
@Bean
CacheManagerCustomizer<ConcurrentMapCacheManager> cacheManagerCustomizer() {
return cacheManager -> cacheManager.setAllowNullValues(false);
}
Аналогичным образом можно настроить и любые другие компоненты:
//Кастомизация свойств Hibernate
@Bean
HibernatePropertiesCustomizer hibernatePropertiesCustomizer() {
return properties -> properties.put("hibernate.integrator_provider",
(IntegratorProvider) () -> List.of(new BeanValidationIntegrator()));
}
//Кастомизация Jackson2ObjectMapperBuilderCustomizer
@Bean
Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> {
builder.serializationInclusion(JsonInclude.Include.NON_EMPTY);
builder.featuresToEnable(
SerializationFeature.WRITE_ENUMS_USING_TO_STRING,
DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
};
}
Но бывает и такое, что подходящего Customizer интерфейса просто нет. В таком случае, можно использовать BeanPostProcessor для кастомизации уже инициализированных бинов:
//Кастомизация springLiquibase бина
@Bean
BeanPostProcessor liquibaseBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof SpringLiquibase springLiquibase) {
springLiquibase.setContexts("my-context");
}
return bean;
}
};
}
#SpringBoot #SpringTips
Please open Telegram to view this post
VIEW IN TELEGRAM
Более миллиона раз разработчики обращались к вопросу на StackOverflow о том, как получить значение из
application.properties
. Возможно, вы удивитесь @Value
– это не единственный способ. И далеко не всегда самый удобный.Способ №1: @Value
Аннотация
@Value
используется для инъекции отдельных значений из файла конфигурации. Например:
@Value("${property.name}")
private String propertyName;
Пожалуй, самый простой, но не самый поддерживаемый вариант, из-за отсутствия группировки и большого количества дублирующего кода, когда значений, с которыми надо работать, становится довольного много.
Способ №2: @ConfigurationProperties
Аннотация
@ConfigurationProperties
используется для группировки связанных свойств в объект класса. Свойства могут настраиваться через .properties
и .yaml
файлы, переменные окружения и т.д.Типичное использование
@ConfigurationProperties
может выглядеть следующим образом:
@ConfigurationProperties(prefix = "app")
@Component
@Getter
@Setter
public class ApplicationProperties {
private String name;
}
@SpringBootTest(properties = "app.name=my-app")
class ApplicationPropertiesTest {
@Autowired
private ApplicationProperties applicationProperties;
@Test
void appName() {
var appName = applicationProperties.getName();
assertEquals("my-app", appName);
}
}
Да, нужно будет создать отдельный класс, зато преимуществ у этого подхода относительно
@Value
довольно много. Перечислим некоторые из них:– Группировка по префиксу
– Возможность иерархического представления свойств
– Это бин и этим всё сказано :)
–
@ConfigurationPropertiesBinding
Отдельно отметим
@ConfigurationPropertiesBinding
. Допустим, нам нужно добавить новое свойство version
, которое будет представлять класс Version
для обработки семантического версионирования (major.minor.patch).
@ConfigurationProperties(prefix = "app")
@Component
@Getter
@Setter
public class ApplicationProperties {
private String name;
private Version version;
}
Для преобразования строкового значения из конфигурации в объект класса
Version
нам потребуется конвертор, который как раз будет отмечен аннотацией @ConfigurationPropertiesBinding
:
@ConfigurationPropertiesBinding
@Component
class SemVerPropertyConverter implements Converter<String, Version> {
@Override
public Version convert(String source) {
return StringUtils.hasLength(source) ? Version.parse(source) : null;
}
}
Пример использования:
@SpringBootTest(properties = {
"app.name=my-app",
"app.version=1.1.0"
})
class ApplicationPropertiesTest {
@Autowired
private ApplicationProperties applicationProperties;
@Test
void appVersion() {
Version version = applicationProperties.getVersion();
assertEquals(1, version.getMajorVersion());
assertEquals(1, version.getMinorVersion());
assertEquals(0, version.getPatchVersion());
}
}
Подробнее про
@ConfigurationProperties
и @ConfigurationPropertiesBinding
вы можете прочитать в документации.Способ №3: Environment
А если вы хотите обойтись без специальных аннотаций, можно использовать
Environment
:
@Autowired
private Environment env;
public void someMethod() {
String property = env.getProperty("property.name");
}
#SpringBoot #SpringTips
Please open Telegram to view this post
VIEW IN TELEGRAM
Валидация данных – ключевой аспект любого приложения. В Spring она часто используется в параметрах методов
@RestController
, например:
@RestController
@RequestMapping("/api/products")
public class ProductController {
@GetMapping("/search")
public ResponseEntity<List<Product>> searchProducts(@RequestParam @NotNull @Size(min = 3, max = 50) String name,
@RequestParam @NotNull @Min(0) @Max(10000) Double price) {
// Логика поиска продуктов
List<Product> products = productService.search(name, price);
return new ResponseEntity<>(products, HttpStatus.OK);
}
}
Валидация в сервисном слое
Валидация помогает гарантировать, что данные, поступающие в приложение, соответствуют требованиям. Для валидации в сервисном слое нужно добавить аннотацию
@Validated
над сервисом:
@Service
@Validated
public class EmailService {
public void send(@Email String email,
@Length(max = 10) String subject,
@NotBlank String body) {
// бизнес-логика
}
}
Теперь метод send вызовет ошибку, если данные не проходят валидацию:
emailService.send(
"i am not email",
"I am too loooooooooong",
""
);
jakarta.validation.ConstraintViolationException: send.body: must not be blank, send.email: must be a well-formed email address, send.subject: length must be between 0 and 10
...
Валидация DTO в методах сервиса
Чтобы не дублировать поля в разных методах, разработчики часто используют DTO. Аннотации валидации применимы и здесь:
public record EmailRequest(
@Email String email,
@Length(max = 10) String subject,
@NotBlank String body
) {
}
Но в этом случае помимо
@Validated
над классом, нужно также не забыть добавить @Valid
перед типом параметра в методе:
@Service
@Validated
public class EmailService {
public void sendBatch(
@Valid List<EmailRequest> requests
) {
// do work
}
}
Следующий код вызовет ошибку:
emailService.sendBatch(
List.of(
new EmailRequest("not email", "test", "Hello"),
new EmailRequest("alex@spring.aio", "I am too loooooooooong", "")
)
);
jakarta.validation.ConstraintViolationException: sendBatch.requests[1].subject: length must be between 0 and 10, sendBatch.requests[1].body: must not be blank, sendBatch.requests[0].email: must be a well-formed email address
Валидация элементов коллекций
Кстати, точно также можно валидировать и элементы коллекций, а также ключи и значения в Map:
@Service
@Validated
public class EmailService {
public void search(
Map<@NotBlank String, @NotBlank String> searchParams
) {
// do work
}
}
Следующий код вызовет ошибку:
searchService.search(Map.of("", ""));
jakarta.validation.ConstraintViolationException: search.searchParams<K>[].<map key>: must not be blank, search.searchParams[].<map value>: must not be blank
#SpringBoot #SpringTips #Validation
Please open Telegram to view this post
VIEW IN TELEGRAM