Spring АйО – Telegram
Spring АйО
9.58K subscribers
375 photos
273 videos
490 links
Русскоязычное сообщество Spring-разработчиков.

Habr: bit.ly/433IK46
YouTube: bit.ly/4h3Ci0x
VK: bit.ly/4hF0OG8
Rutube: bit.ly/4b4UeX6
Яндекс Музыка: bit.ly/3EIizWy

Чат для общения: @spring_aio_chat
Download Telegram
🗓 Еженедельный дайджест №4

Для тех, кто был слишком занят на неделе или просто пропустил некоторые посты, публикуем дайджест!

Spring Boot Tips: Service Layer Validation – разобрались с тем, как валидировать данные на уровне сервисов с помощью аннотаций Valid, Validated и т.д.

Liquibase + Spring Boot: настройка и написание миграций баз данныхузнали, как можно автоматизировать процесс написания скриптом миграций для Liquibase.

Как избежать утечек соединений в Spring Boot приложении? – рассмотрели интересный кейс использования аннотации Transactional, который позволил решить проболему с утечками соединений.

Spring Boot Tips: Кастомные реализации репозиториеввыяснили, как можно "подложить" Spring'у свои реализации репозиториев.

Переписывая историю: от инструментов версионирования БД к практикепогрузились в сравнение Liquibase и Flyway вместе с Александром Шустановым и транскриптом его доклада на Joker 2023.

Первый обзор на GigaIDE - Российская IntelliJ IDEA от СБЕРапосмотрели в действии, что же из себя представляет GigaIDE в сравнении с IntelliJ IDEA CE и Ultimate.

Новые возможности конструкторов в Java (часть 1)узнали, что за собой скрывает JEP №447 и какие возможности он привнесёт в нашу любимую Java.

😌 Подписывайтесь на канал: @spring_aio
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15👍73
Spring Tips: Переопределение и дополнение свойств для Spring Boot тестов

При разработке на Spring Boot часто возникает необходимость переопределять конфигурационные свойства для тестов. Представим, что у нас есть application.properties в директории src/main/resources со следующими свойствами:


spring.application.name=properties
spring.jpa.open-in-view=false


Мы хотим изменить spring.application.name и добавить новое свойство server.port в тестах. Интуитивно разработчики создают application.properties в src/test/resources, ожидая, что он дополнит основной файл. Однако это не так (подробное объяснение можно найти здесь). Рассмотрим несколько способов переопределения свойств в тестах.

1. @ActiveProfiles

Создайте файл application-test.properties в src/test/resources и активируйте его в тестах.


@SpringBootTest
@ActiveProfiles("test")
class ActiveProfileTest {

@Autowired
private Environment env;

@Test
void test() {
Assertions.assertEquals("new-name", env.getProperty("spring.application.name"));
Assertions.assertEquals(false, env.getProperty("spring.jpa.open-in-view", Boolean.class));
Assertions.assertEquals(8099, env.getProperty("server.port", Integer.class));
}
}


2. Директория config

Spring загружает конфигурационные файлы в определенном порядке. Файлы в src/test/resources/config имеют приоритет и переопределяют свойства из src/main/resources. Более подробно ознакомиться с тем, по какому принципу Spring загружает свойства можно в документации.

3. @DynamicPropertySource

Используйте динамическую подмену свойств, если значения известны только во время выполнения.


@SpringBootTest
class DynamicPropertySourceTest {

@Autowired
private Environment env;

@DynamicPropertySource
static void properties(DynamicPropertyRegistry registry) {
registry.add("spring.application.name", () -> "new-name");
registry.add("server.port", () -> 8099);
}

@Test
void test() {
Assertions.assertEquals("new-name", env.getProperty("spring.application.name"));
Assertions.assertEquals(false, env.getProperty("spring.jpa.open-in-view", Boolean.class));
Assertions.assertEquals(8099, env.getProperty("server.port", Integer.class));
}
}


4. @TestPropertySource с параметром locations

Укажите путь к *.properties файлу с нужными значениями.


@SpringBootTest
@TestPropertySource(locations = "classpath:test.properties")
class PropertiesSourceLocationTest {

@Autowired
private Environment env;

@Test
void test() {
Assertions.assertEquals("new-name", env.getProperty("spring.application.name"));
Assertions.assertEquals(false, env.getProperty("spring.jpa.open-in-view", Boolean.class));
Assertions.assertEquals(8099, env.getProperty("server.port", Integer.class));
}
}


Продолжение в комментариях

#SpringBoot #SpringTips
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥19👍94
🏆7 основных способов оптимизировать кеширование в Spring Boot

В условиях растущих требований к производительности современных приложений, кеширование становится одним из ключевых инструментов для их удовлетворения.

В новом переводе от команды Spring АйО вы узнаете про 7 основных техник оптимизации кеширования в Spring Boot, которые могут помочь значительно улучшить производительность. От выбора идеальных кандидатов для кеширования до реализации асинхронного кеша и мониторинга метрик кеша.

📚 Подробнее читайте на Хабре
Please open Telegram to view this post
VIEW IN TELEGRAM
👍20🔥104
🥰 Самая любимая фича в Java 22

Java 22 принесла множество новых фич, но наиболее обсуждаемой стала String Templates. Хотя эта preview-фича не попадет в будущий релиз Java 23, огорчаться не стоит. В Java 22 достаточно других интересных обновлений. Помните их?

Если забыли, самое время освежить память: https://habr.com/ru/articles/801467/.

Ну и, конечно же, выбрать любимую фичу 💚

Поделитесь в комментариях, какая фича Java 22 вам нравится больше всего?
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥93👍3
🐘 Declarative Gradle: рывок или прорыв?

Полгода назад команда Gradle представила новый продукт — Declarative Gradle. Команда Spring АйО, со своей стороны, провела исследование этого продукта и готова поделиться результатами.

TL;DR: технология всё ещё активно развивается, но пока что не поддерживается привычными инструментами, такими как IntelliJ IDEA. Несмотря на это, нам удалось запустить Spring Boot приложение!

📚 Подробнее читайте на Хабре
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🤔83😁3🔥2
🖥 Новые возможности конструкторов в Java (часть 2)

Не так давно мы рассказывали про гибкие конструкторы в Java. В 23 версии Java появится возможность инициализировать поля в том же классе перед явным вызовом конструктора (JEP-482). А пока эта фича не реализована, мы вынуждены использовать вспомогательные методы, если нам необходимо проиницизировать поля.

Рассмотрим класс, который принимает аргумент типа Certificate и должен преобразовать его в массив байтов для конструктора суперкласса.

Java ≤ 22:


public class Sub extends Super {

private static byte[] prepareByteArray(Certificate certificate) {
var publicKey = certificate.getPublicKey();
if (publicKey == null) throw new IllegalArgumentException(..);
return switch (publicKey) {
case RSAKey rsaKey -> ...
case DSAPublicKey dsaKey -> ...
default -> ...
};
}

public Sub(Certificate certificate) {
super(prepareByteArray(certificate));
}

}


В коде выше метод prepareByteArray выполняет необходимую подготовку аргументов. Затем этот метод вызывается внутри конструктора Sub как часть вызова super.

Java ≥ 23:

Можно будет сделать тоже самое, но без дополнительных “приседаний” в виде вынесения логики которую мы хотим выполнить в конструкторе в отдельный метод:


public class Sub extends Super {
public Sub(Certificate certificate) {
var publicKey = certificate.getPublicKey();
if (publicKey == null) throw new IllegalArgumentException(..);
byte[] certBytes = switch (publicKey) {
case RSAKey rsaKey -> ...
case DSAPublicKey dsaKey -> ...
default -> ...
};
super(certBytes);
}
}


Также иногда возникает необходимость передать одно и то же значение в несколько аргументов конструктора суперкласса. Ранее это можно было сделать только с использованием вспомогательного конструктора.

Рассмотрим пример, связанный с системой регистрации студентов в университете. У нас есть базовый класс Person, который представляет человека, и класс Student, который наследует Person. Каждый студент имеет идентификатор, который также используется в качестве номера студенческого билета и учетной записи в университете

Java ≤ 22:


class Person {
private String id;
private String accountId;

public Person(String id, String accountId) {
this.id = id;
this.accountId = accountId;
}
}

class Student extends Person {
private Student(String id) {
super(id, id); // Передача одного и того же значения дважды
}

public Student(String firstName, String lastName) {
this(generateStudentId(firstName, lastName));
}

private static String generateStudentId(String firstName, String lastName) {
// Генерация уникального идентификатора студента
return firstName + ", " + lastName + "-" + System.currentTimeMillis();
}
}


В этом примере вспомогательный конструктор Student(String id) используется для передачи одного и того же id в два аргумента конструктора суперкласса Person.

Java ≥ 23:


class Person {
private String id;
private String accountId;

public Person(String id, String accountId) {
this.id = id;
this.accountId = accountId;
}
}

class Student extends Person {
public Student(String firstName, String lastName) {
String id = generateStudentId(firstName, lastName);
super(id, id); // Передача одного и того же значения дважды
}

private static String generateStudentId(String firstName, String lastName) {
// Генерация уникального идентификатора студента
return firstName + ", " + lastName + "-" + System.currentTimeMillis();
}
}


Ставьте 🔥 если понравился пост и вы были бы рады видеть больше разборов JEP

#Java #JEP
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥40👍32
🗓 Еженедельный дайджест №5

Для тех, кто был слишком занят на неделе или просто пропустил некоторые посты, публикуем дайджест!

Переопределение и дополнение свойств для Spring Boot тестов – собрали в одном месте 6 различных вариантов переопределения свойств

7 основных способов оптимизировать кеширование в Spring Boot – узнали, что, где и как кэшировать, а что лучше не стоит

Самая любимая фича в Java 22 – освежили в памяти все фичи, которые вошли в 22-й релиз Java

Declarative Gradle: рывок или прорыв? – выяснили, что из себя представляет новый проект от Gradle, и попробовали его в действии

Новые возможности конструкторов в Java (часть 2) – продолжили изучать нововведения, связанные с конструкторами объектов

😌@spring_aio
Please open Telegram to view this post
VIEW IN TELEGRAM
11👍9🔥7
🦥 Spring Tips: Аннотация @Lazy

По умолчанию Spring инициализирует все sigleton-бины во время запуска приложения. Аннотация @Lazy позволяет изменить это поведение, инициализируя бины только по мере необходимости. Используя аннотацию @Lazy можно существенно снизить потребление памяти и уменьшить время запуска приложения, отметив бины, которые используются в приложении не так часто и занимают немало памяти.

Способы применения

Чтобы сделать бин лениво инициализируемым, нужно отметить его аннотацией @Lazy в месте его объявления:


@Lazy
@Component
public class MyComponent {
//...
}

@Configuration
@Lazy
public class LazyConfig {
// Все объявленные в этом конфиге бины будут ленивыми
}

@Configuration
public class AppConfig {
//Этот конкретный бин будет ленивым
@Bean
@Lazy
public MyBean myBean() {
return new MyBean();
}
}


А также в месте его инжекции:


@Service
public class MyService {
private final MyBean myBean;
private final MyComponent myComponent;

public MyService(@Lazy MyBean myBean, @Lazy MyComponent myComponent) {
this.myBean = myBean;
this.myComponent = myComponent;
}
}


Если не воспользоваться аннотацией @Lazy в месте объявления бина или в месте его инжекции, то он НЕ БУДЕТ ленивым.

Ленивая инициализация контекста по умолчанию

Чтобы сделать инициализацию ленивой для всего контекста, можно добавить следущее свойство в конфигурационный файл:


spring.main.lazy-initialization=true


Однако стоит быть осторожным с этим подходом. Ошибки инициализации могут возникнуть не во время запуска приложения, а позже – в рантайме, что усложнит их обнаружение и исправление (что в целом справедливо и при ленивой инициализации лишь некоторых бинов). Но если вы всё таки решите использовать глобальную ленивую инициализацию, можно исключить определённые бины, инициализируя их жадно (во время запуска приложения) с помощью @Lazy(false):


@Service
@Lazy(false)
public class MyService {
// код
}


А много ли бинов лениво инициализируется на вашем проекте? Может быть вообще все? Поделитесь своим опытом в комментариях, будет интересно почитать!

#SpringBoot #SpringTips
Please open Telegram to view this post
VIEW IN TELEGRAM
👍316🔥6
🖥 Scoped Values в Java (Часть 1)

Судя по реакциям на предыдущих постах про JEP, они вам нравятся, так что продолжаем разбирать свежые JEP'ы.

Представим себе небольшой веб-фреймворк, который обрабатывает HTTP-запросы. Фреймворк создает контекст для каждого запроса и передает его через методы:


@Override
public void handle(Request request, Response response) {
var userInfo = readUserInfo();
}

private UserInfo readUserInfo() {
return (UserInfo) framework.readKey("userInfo", context);
}


Ранее для передачи контекста использовались переменные типа ThreadLocal:


private final static ThreadLocal<FrameworkContext> CONTEXT = new ThreadLocal<>();

void serve(Request request, Response response) {
var context = createContext(request);
CONTEXT.set(context);
Application.handle(request, response);
}

public PersistedObject readKey(String key) {
var context = CONTEXT.get();
var db = getDBConnection(context);
db.readKey(key);
}


Scoped Values API впервые был представлен в JDK 20 и прошел несколько итераций и улучшений в последующих версиях JDK (JEP 429, JEP 446, JEP 464, JEP 481). Scoped Values представляют собой новый механизм для передачи неизменяемых данных между методами в одном потоке и между дочерними потоками. Этот механизм проще в использовании по сравнению с ThreadLocal и обладает меньшими затратами по времени и памяти.

У подхода, использующего ThreadLocal, есть несколько недостатков:

1. Неограниченная изменяемость — переменные могут изменяться в любое время любым кодом в потоке
2. Неограниченное время жизни — переменные могут существовать дольше, чем необходимо, что может приводить к утечкам памяти
3. Высокая стоимость наследования — при создании дочерних потоков переменные должны копироваться, что увеличивает затраты по памяти

Scoped Values позволяют избежать этих проблем, обеспечивая одноразовую запись и ограниченное время жизни значений:


final static ScopedValue<FrameworkContext> CONTEXT = ScopedValue.newInstance();

void serve(Request request, Response response) {
var context = createContext(request);
ScopedValue.runWhere(CONTEXT, context, () -> Application.handle(request, response));
}

public PersistedObject readKey(String key) {
var context = CONTEXT.get();
var db = getDBConnection(context);
db.readKey(key);
}


В данном случае метод ScopedValue.runWhere связывает значение с текущим потоком на время выполнения лямбда-выражения, после чего связь уничтожается, что улучшает производительность и безопасность кода.

В 23 версии Java фича будет пересмотрена повторно с одним изменением: тип параметра операции метода ScopedValue.callWhere будет являться новым функциональным интерфейсом, который позволит компилятору Java делать вывод о том, может ли быть выброшено проверяемое исключение. Подробнее про ScopedValue.callWhere и улучшения, связанные с ним, поговорим во второй части.

Ставьте 🔥 если хотите вторую часть!

#Java #JEP
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥59👍92
👩‍💻 Flyway + Spring Boot: настройка и написание миграций баз данных

Благодаря этой статье вы узнаете, как подключить и настроить Flyway в Spring Boot приложении, а также как сгенерировать скрипты инициализации и миграции схемы базы данных.

📚 Подробнее читайте на Хабре
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🔥62