Spring АйО – Telegram
Spring АйО
9.58K subscribers
376 photos
274 videos
492 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
Классная новость для всех Spring Boot разработчиков! Spring Academy дает бесплатный доступ ко всем своим обучающим материалам. 9 курсов и 12 гайдов по Spring Boot.

P.S. Есть сложности в регистрации из РФ.

🔗 https://spring.academy/

Подписывайтесь:
😌@spring_aio
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10👏3🤩2👍1
🐳 Идеальный Dockerfile для Spring Boot приложения

Контейнеризовать Spring Boot приложение можно так:

FROM gradle:8.5.0-jdk21
WORKDIR /
COPY / .
RUN ./gradlew installDist
CMD ./build/install/app/bin/app


И так:

FROM bellsoft/liberica-openjdk-alpine:21 AS builder
WORKDIR /application
COPY . .
RUN --mount=type=cache,target=/root/.gradle chmod +x gradlew && ./gradlew clean build -x test

FROM bellsoft/liberica-openjre-alpine:21 AS layers
WORKDIR /application
COPY --from=builder /application/build/libs/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract

FROM bellsoft/liberica-openjre-alpine:21
VOLUME /tmp
RUN adduser -S spring-user
USER spring-user
COPY --from=layers /application/dependencies/ ./
COPY --from=layers /application/spring-boot-loader/ ./
COPY --from=layers /application/snapshot-dependencies/ ./
COPY --from=layers /application/application/ ./

ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]


Второй вариант с одной стороны намного сложнее реализовать, но с другой стороны он даёт множество преимуществ, среди которых можно отметить:

меньший размер финального образа (из-за исключения инструментов сборки и использования JRE вместо JDK);
🛡️ более безопасный подход к работе с файловой системой (за счёт создания и использования пользователя с ограниченными правами);
🚀 ускоренный процесс сборки (благодаря кэшированию зависимостей).

🔗 Подробнее о том, как написать идеальный Dockerfile тут: https://spring.io/guides/topicals/spring-boot-docker

Подписывайтесь:
😌@spring_aio
Please open Telegram to view this post
VIEW IN TELEGRAM
👍144🔥4
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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍113🔥3
This media is not supported in your browser
VIEW IN TELEGRAM
Не то чтобы, нас сильно интересовал Python, но анимация очень интересная.

Жизненный цикл кода от IDE до рабочей программы.
👍8🔥4👌3
В Kotlin 2.1 анонсировали новую интересную функцию под названием "when guards", которая расширяет возможности конструкции when. Guard'ы позволяют добавлять дополнительные проверки с помощью ключевого слова if.


fun printAnimalInfo(animal: Animal) {
when (animal) {
is Animal.Dog if animal.isFriendly -> {
println("This is a friendly dog named ${animal.name}.")
}
is Animal.Dog -> {
println("This is a dog named ${animal.name}.")
}
is Animal.Cat if animal.isLazy -> {
println("This is a lazy cat named ${animal.name}.")
}
is Animal.Cat -> {
println("This is a cat named ${animal.name}.")
}
}
}

sealed class Animal {
class Dog(val name: String, val isFriendly: Boolean) : Animal()
class Cat(val name: String, val isLazy: Boolean) : Animal()
}


Что интересно, это, кажется, первая фича языка, которую Kotlin взял из Java.


record Point(int x, int y) {
}

class Cartesian {
static void printQuadrant(Point p) {
switch (p) {
case Point(var x, var y) when x > 0 && y > 0 -> {
System.out.println("first");
}
default ->
System.out.println("other");
}
}
}


Какой вариант вам кажется более предпочтительным?

👍 - Kotlin, ❤️ - Java
19👍10
🕐 Тонкое управление Scheduled задачами в Spring

Какими способами можно настроить время и условие запуска 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
👍10👌5🔥3
🎉 Встречайте нашу дебютную статью на 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
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥21👍5👏4
🌟 Статическая и динамическая конфигурация в Spring Boot: что выбрать?

В мире облачных сервисов правильная настройка конфигурации играет ключевую роль. В этой статье рассматриваются два подхода: статическая и динамическая конфигурация.

🔧 Статическая конфигурация удобна предсказуемостью и простотой развертывания. Пример на 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
🔥113👍2👏2
EqualsVerifier отмечает 15-летие 🥳

Переопределение методов equals() и hashCode() – непростая задача. Они должны соответствовать не только требованиям Java, но и используемому стеку технологий (например, JPA).

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

Jan Ouwens, создатель библиотеки, делится своим опытом и планами в новой статье.

Узнайте, какие проблемы решались разработчиком по мере развития Java, что было популярно до GitHub, и какие планы у Jan на будущее: https://jqno.nl/post/2024/06/01/looking-back-on-15-years-of-equalsverifier/

Подписывайтесь:
😌@spring_aio
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥53
👩‍💻 Java-рантаймы с точки зрения Spring Boot 👩‍💻

На первый взгляд, выбор правильного 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
👍7👏3🔥2
🐢 Неожиданное падение производительности при переходе на Spring Boot 3.3.0 👩‍💻

Hibernate подложил небольшую свинью, конвертируя JPQL в SQL. Внезапно, запрос поиска записей по списку id с передачей пустого списка деградирует в table full scan.

Однако, Hibernate пофиксил баг в течении 10 часов после поста в запрещенную в РФ соцсеть. Осталось дождаться новой версии Spring Boot, которая включит себе этот фикс.

🔗Подробнее читайте на Хабре

#SpringBoot #Hibernate #BreakingNews
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯14😱5👍3🔥2😁2
🎙 Spring АйО Чат!

Друзья, напоминаем, что у нас есть чат (@spring_aio_chat), в котором вы можете:

💬 Делиться фидбеком о публикуемых материалах;
Задавать вопросы, связанные со Spring и сопутствующими технологиями;
😼 Отвечать на вопросы, если Вы знаете на них ответ.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍4🌚2
Неблокирующие сервисы вчера: WebFlux, Coroutines и Loom.
"Неблокирующие сервисы" сегодня: DockerHub, IntelliJ IDEA Ultimate, Slack и другие.
#программисты_шутят
😁263🔥2💩2👏1
Dynamic Projection в Spring Data JPA

Используя Spring Data, довольно часто могут возникать накладные расходы из-за получения из базы слишком большого количества полей. Чтобы решить эту проблему, мы можем объявлять методы с нативными запросами, DTO или проекциями для случаев, когда необходимо ограничить количество выбираемых полей. Но такой подход приводит к появлению множества очень похожих методов в репозитории. Например:


public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findByName(String name); // выбираем из базы сущность со всеми её полями

List<ProductProjection> findPrjsByName(String name); //выбираем из базы ограниченный набор полей, указанный в некоторой проекции

List<ProductDto> findDtosByName(String name); //выбираем из базы ограниченный набор полей, указанный в некоторой DTO
}


Чтобы решить эту проблему можно прибегнуть к использованию Dynamic Projection:


public interface ProductRepository extends JpaRepository<Product, Long> {
<T> List<T> findByName(String name, Class<T> type);
}


Подробнее о сценариях использования и компромиссах, на которые придется пойти используя Dynamic Projection, читайте в следующей статье: https://maciejwalkowiak.com/blog/spring-data-jpa-dynamic-projections/
🔥15👍11👌3
Мы рады сообщить, что наше сообщество начинает обрастать экспертами, и мы будем знакомить вас с их мнением. Встречайте, Михаил Поливаха, коммитер Spring Data JDBC (Relational), не удержался от комментария по вопросу поддержки проекций в разных модулях Spring Data. Ниже прямая речь Михаила.

Надо сказать, что projection-ы, как функциональность, должна быть доступна во всех модулях spring-data. Вопрос в том, что еще не все модули ее поддержку реализовали до конца.
Наивный читатель мог бы предположить, что поскольку в spring-data-jdbc есть поддержка как interface based projection-ов, так и dynamic projection-ов, то вот это заработает:


public interface MyEntityRepository extends CrudRepository<MyEntity, Long> {

<T> Optional<T> findProjectionByStatus(String status, Class<T> clazz);
}

@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Accessors(chain = true)
@Table
public class MyEntity {

@Id
@EqualsAndHashCode.Include
private Long id;

private String status;

@CreatedDate
private Instant createdAt;
}

interface MyEntityInterfaceProjection {
String status();

Long id();
}

@Test
void whenDynamicProjectsAreInUseViaInterfaces_thenBlast() {
saveInitialEntity();
Optional<MyEntityInterfaceProjection> active = myEntityRepository.findProjectionByStatus("active", MyEntityInterfaceProjection.class);

Assertions.assertThat(active)
.isPresent()
.hasValueSatisfying(myEntityProjection -> {
Assertions.assertThat(myEntityProjection.id() != null && myEntityProjection.status().equalsIgnoreCase("active")).isTrue();
});
}

@NotNull
private MyEntity saveInitialEntity() {
MyEntity myEntity = new MyEntity();
myEntity.setStatus("active");
return myEntityRepository.save(myEntity);
}


Но данный код сгенерит ошибку глубоко в недрах JdbcQueryCreator-a:


java.lang.IllegalStateException: SELECT does not declare a select list
at org.springframework.data.relational.core.sql.SelectValidator.doValidate(SelectValidator.java:57)
at org.springframework.data.relational.core.sql.SelectValidator.validate(SelectValidator.java:49)
at org.springframework.data.relational.core.sql.DefaultSelectBuilder.build(DefaultSelectBuilder.java:207)
at org.springframework.data.jdbc.repository.query.JdbcQueryCreator.complete(JdbcQueryCreator.java:173)
at org.springframework.data.jdbc.repository.query.JdbcQueryCreator.complete(JdbcQueryCreator.java:66)


Связано это с тем, что на данный момент spring-data-jdbc просто не умеет переваривать динамические проджекшены для инфтерфейсов 🙂
Про это мы знаем, бага на это дело пока нет, если он кому-либо важен - можем завести. Хочу также напомнить, что если есть какие-либо инициативы для нововведений в spring-data-jdbc - можно оставлять в комментариях или писать мне в ЛС.
🔥19👍8👏3
⚠️ Уязвимость в плагине JetBrains GitHub

Если вы используете плагин GitHub в IntelliJ IDEA или других продуктах JetBrains, советуем обновиться до последней версии и отозвать токены созданные IDEA в своем GitHub аккаунте.

Подробности в статье.

🔗 https://habr.com/ru/companies/spring_aio/articles/820819/
😱12🤔54😐21
🔧 Spring Data findAll антипаттерн

Spring Data Repository отличная концепция, позволяющая нам абстрагироваться от CRUD операций над domain entity. Достаточно объявить пустой интерфейс и унаследовать его, например, от ListCrudRepository или JpaRepository, если мы работем с JPA.


public interface OwnerRepository extends JpaRepository<Owner, Long> {
}


После этого нам сразу будут доступны основные методы работы с entity - save, update, findById, findAllById, findAll, и т.д. Но некоторые из этих методов могут привести к серъезным проблемам с производительностью и памятью, например метод findAll(). Давайте представим, что нам нужно найти всех Owner у которых есть животные c определенным именем и отсортировать по имени владельца. Поскольку мы находимся всего в одном шаге от вызова findAll() метода для «решения» этой проблемы, не пройдет много времени, пока кто-нибудь не предложит следующее решение:



public List<Owner> findOwnerByPetName(Collection<String> petNames) {
return ownerRepository.findAll()
.stream()
.filter(owner -> owner.getPets()
.stream()
.map(Pet::getName)
.anyMatch(petNames::contains)
)
.sorted(Comparator.comparing(Owner::getFirstName))
.toList();
}


Проблема здесь в том, что мы загрузили всю таблицу Post в память и потом начали фильтровать и сортировать данные, вместо того чтобы сделать это с помощью JPQL или SQL запроса за одно обращение к БД и загрузить в память только те данные, которые нам нужны в дальнейшем. Давайте посмотрим как мог бы выглядеть такой метод репозитория:


//derived method
List<Owner> findByPets_NameInOrderByFirstNameAsc(Collection<String> petNames);

//JPA query method
@Query("select o from Owner o left join o.pets pets where pets.name in ?1 order by o.firstName")
List<Owner> findOwnerByPetName(Collection<String> petNames);



Если у нас в репозитории есть метод findAll(), мы должны понимать что рано или поздно, по мере роста команды, им может кто-то воспользоваться. Возможно следует определять базовый интерфейс репозитория вашем проекте самостоятельно и наследовать его от org.springframework.data.repository.Repository и подконтрольно наполнять его методами.

#SpringData #SpringTips
👍235👀3👎2😴2
🤣20😁7👍2🤔1
🕓 Hibernate и спецификация JPA: приключение на 20 минут 24 часа

На прошлой неделе у нас был пост про внезапное падение производительности в Hibernate 6.5 на достаточно простом запросе. А сегодня вы можете прочитать историю этой проблемы от лица Гевина Кинга, создателя Hibernate.

Гевин Кинг рассказал, как возник этот баг, и как он повлиял на спецификацию JPA 3.2.

📚 Подробней читайте на Хабре
👍13🔥4🤯3👏1
По умолчанию в IntelliJ IDEA работают следующие комбинации клавиш:

1) ⌥ + ← (CTRL + ←) – передвинуть каретку к началу слова
2) ⌥ + → (CTRL + →) – передвинуть каретку к концу слова
3) ⇧ + ⌥ + ← (⇧ + CTRL + ←) – выделить слово до его начала
4) ⇧ + ⌥ + → (⇧ + CTRL + →) – выделить слово до его конца
5) ⌥ + ⌫ (CTRL + ⌫) – удалить слово с текущей позиции каретки и до его начала

Но мало кто знает, что эти шорткаты можно сделать еще удобнее!

Настройте их же для режима CamelHumps и IntelliJ IDEA начнёт понимать слова написанные в camelCase!

#IntelliJIDEA #Tooling #HotKeys
This media is not supported in your browser
VIEW IN TELEGRAM
👍187🔥3👏2🥱2🤨1
Разработчики Каркаса Весеннего Ботинка утверждают, что эти фразы имеют смысл:

- "У меня не применяется совет контроллера"
- "Тебе нужно выстрелить событием"
- "Я сделал этот боб главным, чтобы мои анализы не провалились"

My controller advice did not apply
You need to fire an event
I made this bean primary in order to not to fail my tests


Они держат нас за дураков?
😁36👍1👎1🔥1