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

Commit80f2a04

Browse files
Merge pull request#15 from Amplicode/cyclic-dependency
Циклические зависимости в Spring: разбираемся на практике
2 parentsdb462db +94024c5 commit80f2a04

File tree

100 files changed

+3796
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

100 files changed

+3796
-0
lines changed

‎cyclic-dependencies/README.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#Циклические зависимости в Spring: разбираемся на практике
2+
3+
[![](https://i3.ytimg.com/vi/g7mlFwTEbDU/maxresdefault.jpg)](http://www.youtube.com/watch?v=g7mlFwTEbDU)
4+
5+
Текущая директория содержит проект`cyclic-dependencies`, который был использован в видео.
6+
7+
Все действия производились в ветке`cyclic-dependencies`.
8+
9+
Первый коммит с которого можно начать повторять демо`92f8831dad9042ae6a068bea88e3bd5bb34bd2d9`.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/mvnwtexteol=lf
2+
*.cmdtexteol=crlf
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
HELP.md
2+
target/
3+
!.mvn/wrapper/maven-wrapper.jar
4+
!**/src/main/**/target/
5+
!**/src/test/**/target/
6+
7+
### STS ###
8+
.apt_generated
9+
.classpath
10+
.factorypath
11+
.project
12+
.settings
13+
.springBeans
14+
.sts4-cache
15+
16+
### IntelliJ IDEA ###
17+
.idea
18+
*.iws
19+
*.iml
20+
*.ipr
21+
22+
### NetBeans ###
23+
/nbproject/private/
24+
/nbbuild/
25+
/dist/
26+
/nbdist/
27+
/.nb-gradle/
28+
build/
29+
!**/src/main/**/build/
30+
!**/src/test/**/build/
31+
32+
### VS Code ###
33+
.vscode/
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
wrapperVersion=3.3.2
18+
distributionType=only-script
19+
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
FROM amazoncorretto:21-alpine3.18-jdk AS builder
2+
WORKDIR /application
3+
COPY . .
4+
RUN --mount=type=cache,target=/root/.m2 chmod +x mvnw && ./mvnw clean install -Dmaven.test.skip
5+
6+
FROM amazoncorretto:21-alpine3.18 AS layers
7+
WORKDIR /application
8+
COPY --from=builder /application/target/*.jar app.jar
9+
RUN java -Djarmode=layertools -jar app.jar extract
10+
11+
FROM amazoncorretto:21-alpine3.18
12+
VOLUME /tmp
13+
RUN adduser -S spring-user
14+
USER spring-user
15+
COPY --from=layers /application/dependencies/ ./
16+
COPY --from=layers /application/spring-boot-loader/ ./
17+
COPY --from=layers /application/snapshot-dependencies/ ./
18+
COPY --from=layers /application/application/ ./
19+
20+
ENV JAVA_ERROR_FILE_OPTS="-XX:ErrorFile=/tmp/java_error.log"
21+
ENV JAVA_HEAP_DUMP_OPTS="-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp"
22+
ENV JAVA_ON_OUT_OF_MEMORY_OPTS="-XX:+ExitOnOutOfMemoryError"
23+
ENV JAVA_NATIVE_MEMORY_TRACKING_OPTS="-XX:NativeMemoryTracking=summary -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics"
24+
25+
ENTRYPOINT java \
26+
$JAVA_HEAP_DUMP_OPTS \
27+
$JAVA_ON_OUT_OF_MEMORY_OPTS \
28+
$JAVA_ERROR_FILE_OPTS \
29+
$JAVA_NATIVE_MEMORY_TRACKING_OPTS \
30+
org.springframework.boot.loader.launch.JarLauncher
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Ignore everything by default
2+
*
3+
4+
# Include what we need
5+
!src
6+
!.mvn
7+
!pom.xml
8+
!mvnw
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
##Подготовка
2+
3+
1. Стартуем docker-compose
4+
2. Стартуем приложение
5+
3. Открываем[data.connekt.kts](data.connekt.kts)
6+
4. Запускаем весь файл
7+
8+
9+
##Демо
10+
11+
1. Показываем Эксплорер, показываем какие есть сущности и какие ресты
12+
2. Открываем ProductController. Генрируем запрос для findAll. Запускаем, говорим, как просто это выглядит. Не забываем env предварительно выбрать.
13+
3. Создаем запрос для create. Но делаем это теперь из дерева. Заполняем чем хотим. Но не запускаем.
14+
15+
На этом этапе имеем что-то такое:
16+
17+
```kotlin
18+
GET("http://localhost:8080/products")
19+
20+
POST("http://localhost:8080/products") {
21+
header("Content-Type","application/json")
22+
body(
23+
"""
24+
{
25+
"name": "Тыква",
26+
"price": 158.9
27+
}
28+
""".trimIndent()
29+
)
30+
}
31+
```
32+
33+
4. Простые запросы делаются просто, это хорошо. Может быть теперь сделаем тесты? Пишем для последнего запроса ассерты. Не обязательно, все. Запускаем.
34+
35+
```kotlin
36+
POST("http://localhost:8080/products") {
37+
header("Content-Type","application/json")
38+
body(
39+
"""
40+
{
41+
"name": "Тыква",
42+
"price": 158.9
43+
}
44+
""".trimIndent()
45+
)
46+
} then {
47+
assertThat(
48+
jsonPath().readString("$.name")
49+
).isEqualTo("Тыква")
50+
51+
assertThat(
52+
jsonPath().read("$.price",Double::class.java)
53+
).isEqualTo(158.9)
54+
55+
assertThat(
56+
jsonPath().readLong("$.id")
57+
).isNotNull()
58+
}
59+
```
60+
61+
5. Перейдем к задачам по-сложнее. Попробуем создать новый заказ. Генерируем запрос для`POST /orders/`. Чтобы выполнить, нам нужно указать cityId и customerId. Есть два способа, получить эти самые id. Можно посмотреть через DBeaver, можно выбрать любой.
62+
6. После заполнения, сохраняем значение в переменную. Для этого ее необходимо извлечь через jsonPath. Должно получиться так:
63+
```kotlin
64+
val orderId byPOST("http://localhost:8080/orders") {
65+
header("Content-Type","application/json")
66+
body(
67+
"""
68+
{
69+
"customerId": 1,
70+
"cityId": 1
71+
}
72+
""".trimIndent()
73+
)
74+
} then {
75+
jsonPath().readLong("$.id")
76+
}
77+
```
78+
Теперь мы можем работать с этой переменной! Начнем добавлять продукты в коризну.
79+
80+
7. Открываем[OrderController](src/main/java/ru/amplicode/orders/rest/OrderController.java), addProduct эндпоинт. Генерируем для него запрос.
81+
```kotlin
82+
POST("http://localhost:8080/orders/{orderId}/lines") {
83+
header("Content-Type","application/json")
84+
85+
pathParam("orderId", orderId)
86+
87+
body(
88+
"""
89+
{
90+
"productId": 5,
91+
"amount": 1
92+
}
93+
""".trimIndent()
94+
)
95+
}
96+
```
97+
Запускаем, но упадет409. На складе нет продуктов!
98+
99+
8. Смотрим на эндпоинт `POST/inventory/supply`. Можем даже сгенерить от него. НО. Нужно поставить конкретный продукт в конкретный город и так несколько раз для разных продуктов. Можно сразу все? Да, пишем useCase.
100+
```kotlin
101+
useCase {
102+
data classCity(valid:Long)
103+
val cities:List<City> byGET("$host/cities") then {
104+
assertThat(code).isEqualTo(200)
105+
106+
jsonPath().readList("$",City::class.java)
107+
}
108+
109+
val productIds:List<Long> byGET("$host/products") then {
110+
jsonPath().readList("$.content[*].id",Long::class.java)
111+
}
112+
113+
for (cityin cities) {
114+
for (productIdin productIds) {
115+
POST("$host/inventory/supply") {
116+
header("Content-Type","application/json")
117+
body(
118+
"""
119+
{
120+
"productId":$productId,
121+
"cityId":${city.id},
122+
"amount":${Random.nextInt(100,1000)}
123+
}
124+
""".trimIndent()
125+
)
126+
}
127+
}
128+
}
129+
}
130+
```
131+
132+
9. Кайф, все работает! Но представим, что мы это все задеплоили. Хочется проверить на стенде. Заменяем`http://localhost:8080/` на host, а host объявляем в самом начале вот так:
133+
```kotlin
134+
val host:String by env
135+
```
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<projectversion="4">
3+
<componentname="AmplicodeJpaPluginProjectSettings">
4+
<optionname="lastSelectedLanguage"value="Java" />
5+
</component>
6+
</project>
31.4 KB
Loading
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
services:
2+
postgres:
3+
image:'postgres:17.0'
4+
environment:
5+
-'POSTGRES_DB=orders'
6+
-'POSTGRES_PASSWORD=root'
7+
-'POSTGRES_USER=root'
8+
ports:
9+
-'5432:5432'
10+
kafka:
11+
image:confluentinc/cp-kafka:7.9.0
12+
restart:"no"
13+
ports:
14+
-"29092:29092"
15+
-"9092:9092"
16+
volumes:
17+
-kafka_data:/var/lib/kafka/data
18+
environment:
19+
KAFKA_ADVERTISED_LISTENERS:PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
20+
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP:PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT,CONTROLLER:PLAINTEXT
21+
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR:1
22+
KAFKA_INTER_BROKER_LISTENER_NAME:PLAINTEXT
23+
KAFKA_NODE_ID:1
24+
CLUSTER_ID:FvT61JwQRvmFVu-opRtigg
25+
KAFKA_PROCESS_ROLES:controller,broker
26+
KAFKA_CONTROLLER_QUORUM_VOTERS:1@kafka:9093
27+
KAFKA_CONTROLLER_LISTENER_NAMES:CONTROLLER
28+
KAFKA_LISTENERS:PLAINTEXT://kafka:29092,PLAINTEXT_HOST://0.0.0.0:9092,CONTROLLER://kafka:9093
29+
healthcheck:
30+
test:kafka-topics --bootstrap-server localhost:9092 --list
31+
interval:10s
32+
timeout:5s
33+
start_period:30s
34+
retries:5
35+
labels:
36+
amplicode.image:confluent/kafka
37+
kafkaui:
38+
image:provectuslabs/kafka-ui:v0.7.2
39+
restart:"no"
40+
ports:
41+
-"8989:8080"
42+
environment:
43+
DYNAMIC_CONFIG_ENABLED:"true"
44+
KAFKA_CLUSTERS_0_NAME:FvT61JwQRvmFVu-opRtigg
45+
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS:kafka:29092
46+
healthcheck:
47+
test:wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit -1
48+
interval:10s
49+
timeout:5s
50+
start_period:60s
51+
retries:5
52+
minio:
53+
image:minio/minio:latest
54+
container_name:minio
55+
environment:
56+
MINIO_ROOT_USER:minioadmin
57+
MINIO_ROOT_PASSWORD:minioadmin
58+
volumes:
59+
-minio_data:/data
60+
-minio_config:/root/.minio
61+
ports:
62+
-"9000:9000"# MinIO Web UI
63+
-"9001:9001"# MinIO Console
64+
command:server /data --console-address ":9001"
65+
volumes:
66+
kafka_data:
67+
minio_data:
68+
minio_config:

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp