Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Циклические зависимости в Spring: разбираемся на практике#15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
honest-niceman merged 3 commits intomainfromcyclic-dependency
Jul 5, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletionscyclic-dependencies/README.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
# Циклические зависимости в Spring: разбираемся на практике

[![](https://i3.ytimg.com/vi/g7mlFwTEbDU/maxresdefault.jpg)](http://www.youtube.com/watch?v=g7mlFwTEbDU)

Текущая директория содержит проект `cyclic-dependencies`, который был использован в видео.

Все действия производились в ветке `cyclic-dependencies`.

Первый коммит с которого можно начать повторять демо `92f8831dad9042ae6a068bea88e3bd5bb34bd2d9`.
2 changes: 2 additions & 0 deletionscyclic-dependencies/cyclic-dependencies/.gitattributes
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
/mvnwtexteol=lf
*.cmdtexteol=crlf
33 changes: 33 additions & 0 deletionscyclic-dependencies/cyclic-dependencies/.gitignore
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

### VS Code ###
.vscode/
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
wrapperVersion=3.3.2
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
30 changes: 30 additions & 0 deletionscyclic-dependencies/cyclic-dependencies/Dockerfile
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
FROM amazoncorretto:21-alpine3.18-jdk AS builder
WORKDIR /application
COPY . .
RUN --mount=type=cache,target=/root/.m2 chmod +x mvnw && ./mvnw clean install -Dmaven.test.skip

FROM amazoncorretto:21-alpine3.18 AS layers
WORKDIR /application
COPY --from=builder /application/target/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract

FROM amazoncorretto:21-alpine3.18
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/ ./

ENV JAVA_ERROR_FILE_OPTS="-XX:ErrorFile=/tmp/java_error.log"
ENV JAVA_HEAP_DUMP_OPTS="-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp"
ENV JAVA_ON_OUT_OF_MEMORY_OPTS="-XX:+ExitOnOutOfMemoryError"
ENV JAVA_NATIVE_MEMORY_TRACKING_OPTS="-XX:NativeMemoryTracking=summary -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics"

ENTRYPOINT java \
$JAVA_HEAP_DUMP_OPTS \
$JAVA_ON_OUT_OF_MEMORY_OPTS \
$JAVA_ERROR_FILE_OPTS \
$JAVA_NATIVE_MEMORY_TRACKING_OPTS \
org.springframework.boot.loader.launch.JarLauncher
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
# Ignore everything by default
*

# Include what we need
!src
!.mvn
!pom.xml
!mvnw
135 changes: 135 additions & 0 deletionscyclic-dependencies/cyclic-dependencies/README.MD
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
## Подготовка

1. Стартуем docker-compose
2. Стартуем приложение
3. Открываем [data.connekt.kts](data.connekt.kts)
4. Запускаем весь файл


## Демо

1. Показываем Эксплорер, показываем какие есть сущности и какие ресты
2. Открываем ProductController. Генрируем запрос для findAll. Запускаем, говорим, как просто это выглядит. Не забываем env предварительно выбрать.
3. Создаем запрос для create. Но делаем это теперь из дерева. Заполняем чем хотим. Но не запускаем.

На этом этапе имеем что-то такое:

```kotlin
GET("http://localhost:8080/products")

POST("http://localhost:8080/products") {
header("Content-Type", "application/json")
body(
"""
{
"name": "Тыква",
"price": 158.9
}
""".trimIndent()
)
}
```

4. Простые запросы делаются просто, это хорошо. Может быть теперь сделаем тесты? Пишем для последнего запроса ассерты. Не обязательно, все. Запускаем.

```kotlin
POST("http://localhost:8080/products") {
header("Content-Type", "application/json")
body(
"""
{
"name": "Тыква",
"price": 158.9
}
""".trimIndent()
)
} then {
assertThat(
jsonPath().readString("$.name")
).isEqualTo("Тыква")

assertThat(
jsonPath().read("$.price", Double::class.java)
).isEqualTo(158.9)

assertThat(
jsonPath().readLong("$.id")
).isNotNull()
}
```

5. Перейдем к задачам по-сложнее. Попробуем создать новый заказ. Генерируем запрос для `POST /orders/`. Чтобы выполнить, нам нужно указать cityId и customerId. Есть два способа, получить эти самые id. Можно посмотреть через DBeaver, можно выбрать любой.
6. После заполнения, сохраняем значение в переменную. Для этого ее необходимо извлечь через jsonPath. Должно получиться так:
```kotlin
val orderId by POST("http://localhost:8080/orders") {
header("Content-Type", "application/json")
body(
"""
{
"customerId": 1,
"cityId": 1
}
""".trimIndent()
)
} then {
jsonPath().readLong("$.id")
}
```
Теперь мы можем работать с этой переменной! Начнем добавлять продукты в коризну.

7. Открываем [OrderController](src/main/java/ru/amplicode/orders/rest/OrderController.java), addProduct эндпоинт. Генерируем для него запрос.
```kotlin
POST("http://localhost:8080/orders/{orderId}/lines") {
header("Content-Type", "application/json")

pathParam("orderId", orderId)

body(
"""
{
"productId": 5,
"amount": 1
}
""".trimIndent()
)
}
```
Запускаем, но упадет 409. На складе нет продуктов!

8. Смотрим на эндпоинт `POST /inventory/supply`. Можем даже сгенерить от него. НО. Нужно поставить конкретный продукт в конкретный город и так несколько раз для разных продуктов. Можно сразу все? Да, пишем useCase.
```kotlin
useCase {
data class City(val id: Long)
val cities: List<City> by GET("$host/cities") then {
assertThat(code).isEqualTo(200)

jsonPath().readList("$", City::class.java)
}

val productIds: List<Long> by GET("$host/products") then {
jsonPath().readList("$.content[*].id", Long::class.java)
}

for (city in cities) {
for (productId in productIds) {
POST("$host/inventory/supply") {
header("Content-Type", "application/json")
body(
"""
{
"productId": $productId,
"cityId": ${city.id},
"amount": ${Random.nextInt(100, 1000)}
}
""".trimIndent()
)
}
}
}
}
```

9. Кайф, все работает! Но представим, что мы это все задеплоили. Хочется проверить на стенде. Заменяем `http://localhost:8080/` на host, а host объявляем в самом начале вот так:
```kotlin
val host: String by env
```
6 changes: 6 additions & 0 deletionscyclic-dependencies/cyclic-dependencies/amplicode.xml
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AmplicodeJpaPluginProjectSettings">
<option name="lastSelectedLanguage" value="Java" />
</component>
</project>
View file
Open in desktop
Loading
Sorry, something went wrong.Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 68 additions & 0 deletionscyclic-dependencies/cyclic-dependencies/compose.yaml
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
services:
postgres:
image: 'postgres:17.0'
environment:
- 'POSTGRES_DB=orders'
- 'POSTGRES_PASSWORD=root'
- 'POSTGRES_USER=root'
ports:
- '5432:5432'
kafka:
image: confluentinc/cp-kafka:7.9.0
restart: "no"
ports:
- "29092:29092"
- "9092:9092"
volumes:
- kafka_data:/var/lib/kafka/data
environment:
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT,CONTROLLER:PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_NODE_ID: 1
CLUSTER_ID: FvT61JwQRvmFVu-opRtigg
KAFKA_PROCESS_ROLES: controller,broker
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://0.0.0.0:9092,CONTROLLER://kafka:9093
healthcheck:
test: kafka-topics --bootstrap-server localhost:9092 --list
interval: 10s
timeout: 5s
start_period: 30s
retries: 5
labels:
amplicode.image: confluent/kafka
kafkaui:
image: provectuslabs/kafka-ui:v0.7.2
restart: "no"
ports:
- "8989:8080"
environment:
DYNAMIC_CONFIG_ENABLED: "true"
KAFKA_CLUSTERS_0_NAME: FvT61JwQRvmFVu-opRtigg
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:29092
healthcheck:
test: wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit -1
interval: 10s
timeout: 5s
start_period: 60s
retries: 5
minio:
image: minio/minio:latest
container_name: minio
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
volumes:
- minio_data:/data
- minio_config:/root/.minio
ports:
- "9000:9000" # MinIO Web UI
- "9001:9001" # MinIO Console
command: server /data --console-address ":9001"
volumes:
kafka_data:
minio_data:
minio_config:
11 changes: 11 additions & 0 deletionscyclic-dependencies/cyclic-dependencies/connekt.env.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
{
"dev": {
"host": "http://localhost:8080"
},
"rancher": {
"host": "http://orders.127.0.0.1.sslip.io"
},
"prod": {
"host": "http://158.160.48.236:8080"
}
}
Loading

[8]ページ先頭

©2009-2025 Movatter.jp